@eccenca/gui-elements 22.1.0-rc.0 → 22.1.0-rc.1
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 +7 -0
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +13 -9
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/cjs/cmem/ActivityControl/ActivityExecutionErrorReportModal.js +23 -6
- package/dist/cjs/cmem/ActivityControl/ActivityExecutionErrorReportModal.js.map +1 -1
- package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js +1 -1
- package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
- package/dist/cjs/cmem/react-flow/configuration/_colors-linking.module.scss +1 -1
- package/dist/cjs/common/utils/stringUtils.js +7 -0
- package/dist/cjs/common/utils/stringUtils.js.map +1 -0
- package/dist/cjs/components/Application/ApplicationContent.js +1 -7
- package/dist/cjs/components/Application/ApplicationContent.js.map +1 -1
- package/dist/cjs/components/Application/ApplicationTitle.js +1 -7
- package/dist/cjs/components/Application/ApplicationTitle.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +0 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +5 -4
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/cjs/components/Breadcrumb/BreadcrumbItem.js +1 -1
- package/dist/cjs/components/Button/Button.js +1 -1
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/Card/Card.js +18 -3
- package/dist/cjs/components/Card/Card.js.map +1 -1
- package/dist/cjs/components/Form/FieldItemRow.js +2 -3
- package/dist/cjs/components/Form/FieldItemRow.js.map +1 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js +34 -41
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/cjs/components/Notification/Notification.js +8 -2
- package/dist/cjs/components/Notification/Notification.js.map +1 -1
- package/dist/cjs/components/OverviewItem/OverviewItemList.js +1 -1
- package/dist/cjs/components/OverviewItem/OverviewItemList.js.map +1 -1
- package/dist/cjs/components/Spinner/Spinner.js +1 -1
- package/dist/cjs/components/Spinner/Spinner.js.map +1 -1
- package/dist/cjs/components/Tabs/Tab.js +1 -2
- package/dist/cjs/components/Tabs/Tab.js.map +1 -1
- package/dist/cjs/components/Tabs/Tabs.js +1 -4
- package/dist/cjs/components/Tabs/Tabs.js.map +1 -1
- package/dist/cjs/components/Typography/HtmlContentBlock.js +6 -6
- package/dist/cjs/components/Typography/HtmlContentBlock.js.map +1 -1
- package/dist/cjs/components/Typography/classnames.js +2 -1
- package/dist/cjs/components/Typography/classnames.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeDefault.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/getSmoothStepPath.js +98 -0
- package/dist/cjs/extensions/react-flow/edges/getSmoothStepPath.js.map +1 -0
- package/dist/cjs/extensions/react-flow/edges/utils.js +21 -20
- package/dist/cjs/extensions/react-flow/edges/utils.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js +6 -4
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeContentExtension.js +3 -2
- package/dist/cjs/extensions/react-flow/nodes/NodeContentExtension.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeTools.js +11 -9
- package/dist/cjs/extensions/react-flow/nodes/NodeTools.js.map +1 -1
- package/dist/es5/cmem/ActivityControl/ActivityControlWidget.js +14 -10
- package/dist/es5/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/es5/cmem/ActivityControl/ActivityExecutionErrorReportModal.js +21 -4
- package/dist/es5/cmem/ActivityControl/ActivityExecutionErrorReportModal.js.map +1 -1
- package/dist/es5/cmem/ActivityControl/SilkActivityControl.js +1 -1
- package/dist/es5/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
- package/dist/es5/cmem/react-flow/configuration/_colors-linking.module.scss +1 -1
- package/dist/es5/common/utils/stringUtils.js +3 -0
- package/dist/es5/common/utils/stringUtils.js.map +1 -0
- package/dist/es5/components/Application/ApplicationContent.js +1 -7
- package/dist/es5/components/Application/ApplicationContent.js.map +1 -1
- package/dist/es5/components/Application/ApplicationTitle.js +1 -7
- package/dist/es5/components/Application/ApplicationTitle.js.map +1 -1
- package/dist/es5/components/AutoSuggestion/AutoSuggestion.js +0 -1
- package/dist/es5/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/es5/components/AutocompleteField/AutoCompleteField.js +5 -4
- package/dist/es5/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/es5/components/Breadcrumb/BreadcrumbItem.js +1 -1
- package/dist/es5/components/Button/Button.js +1 -1
- package/dist/es5/components/Button/Button.js.map +1 -1
- package/dist/es5/components/Card/Card.js +18 -3
- package/dist/es5/components/Card/Card.js.map +1 -1
- package/dist/es5/components/Form/FieldItemRow.js +2 -3
- package/dist/es5/components/Form/FieldItemRow.js.map +1 -1
- package/dist/es5/components/MultiSelect/MultiSelect.js +40 -51
- package/dist/es5/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/es5/components/Notification/Notification.js +10 -3
- package/dist/es5/components/Notification/Notification.js.map +1 -1
- package/dist/es5/components/OverviewItem/OverviewItemList.js +1 -1
- package/dist/es5/components/OverviewItem/OverviewItemList.js.map +1 -1
- package/dist/es5/components/Spinner/Spinner.js +2 -2
- package/dist/es5/components/Spinner/Spinner.js.map +1 -1
- package/dist/es5/components/Tabs/Tab.js +1 -2
- package/dist/es5/components/Tabs/Tab.js.map +1 -1
- package/dist/es5/components/Tabs/Tabs.js +1 -4
- package/dist/es5/components/Tabs/Tabs.js.map +1 -1
- package/dist/es5/components/Typography/HtmlContentBlock.js +17 -8
- package/dist/es5/components/Typography/HtmlContentBlock.js.map +1 -1
- package/dist/es5/components/Typography/classnames.js +1 -0
- package/dist/es5/components/Typography/classnames.js.map +1 -1
- package/dist/es5/extensions/react-flow/edges/EdgeDefault.js.map +1 -1
- package/dist/es5/extensions/react-flow/edges/getSmoothStepPath.js +119 -0
- package/dist/es5/extensions/react-flow/edges/getSmoothStepPath.js.map +1 -0
- package/dist/es5/extensions/react-flow/edges/utils.js +21 -20
- package/dist/es5/extensions/react-flow/edges/utils.js.map +1 -1
- package/dist/es5/extensions/react-flow/nodes/NodeContent.js +7 -5
- package/dist/es5/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/es5/extensions/react-flow/nodes/NodeContentExtension.js +3 -2
- package/dist/es5/extensions/react-flow/nodes/NodeContentExtension.js.map +1 -1
- package/dist/es5/extensions/react-flow/nodes/NodeTools.js +12 -10
- package/dist/es5/extensions/react-flow/nodes/NodeTools.js.map +1 -1
- package/dist/es5/index.js +1 -3
- package/dist/es5/index.js.map +1 -1
- package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +9 -0
- package/dist/types/cmem/ActivityControl/SilkActivityControl.d.ts +2 -0
- package/dist/types/common/utils/stringUtils.d.ts +2 -0
- package/dist/types/components/Card/Card.d.ts +9 -4
- package/dist/types/components/Form/FieldItemRow.d.ts +1 -0
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +16 -9
- package/dist/types/components/Notification/Notification.d.ts +55 -2
- package/dist/types/components/OverviewItem/OverviewItemList.d.ts +1 -1
- package/dist/types/components/Spinner/Spinner.d.ts +1 -1
- package/dist/types/components/Typography/HtmlContentBlock.d.ts +15 -5
- package/dist/types/components/Typography/classnames.d.ts +1 -0
- package/dist/types/extensions/react-flow/edges/getSmoothStepPath.d.ts +18 -0
- package/dist/types/extensions/react-flow/nodes/NodeContent.d.ts +8 -2
- package/dist/types/extensions/react-flow/nodes/NodeContentExtension.d.ts +5 -1
- package/dist/types/extensions/react-flow/nodes/NodeTools.d.ts +7 -1
- package/dist/types/index.d.ts +1 -3
- package/package.json +22 -13
- package/src/cmem/ActivityControl/ActivityControlWidget.stories.tsx +6 -6
- package/src/cmem/ActivityControl/ActivityControlWidget.tsx +27 -14
- package/src/cmem/ActivityControl/ActivityExecutionErrorReportModal.tsx +11 -3
- package/src/cmem/ActivityControl/SilkActivityControl.tsx +5 -1
- package/src/cmem/react-flow/_nodes.scss +12 -6
- package/src/cmem/react-flow/configuration/_colors-linking.module.scss +1 -1
- package/src/common/utils/stringUtils.ts +2 -0
- package/src/components/Application/ApplicationContent.tsx +0 -5
- package/src/components/Application/ApplicationTitle.tsx +0 -5
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +0 -1
- package/src/components/AutocompleteField/AutoCompleteField.tsx +5 -4
- package/src/components/Breadcrumb/BreadcrumbItem.tsx +5 -5
- package/src/components/Breadcrumb/BreadcrumbList.tsx +1 -1
- package/src/components/Button/Button.stories.tsx +1 -1
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.tsx +23 -4
- package/src/components/Card/card.scss +4 -0
- package/src/components/Checkbox/checkbox.scss +4 -0
- package/src/components/Form/FieldItem.tsx +3 -3
- package/src/components/Form/FieldItemRow.tsx +2 -4
- package/src/components/Icon/IconButton.tsx +2 -2
- package/src/components/Menu/MenuItem.tsx +1 -1
- package/src/components/MultiSelect/MultiSelect.stories.tsx +15 -7
- package/src/components/MultiSelect/MultiSelect.tsx +63 -60
- package/src/components/Notification/Notification.stories.tsx +82 -0
- package/src/components/Notification/Notification.tsx +62 -4
- package/src/components/Notification/notification.scss +2 -1
- package/src/components/OverviewItem/OverviewItemList.tsx +2 -2
- package/src/components/RadioButton/radiobutton.scss +4 -0
- package/src/components/Spinner/Spinner.tsx +1 -1
- package/src/components/Switch/switch.scss +1 -0
- package/src/components/Tabs/Tab.tsx +1 -2
- package/src/components/Tabs/Tabs.tsx +1 -5
- package/src/components/Tabs/stories/Tabs.stories.tsx +3 -3
- package/src/components/Tag/tag.scss +1 -1
- package/src/components/Toolbar/toolbar.scss +1 -1
- package/src/components/Typography/HtmlContentBlock.tsx +27 -4
- package/src/components/Typography/classnames.ts +1 -0
- package/src/components/Typography/typography.scss +8 -0
- package/src/extensions/react-flow/_react-flow.scss +4 -0
- package/src/extensions/react-flow/edges/EdgeDefault.tsx +0 -1
- package/src/extensions/react-flow/edges/getSmoothStepPath.ts +118 -0
- package/src/extensions/react-flow/edges/utils.ts +21 -29
- package/src/extensions/react-flow/nodes/NodeContent.tsx +13 -7
- package/src/extensions/react-flow/nodes/NodeContentExtension.tsx +6 -0
- package/src/extensions/react-flow/nodes/NodeTools.tsx +25 -9
- package/src/extensions/react-flow/nodes/_nodes.scss +31 -12
- package/src/extensions/react-flow/nodes/stories/NodeContent.stories.tsx +10 -1
- package/src/extensions/react-flow/nodes/stories/NodeContentExtension.stories.tsx +23 -0
- package/src/index.ts +3 -3
|
@@ -272,7 +272,8 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
272
272
|
// Disable highlighting, since we used empty string search
|
|
273
273
|
enableHighlighting = false;
|
|
274
274
|
// Put selected item at the top if it is not in the result list
|
|
275
|
-
if (!!selectedItem && itemIndexOf(emptyStringResults, selectedItem)
|
|
275
|
+
if (!!selectedItem && itemIndexOf(emptyStringResults, selectedItem) > -1) {
|
|
276
|
+
emptyStringResults.splice(itemIndexOf(emptyStringResults, selectedItem), 1)
|
|
276
277
|
result = [selectedItem, ...emptyStringResults];
|
|
277
278
|
} else {
|
|
278
279
|
result = emptyStringResults;
|
|
@@ -346,7 +347,7 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
346
347
|
};
|
|
347
348
|
const updatedPopOverProps: Partial<IPopoverProps> = {
|
|
348
349
|
minimal: true,
|
|
349
|
-
position: "bottom",
|
|
350
|
+
position: "bottom-left",
|
|
350
351
|
popoverClassName: `${eccgui}-autocompletefield__options`,
|
|
351
352
|
wrapperTagName: "div",
|
|
352
353
|
boundary: "window",
|
|
@@ -385,12 +386,12 @@ export function AutoCompleteField<T extends any, UPDATE_VALUE extends any>(props
|
|
|
385
386
|
onQueryChange={(q) => setQuery(q)}
|
|
386
387
|
closeOnSelect={true}
|
|
387
388
|
query={query}
|
|
388
|
-
//
|
|
389
|
+
// This leads to odd compile errors without "as any"
|
|
389
390
|
popoverProps={updatedPopOverProps as any}
|
|
390
391
|
selectedItem={selectedItem}
|
|
391
392
|
fill
|
|
392
393
|
{...createNewItemProps}
|
|
393
|
-
//
|
|
394
|
+
// This leads to odd compile errors without "as any"
|
|
394
395
|
inputProps={updatedInputProps as any}
|
|
395
396
|
itemListRenderer={listLoading ? () => <Menu><MenuItem disabled={true} text={<Spinner position={"inline"} />} style={fieldWidthLimits} /></Menu> : undefined}
|
|
396
397
|
/>
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
8
8
|
import { openInNewTab } from "../../common/utils/openInNewTab";
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// FIXME: enforce href and remove onClick later
|
|
11
11
|
export type IBreadcrumbItemProps = Omit<
|
|
12
12
|
IBlueprintBreadcrumbItemProps,
|
|
13
13
|
// we remove some properties that are currently not necessary, required usage should be discussed
|
|
@@ -19,14 +19,14 @@ export type IBreadcrumbItemProps = Omit<
|
|
|
19
19
|
|
|
20
20
|
function BreadcrumbItem({
|
|
21
21
|
className = "",
|
|
22
|
-
onClick,
|
|
23
|
-
href,
|
|
22
|
+
onClick,
|
|
23
|
+
href,
|
|
24
24
|
//itemDivider='',
|
|
25
25
|
...otherBlueprintBreadcrumbProps
|
|
26
26
|
}: IBreadcrumbItemProps) {
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
/*
|
|
29
|
-
|
|
29
|
+
FIXME: adding `data-divider` does not work this way because BlueprintJS
|
|
30
30
|
breadcrumb component does not support (and forward) it on HTML element
|
|
31
31
|
level. The idea is to add the divider as data-* property to use it via
|
|
32
32
|
CSS/Sass as content for the pseudo element, currently done static in CSS
|
|
@@ -18,7 +18,7 @@ type ReducedBreadcrumbsProps = Omit<
|
|
|
18
18
|
"popoverProps"
|
|
19
19
|
>;
|
|
20
20
|
|
|
21
|
-
//
|
|
21
|
+
// FIXME: enforce onItemClick later when href value can always be routed correctly
|
|
22
22
|
interface IBreadcrumbListProps extends ReducedBreadcrumbsProps {
|
|
23
23
|
/**
|
|
24
24
|
list of breadcrumb items to display
|
|
@@ -7,18 +7,23 @@ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
|
7
7
|
|
|
8
8
|
export interface CardProps extends BlueprintCardProps {
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* `<Card />` element is included in DOM as simple `div` element.
|
|
11
11
|
* By default it is a HTML `section`.
|
|
12
12
|
*/
|
|
13
13
|
isOnlyLayout?: boolean;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Take the full height of container to display the card.
|
|
16
16
|
*/
|
|
17
17
|
fullHeight?: boolean;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Background color is slightly altered to differ card display from other cards.
|
|
20
20
|
*/
|
|
21
21
|
elevated?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* When card (or its children) get focus the card is scrolled into the viewport.
|
|
24
|
+
* Property value defined which part of the card is always scrolled in, this may important when the card is larger than the viewport.
|
|
25
|
+
*/
|
|
26
|
+
scrollinOnFocus?: "start" | "center" | "end";
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
/**
|
|
@@ -32,26 +37,40 @@ function Card({
|
|
|
32
37
|
isOnlyLayout=false,
|
|
33
38
|
fullHeight=false,
|
|
34
39
|
elevated=false,
|
|
40
|
+
scrollinOnFocus,
|
|
35
41
|
interactive,
|
|
36
42
|
...otherProps
|
|
37
43
|
}: CardProps) {
|
|
44
|
+
const scrollIn = !!scrollinOnFocus ? {
|
|
45
|
+
tabIndex: 0,
|
|
46
|
+
onFocus: (e: any) => {
|
|
47
|
+
const el = e.target.closest(".diapp-iframewindow__content");
|
|
48
|
+
setTimeout(()=>{if (el) el.scrollIntoView({
|
|
49
|
+
behavior: "smooth",
|
|
50
|
+
block: scrollinOnFocus,
|
|
51
|
+
inline: scrollinOnFocus,
|
|
52
|
+
})}, 100);
|
|
53
|
+
}
|
|
54
|
+
} : {}
|
|
38
55
|
const cardElement = (
|
|
39
56
|
<BlueprintCard
|
|
40
57
|
className={
|
|
41
58
|
`${eccgui}-card ` +
|
|
42
59
|
(fullHeight ? ` ${eccgui}-card--fullheight ` : '') +
|
|
43
60
|
(elevated ? ` ${eccgui}-card--elevated ` : '') +
|
|
61
|
+
(!!scrollinOnFocus ? ` ${eccgui}-card--scrollonfocus ` : '') +
|
|
44
62
|
className
|
|
45
63
|
}
|
|
46
64
|
elevation={elevation}
|
|
47
65
|
interactive={!!otherProps.onClick ? true : interactive}
|
|
66
|
+
{...scrollIn}
|
|
48
67
|
{...otherProps}
|
|
49
68
|
>
|
|
50
69
|
{children}
|
|
51
70
|
</BlueprintCard>
|
|
52
71
|
);
|
|
53
72
|
|
|
54
|
-
//
|
|
73
|
+
// FIXME: improve Card element so it is itself a section html element
|
|
55
74
|
return isOnlyLayout === false ? <section>{cardElement}</section> : cardElement;
|
|
56
75
|
};
|
|
57
76
|
|
|
@@ -44,6 +44,10 @@ $eccgui-size-card-spacing: $eccgui-size-typo-base !default;
|
|
|
44
44
|
@extend .#{$eccgui}-grid__row--fullheight;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
.#{$eccgui}-card--scrollonfocus {
|
|
48
|
+
outline: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
.#{$eccgui}-card__header {
|
|
48
52
|
padding: $eccgui-size-card-spacing/2 $eccgui-size-card-spacing/2 $eccgui-size-card-spacing/2 $eccgui-size-card-spacing;
|
|
49
53
|
box-sizing: content-box;
|
|
@@ -35,6 +35,10 @@ $switch-checked-background-color-disabled: rgba($switch-checked-background-color
|
|
|
35
35
|
box-shadow: none;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
input:focus ~ .#{$ns}-control-indicator {
|
|
39
|
+
outline-offset: 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
38
42
|
input:disabled ~ .#{$ns}-control-indicator,
|
|
39
43
|
input:disabled:checked ~ .#{$ns}-control-indicator,
|
|
40
44
|
{
|
|
@@ -5,10 +5,10 @@ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
|
5
5
|
import {TestableComponent} from "../interfaces";
|
|
6
6
|
|
|
7
7
|
/*
|
|
8
|
-
|
|
8
|
+
FIXME: Improve development convenience and prevent development errors
|
|
9
9
|
|
|
10
|
-
* disabled
|
|
11
|
-
currently this need to be dome
|
|
10
|
+
* disabled state could be automatically forwarded to inserted input element,
|
|
11
|
+
currently this need to be dome explicitly .
|
|
12
12
|
* input id could be forwarded to label and input element
|
|
13
13
|
* input id could be created when not given
|
|
14
14
|
*/
|
|
@@ -2,11 +2,9 @@ import React from "react";
|
|
|
2
2
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* allow grow factors for children
|
|
5
|
+
FIXME: allow grow factors for children
|
|
8
6
|
*/
|
|
9
|
-
|
|
7
|
+
/** Allows to layout field items horizontally. */
|
|
10
8
|
function FieldItemRow({ children, className, ...otherProps }: any) {
|
|
11
9
|
return <div className={`${eccgui}-fielditem__row` + (className ? " " + className : "")}>{children}</div>;
|
|
12
10
|
}
|
|
@@ -10,7 +10,7 @@ interface IconButtonProps extends ButtonProps {
|
|
|
10
10
|
className?: string
|
|
11
11
|
// Tooltip text
|
|
12
12
|
text?: string,
|
|
13
|
-
// Time after tooltip text is
|
|
13
|
+
// Time after tooltip text is visible when icon is hovered/focuses
|
|
14
14
|
tooltipOpenDelay?: number,
|
|
15
15
|
// Display large icon version
|
|
16
16
|
large?: boolean,
|
|
@@ -18,7 +18,7 @@ interface IconButtonProps extends ButtonProps {
|
|
|
18
18
|
small?: boolean
|
|
19
19
|
// If the tooltip should be set as title attribute instead of the Tooltip component. If true, all other tooltip parameters are ignored.
|
|
20
20
|
tooltipAsTitle?: boolean
|
|
21
|
-
// If set, button will become an anchor button. FIXME: Setting this parameter should not change the type of the button. Split into several components.
|
|
21
|
+
// If set, button will become an anchor button. FIXME: CMEM-3742: Setting this parameter should not change the type of the button. Split into several components.
|
|
22
22
|
href?: string
|
|
23
23
|
// Additional description of the icon function to improve accessibility, if not defined then the text label is used as fallback
|
|
24
24
|
description?: string
|
|
@@ -11,7 +11,7 @@ interface IProps {
|
|
|
11
11
|
icon?: ValidIconName | string[]
|
|
12
12
|
// Props defined by the Blueprint component that should be forwarded
|
|
13
13
|
internalProps?: Partial<MenuItemProps> & React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
14
|
-
// FIXME: For backward compatibility, should be avoided in all code bases
|
|
14
|
+
// FIXME: CMEM-3742: For backward compatibility, should be avoided in all code bases
|
|
15
15
|
[key: string]: any
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ComponentStory, ComponentMeta } from "@storybook/react";
|
|
3
|
-
import {
|
|
3
|
+
import { loremIpsum } from "react-lorem-ipsum";
|
|
4
4
|
|
|
5
5
|
import MultiSelectExample from "./MultiSelect";
|
|
6
6
|
|
|
@@ -36,8 +36,16 @@ const Template: ComponentStory<typeof MultiSelectExample> = (args) => <MultiSele
|
|
|
36
36
|
|
|
37
37
|
export const Default = Template.bind({});
|
|
38
38
|
|
|
39
|
-
const
|
|
40
|
-
|
|
39
|
+
const testLabels = loremIpsum({
|
|
40
|
+
p: 1,
|
|
41
|
+
avgSentencesPerParagraph: 5,
|
|
42
|
+
avgWordsPerSentence: 1,
|
|
43
|
+
startWithLoremIpsum: false,
|
|
44
|
+
random: false
|
|
45
|
+
}).toString().split(".");
|
|
46
|
+
|
|
47
|
+
const items = new Array(5).fill(undefined).map((_, id) => {
|
|
48
|
+
const testLabel = testLabels[id];
|
|
41
49
|
return { testLabel, testId: `${testLabel}-id` };
|
|
42
50
|
});
|
|
43
51
|
|
|
@@ -45,8 +53,8 @@ Default.args = {
|
|
|
45
53
|
items,
|
|
46
54
|
canCreateNewItem: true,
|
|
47
55
|
prePopulateWithItems: false,
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
itemId: item => item.testId,
|
|
57
|
+
itemLabel: item => item.testLabel,
|
|
50
58
|
openOnKeyDown: true,
|
|
51
59
|
};
|
|
52
60
|
|
|
@@ -58,6 +66,6 @@ openDropdownWhenFocused.args = {
|
|
|
58
66
|
items,
|
|
59
67
|
canCreateNewItem: true,
|
|
60
68
|
prePopulateWithItems: false,
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
itemId: item => item.testId,
|
|
70
|
+
itemLabel: item => item.testLabel,
|
|
63
71
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
import { MultiSelect as BlueprintMultiSelect, MultiSelectProps
|
|
2
|
+
import {Intent as BlueprintIntent} from "@blueprintjs/core";
|
|
3
|
+
import {IItemRendererProps, MultiSelect as BlueprintMultiSelect, MultiSelectProps} from "@blueprintjs/select";
|
|
4
4
|
import MenuItem from "../Menu/MenuItem";
|
|
5
5
|
import Highlighter from "../Typography/Highlighter";
|
|
6
6
|
import Button from "../Button/Button";
|
|
7
7
|
import OverflowText from "../Typography/OverflowText";
|
|
8
|
+
import {HTMLInputProps} from "@blueprintjs/core/src/common/props";
|
|
9
|
+
import {removeExtraSpaces} from "../../common/utils/stringUtils";
|
|
8
10
|
|
|
9
11
|
interface SelectedParamsType<T> {
|
|
10
12
|
newlySelected: T;
|
|
@@ -14,18 +16,14 @@ interface SelectedParamsType<T> {
|
|
|
14
16
|
|
|
15
17
|
interface IProps<T> extends Pick<MultiSelectProps<T>, "items" | "placeholder" | "openOnKeyDown"> {
|
|
16
18
|
/**
|
|
17
|
-
*
|
|
19
|
+
* Returns the unique ID of an item. This will be used for equality of items.
|
|
18
20
|
*/
|
|
19
|
-
|
|
21
|
+
itemId: (item: T) => string;
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
23
|
+
* Returns the label of an item.
|
|
22
24
|
* this would be used in the item selection list as well as the multi-select input
|
|
23
25
|
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* if new items that are not in the original item list can be created and appended
|
|
27
|
-
*/
|
|
28
|
-
canCreateNewItem?: boolean;
|
|
26
|
+
itemLabel: (item: T) => string;
|
|
29
27
|
/**
|
|
30
28
|
* When set to true will set the multi-select value with all the items provided
|
|
31
29
|
*/
|
|
@@ -43,6 +41,9 @@ interface IProps<T> extends Pick<MultiSelectProps<T>, "items" | "placeholder" |
|
|
|
43
41
|
*/
|
|
44
42
|
tagInputProps?: MultiSelectProps<T>["tagInputProps"];
|
|
45
43
|
|
|
44
|
+
/** Additional properties for the (query) input field of the multi-selection. */
|
|
45
|
+
inputProps?: HTMLInputProps;
|
|
46
|
+
|
|
46
47
|
/**
|
|
47
48
|
* prop to listen for query changes, when text is entered in the multi-select input
|
|
48
49
|
*/
|
|
@@ -62,6 +63,14 @@ interface IProps<T> extends Pick<MultiSelectProps<T>, "items" | "placeholder" |
|
|
|
62
63
|
* If omitted, "No results." will be rendered in this case.
|
|
63
64
|
*/
|
|
64
65
|
newItemCreationText?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Allows to creates new item from a given query. If this is not provided then no new items can be created.
|
|
68
|
+
*/
|
|
69
|
+
createNewItemFromQuery?: (query: string) => T
|
|
70
|
+
/**
|
|
71
|
+
* Items that were newly created and not taken from the list will be post-fixed with this string.
|
|
72
|
+
*/
|
|
73
|
+
newItemPostfix?: string
|
|
65
74
|
/**
|
|
66
75
|
* The input element is displayed with primary color scheme.
|
|
67
76
|
*/
|
|
@@ -87,21 +96,23 @@ interface IProps<T> extends Pick<MultiSelectProps<T>, "items" | "placeholder" |
|
|
|
87
96
|
function MultiSelect<T>({
|
|
88
97
|
items,
|
|
89
98
|
prePopulateWithItems,
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
itemId,
|
|
100
|
+
itemLabel,
|
|
92
101
|
onSelection,
|
|
93
|
-
canCreateNewItem,
|
|
94
102
|
popoverProps,
|
|
95
103
|
tagInputProps,
|
|
104
|
+
inputProps,
|
|
96
105
|
runOnQueryChange,
|
|
97
106
|
fullWidth = true,
|
|
98
107
|
noResultText = "No results.",
|
|
99
|
-
newItemCreationText = "Add new
|
|
108
|
+
newItemCreationText = "Add new item",
|
|
109
|
+
newItemPostfix = " (new item)",
|
|
100
110
|
hasStatePrimary,
|
|
101
111
|
hasStateDanger,
|
|
102
112
|
hasStateSuccess,
|
|
103
113
|
hasStateWarning,
|
|
104
114
|
disabled,
|
|
115
|
+
createNewItemFromQuery,
|
|
105
116
|
...otherProps
|
|
106
117
|
}: IProps<T>) {
|
|
107
118
|
const [createdItems, setCreatedItems] = React.useState<T[]>([]);
|
|
@@ -111,7 +122,7 @@ function MultiSelect<T>({
|
|
|
111
122
|
const [query, setQuery] = React.useState<string | undefined>(undefined);
|
|
112
123
|
//currently focused element in popover list
|
|
113
124
|
const [focusedItem, setFocusedItem] = React.useState<T | null>(null);
|
|
114
|
-
const
|
|
125
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
115
126
|
|
|
116
127
|
let intent;
|
|
117
128
|
switch (true) {
|
|
@@ -138,7 +149,7 @@ function MultiSelect<T>({
|
|
|
138
149
|
setItemsCopy([...items, ...createdItems]);
|
|
139
150
|
setFilteredItemList([...items, ...createdItems]);
|
|
140
151
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
141
|
-
}, [items.map((item) => item
|
|
152
|
+
}, [items.map((item) => itemId(item)).join("|")]);
|
|
142
153
|
|
|
143
154
|
React.useEffect(() => {
|
|
144
155
|
onSelection &&
|
|
@@ -150,8 +161,8 @@ function MultiSelect<T>({
|
|
|
150
161
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
151
162
|
}, [
|
|
152
163
|
onSelection,
|
|
153
|
-
selectedItems.map((item) => item
|
|
154
|
-
createdItems.map((item) => item
|
|
164
|
+
selectedItems.map((item) => itemId(item)).join("|"),
|
|
165
|
+
createdItems.map((item) => itemId(item)).join("|"),
|
|
155
166
|
]);
|
|
156
167
|
|
|
157
168
|
/**
|
|
@@ -160,7 +171,7 @@ function MultiSelect<T>({
|
|
|
160
171
|
* @returns
|
|
161
172
|
*/
|
|
162
173
|
const itemHasBeenSelectedAlready = (matcher: string) => {
|
|
163
|
-
return !!selectedItems.find((item) =>
|
|
174
|
+
return !!selectedItems.find((item) => itemId(item) === matcher);
|
|
164
175
|
};
|
|
165
176
|
|
|
166
177
|
/**
|
|
@@ -168,7 +179,7 @@ function MultiSelect<T>({
|
|
|
168
179
|
* @param matcher
|
|
169
180
|
*/
|
|
170
181
|
const removeItemSelection = (matcher: string) => {
|
|
171
|
-
setSelectedItems((items) => items.filter((
|
|
182
|
+
setSelectedItems((items) => items.filter((item) => itemId(item) !== matcher));
|
|
172
183
|
};
|
|
173
184
|
|
|
174
185
|
/**
|
|
@@ -177,12 +188,12 @@ function MultiSelect<T>({
|
|
|
177
188
|
* @param item
|
|
178
189
|
*/
|
|
179
190
|
const onItemSelect = (item: T) => {
|
|
180
|
-
if (itemHasBeenSelectedAlready(
|
|
181
|
-
removeItemSelection(
|
|
191
|
+
if (itemHasBeenSelectedAlready(itemId(item))) {
|
|
192
|
+
removeItemSelection(itemId(item));
|
|
182
193
|
} else {
|
|
183
194
|
setSelectedItems((items) => [...items, item]);
|
|
184
195
|
}
|
|
185
|
-
|
|
196
|
+
inputRef.current?.select()
|
|
186
197
|
};
|
|
187
198
|
|
|
188
199
|
/**
|
|
@@ -192,10 +203,11 @@ function MultiSelect<T>({
|
|
|
192
203
|
const onQueryChange = async (query: string) => {
|
|
193
204
|
if (query.length) {
|
|
194
205
|
setQuery(query);
|
|
206
|
+
setFilteredItemList([])
|
|
195
207
|
const resultFromQuery = runOnQueryChange && (await runOnQueryChange(removeExtraSpaces(query)));
|
|
196
208
|
setFilteredItemList(() =>
|
|
197
|
-
[...(resultFromQuery ?? itemsCopy), ...createdItems].filter(
|
|
198
|
-
|
|
209
|
+
[...(resultFromQuery ?? itemsCopy), ...createdItems].filter(item =>
|
|
210
|
+
itemLabel(item).toLowerCase().includes(query.toLowerCase())
|
|
199
211
|
)
|
|
200
212
|
);
|
|
201
213
|
}
|
|
@@ -208,24 +220,22 @@ function MultiSelect<T>({
|
|
|
208
220
|
|
|
209
221
|
/**
|
|
210
222
|
* defines how an item in the item list is displayed
|
|
211
|
-
* @param tag
|
|
212
|
-
* @param param
|
|
213
|
-
* @returns
|
|
214
223
|
*/
|
|
215
224
|
const onItemRenderer = (item: T, { handleClick, modifiers }: IItemRendererProps) => {
|
|
216
225
|
if (!modifiers.matchesPredicate) {
|
|
217
226
|
return null;
|
|
218
227
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
let label = itemLabel(item)
|
|
229
|
+
if(createdItems.find(created => itemId(created) === itemId(item))) {
|
|
230
|
+
label += newItemPostfix
|
|
231
|
+
}
|
|
222
232
|
return (
|
|
223
233
|
<MenuItem
|
|
224
234
|
active={modifiers.active}
|
|
225
|
-
key={
|
|
226
|
-
icon={itemHasBeenSelectedAlready(
|
|
235
|
+
key={itemId(item)}
|
|
236
|
+
icon={itemHasBeenSelectedAlready(itemId(item)) ? "state-checked" : "state-unchecked"}
|
|
227
237
|
onClick={handleClick}
|
|
228
|
-
text={optionRenderer(
|
|
238
|
+
text={optionRenderer(label)}
|
|
229
239
|
shouldDismissPopover={false}
|
|
230
240
|
/>
|
|
231
241
|
);
|
|
@@ -246,18 +256,14 @@ function MultiSelect<T>({
|
|
|
246
256
|
*/
|
|
247
257
|
const removeTagFromSelectionViaIndex = (label: string, index: number) => {
|
|
248
258
|
setSelectedItems([...selectedItems.slice(0, index), ...selectedItems.slice(index + 1)]);
|
|
249
|
-
setCreatedItems(
|
|
259
|
+
setCreatedItems(items => items.filter(item => itemLabel(item) !== label));
|
|
250
260
|
};
|
|
251
261
|
|
|
252
|
-
const removeExtraSpaces = (text: string) => text.replace(/\s+/g, " ").trim();
|
|
253
|
-
|
|
254
262
|
/**
|
|
255
|
-
*
|
|
256
|
-
* @param event
|
|
257
|
-
* @param label
|
|
263
|
+
* Utility function to create a new Item. createNewItemFromQuery is assumed to be defined!
|
|
258
264
|
*/
|
|
259
|
-
const createNewItem = (
|
|
260
|
-
const newItem =
|
|
265
|
+
const createNewItem = (query: string): T => {
|
|
266
|
+
const newItem = createNewItemFromQuery!!(query);
|
|
261
267
|
//set new items
|
|
262
268
|
setCreatedItems((items) => [...items, newItem]);
|
|
263
269
|
setQuery("");
|
|
@@ -270,10 +276,10 @@ function MultiSelect<T>({
|
|
|
270
276
|
* @param event
|
|
271
277
|
*/
|
|
272
278
|
const handleOnKeyUp = (event: React.KeyboardEvent<HTMLElement>) => {
|
|
273
|
-
if (event.key === "Enter" && !filteredItemList.length && !!query) {
|
|
274
|
-
createNewItem(
|
|
279
|
+
if (event.key === "Enter" && !filteredItemList.length && !!query && createNewItemFromQuery) {
|
|
280
|
+
createNewItem(query);
|
|
275
281
|
}
|
|
276
|
-
|
|
282
|
+
inputRef.current?.focus();
|
|
277
283
|
};
|
|
278
284
|
|
|
279
285
|
/**
|
|
@@ -284,8 +290,9 @@ function MultiSelect<T>({
|
|
|
284
290
|
const handleOnKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
|
|
285
291
|
if (event.key === "Tab" && !!query) {
|
|
286
292
|
event.preventDefault();
|
|
287
|
-
focusedItem ? onItemSelect(focusedItem) : onItemSelect(createNewItem(
|
|
288
|
-
|
|
293
|
+
focusedItem ? onItemSelect(focusedItem) : onItemSelect(createNewItem(query));
|
|
294
|
+
setQuery("");
|
|
295
|
+
setTimeout(() => inputRef.current?.focus());
|
|
289
296
|
}
|
|
290
297
|
};
|
|
291
298
|
|
|
@@ -297,14 +304,14 @@ function MultiSelect<T>({
|
|
|
297
304
|
* @returns
|
|
298
305
|
*/
|
|
299
306
|
const newItemRenderer = (label: string, active: boolean, handleClick: React.MouseEventHandler<HTMLElement>) => {
|
|
300
|
-
if (!
|
|
307
|
+
if (!createNewItemFromQuery) return undefined;
|
|
301
308
|
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
|
302
|
-
createNewItem(
|
|
309
|
+
createNewItem(label);
|
|
303
310
|
handleClick(e);
|
|
304
311
|
};
|
|
305
312
|
return (
|
|
306
313
|
<MenuItem
|
|
307
|
-
id={"new-
|
|
314
|
+
id={"new-item"}
|
|
308
315
|
icon="item-add-artefact"
|
|
309
316
|
active={active}
|
|
310
317
|
key={label}
|
|
@@ -320,32 +327,28 @@ function MultiSelect<T>({
|
|
|
320
327
|
) : undefined;
|
|
321
328
|
|
|
322
329
|
return (
|
|
323
|
-
<BlueprintMultiSelect
|
|
330
|
+
<BlueprintMultiSelect<T>
|
|
324
331
|
{...otherProps}
|
|
325
332
|
query={query}
|
|
326
333
|
onQueryChange={onQueryChange}
|
|
327
334
|
items={filteredItemList}
|
|
328
335
|
onItemSelect={onItemSelect}
|
|
329
336
|
itemRenderer={onItemRenderer}
|
|
330
|
-
itemsEqual={(a: T, b: T) => a
|
|
337
|
+
itemsEqual={(a: T, b: T) => itemId(a) === itemId(b)}
|
|
331
338
|
selectedItems={selectedItems}
|
|
332
339
|
noResults={<MenuItem disabled={true} text={noResultText} />}
|
|
333
|
-
tagRenderer={
|
|
340
|
+
tagRenderer={item => itemLabel(item)}
|
|
334
341
|
createNewItemRenderer={newItemRenderer}
|
|
335
342
|
onActiveItemChange={(activeItem) => setFocusedItem(activeItem)}
|
|
336
343
|
fill={fullWidth}
|
|
337
|
-
createNewItemFromQuery={
|
|
338
|
-
({
|
|
339
|
-
[labelProp]: removeExtraSpaces(query),
|
|
340
|
-
[equalityProp]: removeExtraSpaces(query),
|
|
341
|
-
} as any)
|
|
342
|
-
}
|
|
344
|
+
createNewItemFromQuery={createNewItemFromQuery}
|
|
343
345
|
tagInputProps={{
|
|
344
346
|
inputProps: {
|
|
345
347
|
id: "item",
|
|
346
348
|
autoComplete: "off",
|
|
349
|
+
...inputProps
|
|
347
350
|
},
|
|
348
|
-
inputRef:
|
|
351
|
+
inputRef: inputRef,
|
|
349
352
|
intent,
|
|
350
353
|
addOnBlur: true,
|
|
351
354
|
onKeyDown: handleOnKeyDown,
|