@eccenca/gui-elements 24.2.0-rc.1 → 24.2.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/CHANGELOG.md +56 -0
- package/README.md +10 -7
- package/dist/cjs/cmem/markdown/Markdown.js +13 -11
- package/dist/cjs/cmem/markdown/Markdown.js.map +1 -1
- package/dist/cjs/cmem/markdown/highlightSearchWords.js +6 -1
- package/dist/cjs/cmem/markdown/highlightSearchWords.js.map +1 -1
- package/dist/cjs/common/Intent/index.js +4 -11
- package/dist/cjs/common/Intent/index.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +35 -24
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/cjs/components/Button/Button.js +2 -2
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextMenu.js +3 -2
- package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js +46 -2
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/cjs/components/Form/FieldItem.js +3 -2
- package/dist/cjs/components/Form/FieldItem.js.map +1 -1
- package/dist/cjs/components/Form/FieldSet.js +3 -2
- package/dist/cjs/components/Form/FieldSet.js.map +1 -1
- package/dist/cjs/components/Icon/canonicalIconNames.js +1 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js +18 -14
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/cjs/components/Notification/Notification.js +7 -4
- package/dist/cjs/components/Notification/Notification.js.map +1 -1
- package/dist/cjs/components/OverviewItem/OverviewItemActions.js +9 -2
- package/dist/cjs/components/OverviewItem/OverviewItemActions.js.map +1 -1
- package/dist/cjs/components/Spinner/Spinner.js +7 -5
- package/dist/cjs/components/Spinner/Spinner.js.map +1 -1
- package/dist/cjs/components/Tooltip/Tooltip.js +75 -7
- package/dist/cjs/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/esm/cmem/markdown/Markdown.js +13 -11
- package/dist/esm/cmem/markdown/Markdown.js.map +1 -1
- package/dist/esm/cmem/markdown/highlightSearchWords.js +6 -1
- package/dist/esm/cmem/markdown/highlightSearchWords.js.map +1 -1
- package/dist/esm/common/Intent/index.js +14 -10
- package/dist/esm/common/Intent/index.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +40 -31
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/esm/components/Button/Button.js +2 -2
- package/dist/esm/components/Button/Button.js.map +1 -1
- package/dist/esm/components/ContextOverlay/ContextMenu.js +3 -2
- package/dist/esm/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/esm/components/ContextOverlay/ContextOverlay.js +63 -3
- package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/esm/components/Form/FieldItem.js +3 -2
- package/dist/esm/components/Form/FieldItem.js.map +1 -1
- package/dist/esm/components/Form/FieldSet.js +3 -2
- package/dist/esm/components/Form/FieldSet.js.map +1 -1
- package/dist/esm/components/Icon/canonicalIconNames.js +1 -0
- package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/esm/components/MultiSelect/MultiSelect.js +18 -14
- package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/Notification/Notification.js +7 -4
- package/dist/esm/components/Notification/Notification.js.map +1 -1
- package/dist/esm/components/OverviewItem/OverviewItemActions.js +25 -2
- package/dist/esm/components/OverviewItem/OverviewItemActions.js.map +1 -1
- package/dist/esm/components/Spinner/Spinner.js +9 -5
- package/dist/esm/components/Spinner/Spinner.js.map +1 -1
- package/dist/esm/components/Tooltip/Tooltip.js +92 -8
- package/dist/esm/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/types/cmem/markdown/Markdown.d.ts +8 -1
- package/dist/types/common/Intent/index.d.ts +10 -1
- package/dist/types/components/Button/Button.d.ts +5 -1
- package/dist/types/components/ContextOverlay/ContextMenu.d.ts +9 -2
- package/dist/types/components/ContextOverlay/ContextOverlay.d.ts +6 -1
- package/dist/types/components/Form/FieldItem.d.ts +10 -1
- package/dist/types/components/Form/FieldSet.d.ts +10 -1
- package/dist/types/components/Icon/canonicalIconNames.d.ts +1 -0
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +12 -4
- package/dist/types/components/Notification/Notification.d.ts +10 -1
- package/dist/types/components/OverviewItem/OverviewItemActions.d.ts +13 -1
- package/dist/types/components/Spinner/Spinner.d.ts +8 -3
- package/dist/types/components/Table/TableContainer.d.ts +2 -2
- package/dist/types/components/Table/TableExpandRow.d.ts +1 -1
- package/dist/types/components/Table/index.d.ts +1 -0
- package/dist/types/components/Tooltip/Tooltip.d.ts +9 -1
- package/package.json +10 -7
- package/src/cmem/markdown/Markdown.tsx +25 -14
- package/src/cmem/markdown/highlightSearchWords.test.ts +8 -2
- package/src/cmem/markdown/highlightSearchWords.ts +6 -1
- package/src/common/Intent/index.ts +6 -6
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +43 -33
- package/src/components/Button/Button.stories.tsx +10 -6
- package/src/components/Button/Button.tsx +7 -2
- package/src/components/ContextOverlay/ContextMenu.stories.tsx +1 -1
- package/src/components/ContextOverlay/ContextMenu.tsx +26 -13
- package/src/components/ContextOverlay/ContextOverlay.tsx +83 -5
- package/src/components/Form/FieldItem.tsx +14 -3
- package/src/components/Form/FieldSet.tsx +13 -2
- package/src/components/Form/Stories/FieldItem.stories.tsx +4 -0
- package/src/components/Form/Stories/FieldSet.stories.tsx +4 -0
- package/src/components/Icon/canonicalIconNames.tsx +1 -0
- package/src/components/MultiSelect/MultiSelect.tsx +27 -15
- package/src/components/MultiSuggestField/MultiSuggestField.stories.tsx +6 -0
- package/src/components/Notification/Notification.stories.tsx +4 -0
- package/src/components/Notification/Notification.tsx +17 -4
- package/src/components/OverviewItem/OverviewItemActions.tsx +24 -1
- package/src/components/OverviewItem/stories/OverviewItemList.stories.tsx +2 -7
- package/src/components/OverviewItem/stories/OverviewItemListPerformance.tsx +174 -0
- package/src/components/OverviewItem/stories/OverviewItemPerformance.stories.tsx +19 -0
- package/src/components/Spinner/Spinner.tsx +13 -5
- package/src/components/Spinner/Stories/spinner.stories.tsx +6 -1
- package/src/components/Table/TableContainer.tsx +2 -2
- package/src/components/Table/TableExpandRow.tsx +1 -1
- package/src/components/Table/index.tsx +1 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +3 -2
- package/src/components/Tooltip/Tooltip.tsx +121 -10
|
@@ -7,6 +7,18 @@ export interface OverviewItemActionsProps extends React.HTMLAttributes<HTMLDivEl
|
|
|
7
7
|
* Display it only when the parent `OverviewItem` is hovered or focused.
|
|
8
8
|
*/
|
|
9
9
|
hiddenInteractions?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Delay the rendering of the children by a time in milliseconds.
|
|
12
|
+
* Could be used to prevent browser freezes for the initial `OverviewItem` rendering.
|
|
13
|
+
* In general, it is better to fix the cause, i.e. action elements that are expensive to initialize/render should be
|
|
14
|
+
* optimized or replaced etc. This workaround only prevents the browser from getting blocked completely and does NOT
|
|
15
|
+
* solve the actual performance issue.
|
|
16
|
+
*/
|
|
17
|
+
delayDisplayChildren?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Display element while the rendering of the actual children is delayed.
|
|
20
|
+
*/
|
|
21
|
+
delaySkeleton?: JSX.Element;
|
|
10
22
|
}
|
|
11
23
|
|
|
12
24
|
/**
|
|
@@ -17,8 +29,19 @@ export const OverviewItemActions = ({
|
|
|
17
29
|
children,
|
|
18
30
|
className = "",
|
|
19
31
|
hiddenInteractions = false,
|
|
32
|
+
delayDisplayChildren = 0,
|
|
33
|
+
delaySkeleton = <></>,
|
|
20
34
|
...restProps
|
|
21
35
|
}: OverviewItemActionsProps) => {
|
|
36
|
+
const [showActions, setShowActions] = React.useState(!(delayDisplayChildren > 0));
|
|
37
|
+
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
// Delay rendering of item actions when they are hidden anyways, because rendering interaction elements like context menus currently has a large performance impact.
|
|
40
|
+
if (!showActions && delayDisplayChildren > 0) {
|
|
41
|
+
setTimeout(() => setShowActions(true), delayDisplayChildren);
|
|
42
|
+
}
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
22
45
|
return (
|
|
23
46
|
<div
|
|
24
47
|
{...restProps}
|
|
@@ -28,7 +51,7 @@ export const OverviewItemActions = ({
|
|
|
28
51
|
(className ? ` ${className}` : "")
|
|
29
52
|
}
|
|
30
53
|
>
|
|
31
|
-
{children}
|
|
54
|
+
{showActions ? children : delaySkeleton}
|
|
32
55
|
</div>
|
|
33
56
|
);
|
|
34
57
|
};
|
|
@@ -12,7 +12,7 @@ export default {
|
|
|
12
12
|
},
|
|
13
13
|
argTypes: {
|
|
14
14
|
children: {
|
|
15
|
-
control:
|
|
15
|
+
control: false,
|
|
16
16
|
description: "Should contain only `OverviewItem` elements, maybe wrapped inside cards.",
|
|
17
17
|
},
|
|
18
18
|
},
|
|
@@ -26,10 +26,5 @@ ItemList.args = {
|
|
|
26
26
|
hasDivider: true,
|
|
27
27
|
densityHigh: false,
|
|
28
28
|
columns: 1,
|
|
29
|
-
children:
|
|
30
|
-
<OverviewItem {...ItemExample.args} />,
|
|
31
|
-
<OverviewItem {...ItemExample.args} />,
|
|
32
|
-
<OverviewItem {...ItemExample.args} />,
|
|
33
|
-
<OverviewItem {...ItemExample.args} />,
|
|
34
|
-
],
|
|
29
|
+
children: Array(4).fill(<OverviewItem {...ItemExample.args} />),
|
|
35
30
|
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { loremIpsum } from "react-lorem-ipsum";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ApplicationContainer,
|
|
6
|
+
Button,
|
|
7
|
+
ContextMenu,
|
|
8
|
+
Depiction,
|
|
9
|
+
Icon,
|
|
10
|
+
IconButton,
|
|
11
|
+
OverflowText,
|
|
12
|
+
OverviewItem,
|
|
13
|
+
OverviewItemActions,
|
|
14
|
+
OverviewItemDescription,
|
|
15
|
+
OverviewItemLine,
|
|
16
|
+
OverviewItemList,
|
|
17
|
+
Spinner,
|
|
18
|
+
Tooltip,
|
|
19
|
+
} from "./../../../../index";
|
|
20
|
+
import { Default as ContextMenuExample } from "./../../ContextOverlay/ContextMenu.stories";
|
|
21
|
+
import canonicalIcons, { ValidIconName } from "./../../Icon/canonicalIconNames";
|
|
22
|
+
|
|
23
|
+
interface OverviewItemListPerformanceProps {
|
|
24
|
+
/** list length */
|
|
25
|
+
length: number;
|
|
26
|
+
/** include `OverviewItem` elements in list */
|
|
27
|
+
useOverviewitem: boolean;
|
|
28
|
+
/** include depiction */
|
|
29
|
+
withDepiction: boolean;
|
|
30
|
+
/** include description */
|
|
31
|
+
withDescription: boolean;
|
|
32
|
+
/** include icon button in hidden actions */
|
|
33
|
+
withIconButtonInHiddenActions: boolean;
|
|
34
|
+
/** include button in actions */
|
|
35
|
+
withButtonInActions: boolean;
|
|
36
|
+
/** inlcude context menu in actions */
|
|
37
|
+
withContextMenuInActions: boolean;
|
|
38
|
+
/** include tooltips on all elments that can have one */
|
|
39
|
+
withTooltips: boolean;
|
|
40
|
+
/** delay rendering of action items */
|
|
41
|
+
delayActions: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const createTextArray = (items: number, length: number) => {
|
|
45
|
+
return loremIpsum({
|
|
46
|
+
p: 1,
|
|
47
|
+
avgWordsPerSentence: length,
|
|
48
|
+
avgSentencesPerParagraph: items,
|
|
49
|
+
startWithLoremIpsum: false,
|
|
50
|
+
random: false,
|
|
51
|
+
})[0].split(". ");
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const textShort = createTextArray(100, 3);
|
|
55
|
+
const textLong = createTextArray(100, 25);
|
|
56
|
+
|
|
57
|
+
export const OverviewItemListPerformance = ({
|
|
58
|
+
length = 500,
|
|
59
|
+
useOverviewitem = false,
|
|
60
|
+
withDepiction = false,
|
|
61
|
+
withDescription = true,
|
|
62
|
+
withButtonInActions = false,
|
|
63
|
+
withIconButtonInHiddenActions = false,
|
|
64
|
+
withTooltips = false,
|
|
65
|
+
withContextMenuInActions = false,
|
|
66
|
+
delayActions = 1,
|
|
67
|
+
}: OverviewItemListPerformanceProps) => {
|
|
68
|
+
const renderStart = new Date();
|
|
69
|
+
const containerRef = React.useRef(null);
|
|
70
|
+
const observerRef = React.useRef<MutationObserver | undefined>(undefined);
|
|
71
|
+
|
|
72
|
+
const iconNames = Object.keys(canonicalIcons);
|
|
73
|
+
|
|
74
|
+
const ItemWrapper = useOverviewitem ? OverviewItem : "div";
|
|
75
|
+
const ItemDescription = useOverviewitem ? OverviewItemDescription : "div";
|
|
76
|
+
const ItemLine = useOverviewitem ? OverviewItemLine : "div";
|
|
77
|
+
const ItemActions = useOverviewitem ? OverviewItemActions : "span";
|
|
78
|
+
|
|
79
|
+
const actionsProps = useOverviewitem
|
|
80
|
+
? { delayDisplayChildren: delayActions, delaySkeleton: <Spinner position="inline" size="tiny" /> }
|
|
81
|
+
: {};
|
|
82
|
+
const hiddenActionsProps = useOverviewitem ? { ...actionsProps, hiddenInteractions: true } : {};
|
|
83
|
+
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
const renderEnd = new Date();
|
|
86
|
+
// eslint-disable-next-line no-console
|
|
87
|
+
console.log(
|
|
88
|
+
"OverviewItemListPerformance Rendering time (s)",
|
|
89
|
+
(renderEnd.getTime() - renderStart.getTime()) / 1000
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (containerRef.current) {
|
|
93
|
+
let changeCount = 0;
|
|
94
|
+
const changeReporter = () => {
|
|
95
|
+
const renderChange = new Date();
|
|
96
|
+
// eslint-disable-next-line no-console
|
|
97
|
+
console.log(
|
|
98
|
+
`Change ${++changeCount} after time (s)`,
|
|
99
|
+
(renderChange.getTime() - renderEnd.getTime()) / 1000
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
if (observerRef.current) {
|
|
103
|
+
observerRef.current.disconnect();
|
|
104
|
+
}
|
|
105
|
+
observerRef.current = new MutationObserver(changeReporter);
|
|
106
|
+
observerRef.current.observe(containerRef.current, { childList: true, subtree: true });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div ref={containerRef}>
|
|
112
|
+
<ApplicationContainer>
|
|
113
|
+
<OverviewItemList hasDivider hasSpacing columns={useOverviewitem ? 2 : 1}>
|
|
114
|
+
{Array(length)
|
|
115
|
+
.fill("x")
|
|
116
|
+
.map((_, id) => {
|
|
117
|
+
return (
|
|
118
|
+
<ItemWrapper key={id}>
|
|
119
|
+
{withDepiction && (
|
|
120
|
+
<Depiction
|
|
121
|
+
size="small"
|
|
122
|
+
image={<Icon name={iconNames[id % iconNames.length] as ValidIconName} />}
|
|
123
|
+
caption={withTooltips ? textShort[(id + 10) % textShort.length] : undefined}
|
|
124
|
+
captionPosition={withTooltips ? "tooltip" : "none"}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
{withDescription && (
|
|
128
|
+
<ItemDescription>
|
|
129
|
+
<ItemLine large={useOverviewitem ? true : undefined}>
|
|
130
|
+
{textShort[id % textShort.length]}
|
|
131
|
+
</ItemLine>
|
|
132
|
+
<ItemLine small={useOverviewitem ? true : undefined}>
|
|
133
|
+
{withTooltips ? (
|
|
134
|
+
<Tooltip content={textLong[id % textLong.length]}>
|
|
135
|
+
<OverflowText>{textLong[id % textLong.length]}</OverflowText>
|
|
136
|
+
</Tooltip>
|
|
137
|
+
) : (
|
|
138
|
+
<OverflowText>{textLong[id % textLong.length]}</OverflowText>
|
|
139
|
+
)}
|
|
140
|
+
</ItemLine>
|
|
141
|
+
</ItemDescription>
|
|
142
|
+
)}
|
|
143
|
+
{withIconButtonInHiddenActions && (
|
|
144
|
+
<ItemActions {...hiddenActionsProps}>
|
|
145
|
+
<IconButton
|
|
146
|
+
name={iconNames[(id + 23) % iconNames.length] as ValidIconName}
|
|
147
|
+
text={textShort[(id + 27) % textShort.length]}
|
|
148
|
+
tooltipAsTitle={!withTooltips}
|
|
149
|
+
/>
|
|
150
|
+
</ItemActions>
|
|
151
|
+
)}
|
|
152
|
+
{(withButtonInActions || withContextMenuInActions) && (
|
|
153
|
+
<ItemActions {...actionsProps}>
|
|
154
|
+
{withButtonInActions && (
|
|
155
|
+
<Button onClick={() => alert("Button clicked")}>
|
|
156
|
+
{textShort[(id + 77) % textShort.length]}
|
|
157
|
+
</Button>
|
|
158
|
+
)}
|
|
159
|
+
{withContextMenuInActions && (
|
|
160
|
+
<ContextMenu
|
|
161
|
+
{...ContextMenuExample.args}
|
|
162
|
+
tooltipAsTitle={!withTooltips}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
165
|
+
</ItemActions>
|
|
166
|
+
)}
|
|
167
|
+
</ItemWrapper>
|
|
168
|
+
);
|
|
169
|
+
})}
|
|
170
|
+
</OverviewItemList>
|
|
171
|
+
</ApplicationContainer>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Meta, StoryFn } from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import { OverviewItemListPerformance } from "./OverviewItemListPerformance";
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: "Components/OverviewItem",
|
|
8
|
+
component: OverviewItemListPerformance,
|
|
9
|
+
argTypes: {},
|
|
10
|
+
} as Meta<typeof OverviewItemListPerformance>;
|
|
11
|
+
|
|
12
|
+
const Template: StoryFn<typeof OverviewItemListPerformance> = (args) => (
|
|
13
|
+
<OverviewItemListPerformance {...args}></OverviewItemListPerformance>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const ListPerformance = Template.bind({});
|
|
17
|
+
ListPerformance.args = {
|
|
18
|
+
useOverviewitem: true,
|
|
19
|
+
};
|
|
@@ -11,14 +11,19 @@ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
|
11
11
|
type SpinnerPosition = "local" | "inline" | "global";
|
|
12
12
|
type SpinnerSize = "tiny" | "small" | "medium" | "large" | "xlarge" | "inherit";
|
|
13
13
|
type SpinnerStroke = "thin" | "medium" | "bold";
|
|
14
|
-
type Intent = "inherit" | "primary" | "success" | "warning" | "danger";
|
|
14
|
+
type Intent = "inherit" | "primary" | "success" | "warning" | "danger" | "none";
|
|
15
15
|
|
|
16
16
|
/** A spinner that is either displayed globally or locally. */
|
|
17
|
-
export interface SpinnerProps extends Omit<BlueprintSpinnerProps, "size"> {
|
|
17
|
+
export interface SpinnerProps extends Omit<BlueprintSpinnerProps, "size" | "intent"> {
|
|
18
18
|
/**
|
|
19
19
|
* intent value or a valid css color definition
|
|
20
|
+
* @deprecated (v25) it will allow in the future only a color value string and that for other states the intent property needs to be used
|
|
20
21
|
*/
|
|
21
22
|
color?: Intent | string;
|
|
23
|
+
/**
|
|
24
|
+
* Intent state of the field item.
|
|
25
|
+
*/
|
|
26
|
+
intent?: Intent;
|
|
22
27
|
/**
|
|
23
28
|
* Additional CSS class names.
|
|
24
29
|
*/
|
|
@@ -66,12 +71,14 @@ export interface SpinnerProps extends Omit<BlueprintSpinnerProps, "size"> {
|
|
|
66
71
|
export const Spinner = ({
|
|
67
72
|
className = "",
|
|
68
73
|
color = "inherit",
|
|
74
|
+
intent,
|
|
69
75
|
position = "local",
|
|
70
76
|
size,
|
|
71
77
|
stroke,
|
|
72
78
|
showLocalBackdrop = false,
|
|
73
79
|
delay = 0,
|
|
74
80
|
overlayProps,
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
75
82
|
description = "Loading indicator", // currently unsupported (FIXME):
|
|
76
83
|
...otherProps
|
|
77
84
|
}: SpinnerProps) => {
|
|
@@ -91,8 +98,9 @@ export const Spinner = ({
|
|
|
91
98
|
};
|
|
92
99
|
|
|
93
100
|
const spinnerElement = position === "inline" ? "span" : "div";
|
|
94
|
-
|
|
95
|
-
const
|
|
101
|
+
|
|
102
|
+
const spinnerColor = !intent && availableIntent.indexOf(color) < 0 ? color : null;
|
|
103
|
+
const spinnerIntent = !intent && availableIntent.indexOf(color) < 0 ? "usercolor" : intent || color;
|
|
96
104
|
|
|
97
105
|
let spinnerSize;
|
|
98
106
|
let spinnerStroke;
|
|
@@ -130,7 +138,7 @@ export const Spinner = ({
|
|
|
130
138
|
/>
|
|
131
139
|
);
|
|
132
140
|
|
|
133
|
-
if (spinnerColor) {
|
|
141
|
+
if (spinnerColor && spinnerIntent === "usercolor") {
|
|
134
142
|
spinner = <span style={{ color: spinnerColor }}>{spinner}</span>;
|
|
135
143
|
}
|
|
136
144
|
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Meta, StoryFn } from "@storybook/react";
|
|
3
3
|
|
|
4
|
+
import { helpersArgTypes } from "../../../../.storybook/helpers";
|
|
4
5
|
import Spinner from "../Spinner";
|
|
5
6
|
export default {
|
|
6
7
|
title: "Components/Spinner",
|
|
7
8
|
component: Spinner,
|
|
8
9
|
argTypes: {
|
|
9
|
-
color: { control: "
|
|
10
|
+
color: { control: "color" },
|
|
11
|
+
intent: {
|
|
12
|
+
...helpersArgTypes.exampleIntent,
|
|
13
|
+
options: ["UNDEFINED", "primary", "success", "warning", "danger", "none"],
|
|
14
|
+
},
|
|
10
15
|
position: { control: "radio", options: ["local", "inline", "global"] },
|
|
11
16
|
size: { control: "radio", options: ["tiny", "small", "medium", "large", "xlarge", "inherit"] },
|
|
12
17
|
stroke: { control: "radio", options: ["thin", "medium", "bold"] },
|
|
@@ -11,7 +11,7 @@ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
|
11
11
|
|
|
12
12
|
import { TableRowHeightSize, tableRowHeightSizes } from "./Table";
|
|
13
13
|
|
|
14
|
-
interface TableDataContainerProps
|
|
14
|
+
export interface TableDataContainerProps
|
|
15
15
|
extends Omit<
|
|
16
16
|
CarbonDataTableProps<
|
|
17
17
|
Array<Omit<CarbonDataTableRow<Array<CarbonDataTableHeader>>, "cells">>,
|
|
@@ -23,7 +23,7 @@ interface TableDataContainerProps
|
|
|
23
23
|
children(signature: any): JSX.Element;
|
|
24
24
|
size?: TableRowHeightSize;
|
|
25
25
|
}
|
|
26
|
-
interface TableSimpleContainerProps
|
|
26
|
+
export interface TableSimpleContainerProps
|
|
27
27
|
extends Omit<CarbonTableContainerProps, "description" | "stickyHeader" | "title" | "useStaticWidth">,
|
|
28
28
|
React.HTMLAttributes<HTMLDivElement> {
|
|
29
29
|
children?: JSX.Element;
|
|
@@ -11,7 +11,7 @@ import { TableCell } from "./index";
|
|
|
11
11
|
// workaround to get type/interface
|
|
12
12
|
type CarbonTableExpandRowProps = React.ComponentProps<typeof CarbonTableExpandRow>;
|
|
13
13
|
export interface TableExpandRowProps
|
|
14
|
-
extends Omit<CarbonTableExpandRowProps, "ref" | "ariaLabel" | "expandIconDescription" | "aria-label">,
|
|
14
|
+
extends Omit<CarbonTableExpandRowProps, "children" | "ref" | "ariaLabel" | "expandIconDescription" | "aria-label">,
|
|
15
15
|
React.HTMLAttributes<HTMLTableRowElement> {
|
|
16
16
|
/**
|
|
17
17
|
* This text is displayed as tooltip for the button that toggles the expanded/collapsed state.
|
|
@@ -7,3 +7,4 @@ export * from "./TableCell";
|
|
|
7
7
|
|
|
8
8
|
// TODO, we may wrap to add own classes (currently not necessary)
|
|
9
9
|
export { TableHead, TableBody, TableExpandedRow, TableHeader } from "@carbon/react";
|
|
10
|
+
export type { TableHeadProps, TableBodyProps, TableExpandedRowProps, TableHeaderProps, DataTableRenderProps } from "@carbon/react";
|
|
@@ -11,9 +11,10 @@ export default {
|
|
|
11
11
|
argTypes: {},
|
|
12
12
|
} as Meta<typeof Tooltip>;
|
|
13
13
|
|
|
14
|
+
let forcedUpdateKey = 0; // @see https://github.com/storybookjs/storybook/issues/13375#issuecomment-1291011856
|
|
14
15
|
const Template: StoryFn<typeof Tooltip> = (args) => (
|
|
15
16
|
<OverlaysProvider>
|
|
16
|
-
<Tooltip {...args} />
|
|
17
|
+
<Tooltip {...args} key={++forcedUpdateKey} />
|
|
17
18
|
</OverlaysProvider>
|
|
18
19
|
);
|
|
19
20
|
|
|
@@ -30,7 +31,7 @@ const testContent = loremIpsum({
|
|
|
30
31
|
* */
|
|
31
32
|
export const Default = Template.bind({});
|
|
32
33
|
Default.args = {
|
|
33
|
-
children:
|
|
34
|
+
children: "hover me",
|
|
34
35
|
content: testContent,
|
|
35
36
|
addIndicator: true,
|
|
36
37
|
};
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Classes as BlueprintClasses,
|
|
4
4
|
Tooltip as BlueprintTooltip,
|
|
5
5
|
TooltipProps as BlueprintTooltipProps,
|
|
6
|
+
Utils as BlueprintUtils,
|
|
6
7
|
} from "@blueprintjs/core";
|
|
7
8
|
|
|
8
9
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
@@ -33,6 +34,14 @@ export interface TooltipProps extends Omit<BlueprintTooltipProps, "position"> {
|
|
|
33
34
|
* Set properties for the Markdown parser
|
|
34
35
|
*/
|
|
35
36
|
markdownProps?: Omit<MarkdownProps, "children">;
|
|
37
|
+
/**
|
|
38
|
+
* Use the overlay target as placeholder before the real `<Tooltip /` is rendered on first hover or focus event.
|
|
39
|
+
* This can boost performance massive but it is currently experimental.
|
|
40
|
+
* Placeholders are never used when `disabled`, `defaultIsOpen` or `isOpen` is set to `true`, or if `renderTarget` is set.
|
|
41
|
+
* If the tooltip `content` is only a string then a placeholder is automatically used, too.
|
|
42
|
+
* You can prevent it in any case by setting it to `false`.
|
|
43
|
+
*/
|
|
44
|
+
usePlaceholder?: boolean;
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
export const Tooltip = ({
|
|
@@ -43,8 +52,106 @@ export const Tooltip = ({
|
|
|
43
52
|
addIndicator = false,
|
|
44
53
|
markdownEnabler = "\n\n",
|
|
45
54
|
markdownProps,
|
|
46
|
-
|
|
55
|
+
usePlaceholder,
|
|
56
|
+
hoverOpenDelay = 500,
|
|
57
|
+
...otherTooltipProps
|
|
47
58
|
}: TooltipProps) => {
|
|
59
|
+
const placeholderRef = React.useRef(null);
|
|
60
|
+
const eventMemory = React.useRef<null | "afterhover" | "afterfocus">(null);
|
|
61
|
+
const searchId = React.useRef<null | string>(null);
|
|
62
|
+
const swapDelayTime = 100;
|
|
63
|
+
const [placeholder, setPlaceholder] = React.useState<boolean>(
|
|
64
|
+
!otherTooltipProps.disabled &&
|
|
65
|
+
!otherTooltipProps.defaultIsOpen &&
|
|
66
|
+
!otherTooltipProps.isOpen &&
|
|
67
|
+
otherTooltipProps.renderTarget === undefined &&
|
|
68
|
+
hoverOpenDelay > swapDelayTime &&
|
|
69
|
+
(usePlaceholder === true || (typeof content === "string" && usePlaceholder !== false))
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const targetClassName =
|
|
73
|
+
`${eccgui}-tooltip__wrapper` +
|
|
74
|
+
(className ? " " + className : "") +
|
|
75
|
+
(addIndicator === true ? " " + BlueprintClasses.TOOLTIP_INDICATOR : "");
|
|
76
|
+
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
if (placeholderRef.current !== null) {
|
|
79
|
+
const swap = (ev: MouseEvent | globalThis.FocusEvent) => {
|
|
80
|
+
const swapDelay = setTimeout(() => {
|
|
81
|
+
// we delay the swap to prevent unwanted effects
|
|
82
|
+
// (e.g. forced mouseover after the swap but the cursor is already somewhere else)
|
|
83
|
+
eventMemory.current = ev.type === "focusin" ? "afterfocus" : "afterhover";
|
|
84
|
+
searchId.current = Date.now().toString(16) + Math.random().toString(16).slice(2);
|
|
85
|
+
setPlaceholder(false);
|
|
86
|
+
}, swapDelayTime);
|
|
87
|
+
if (placeholderRef.current !== null) {
|
|
88
|
+
const eventType = ev.type === "focusin" ? "focusout" : "mouseleave";
|
|
89
|
+
(placeholderRef.current as HTMLElement).addEventListener(
|
|
90
|
+
eventType,
|
|
91
|
+
() => {
|
|
92
|
+
if (eventType === "focusout" && eventMemory.current === "afterfocus" ||
|
|
93
|
+
eventType === "mouseleave" && eventMemory.current === "afterhover") {
|
|
94
|
+
eventMemory.current = null
|
|
95
|
+
}
|
|
96
|
+
clearTimeout(swapDelay)
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
(placeholderRef.current as HTMLElement).addEventListener("mouseenter", swap);
|
|
102
|
+
(placeholderRef.current as HTMLElement).addEventListener("focusin", swap);
|
|
103
|
+
return () => {
|
|
104
|
+
if (placeholderRef.current) {
|
|
105
|
+
(placeholderRef.current as HTMLElement).removeEventListener("mouseenter", swap);
|
|
106
|
+
(placeholderRef.current as HTMLElement).removeEventListener("focusin", swap);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return () => {};
|
|
111
|
+
}, [!!placeholderRef.current]);
|
|
112
|
+
|
|
113
|
+
const refocus = React.useCallback((node) => {
|
|
114
|
+
if (eventMemory.current && node) {
|
|
115
|
+
// we do not have a `targetRef` here, so we need to workaround it
|
|
116
|
+
// const target = node.targetRef.current.children[0];
|
|
117
|
+
const target = document.body.querySelector(
|
|
118
|
+
`[data-postplaceholder=id${eventMemory.current}${searchId.current}]`
|
|
119
|
+
)?.children[0];
|
|
120
|
+
if (target) {
|
|
121
|
+
switch (eventMemory.current) {
|
|
122
|
+
case "afterfocus":
|
|
123
|
+
(target as HTMLElement).focus();
|
|
124
|
+
break;
|
|
125
|
+
case "afterhover":
|
|
126
|
+
(target as HTMLElement).dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}, []);
|
|
132
|
+
|
|
133
|
+
const displayPlaceholder = () => {
|
|
134
|
+
const PlaceholderElement = otherTooltipProps?.targetTagName ?? (otherTooltipProps?.fill ? "div" : "span");
|
|
135
|
+
const childTarget = BlueprintUtils.ensureElement(React.Children.toArray(children)[0]);
|
|
136
|
+
if (!childTarget) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return React.createElement(
|
|
140
|
+
PlaceholderElement,
|
|
141
|
+
{
|
|
142
|
+
...otherTooltipProps?.targetProps,
|
|
143
|
+
className: `${BlueprintClasses.POPOVER_TARGET} ${targetClassName} ${eccgui}-tooltip__wrapper--placeholder`,
|
|
144
|
+
ref: placeholderRef,
|
|
145
|
+
},
|
|
146
|
+
React.cloneElement(childTarget, {
|
|
147
|
+
...childTarget.props,
|
|
148
|
+
className:
|
|
149
|
+
childTarget.props.className ?? "" + (otherTooltipProps.fill ? ` ${BlueprintClasses.FILL}` : ""),
|
|
150
|
+
tabIndex: 0,
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
48
155
|
let tooltipContent = content;
|
|
49
156
|
|
|
50
157
|
if (
|
|
@@ -55,23 +162,27 @@ export const Tooltip = ({
|
|
|
55
162
|
tooltipContent = <Markdown {...markdownProps}>{content}</Markdown>;
|
|
56
163
|
}
|
|
57
164
|
|
|
58
|
-
return (
|
|
165
|
+
return placeholder ? (
|
|
166
|
+
displayPlaceholder()
|
|
167
|
+
) : (
|
|
59
168
|
<BlueprintTooltip
|
|
60
169
|
lazy={true}
|
|
61
|
-
hoverOpenDelay={
|
|
62
|
-
{...
|
|
170
|
+
hoverOpenDelay={hoverOpenDelay - swapDelayTime}
|
|
171
|
+
{...otherTooltipProps}
|
|
63
172
|
content={tooltipContent}
|
|
64
|
-
className={
|
|
65
|
-
`${eccgui}-tooltip__wrapper` +
|
|
66
|
-
(className ? " " + className : "") +
|
|
67
|
-
(addIndicator === true ? " " + BlueprintClasses.TOOLTIP_INDICATOR : "")
|
|
68
|
-
}
|
|
69
|
-
//targetClassName={`${eccgui}-tooltip__target` + (className ? " " + className + "__target" : "")}
|
|
173
|
+
className={targetClassName}
|
|
70
174
|
popoverClassName={
|
|
71
175
|
`${eccgui}-tooltip__content` +
|
|
72
176
|
` ${eccgui}-tooltip--${size}` +
|
|
73
177
|
(className ? " " + className + "__content" : "")
|
|
74
178
|
}
|
|
179
|
+
ref={refocus}
|
|
180
|
+
targetProps={
|
|
181
|
+
{
|
|
182
|
+
...otherTooltipProps.targetProps,
|
|
183
|
+
"data-postplaceholder": `id${eventMemory.current}${searchId.current}`,
|
|
184
|
+
} as React.HTMLProps<HTMLElement>
|
|
185
|
+
}
|
|
75
186
|
>
|
|
76
187
|
{children}
|
|
77
188
|
</BlueprintTooltip>
|