@eccenca/gui-elements 23.0.0-rc.1 → 23.0.0-rc.2
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 +13 -0
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +4 -10
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/cjs/cmem/react-flow/ReactFlow/ReactFlow.js +2 -1
- package/dist/cjs/cmem/react-flow/ReactFlow/ReactFlow.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +19 -19
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js +4 -12
- package/dist/cjs/components/AutoSuggestion/AutoSuggestionList.js.map +1 -1
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +15 -6
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.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 +6 -5
- package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js +6 -8
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/cjs/components/Form/FieldItem.js +2 -2
- package/dist/cjs/components/Form/FieldItem.js.map +1 -1
- package/dist/cjs/components/Icon/Icon.js +2 -2
- package/dist/cjs/components/Icon/Icon.js.map +1 -1
- package/dist/cjs/components/Icon/IconButton.js +2 -2
- package/dist/cjs/components/Icon/IconButton.js.map +1 -1
- package/dist/cjs/components/Label/Label.js +6 -6
- package/dist/cjs/components/Label/Label.js.map +1 -1
- package/dist/cjs/components/Menu/MenuItem.js +2 -5
- package/dist/cjs/components/Menu/MenuItem.js.map +1 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js +8 -10
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/cjs/components/Select/Select.js +29 -0
- package/dist/cjs/components/Select/Select.js.map +1 -0
- package/dist/cjs/components/Tabs/TabTitle.js +2 -4
- package/dist/cjs/components/Tabs/TabTitle.js.map +1 -1
- package/dist/cjs/components/Tooltip/Tooltip.js +7 -4
- package/dist/cjs/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeTools.js +2 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeTools.js.map +1 -1
- package/dist/cjs/extensions/react-flow/handles/HandleContent.js +1 -1
- package/dist/cjs/extensions/react-flow/handles/HandleContent.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeTools.js +1 -3
- package/dist/cjs/extensions/react-flow/nodes/NodeTools.js.map +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +4 -10
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/esm/cmem/react-flow/ReactFlow/ReactFlow.js +2 -1
- package/dist/esm/cmem/react-flow/ReactFlow/ReactFlow.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +23 -21
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestionList.js +4 -12
- package/dist/esm/components/AutoSuggestion/AutoSuggestionList.js.map +1 -1
- package/dist/esm/components/AutocompleteField/AutoCompleteField.js +32 -23
- package/dist/esm/components/AutocompleteField/AutoCompleteField.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 +6 -5
- package/dist/esm/components/ContextOverlay/ContextMenu.js.map +1 -1
- package/dist/esm/components/ContextOverlay/ContextOverlay.js +6 -8
- package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/esm/components/Form/FieldItem.js +2 -2
- package/dist/esm/components/Form/FieldItem.js.map +1 -1
- package/dist/esm/components/Icon/Icon.js +2 -2
- package/dist/esm/components/Icon/Icon.js.map +1 -1
- package/dist/esm/components/Icon/IconButton.js +2 -2
- package/dist/esm/components/Icon/IconButton.js.map +1 -1
- package/dist/esm/components/Label/Label.js +6 -6
- package/dist/esm/components/Label/Label.js.map +1 -1
- package/dist/esm/components/Menu/MenuItem.js +2 -5
- package/dist/esm/components/Menu/MenuItem.js.map +1 -1
- package/dist/esm/components/MultiSelect/MultiSelect.js +5 -7
- package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/Select/Select.js +35 -0
- package/dist/esm/components/Select/Select.js.map +1 -0
- package/dist/esm/components/Tabs/TabTitle.js +2 -4
- package/dist/esm/components/Tabs/TabTitle.js.map +1 -1
- package/dist/esm/components/Tooltip/Tooltip.js +7 -4
- package/dist/esm/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeTools.js +2 -1
- package/dist/esm/extensions/react-flow/edges/EdgeTools.js.map +1 -1
- package/dist/esm/extensions/react-flow/handles/HandleContent.js +1 -1
- package/dist/esm/extensions/react-flow/handles/HandleContent.js.map +1 -1
- package/dist/esm/extensions/react-flow/nodes/NodeTools.js +1 -3
- package/dist/esm/extensions/react-flow/nodes/NodeTools.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/types/components/AutocompleteField/AutoCompleteField.d.ts +7 -4
- package/dist/types/components/Button/Button.d.ts +2 -2
- package/dist/types/components/ContextOverlay/ContextMenu.d.ts +37 -3
- package/dist/types/components/ContextOverlay/ContextOverlay.d.ts +12 -1
- package/dist/types/components/Form/FieldItem.d.ts +3 -2
- package/dist/types/components/Icon/Icon.d.ts +32 -7
- package/dist/types/components/Icon/IconButton.d.ts +21 -7
- package/dist/types/components/Label/Label.d.ts +30 -2
- package/dist/types/components/Menu/MenuItem.d.ts +3 -7
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +13 -9
- package/dist/types/components/Select/Select.d.ts +22 -0
- package/dist/types/components/Tooltip/Tooltip.d.ts +13 -11
- package/dist/types/components/interfaces.d.ts +1 -0
- package/dist/types/extensions/react-flow/edges/EdgeTools.d.ts +2 -2
- package/dist/types/extensions/react-flow/nodes/NodeTools.d.ts +2 -2
- package/dist/types/index.d.ts +2 -1
- package/package.json +8 -5
- package/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx +2 -2
- package/src/cmem/ActivityControl/ActivityControlWidget.tsx +5 -10
- package/src/cmem/react-flow/ReactFlow/ReactFlow.stories.tsx +48 -15
- package/src/cmem/react-flow/ReactFlow/ReactFlow.tsx +2 -0
- package/src/common/utils/CssCustomProperties.ts +12 -10
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +23 -23
- package/src/components/AutoSuggestion/AutoSuggestionList.tsx +15 -17
- package/src/components/AutoSuggestion/tests/AutoSuggestionList.test.tsx +1 -1
- package/src/components/AutocompleteField/AutoCompleteField.tsx +44 -19
- package/src/components/AutocompleteField/autocompletefield.scss +5 -3
- package/src/components/AutocompleteField/stories/AutoCompleteField.stories.tsx +75 -0
- package/src/components/Button/Button.tsx +3 -3
- package/src/components/ContextOverlay/ContextMenu.stories.tsx +30 -0
- package/src/components/ContextOverlay/ContextMenu.tsx +46 -8
- package/src/components/ContextOverlay/ContextOverlay.stories.tsx +148 -0
- package/src/components/ContextOverlay/ContextOverlay.tsx +16 -9
- package/src/components/ContextOverlay/contextoverlay.scss +3 -3
- package/src/components/Form/FieldItem.tsx +4 -4
- package/src/components/Icon/Icon.tsx +33 -18
- package/src/components/Icon/IconButton.tsx +24 -19
- package/src/components/Icon/stories/Icon.stories.tsx +52 -0
- package/src/components/Icon/stories/IconButton.stories.tsx +27 -0
- package/src/components/Label/Label.stories.tsx +27 -0
- package/src/components/Label/Label.tsx +47 -15
- package/src/components/Menu/MenuItem.tsx +9 -16
- package/src/components/Menu/menu.scss +4 -3
- package/src/components/MultiSelect/MultiSelect.tsx +30 -18
- package/src/components/Select/Select.stories.tsx +35 -0
- package/src/components/Select/Select.tsx +38 -0
- package/src/components/Select/_select.scss +8 -0
- package/src/components/Tabs/TabTitle.tsx +2 -4
- package/src/components/Tabs/tabs.scss +1 -1
- package/src/components/Tag/tag.scss +26 -22
- package/src/components/Tooltip/Tooltip.stories.tsx +36 -0
- package/src/components/Tooltip/Tooltip.tsx +23 -18
- package/src/components/Tooltip/tooltip.scss +6 -6
- package/src/components/Typography/typography.scss +1 -0
- package/src/components/interfaces.ts +3 -1
- package/src/extensions/react-flow/edges/EdgeTools.tsx +6 -4
- package/src/extensions/react-flow/handles/HandleContent.tsx +0 -1
- package/src/extensions/react-flow/nodes/NodeTools.tsx +10 -7
- package/src/extensions/react-flow/nodes/stories/NodeContent.stories.tsx +18 -0
- package/src/includes/blueprintjs/_variables.scss +1 -1
- package/src/index.scss +9 -5
- package/src/index.ts +3 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eccenca/gui-elements",
|
|
3
3
|
"description": "GUI elements based on other libraries, usable in React application, written in Typescript.",
|
|
4
|
-
"version": "23.0.0-rc.
|
|
4
|
+
"version": "23.0.0-rc.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/eccenca/gui-elements",
|
|
7
7
|
"bugs": "https://github.com/eccenca/gui-elements/issues",
|
|
@@ -60,8 +60,10 @@
|
|
|
60
60
|
"check:package": "pjv --warnings --recommendations"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@blueprintjs/
|
|
64
|
-
"@blueprintjs/
|
|
63
|
+
"@blueprintjs/colors": "^4.1.3",
|
|
64
|
+
"@blueprintjs/core": "4.0.4",
|
|
65
|
+
"@blueprintjs/popover2": "1.0.4",
|
|
66
|
+
"@blueprintjs/select": "4.3.1",
|
|
65
67
|
"@carbon/icons": "10.44.0",
|
|
66
68
|
"@carbon/icons-react": "10.44.0",
|
|
67
69
|
"@mavrin/remark-typograf": "^2.2.0",
|
|
@@ -135,8 +137,9 @@
|
|
|
135
137
|
"react-dom": ">=16"
|
|
136
138
|
},
|
|
137
139
|
"resolutions": {
|
|
138
|
-
"**/@blueprintjs/core": "
|
|
139
|
-
"**/@blueprintjs/
|
|
140
|
+
"**/@blueprintjs/core": "4.0.4",
|
|
141
|
+
"**/@blueprintjs/popover2": "1.0.4",
|
|
142
|
+
"**/@blueprintjs/select": "4.3.1",
|
|
140
143
|
"**/@carbon/icons": "10.44.0",
|
|
141
144
|
"**/@carbon/icons-react": "10.44.0",
|
|
142
145
|
"**/carbon-components": "10.44.0",
|
|
@@ -66,10 +66,10 @@ const actions = [
|
|
|
66
66
|
];
|
|
67
67
|
|
|
68
68
|
const commonWidgetArgs = {
|
|
69
|
-
label: loremIpsum({ p: 1, avgSentencesPerParagraph: 1, avgWordsPerSentence: 4, random: false }),
|
|
69
|
+
label: loremIpsum({ p: 1, avgSentencesPerParagraph: 1, avgWordsPerSentence: 4, random: false }).toString(),
|
|
70
70
|
border: true,
|
|
71
71
|
activityActions: actions,
|
|
72
|
-
statusMessage: loremIpsum({ p: 1, startWithLoremIpsum: false, random: false }),
|
|
72
|
+
statusMessage: loremIpsum({ p: 1, startWithLoremIpsum: false, random: false }).toString(),
|
|
73
73
|
progressSpinner: {
|
|
74
74
|
intent: "none",
|
|
75
75
|
value: 0.5,
|
|
@@ -151,15 +151,8 @@ export function ActivityControlWidget(props: IActivityControlProps) {
|
|
|
151
151
|
<Tooltip
|
|
152
152
|
content={props.statusMessage}
|
|
153
153
|
size="large"
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
boundary: "viewport",
|
|
157
|
-
modifiers: {
|
|
158
|
-
flip: {
|
|
159
|
-
enabled: false
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}}
|
|
154
|
+
placement="top-start"
|
|
155
|
+
rootBoundary="viewport"
|
|
163
156
|
>
|
|
164
157
|
{props.statusMessage}
|
|
165
158
|
</Tooltip>
|
|
@@ -181,9 +174,11 @@ export function ActivityControlWidget(props: IActivityControlProps) {
|
|
|
181
174
|
name={action.icon}
|
|
182
175
|
text={action.tooltip}
|
|
183
176
|
onClick={action.action}
|
|
184
|
-
tooltipOpenDelay={200}
|
|
185
177
|
disabled={action.disabled}
|
|
186
178
|
hasStateWarning={action.hasStateWarning}
|
|
179
|
+
tooltipProps={{
|
|
180
|
+
hoverOpenDelay: 200
|
|
181
|
+
}}
|
|
187
182
|
/>
|
|
188
183
|
);
|
|
189
184
|
})}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback, FC } from "react";
|
|
2
2
|
import { ComponentStory, ComponentMeta } from "@storybook/react";
|
|
3
|
-
import IconButton from "
|
|
4
|
-
import {
|
|
5
|
-
import { ArrowHeadType, Elements, getMarkerEnd } from "react-flow-renderer";
|
|
3
|
+
import { ReactFlow, EdgeTools, NodeTools, IconButton } from "./../../../index";
|
|
4
|
+
import { ReactFlowProps } from "./ReactFlow";
|
|
5
|
+
import { ArrowHeadType, Elements, FlowElement, getMarkerEnd } from "react-flow-renderer";
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
8
|
title: "CMEM/React Flow/Configurations",
|
|
@@ -17,7 +17,8 @@ const nodeExamples = {
|
|
|
17
17
|
id: 'unspecified-1',
|
|
18
18
|
type: "default",
|
|
19
19
|
data: {
|
|
20
|
-
label: "Default ", content: "Example content.", minimalShape: "none"
|
|
20
|
+
label: "Default ", content: "Example content.", minimalShape: "none",
|
|
21
|
+
menuButtons: <NodeTools>Pass your menu here.</NodeTools>
|
|
21
22
|
},
|
|
22
23
|
position: { x: 200, y: 50 },
|
|
23
24
|
},
|
|
@@ -42,7 +43,8 @@ const nodeExamples = {
|
|
|
42
43
|
id: 'linking-1',
|
|
43
44
|
type: "sourcepath",
|
|
44
45
|
data: {
|
|
45
|
-
label: "Source path", content: "Example content.", minimalShape: "none"
|
|
46
|
+
label: "Source path", content: "Example content.", minimalShape: "none",
|
|
47
|
+
menuButtons: <NodeTools>Pass your menu here.</NodeTools>
|
|
46
48
|
},
|
|
47
49
|
position: { x: 100, y: 50 },
|
|
48
50
|
},
|
|
@@ -91,7 +93,8 @@ const nodeExamples = {
|
|
|
91
93
|
id: 'workflow-1',
|
|
92
94
|
type: "dataset",
|
|
93
95
|
data: {
|
|
94
|
-
label: "Dataset", content: "Example content.", minimalShape: "none"
|
|
96
|
+
label: "Dataset", content: "Example content.", minimalShape: "none",
|
|
97
|
+
menuButtons: <NodeTools>Pass your menu here.</NodeTools>
|
|
95
98
|
},
|
|
96
99
|
position: { x: 100, y: 50 },
|
|
97
100
|
},
|
|
@@ -140,7 +143,8 @@ const nodeExamples = {
|
|
|
140
143
|
id: 'graph-1',
|
|
141
144
|
type: "default",
|
|
142
145
|
data: {
|
|
143
|
-
label: "Default ", content: "Example content.", minimalShape: "none"
|
|
146
|
+
label: "Default ", content: "Example content.", minimalShape: "none",
|
|
147
|
+
menuButtons: <NodeTools>Pass your menu here.</NodeTools>
|
|
144
148
|
},
|
|
145
149
|
position: { x: 100, y: 50 },
|
|
146
150
|
},
|
|
@@ -264,12 +268,36 @@ const nodeExamples = {
|
|
|
264
268
|
const ReactFlowExample: FC<ReactFlowProps> = (args) => {
|
|
265
269
|
const [reactflowInstance, setReactflowInstance] = useState(null);
|
|
266
270
|
const [elements, setElements] = useState([] as Elements);
|
|
267
|
-
|
|
271
|
+
const [edgeTools, setEdgeTools] = useState<JSX.Element>(<></>);
|
|
268
272
|
|
|
269
273
|
useEffect(() => {
|
|
270
274
|
setElements(nodeExamples[args.configuration ?? "unspecified"] as Elements);
|
|
271
275
|
}, [args]);
|
|
272
276
|
|
|
277
|
+
// Helper methods for nodes and edges
|
|
278
|
+
const isNode = (element: FlowElement & { source?: string }): boolean =>
|
|
279
|
+
!element.source;
|
|
280
|
+
const isEdge = (element: FlowElement & { source?: string }): boolean =>
|
|
281
|
+
!isNode(element);
|
|
282
|
+
|
|
283
|
+
// Fired when clicked on any elements, e.g. edge or node. Used to show the edge menu.
|
|
284
|
+
const onElementClick = React.useCallback((event, element) => {
|
|
285
|
+
if (isEdge(element)) {
|
|
286
|
+
event.preventDefault();
|
|
287
|
+
setEdgeTools(
|
|
288
|
+
<EdgeTools
|
|
289
|
+
posOffset={{ left: event.clientX, top: event.clientY }}
|
|
290
|
+
onClose={() => {
|
|
291
|
+
setEdgeTools(<></>);
|
|
292
|
+
}}
|
|
293
|
+
>
|
|
294
|
+
EdgeTools demo,
|
|
295
|
+
add elements here.
|
|
296
|
+
</EdgeTools>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}, []);
|
|
300
|
+
|
|
273
301
|
const onLoad = useCallback(
|
|
274
302
|
(rfi) => {
|
|
275
303
|
if (!reactflowInstance) {
|
|
@@ -279,13 +307,18 @@ const ReactFlowExample: FC<ReactFlowProps> = (args) => {
|
|
|
279
307
|
[reactflowInstance]
|
|
280
308
|
);
|
|
281
309
|
|
|
282
|
-
return
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
310
|
+
return <>
|
|
311
|
+
<ReactFlow
|
|
312
|
+
configuration={args.configuration}
|
|
313
|
+
elements={elements}
|
|
314
|
+
style={{ height: '400px' }}
|
|
315
|
+
onLoad={onLoad}
|
|
316
|
+
defaultZoom={1}
|
|
317
|
+
onElementClick={onElementClick}
|
|
318
|
+
onEdgeContextMenu={onElementClick}
|
|
319
|
+
/>
|
|
320
|
+
{edgeTools}
|
|
321
|
+
</>
|
|
289
322
|
}
|
|
290
323
|
|
|
291
324
|
const Template: ComponentStory<typeof ReactFlow> = (args) => (
|
|
@@ -22,6 +22,7 @@ export interface ReactFlowProps extends ReactFlowOriginalProps {
|
|
|
22
22
|
export const ReactFlow = React.forwardRef<HTMLDivElement, ReactFlowProps>((
|
|
23
23
|
{
|
|
24
24
|
configuration = "unspecified",
|
|
25
|
+
children,
|
|
25
26
|
...originalProps
|
|
26
27
|
},
|
|
27
28
|
ref) => {
|
|
@@ -41,6 +42,7 @@ export const ReactFlow = React.forwardRef<HTMLDivElement, ReactFlowProps>((
|
|
|
41
42
|
edgeTypes={ configReactFlow[configuration].edgeTypes }
|
|
42
43
|
{...originalProps}
|
|
43
44
|
>
|
|
45
|
+
{ children }
|
|
44
46
|
<ReactFlowMarkers />
|
|
45
47
|
</ReactFlowOriginal>
|
|
46
48
|
);
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* @see https://css-tricks.com/how-to-get-all-custom-properties-on-a-page-in-javascript/
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
type AllowedCSSRule = CSSStyleRule | CSSPageRule; // they have necessary `selectorText` and `style` properties
|
|
7
|
+
|
|
6
8
|
interface getLocalCssStyleRulesProps {
|
|
7
|
-
cssRuleType?: "CSSStyleRule";
|
|
9
|
+
cssRuleType?: "CSSStyleRule" | "CSSPageRule";
|
|
8
10
|
selectorText?: string;
|
|
9
11
|
}
|
|
10
12
|
interface getLocalCssStyleRulePropertiesProps extends getLocalCssStyleRulesProps {
|
|
@@ -43,7 +45,7 @@ export default class CssCustomProperties {
|
|
|
43
45
|
|
|
44
46
|
static listLocalStylesheets = (): CSSStyleSheet[] => {
|
|
45
47
|
if (document && document.styleSheets) {
|
|
46
|
-
return Array.from(document.styleSheets)
|
|
48
|
+
return (Array.from(document.styleSheets) as CSSStyleSheet[])
|
|
47
49
|
.filter((stylesheet) => {
|
|
48
50
|
// is inline stylesheet or from same domain
|
|
49
51
|
if (!stylesheet.href) {
|
|
@@ -53,7 +55,7 @@ export default class CssCustomProperties {
|
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
return [];
|
|
58
|
+
return [] as CSSStyleSheet[];
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
static listLocalCssRules = (): CSSRule[] => {
|
|
@@ -64,16 +66,16 @@ export default class CssCustomProperties {
|
|
|
64
66
|
.flat();
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
static listLocalCssStyleRules = (filter: getLocalCssStyleRulesProps = {}):
|
|
69
|
+
static listLocalCssStyleRules = (filter: getLocalCssStyleRulesProps = {}): AllowedCSSRule[] => {
|
|
68
70
|
const {cssRuleType = "CSSStyleRule", selectorText} = filter;
|
|
69
71
|
const cssStyleRules = CssCustomProperties.listLocalCssRules()
|
|
70
|
-
.filter((
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
72
|
+
.filter((rule) => {
|
|
73
|
+
const cssrule = rule as AllowedCSSRule;
|
|
74
|
+
if((cssrule).style) {
|
|
75
|
+
if (cssrule.constructor.name !== cssRuleType) {
|
|
74
76
|
return false;
|
|
75
77
|
}
|
|
76
|
-
if (!!selectorText &&
|
|
78
|
+
if (!!selectorText && cssrule.selectorText !== selectorText) {
|
|
77
79
|
return false;
|
|
78
80
|
}
|
|
79
81
|
return true;
|
|
@@ -81,7 +83,7 @@ export default class CssCustomProperties {
|
|
|
81
83
|
return false;
|
|
82
84
|
}
|
|
83
85
|
})
|
|
84
|
-
return cssStyleRules as
|
|
86
|
+
return cssStyleRules as AllowedCSSRule[]
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
static listLocalCssStyleRuleProperties = (filter: getLocalCssStyleRulePropertiesProps = {}) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {useEffect, useMemo, useState} from "react";
|
|
2
2
|
import CodeMirror from "codemirror";
|
|
3
3
|
import {FieldItem, IconButton, Spinner, Toolbar, ToolbarSection} from "./../../";
|
|
4
4
|
import {Classes as BlueprintClassNames} from "@blueprintjs/core";
|
|
@@ -235,7 +235,7 @@ const AutoSuggestion = ({
|
|
|
235
235
|
editorState.index = 0
|
|
236
236
|
}, [suggestionResponse, editorState]);
|
|
237
237
|
|
|
238
|
-
const asyncCheckInput = async (inputString: string) => {
|
|
238
|
+
const asyncCheckInput = useMemo(() => async (inputString: string) => {
|
|
239
239
|
if(!checkInput) {
|
|
240
240
|
return
|
|
241
241
|
}
|
|
@@ -249,16 +249,29 @@ const AutoSuggestion = ({
|
|
|
249
249
|
} finally {
|
|
250
250
|
setPathValidationPending(false)
|
|
251
251
|
}
|
|
252
|
-
}
|
|
252
|
+
}, [checkInput])
|
|
253
253
|
|
|
254
|
-
const checkValuePathValidity =
|
|
255
|
-
debounce((inputString: string) => asyncCheckInput(inputString),
|
|
256
|
-
[
|
|
254
|
+
const checkValuePathValidity = useMemo(
|
|
255
|
+
() => debounce((inputString: string) => asyncCheckInput(inputString), 1000),
|
|
256
|
+
[asyncCheckInput]
|
|
257
257
|
)
|
|
258
258
|
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
const asyncHandleEditorInputChange = useMemo(() => async (inputString: string, cursorPosition: number) => {
|
|
260
|
+
setSuggestionsPending(true)
|
|
261
|
+
try {
|
|
262
|
+
const result: IPartialAutoCompleteResult | undefined = await fetchSuggestions(inputString, cursorPosition)
|
|
263
|
+
setSuggestionResponse(result)
|
|
264
|
+
} catch(e) {
|
|
265
|
+
setSuggestionResponse(undefined)
|
|
266
|
+
// TODO: Error handling
|
|
267
|
+
} finally {
|
|
268
|
+
setSuggestionsPending(false)
|
|
269
|
+
}
|
|
270
|
+
}, [fetchSuggestions])
|
|
271
|
+
|
|
272
|
+
const handleEditorInputChange = useMemo(() =>
|
|
273
|
+
debounce((inputString: string, cursorPosition: number) => asyncHandleEditorInputChange(inputString, cursorPosition), 1000),
|
|
274
|
+
[asyncHandleEditorInputChange]
|
|
262
275
|
)
|
|
263
276
|
|
|
264
277
|
React.useEffect(() => {
|
|
@@ -276,19 +289,6 @@ const AutoSuggestion = ({
|
|
|
276
289
|
return checkValuePathValidity.cancel
|
|
277
290
|
}, [value, checkValuePathValidity])
|
|
278
291
|
|
|
279
|
-
const asyncHandleEditorInputChange = async (inputString: string, cursorPosition: number) => {
|
|
280
|
-
setSuggestionsPending(true)
|
|
281
|
-
try {
|
|
282
|
-
const result: IPartialAutoCompleteResult | undefined = await fetchSuggestions(inputString, cursorPosition)
|
|
283
|
-
setSuggestionResponse(result)
|
|
284
|
-
} catch(e) {
|
|
285
|
-
setSuggestionResponse(undefined)
|
|
286
|
-
// TODO: Error handling
|
|
287
|
-
} finally {
|
|
288
|
-
setSuggestionsPending(false)
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
292
|
const handleChange = (val: string) => {
|
|
293
293
|
setValue(val);
|
|
294
294
|
onChange(val)
|
|
@@ -427,7 +427,7 @@ const AutoSuggestion = ({
|
|
|
427
427
|
|
|
428
428
|
return label ? (
|
|
429
429
|
<FieldItem
|
|
430
|
-
|
|
430
|
+
labelProps={{
|
|
431
431
|
text: (
|
|
432
432
|
<>
|
|
433
433
|
{label}
|
|
@@ -143,23 +143,21 @@ export const AutoSuggestionList = ({
|
|
|
143
143
|
{options.map((item, index) => (
|
|
144
144
|
<MenuItem
|
|
145
145
|
key={index}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
},
|
|
146
|
+
active={currentlyFocusedIndex === index}
|
|
147
|
+
onMouseDown={(e: any) => e.preventDefault()}
|
|
148
|
+
onClick={() => onItemSelectionChange(item)}
|
|
149
|
+
text={(
|
|
150
|
+
<Item
|
|
151
|
+
ref={generateRef(index)}
|
|
152
|
+
item={item}
|
|
153
|
+
/>
|
|
154
|
+
)}
|
|
155
|
+
onMouseEnter={() => setHoveredItem(item)}
|
|
156
|
+
onMouseLeave={() => setHoveredItem(undefined)}
|
|
157
|
+
onMouseOver={() => {
|
|
158
|
+
if (item.value !== hoveredItem?.value) {
|
|
159
|
+
setHoveredItem(item);
|
|
160
|
+
}
|
|
163
161
|
}}
|
|
164
162
|
/>
|
|
165
163
|
))}
|
|
@@ -84,7 +84,7 @@ describe("Dropdown list", () => {
|
|
|
84
84
|
options: mockOptions
|
|
85
85
|
}
|
|
86
86
|
const {container} = render(<AutoSuggestionList {...props} />)
|
|
87
|
-
const activeListItems = Array.from(container.querySelectorAll("li .
|
|
87
|
+
const activeListItems = Array.from(container.querySelectorAll("li .bp4-menu-item.bp4-active"))
|
|
88
88
|
expect(activeListItems.length).toBe(1)
|
|
89
89
|
})
|
|
90
90
|
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import React, {useEffect, useRef, useState} from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
HTMLInputProps as BlueprintHTMLInputProps,
|
|
4
|
+
InputGroupProps as BlueprintInputGroupProps,
|
|
5
|
+
IRefObject
|
|
6
|
+
} from "@blueprintjs/core";
|
|
7
|
+
import {
|
|
8
|
+
Suggest2 as Suggest
|
|
9
|
+
} from "@blueprintjs/select";
|
|
10
|
+
import {
|
|
11
|
+
Highlighter,
|
|
12
|
+
IconButton,
|
|
13
|
+
Menu,
|
|
14
|
+
Notification,
|
|
15
|
+
MenuItem,
|
|
16
|
+
OverflowText,
|
|
17
|
+
Spinner,
|
|
18
|
+
ContextOverlayProps,
|
|
19
|
+
} from "../../index";
|
|
5
20
|
import {CLASSPREFIX as eccgui} from "../../configuration/constants";
|
|
6
21
|
|
|
7
22
|
type SearchFunction<T extends any> = (value: string) => T[];
|
|
@@ -70,12 +85,12 @@ export interface IAutoCompleteFieldProps<T extends any, UPDATE_VALUE extends any
|
|
|
70
85
|
* `query` and `onQueryChange` instead of `inputProps.value` and
|
|
71
86
|
* `inputProps.onChange`.
|
|
72
87
|
*/
|
|
73
|
-
inputProps?:
|
|
88
|
+
inputProps?: BlueprintInputGroupProps & BlueprintHTMLInputProps;
|
|
74
89
|
|
|
75
90
|
/**
|
|
76
|
-
* Optional props of the
|
|
91
|
+
* Optional props of the internally used `<ContextOverlay/>` element..
|
|
77
92
|
*/
|
|
78
|
-
|
|
93
|
+
contextOverlayProps?: Partial<Omit<ContextOverlayProps, "content" | "children">>
|
|
79
94
|
|
|
80
95
|
/** Defines if a value can be reset, i.e. a reset icon is shown and the value is set to a specific value.
|
|
81
96
|
* When undefined, a value cannot be reset.
|
|
@@ -122,6 +137,9 @@ export interface IAutoCompleteFieldProps<T extends any, UPDATE_VALUE extends any
|
|
|
122
137
|
* @param selectedValue The currently selected value.
|
|
123
138
|
*/
|
|
124
139
|
resetQueryToValue?(selectedValue: T): string
|
|
140
|
+
|
|
141
|
+
/** If an error occurs during the auto-completion request, the error details will be prefixed with this string. */
|
|
142
|
+
requestErrorPrefix?: string
|
|
125
143
|
}
|
|
126
144
|
|
|
127
145
|
AutoCompleteField.defaultProps = {
|
|
@@ -160,6 +178,7 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
160
178
|
itemValueRenderer,
|
|
161
179
|
resetQueryToValue,
|
|
162
180
|
itemValueString,
|
|
181
|
+
requestErrorPrefix = "",
|
|
163
182
|
...otherProps
|
|
164
183
|
} = props;
|
|
165
184
|
const [selectedItem, setSelectedItem] = useState<T | undefined>(initialValue);
|
|
@@ -169,11 +188,12 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
169
188
|
const [query, setQuery] = useState<string>("");
|
|
170
189
|
const [hasFocus, setHasFocus] = useState<boolean>(false);
|
|
171
190
|
const [highlightingEnabled, setHighlightingEnabled] = useState<boolean>(true);
|
|
191
|
+
const [requestError, setRequestError] = useState<string | undefined>(undefined)
|
|
172
192
|
|
|
173
193
|
// The suggestions that match the user's input
|
|
174
194
|
const [filtered, setFiltered] = useState<T[]>([]);
|
|
175
195
|
|
|
176
|
-
const
|
|
196
|
+
const BlueprintSuggestAutocomplete = Suggest.ofType<T>();
|
|
177
197
|
|
|
178
198
|
// Sets the query to the item value if it has a valid string value
|
|
179
199
|
const setQueryToSelectedValue = (item?: T) => {
|
|
@@ -262,6 +282,7 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
262
282
|
// Fetches the results for the given query
|
|
263
283
|
const fetchQueryResults = async (input: string) => {
|
|
264
284
|
setListLoading(true);
|
|
285
|
+
setRequestError(undefined)
|
|
265
286
|
try {
|
|
266
287
|
let result = await onSearch(input);
|
|
267
288
|
const onlySelectItemReturned = result.length <= 1 && selectedItem && input.length > 0 &&
|
|
@@ -282,8 +303,9 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
282
303
|
}
|
|
283
304
|
setHighlightingEnabled(enableHighlighting);
|
|
284
305
|
setFiltered(result);
|
|
285
|
-
} catch (e) {
|
|
286
|
-
|
|
306
|
+
} catch (e: any) {
|
|
307
|
+
const details = e?.message ?? ""
|
|
308
|
+
setRequestError(requestErrorPrefix + details)
|
|
287
309
|
} finally {
|
|
288
310
|
setListLoading(false);
|
|
289
311
|
}
|
|
@@ -325,6 +347,9 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
325
347
|
onChange?.(resetValue);
|
|
326
348
|
setQuery("");
|
|
327
349
|
};
|
|
350
|
+
const requestErrorRenderer = () => {
|
|
351
|
+
return <Notification danger={true} message={requestError} />
|
|
352
|
+
}
|
|
328
353
|
// Optional clear button to reset the selected value
|
|
329
354
|
const clearButton = reset &&
|
|
330
355
|
selectedItem != null &&
|
|
@@ -339,21 +364,20 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
339
364
|
/>
|
|
340
365
|
) : undefined;
|
|
341
366
|
// Additional properties for the input element of the auto-completion widget
|
|
342
|
-
const updatedInputProps:
|
|
367
|
+
const updatedInputProps: BlueprintInputGroupProps & BlueprintHTMLInputProps = {
|
|
343
368
|
rightElement: clearButton,
|
|
344
369
|
autoFocus: autoFocus,
|
|
345
370
|
onBlur: handleOnFocusOut,
|
|
346
371
|
onFocus: handleOnFocusIn,
|
|
347
372
|
...otherProps.inputProps,
|
|
348
373
|
};
|
|
349
|
-
const
|
|
374
|
+
const updatedContextOverlayProps: Partial<Omit<ContextOverlayProps, "content" | "children">> = {
|
|
350
375
|
minimal: true,
|
|
351
|
-
|
|
376
|
+
placement: "bottom-start",
|
|
352
377
|
popoverClassName: `${eccgui}-autocompletefield__options`,
|
|
353
|
-
|
|
354
|
-
boundary: "window",
|
|
378
|
+
rootBoundary: "viewport",
|
|
355
379
|
onClosed: onPopoverClose,
|
|
356
|
-
...otherProps.
|
|
380
|
+
...otherProps.contextOverlayProps,
|
|
357
381
|
}
|
|
358
382
|
if(selectedItem !== undefined) {
|
|
359
383
|
// Makes sure that even when an empty string is selected, the placeholder won't be shown.
|
|
@@ -375,12 +399,13 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
375
399
|
} : {}
|
|
376
400
|
return (
|
|
377
401
|
<div ref={fieldRef}>
|
|
378
|
-
<
|
|
402
|
+
<BlueprintSuggestAutocomplete
|
|
379
403
|
className={`${eccgui}-autocompletefield__input`}
|
|
380
404
|
disabled={disabled}
|
|
381
|
-
|
|
405
|
+
// Need to display error messages in list
|
|
406
|
+
items={requestError ? [requestError as T] : filtered}
|
|
382
407
|
inputValueRenderer={selectedItem !== undefined ? itemValueRenderer : () => ""}
|
|
383
|
-
itemRenderer={optionRenderer}
|
|
408
|
+
itemRenderer={requestError ? requestErrorRenderer : optionRenderer}
|
|
384
409
|
itemsEqual={areEqualItems}
|
|
385
410
|
noResults={<MenuItem disabled={true} text={noResultText} style={fieldWidthLimits} />}
|
|
386
411
|
onItemSelect={onSelectionChange}
|
|
@@ -388,7 +413,7 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
388
413
|
closeOnSelect={true}
|
|
389
414
|
query={query}
|
|
390
415
|
// This leads to odd compile errors without "as any"
|
|
391
|
-
popoverProps={
|
|
416
|
+
popoverProps={updatedContextOverlayProps as any}
|
|
392
417
|
selectedItem={selectedItem}
|
|
393
418
|
fill
|
|
394
419
|
{...createNewItemProps}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {ComponentMeta, ComponentStory} from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import {AutoCompleteField, IAutoCompleteFieldProps} from "../AutoCompleteField";
|
|
5
|
+
import {extractSearchWords, matchesAllWords} from "../../Typography/Highlighter";
|
|
6
|
+
import {createNewItemRendererFactory} from "../autoCompleteFieldUtils";
|
|
7
|
+
|
|
8
|
+
const AutoCompleteFieldStory: ComponentMeta<typeof AutoCompleteField> = {
|
|
9
|
+
title: "Components/AutoCompleteField",
|
|
10
|
+
component: AutoCompleteField,
|
|
11
|
+
argTypes: {
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Template: ComponentStory<typeof AutoCompleteField> = (args) => (
|
|
16
|
+
<AutoCompleteField {...args}></AutoCompleteField>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export const Default = Template.bind({});
|
|
20
|
+
export const AdaptQueryAfterSelection = Template.bind({})
|
|
21
|
+
export const AllowReset = Template.bind({})
|
|
22
|
+
export const AllowCustomValues = Template.bind({})
|
|
23
|
+
|
|
24
|
+
// Renders string values as string
|
|
25
|
+
const defaultRenderer = (item: string) => item
|
|
26
|
+
|
|
27
|
+
const defaultArgs: IAutoCompleteFieldProps<string, string> = {
|
|
28
|
+
itemRenderer: (item: string) => `Rendered item: ${item}`,
|
|
29
|
+
itemValueRenderer: (item: string) => `Selected item: ${item}`,
|
|
30
|
+
itemValueString: (item: string) => `Query: ${item}`,
|
|
31
|
+
onSearch: (query: string) => {
|
|
32
|
+
const multiWord = extractSearchWords(query, true)
|
|
33
|
+
return ["search item A", "search element B", "search object C"]
|
|
34
|
+
.filter(word => matchesAllWords(word.toLowerCase(), multiWord))
|
|
35
|
+
},
|
|
36
|
+
onChange: elem => {
|
|
37
|
+
console.log(`Entry '${elem}' selected!`)
|
|
38
|
+
},
|
|
39
|
+
itemValueSelector: defaultRenderer,
|
|
40
|
+
noResultText: "No result"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Changes the search query for the selected item, e.g. when the rendered item text should be different from the query finding this item.
|
|
44
|
+
const adaptQueryAfterSelection: IAutoCompleteFieldProps<string, string> = {
|
|
45
|
+
...defaultArgs,
|
|
46
|
+
resetQueryToValue: (selectedValue: string) => selectedValue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Allows to reset a selection
|
|
50
|
+
const resetSelection: IAutoCompleteFieldProps<string, string> = {
|
|
51
|
+
...defaultArgs,
|
|
52
|
+
reset: {
|
|
53
|
+
resetValue: "",
|
|
54
|
+
resetButtonText: "Clear selected value",
|
|
55
|
+
resettableValue: (value: string) => true
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const allowCustomValues: IAutoCompleteFieldProps<string, string> = {
|
|
60
|
+
...defaultArgs,
|
|
61
|
+
createNewItem: {
|
|
62
|
+
itemRenderer: createNewItemRendererFactory(
|
|
63
|
+
(query: string) => `Create new item: ${query}`,
|
|
64
|
+
"item-add-artefact"
|
|
65
|
+
),
|
|
66
|
+
itemFromQuery: item => item,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Default.args = defaultArgs;
|
|
71
|
+
AdaptQueryAfterSelection.args = adaptQueryAfterSelection
|
|
72
|
+
AllowReset.args = resetSelection
|
|
73
|
+
AllowCustomValues.args = allowCustomValues
|
|
74
|
+
|
|
75
|
+
export default AutoCompleteFieldStory
|