@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.
- package/children-highlighter/use-children-highlighter.tsx +5 -3
- package/component-highlighter.docs.md +3 -14
- package/dist/children-highlighter/use-children-highlighter.d.ts +7 -2
- package/dist/children-highlighter/use-children-highlighter.js.map +1 -1
- package/dist/component-highlighter.docs.md +3 -14
- package/dist/element-highlighter/element-highlighter.compositions.d.ts +2 -0
- package/dist/element-highlighter/element-highlighter.compositions.js +33 -17
- package/dist/element-highlighter/element-highlighter.compositions.js.map +1 -1
- package/dist/element-highlighter/element-highlighter.d.ts +6 -10
- package/dist/element-highlighter/element-highlighter.js +4 -4
- package/dist/element-highlighter/element-highlighter.js.map +1 -1
- package/dist/element-highlighter/index.d.ts +1 -1
- package/dist/frame/frame.d.ts +8 -4
- package/dist/frame/frame.js +69 -31
- package/dist/frame/frame.js.map +1 -1
- package/dist/frame/frame.module.scss +8 -1
- package/dist/hover-highlighter/bubble-to-component.d.ts +19 -0
- package/dist/hover-highlighter/bubble-to-component.js +43 -0
- package/dist/hover-highlighter/bubble-to-component.js.map +1 -0
- package/dist/hover-highlighter/bubble-to-component.spec.d.ts +1 -0
- package/dist/hover-highlighter/bubble-to-component.spec.js +38 -0
- package/dist/hover-highlighter/bubble-to-component.spec.js.map +1 -0
- package/dist/hover-highlighter/use-hover-highlighter.d.ts +6 -1
- package/dist/hover-highlighter/use-hover-highlighter.js +2 -23
- package/dist/hover-highlighter/use-hover-highlighter.js.map +1 -1
- package/dist/hybrid-highlighter/hybrid-highlighter.js +1 -1
- package/dist/hybrid-highlighter/hybrid-highlighter.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/label/component-strip.d.ts +1 -1
- package/dist/label/component-strip.js +7 -1
- package/dist/label/component-strip.js.map +1 -1
- package/dist/label/label-container.d.ts +4 -5
- package/dist/label/label-container.js +29 -13
- package/dist/label/label-container.js.map +1 -1
- package/dist/label/label.d.ts +1 -1
- package/dist/label/label.module.scss +8 -0
- package/dist/label/other-components.d.ts +1 -1
- package/dist/mock-component.d.ts +2 -0
- package/dist/mock-component.js +30 -0
- package/dist/mock-component.js.map +1 -0
- package/element-highlighter/element-highlighter.compositions.tsx +53 -19
- package/element-highlighter/element-highlighter.tsx +13 -25
- package/element-highlighter/index.ts +1 -1
- package/frame/frame.module.scss +8 -1
- package/frame/frame.tsx +92 -44
- package/hover-highlighter/bubble-to-component.spec.tsx +57 -0
- package/hover-highlighter/bubble-to-component.tsx +69 -0
- package/hover-highlighter/use-hover-highlighter.tsx +5 -36
- package/hybrid-highlighter/hybrid-highlighter.tsx +7 -2
- package/index.ts +1 -1
- package/label/component-strip.tsx +11 -2
- package/label/label-container.tsx +42 -20
- package/label/label.module.scss +8 -0
- package/label/label.tsx +1 -1
- package/label/other-components.tsx +1 -1
- package/mock-component.tsx +9 -0
- package/package-tar/teambit-react.ui.component-highlighter-0.0.514.tgz +0 -0
- package/package.json +9 -10
- package/{preview-1647509820722.js → preview-1649066106369.js} +2 -2
- package/dist/use-animation-frame.d.ts +0 -1
- package/dist/use-animation-frame.js +0 -23
- package/dist/use-animation-frame.js.map +0 -1
- package/package-tar/teambit-react.ui.component-highlighter-0.0.511.tgz +0 -0
- package/use-animation-frame.tsx +0 -20
package/frame/frame.tsx
CHANGED
|
@@ -1,64 +1,112 @@
|
|
|
1
|
-
import 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 '@
|
|
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
|
|
7
|
-
import { resizeToMatchReference } from '@teambit/base-ui.utils.popper-js.resize-to-match-reference';
|
|
6
|
+
import styles from './frame.module.scss';
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
const isReady = x !== null;
|
|
55
97
|
|
|
56
98
|
return (
|
|
57
99
|
<div
|
|
58
|
-
ref={
|
|
59
|
-
className={classnames(className, stylesClass)}
|
|
60
|
-
style={{
|
|
61
|
-
|
|
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 {
|
|
13
|
-
import {
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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, {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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
|
-
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
reference(targetRef.current);
|
|
47
|
+
}, [targetRef.current, reference]);
|
|
42
48
|
|
|
43
|
-
|
|
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
|
-
|
|
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
|
}
|
package/label/label.module.scss
CHANGED
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
|
+
};
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teambit/react.ui.component-highlighter",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
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.
|
|
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.
|
|
2
|
-
export const overview = [require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.
|
|
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"}
|
|
Binary file
|