@eccenca/gui-elements 24.4.1-featurepreparefinalnextcmem6943.2 → 25.0.0-rc.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 +29 -12
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +1 -1
- package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +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/Dialog/Modal.js +15 -3
- package/dist/cjs/components/Dialog/Modal.js.map +1 -1
- package/dist/cjs/components/Dialog/ModalContext.js +45 -0
- package/dist/cjs/components/Dialog/ModalContext.js.map +1 -0
- package/dist/cjs/components/Dialog/index.js +1 -0
- package/dist/cjs/components/Dialog/index.js.map +1 -1
- package/dist/cjs/components/Icon/canonicalIconNames.js +4 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/Spinner/Spinner.js +2 -1
- package/dist/cjs/components/Spinner/Spinner.js.map +1 -1
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js +2 -2
- package/dist/cjs/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/esm/components/AutocompleteField/AutoCompleteField.js +1 -1
- package/dist/esm/components/AutocompleteField/AutoCompleteField.js.map +1 -1
- package/dist/esm/components/Button/Button.js +1 -1
- package/dist/esm/components/Button/Button.js.map +1 -1
- package/dist/esm/components/Dialog/Modal.js +15 -3
- package/dist/esm/components/Dialog/Modal.js.map +1 -1
- package/dist/esm/components/Dialog/ModalContext.js +63 -0
- package/dist/esm/components/Dialog/ModalContext.js.map +1 -0
- package/dist/esm/components/Dialog/index.js +1 -0
- package/dist/esm/components/Dialog/index.js.map +1 -1
- package/dist/esm/components/Icon/canonicalIconNames.js +4 -0
- package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/esm/components/Spinner/Spinner.js +2 -1
- package/dist/esm/components/Spinner/Spinner.js.map +1 -1
- package/dist/esm/extensions/react-flow/nodes/NodeContent.js +2 -2
- package/dist/esm/extensions/react-flow/nodes/NodeContent.js.map +1 -1
- package/dist/types/components/Button/Button.d.ts +14 -7
- package/dist/types/components/Dialog/Modal.d.ts +9 -1
- package/dist/types/components/Dialog/ModalContext.d.ts +13 -0
- package/dist/types/components/Dialog/index.d.ts +1 -0
- package/dist/types/components/Icon/canonicalIconNames.d.ts +4 -0
- package/dist/types/components/Spinner/Spinner.d.ts +11 -4
- package/dist/types/extensions/react-flow/edges/EdgeLabel.d.ts +1 -1
- package/package.json +1 -1
- package/src/cmem/react-flow/_minimap.scss +10 -0
- package/src/cmem/react-flow/configuration/_colors-graph.scss +12 -12
- package/src/cmem/react-flow/configuration/_colors-linking.scss +8 -8
- package/src/cmem/react-flow/configuration/_colors-workflow.scss +11 -11
- package/src/common/scss/_color-functions.scss +5 -0
- package/src/components/AutocompleteField/AutoCompleteField.tsx +1 -0
- package/src/components/Button/Button.stories.tsx +7 -1
- package/src/components/Button/Button.tsx +16 -9
- package/src/components/Button/button.scss +86 -24
- package/src/components/Chat/stories/ChatField.stories.tsx +6 -1
- package/src/components/Dialog/Modal.tsx +28 -3
- package/src/components/Dialog/ModalContext.tsx +48 -0
- package/src/components/Dialog/index.ts +1 -0
- package/src/components/Dialog/stories/Modal.stories.tsx +143 -2
- package/src/components/Icon/canonicalIconNames.tsx +4 -0
- package/src/components/Icon/icon.scss +6 -0
- package/src/components/Icon/stories/Icon.stories.tsx +65 -5
- package/src/components/Icon/stories/IconButton.stories.tsx +2 -1
- package/src/components/Notification/Notification.stories.tsx +20 -6
- package/src/components/Notification/notification.scss +7 -2
- package/src/components/ProgressBar/Stories/ProgressBar.stories.tsx +7 -1
- package/src/components/Select/Select.stories.tsx +1 -1
- package/src/components/Spinner/Spinner.tsx +13 -3
- package/src/components/Spinner/Stories/spinner.stories.tsx +1 -1
- package/src/components/Spinner/spinner.scss +5 -1
- package/src/components/Tag/tag.scss +89 -68
- package/src/components/TextField/textfield.scss +23 -15
- package/src/components/VisualTour/stories/VisualTour.stories.tsx +1 -1
- package/src/configuration/_palettes.scss +1 -0
- package/src/extensions/react-flow/_react-flow_v12.scss +10 -14
- package/src/extensions/react-flow/edges/EdgeLabel.tsx +1 -1
- package/src/extensions/react-flow/edges/_edges.scss +4 -0
- package/src/extensions/react-flow/edges/stories/EdgeDefault.stories.tsx +5 -5
- package/src/extensions/react-flow/nodes/NodeContent.tsx +2 -2
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
.#{$eccgui}-configuration--colors__react-flow-workflow {
|
|
2
2
|
--#{$eccgui}-project-node: #{eccgui-color-var("layout", "magenta", 700)};
|
|
3
|
-
--#{$eccgui}-project-node-bright: #{eccgui-color-var("layout", "magenta",
|
|
4
|
-
--#{$eccgui}-dataset-node: #{eccgui-color-var("layout", "
|
|
5
|
-
--#{$eccgui}-dataset-node-bright: #{eccgui-color-var("layout", "
|
|
6
|
-
--#{$eccgui}-linking-node: #{eccgui-color-var("layout", "
|
|
7
|
-
--#{$eccgui}-linking-node-bright: #{eccgui-color-var("layout", "
|
|
8
|
-
--#{$eccgui}-transform-node: #{eccgui-color-var("layout", "
|
|
9
|
-
--#{$eccgui}-transform-node-bright: #{eccgui-color-var("layout", "
|
|
3
|
+
--#{$eccgui}-project-node-bright: #{eccgui-color-var("layout", "magenta", 300)};
|
|
4
|
+
--#{$eccgui}-dataset-node: #{eccgui-color-var("layout", "petrol", 700)};
|
|
5
|
+
--#{$eccgui}-dataset-node-bright: #{eccgui-color-var("layout", "petrol", 300)};
|
|
6
|
+
--#{$eccgui}-linking-node: #{eccgui-color-var("layout", "cyan", 700)};
|
|
7
|
+
--#{$eccgui}-linking-node-bright: #{eccgui-color-var("layout", "cyan", 300)};
|
|
8
|
+
--#{$eccgui}-transform-node: #{eccgui-color-var("layout", "teal", 700)};
|
|
9
|
+
--#{$eccgui}-transform-node-bright: #{eccgui-color-var("layout", "teal", 300)};
|
|
10
10
|
--#{$eccgui}-task-node: #{eccgui-color-var("layout", "lime", 700)};
|
|
11
|
-
--#{$eccgui}-task-node-bright: #{eccgui-color-var("layout", "lime",
|
|
11
|
+
--#{$eccgui}-task-node-bright: #{eccgui-color-var("layout", "lime", 300)};
|
|
12
12
|
--#{$eccgui}-workflow-node: #{eccgui-color-var("layout", "purple", 700)};
|
|
13
|
-
--#{$eccgui}-workflow-node-bright: #{eccgui-color-var("layout", "purple",
|
|
14
|
-
--#{$eccgui}-
|
|
15
|
-
--#{$eccgui}-
|
|
13
|
+
--#{$eccgui}-workflow-node-bright: #{eccgui-color-var("layout", "purple", 300)};
|
|
14
|
+
--#{$eccgui}-replaceable-input: #{eccgui-color-var("layout", "amber", 700)};
|
|
15
|
+
--#{$eccgui}-replaceable-input-bright: #{eccgui-color-var("layout", "amber", 300)};
|
|
16
16
|
}
|
|
@@ -86,6 +86,11 @@
|
|
|
86
86
|
* Created to replace them easily for CSS custom properties.
|
|
87
87
|
*/
|
|
88
88
|
@function eccgui-color-rgba($color, $alpha) {
|
|
89
|
+
@if meta.type-of($alpha) != "number" {
|
|
90
|
+
// in case it is for example a CSS custom property
|
|
91
|
+
@return eccgui-color-mix($color $alpha, transparent);
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
@if $alpha > 0 {
|
|
90
95
|
@return eccgui-color-mix($color 100% * $alpha, transparent);
|
|
91
96
|
} @else {
|
|
@@ -494,6 +494,7 @@ export function SuggestField<T, UPDATE_VALUE>(props: SuggestFieldProps<T, UPDATE
|
|
|
494
494
|
query={query}
|
|
495
495
|
// This leads to odd compile errors without "as any"
|
|
496
496
|
popoverProps={updatedContextOverlayProps as any}
|
|
497
|
+
popoverContentProps={{className: "nodrag"}}
|
|
497
498
|
selectedItem={selectedItem}
|
|
498
499
|
fill={fill}
|
|
499
500
|
{...createNewItemProps}
|
|
@@ -20,7 +20,7 @@ export default {
|
|
|
20
20
|
},
|
|
21
21
|
intent: {
|
|
22
22
|
...helpersArgTypes.exampleIntent,
|
|
23
|
-
options: ["UNDEFINED", "primary", "success", "warning", "danger"],
|
|
23
|
+
options: ["UNDEFINED", "primary", "accent", "success", "warning", "danger"],
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
26
|
} as Meta<typeof Button>;
|
|
@@ -62,6 +62,8 @@ const TemplateSemantic: StoryFn<typeof Button> = (args) => (
|
|
|
62
62
|
<Button {...args} affirmative text="Affirmative action" />
|
|
63
63
|
<Spacing vertical />
|
|
64
64
|
<Button {...args} disruptive text="Disruptive action" />
|
|
65
|
+
<Spacing vertical />
|
|
66
|
+
<Button {...args} elevated text="Elevated action" />
|
|
65
67
|
</OverlaysProvider>
|
|
66
68
|
);
|
|
67
69
|
export const ButtonSemantics = TemplateSemantic.bind({});
|
|
@@ -74,6 +76,10 @@ const TemplateIntent: StoryFn<typeof Button> = (args) => (
|
|
|
74
76
|
<Button {...args} text="Warning" intent="warning" />
|
|
75
77
|
<Spacing vertical />
|
|
76
78
|
<Button {...args} text="Danger" intent="danger" />
|
|
79
|
+
<Spacing vertical />
|
|
80
|
+
<Button {...args} text="Primary" intent="primary" />
|
|
81
|
+
<Spacing vertical />
|
|
82
|
+
<Button {...args} text="Accent" intent="accent" />
|
|
77
83
|
</OverlaysProvider>
|
|
78
84
|
);
|
|
79
85
|
export const ButtonIntent = TemplateIntent.bind({});
|
|
@@ -17,19 +17,23 @@ import Tooltip, { TooltipProps } from "./../Tooltip/Tooltip";
|
|
|
17
17
|
interface AdditionalButtonProps {
|
|
18
18
|
/**
|
|
19
19
|
* Always use this when the button triggers an affirmative action, e.g. confirm a process.
|
|
20
|
-
* The button is displayed with
|
|
20
|
+
* The button is displayed with accent color intent.
|
|
21
21
|
*/
|
|
22
22
|
affirmative?: boolean;
|
|
23
23
|
/**
|
|
24
24
|
* Always use this when the button triggers an disruptive action, e.g. delete or remove.
|
|
25
|
-
* The button is displayed with
|
|
25
|
+
* The button is displayed with danger color intent.
|
|
26
26
|
*/
|
|
27
27
|
disruptive?: boolean;
|
|
28
28
|
/**
|
|
29
29
|
* Use this when a button is important enough to highlight it in a set of other buttons.
|
|
30
|
-
* The button is displayed with
|
|
30
|
+
* The button is displayed with accent color intent.
|
|
31
31
|
*/
|
|
32
32
|
elevated?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Intent state visualized by color.
|
|
35
|
+
*/
|
|
36
|
+
intent?: BlueprintIntent | "accent";
|
|
33
37
|
/**
|
|
34
38
|
* Content displayed in a badge that is attached to the button.
|
|
35
39
|
* By default it is displayed `{ size: "small", position: "top-right", maxLength: 2 }` and with the same intent state of the button.
|
|
@@ -49,18 +53,21 @@ interface AdditionalButtonProps {
|
|
|
49
53
|
*/
|
|
50
54
|
tooltipProps?: Partial<Omit<TooltipProps, "content" | "children">>;
|
|
51
55
|
/**
|
|
52
|
-
*
|
|
56
|
+
* Icon displayed on button start.
|
|
53
57
|
*/
|
|
54
|
-
//href?: string;
|
|
55
58
|
icon?: ValidIconName | JSX.Element;
|
|
59
|
+
/**
|
|
60
|
+
* Icon displayed on button end.
|
|
61
|
+
*/
|
|
56
62
|
rightIcon?: ValidIconName | JSX.Element;
|
|
57
|
-
//target?: string;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
interface ExtendedButtonProps
|
|
65
|
+
interface ExtendedButtonProps
|
|
66
|
+
extends AdditionalButtonProps,
|
|
67
|
+
Omit<BlueprintButtonProps, "intent" | "icon" | "rightIcon"> {}
|
|
61
68
|
interface ExtendedAnchorButtonProps
|
|
62
69
|
extends AdditionalButtonProps,
|
|
63
|
-
Omit<BlueprintAnchorButtonProps, "icon" | "rightIcon"> {}
|
|
70
|
+
Omit<BlueprintAnchorButtonProps, "intent" | "icon" | "rightIcon"> {}
|
|
64
71
|
|
|
65
72
|
export type ButtonProps = ExtendedButtonProps & ExtendedAnchorButtonProps;
|
|
66
73
|
|
|
@@ -86,7 +93,7 @@ export const Button = ({
|
|
|
86
93
|
let intentByFunction;
|
|
87
94
|
switch (true) {
|
|
88
95
|
case affirmative || elevated:
|
|
89
|
-
intentByFunction =
|
|
96
|
+
intentByFunction = "accent";
|
|
90
97
|
break;
|
|
91
98
|
case disruptive:
|
|
92
99
|
intentByFunction = BlueprintIntent.DANGER;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@use "sass:color";
|
|
2
2
|
@use "sass:map";
|
|
3
|
+
@use "sass:list";
|
|
3
4
|
|
|
4
5
|
$button-height: $pt-button-height;
|
|
5
6
|
$button-border-width: 1px; // !default;
|
|
@@ -49,64 +50,125 @@ $dark-button-gradient: $button-gradient; // !default;
|
|
|
49
50
|
// $button-outlined-border-disabled-intent-opacity: 0.2 !default;
|
|
50
51
|
|
|
51
52
|
$button-intents: (
|
|
52
|
-
|
|
53
|
+
// default - hover - active
|
|
54
|
+
"primary":
|
|
55
|
+
(
|
|
56
|
+
eccgui-color-var("identity", "brand", "900"),
|
|
57
|
+
eccgui-color-mix(
|
|
58
|
+
eccgui-color-var("identity", "brand", "900"),
|
|
59
|
+
eccgui-color-var("identity", "text", "900") 10%
|
|
60
|
+
),
|
|
61
|
+
eccgui-color-mix(
|
|
62
|
+
eccgui-color-var("identity", "brand", "900"),
|
|
63
|
+
eccgui-color-var("identity", "text", "900") 20%
|
|
64
|
+
)
|
|
65
|
+
),
|
|
66
|
+
"accent": (
|
|
53
67
|
eccgui-color-var("identity", "accent", "900"),
|
|
54
|
-
eccgui-color-
|
|
55
|
-
|
|
68
|
+
eccgui-color-mix(
|
|
69
|
+
eccgui-color-var("identity", "accent", "900"),
|
|
70
|
+
eccgui-color-var("identity", "text", "900") 10%
|
|
71
|
+
),
|
|
72
|
+
eccgui-color-mix(eccgui-color-var("identity", "accent", "900"), eccgui-color-var("identity", "text", "900") 20%)
|
|
56
73
|
),
|
|
57
74
|
"success": (
|
|
58
75
|
eccgui-color-var("semantic", "success", "900"),
|
|
59
|
-
eccgui-color-
|
|
60
|
-
|
|
76
|
+
eccgui-color-mix(
|
|
77
|
+
eccgui-color-var("semantic", "success", "900"),
|
|
78
|
+
eccgui-color-var("identity", "text", "900") 10%
|
|
79
|
+
),
|
|
80
|
+
eccgui-color-mix(
|
|
81
|
+
eccgui-color-var("semantic", "success", "900"),
|
|
82
|
+
eccgui-color-var("identity", "text", "900") 20%
|
|
83
|
+
)
|
|
61
84
|
),
|
|
62
85
|
"warning": (
|
|
63
86
|
eccgui-color-var("semantic", "warning", "900"),
|
|
64
|
-
eccgui-color-
|
|
65
|
-
|
|
87
|
+
eccgui-color-mix(
|
|
88
|
+
eccgui-color-var("semantic", "warning", "900"),
|
|
89
|
+
eccgui-color-var("identity", "text", "900") 10%
|
|
90
|
+
),
|
|
91
|
+
eccgui-color-mix(
|
|
92
|
+
eccgui-color-var("semantic", "warning", "900"),
|
|
93
|
+
eccgui-color-var("identity", "text", "900") 20%
|
|
94
|
+
)
|
|
66
95
|
),
|
|
67
96
|
"danger": (
|
|
68
97
|
eccgui-color-var("semantic", "danger", "900"),
|
|
69
|
-
eccgui-color-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
eccgui-color-mix(
|
|
99
|
+
eccgui-color-var("semantic", "danger", "900"),
|
|
100
|
+
eccgui-color-var("identity", "text", "900") 10%
|
|
101
|
+
),
|
|
102
|
+
eccgui-color-mix(eccgui-color-var("semantic", "danger", "900"), eccgui-color-var("identity", "text", "900") 20%)
|
|
103
|
+
)
|
|
72
104
|
);
|
|
73
105
|
|
|
74
106
|
@import "~@blueprintjs/core/src/components/button/button";
|
|
75
107
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.#{$ns}-large {
|
|
80
|
-
min-height: mini-units(6);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// special case override: blueprint do not use configured colors here
|
|
84
|
-
&.#{$ns}-intent-warning {
|
|
85
|
-
@include pt-button-intent(map.get($button-intents, "warning")...);
|
|
108
|
+
@mixin eccgui-enhance-blueprint-button-intent($intentvalue) {
|
|
109
|
+
&.#{$ns}-intent-#{$intentvalue} {
|
|
110
|
+
@include pt-button-intent(map.get($button-intents, $intentvalue)...);
|
|
86
111
|
|
|
87
112
|
&:not(.#{$ns}-disabled).#{$ns}-icon > svg {
|
|
88
113
|
fill: eccgui-color-rgba($white, 0.7);
|
|
89
114
|
}
|
|
90
115
|
|
|
91
116
|
&:not(.#{$ns}-disabled):not(.#{$ns}-minimal):not(.#{$ns}-outlined) {
|
|
92
|
-
@include pt-button-intent(map.get($button-intents,
|
|
117
|
+
@include pt-button-intent(map.get($button-intents, $intentvalue)...);
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
&.#{$ns}-minimal,
|
|
96
121
|
&.#{$ns}-outlined {
|
|
97
|
-
color: $
|
|
122
|
+
color: list.nth(map.get($button-intents, $intentvalue), 1);
|
|
98
123
|
background: none;
|
|
99
|
-
border-color: $
|
|
124
|
+
border-color: list.nth(map.get($button-intents, $intentvalue), 1);
|
|
100
125
|
box-shadow: none;
|
|
101
126
|
|
|
102
127
|
&:disabled,
|
|
103
128
|
&.#{$ns}-disabled {
|
|
104
|
-
color: eccgui-color-rgba($
|
|
129
|
+
color: eccgui-color-rgba(list.nth(map.get($button-intents, $intentvalue), 1), $eccgui-opacity-disabled);
|
|
130
|
+
border-color: eccgui-color-rgba(
|
|
131
|
+
list.nth(map.get($button-intents, $intentvalue), 1),
|
|
132
|
+
$eccgui-opacity-disabled
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&:active:not(:disabled):not(.#{$ns}-disabled),
|
|
137
|
+
&.#{$ns}-active:not(:disabled):not(.#{$ns}-disabled) {
|
|
138
|
+
color: list.nth(map.get($button-intents, $intentvalue), 3);
|
|
139
|
+
background-color: eccgui-color-rgba(
|
|
140
|
+
list.nth(map.get($button-intents, $intentvalue), 3),
|
|
141
|
+
$eccgui-opacity-ghostly
|
|
142
|
+
);
|
|
143
|
+
border-color: list.nth(map.get($button-intents, $intentvalue), 3);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
&:focus:not(:disabled):not(.#{$ns}-disabled),
|
|
147
|
+
&:hover:not(:disabled):not(.#{$ns}-disabled) {
|
|
148
|
+
color: list.nth(map.get($button-intents, $intentvalue), 2);
|
|
149
|
+
background-color: eccgui-color-rgba(
|
|
150
|
+
list.nth(map.get($button-intents, $intentvalue), 2),
|
|
151
|
+
0.5 * $eccgui-opacity-ghostly
|
|
152
|
+
);
|
|
153
|
+
border-color: list.nth(map.get($button-intents, $intentvalue), 2);
|
|
105
154
|
}
|
|
106
155
|
}
|
|
107
156
|
}
|
|
108
157
|
}
|
|
109
158
|
|
|
159
|
+
.#{$ns}-button {
|
|
160
|
+
position: relative;
|
|
161
|
+
|
|
162
|
+
.#{$ns}-large {
|
|
163
|
+
min-height: mini-units(6);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// special case override: blueprint do not use configured colors here
|
|
167
|
+
@include eccgui-enhance-blueprint-button-intent("primary");
|
|
168
|
+
@include eccgui-enhance-blueprint-button-intent("accent");
|
|
169
|
+
@include eccgui-enhance-blueprint-button-intent("warning");
|
|
170
|
+
}
|
|
171
|
+
|
|
110
172
|
.#{$ns}-button-text {
|
|
111
173
|
min-width: 0;
|
|
112
174
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Meta, StoryFn } from "@storybook/react";
|
|
3
3
|
|
|
4
|
-
import { ChatField } from "../../../index";
|
|
4
|
+
import { ChatField, ContextMenu, MenuItem } from "../../../index";
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
7
|
title: "Components/Chat/ChatField",
|
|
@@ -15,4 +15,9 @@ const TemplateFull: StoryFn<typeof ChatField> = (args) => <ChatField {...args} k
|
|
|
15
15
|
export const Default = TemplateFull.bind({});
|
|
16
16
|
Default.args = {
|
|
17
17
|
onTextSubmit: (value) => alert(value),
|
|
18
|
+
rightElement: (
|
|
19
|
+
<ContextMenu>
|
|
20
|
+
<MenuItem text="Just a test" />
|
|
21
|
+
</ContextMenu>
|
|
22
|
+
),
|
|
18
23
|
};
|
|
@@ -5,12 +5,13 @@ import {
|
|
|
5
5
|
Overlay2Props as BlueprintOverlayProps,
|
|
6
6
|
} from "@blueprintjs/core";
|
|
7
7
|
|
|
8
|
+
import { preventReactFlowActionsClasses } from "../../cmem";
|
|
8
9
|
import { utils } from "../../common";
|
|
9
10
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
10
11
|
import { TestableComponent } from "../interfaces";
|
|
11
12
|
|
|
12
13
|
import { Card } from "./../Card";
|
|
13
|
-
import {
|
|
14
|
+
import { ModalContext } from "./ModalContext";
|
|
14
15
|
|
|
15
16
|
export interface ModalProps extends TestableComponent, BlueprintOverlayProps {
|
|
16
17
|
children: React.ReactNode | React.ReactNode[];
|
|
@@ -43,9 +44,17 @@ export interface ModalProps extends TestableComponent, BlueprintOverlayProps {
|
|
|
43
44
|
* If this option is used inflationary then this could harm the visibility of other overlays.
|
|
44
45
|
*/
|
|
45
46
|
forceTopPosition?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Modal ID that should be globally unique. If a ModalContext is provided this can be used to track opening/closing of this modal.
|
|
49
|
+
*/
|
|
50
|
+
modalId?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Prevents that pan and zooming actions of an existing react-flow instance are triggered while this Modal is open.
|
|
53
|
+
*/
|
|
54
|
+
preventReactFlowEvents?: boolean;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
|
-
export type ModalSize = "tiny" | "small" | "regular" | "large" | "xlarge" | "fullscreen"
|
|
57
|
+
export type ModalSize = "tiny" | "small" | "regular" | "large" | "xlarge" | "fullscreen";
|
|
49
58
|
|
|
50
59
|
/**
|
|
51
60
|
* Displays contents on top of other elements, used to create dialogs.
|
|
@@ -68,8 +77,24 @@ export const Modal = ({
|
|
|
68
77
|
onOpening,
|
|
69
78
|
"data-test-id": dataTestId,
|
|
70
79
|
"data-testid": dataTestid,
|
|
80
|
+
modalId,
|
|
81
|
+
preventReactFlowEvents = true,
|
|
71
82
|
...otherProps
|
|
72
83
|
}: ModalProps) => {
|
|
84
|
+
const modalContext = React.useContext(ModalContext)
|
|
85
|
+
const uniqueModalId = React.useRef<string>(modalId ?? Date.now().toString(36) + Math.random().toString(36).substring(2))
|
|
86
|
+
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
return () => {
|
|
89
|
+
// Make sure to always remove flag when modal is removed
|
|
90
|
+
modalContext.setModalOpen(uniqueModalId.current, false)
|
|
91
|
+
}
|
|
92
|
+
}, [])
|
|
93
|
+
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
modalContext.setModalOpen(uniqueModalId.current, otherProps.isOpen)
|
|
96
|
+
}, [otherProps.isOpen])
|
|
97
|
+
|
|
73
98
|
const backdropProps: React.HTMLProps<HTMLDivElement> | undefined =
|
|
74
99
|
!canOutsideClickClose && canEscapeKeyClose
|
|
75
100
|
? {
|
|
@@ -117,7 +142,7 @@ export const Modal = ({
|
|
|
117
142
|
<BlueprintOverlay
|
|
118
143
|
{...otherProps}
|
|
119
144
|
backdropProps={backdropProps}
|
|
120
|
-
className={`${overlayClassName} ${preventReactFlowActionsClasses}`}
|
|
145
|
+
className={`${overlayClassName} ${preventReactFlowEvents ? preventReactFlowActionsClasses : ""}`}
|
|
121
146
|
backdropClassName={`${eccgui}-dialog__backdrop`}
|
|
122
147
|
canOutsideClickClose={canOutsideClickClose}
|
|
123
148
|
canEscapeKeyClose={canEscapeKeyClose}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export interface ModalContextProps {
|
|
4
|
+
/** Set that a specific modal is currently being open (or closed) */
|
|
5
|
+
setModalOpen: (modalId: string, isOpen: boolean) => void;
|
|
6
|
+
|
|
7
|
+
/** The currently opened modals ordered by when they have been opened. Oldest coming first. */
|
|
8
|
+
openModalStack: string[] | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Can be provided in the application to react to modal related changes. */
|
|
12
|
+
export const ModalContext = React.createContext<ModalContextProps>({
|
|
13
|
+
setModalOpen: () => {},
|
|
14
|
+
openModalStack: [],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/** Default implementation for modal context props.
|
|
18
|
+
* Tracks open modals in a stack representation.
|
|
19
|
+
**/
|
|
20
|
+
export const useModalContext = (): ModalContextProps => {
|
|
21
|
+
// A stack of modal IDs. These should reflect a stacked opening of modals on top of each other.
|
|
22
|
+
const [openModalStack, setOpenModalStack] = React.useState<string[]>([]);
|
|
23
|
+
|
|
24
|
+
const setModalOpen = React.useCallback((modalId: string, isOpen: boolean) => {
|
|
25
|
+
setOpenModalStack((old) => {
|
|
26
|
+
if (isOpen) {
|
|
27
|
+
return [...old, modalId];
|
|
28
|
+
} else {
|
|
29
|
+
const idx = old.findIndex((id) => modalId === id);
|
|
30
|
+
switch (idx) {
|
|
31
|
+
case -1:
|
|
32
|
+
// Trying to close modal that has not been registered as open!
|
|
33
|
+
return old;
|
|
34
|
+
case old.length - 1:
|
|
35
|
+
return old.slice(0, idx);
|
|
36
|
+
default:
|
|
37
|
+
// Modal in between is closed. Consider all modals after it also as closed.
|
|
38
|
+
return old.slice(0, idx);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
openModalStack: openModalStack.length ? [...openModalStack] : undefined,
|
|
46
|
+
setModalOpen,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { OverlaysProvider } from "@blueprintjs/core";
|
|
2
|
+
import { Classes, OverlaysProvider } from "@blueprintjs/core";
|
|
3
3
|
import { Meta, StoryFn } from "@storybook/react";
|
|
4
4
|
import { fn } from "@storybook/test";
|
|
5
5
|
|
|
6
6
|
import { SimpleCard } from "../../Card/stories/Card.stories";
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
Modal,
|
|
13
|
+
ModalContext,
|
|
14
|
+
ModalSize,
|
|
15
|
+
Spacing,
|
|
16
|
+
useModalContext,
|
|
17
|
+
} from "./../../../../index";
|
|
9
18
|
|
|
10
19
|
export default {
|
|
11
20
|
title: "Components/Dialog/Modal",
|
|
@@ -33,3 +42,135 @@ Default.args = {
|
|
|
33
42
|
onOpening: fn(),
|
|
34
43
|
onClosing: fn(),
|
|
35
44
|
};
|
|
45
|
+
|
|
46
|
+
const ContextTemplate = ({ children }: React.HTMLAttributes<HTMLDivElement>) => {
|
|
47
|
+
const { setModalOpen, openModalStack } = useModalContext();
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<OverlaysProvider>
|
|
51
|
+
<div style={{ height: "70vh", position: "relative" }} id={"modalPortal"}>
|
|
52
|
+
<ModalContext.Provider value={{ setModalOpen, openModalStack }}>{children}</ModalContext.Provider>
|
|
53
|
+
</div>
|
|
54
|
+
</OverlaysProvider>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const ModalContent = ({ children }: React.HTMLAttributes<HTMLDivElement>) => {
|
|
59
|
+
return (
|
|
60
|
+
<Card style={{ height: "100%" }}>
|
|
61
|
+
<CardContent>{children}</CardContent>
|
|
62
|
+
</Card>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Component for nested modals. */
|
|
67
|
+
const ExampleModal = ({
|
|
68
|
+
id,
|
|
69
|
+
size,
|
|
70
|
+
children,
|
|
71
|
+
}: {
|
|
72
|
+
id?: string;
|
|
73
|
+
size: ModalSize;
|
|
74
|
+
children?: React.HTMLAttributes<HTMLDivElement>["children"];
|
|
75
|
+
}) => {
|
|
76
|
+
const [isOpen, setIsOpen] = React.useState(true);
|
|
77
|
+
const [portalElement, setPortalElement] = React.useState<HTMLElement | undefined>();
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
setPortalElement(document.getElementById("modalPortal")!);
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Modal
|
|
85
|
+
modalId={id}
|
|
86
|
+
size={size}
|
|
87
|
+
isOpen={isOpen}
|
|
88
|
+
usePortal={true}
|
|
89
|
+
portalContainer={portalElement}
|
|
90
|
+
hasBackdrop={true}
|
|
91
|
+
onOpened={() => {
|
|
92
|
+
// workaround, Blueprint attach a class to body tht prevents scrolling, probably it is attached to the wrong portal
|
|
93
|
+
document.body.classList.remove(Classes.OVERLAY_OPEN);
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<ModalContent>
|
|
97
|
+
Modal with constant modal ID "{id}".
|
|
98
|
+
<Spacing />
|
|
99
|
+
<TrackingContent />
|
|
100
|
+
<Spacing />
|
|
101
|
+
{children}
|
|
102
|
+
<Spacing />
|
|
103
|
+
<Button key={"close"} onClick={() => setIsOpen(false)}>
|
|
104
|
+
Close
|
|
105
|
+
</Button>
|
|
106
|
+
</ModalContent>
|
|
107
|
+
</Modal>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const InnerModal = () => {
|
|
112
|
+
return <ExampleModal id="innerModal" size="small" />;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const MiddleModal = () => {
|
|
116
|
+
return (
|
|
117
|
+
<ExampleModal id="middleModal" size="regular">
|
|
118
|
+
<InnerModal />
|
|
119
|
+
</ExampleModal>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** Shows the current stack of open modals. */
|
|
124
|
+
const TrackingContent = () => {
|
|
125
|
+
const modalContext = React.useContext(ModalContext);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<ul>
|
|
129
|
+
{(modalContext.openModalStack ?? []).map((modalId, idx) => (
|
|
130
|
+
<li key={modalId}>
|
|
131
|
+
{idx + 1}. {modalId}
|
|
132
|
+
</li>
|
|
133
|
+
))}
|
|
134
|
+
</ul>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* `ModalContext` can be used as provider to track a stack of modals.
|
|
140
|
+
*
|
|
141
|
+
* ```(Javascript)
|
|
142
|
+
* const ContextTemplate = () => {
|
|
143
|
+
* const { setModalOpen, openModalStack } = useModalContext();
|
|
144
|
+
* return (
|
|
145
|
+
* <ModalContext.Provider value={{ setModalOpen, openModalStack }}>
|
|
146
|
+
* <SimpleDialog size="large" isOpen>
|
|
147
|
+
* <OtherModal />
|
|
148
|
+
* </SimpleDialog>
|
|
149
|
+
* </ModalContext.Provider>
|
|
150
|
+
* );
|
|
151
|
+
* };
|
|
152
|
+
*
|
|
153
|
+
* const OtherModal = () => {
|
|
154
|
+
* const modalContext = React.useContext(ModalContext);
|
|
155
|
+
* return (
|
|
156
|
+
* <SimpleDialog size="small">
|
|
157
|
+
* <ul>
|
|
158
|
+
* {(modalContext.openModalStack ?? []).map((modalId, idx) => (
|
|
159
|
+
* <li key={modalId}>
|
|
160
|
+
* {idx + 1}. {modalId}
|
|
161
|
+
* </li>
|
|
162
|
+
* ))}
|
|
163
|
+
* </ul>
|
|
164
|
+
* </SimpleDialog>
|
|
165
|
+
* );
|
|
166
|
+
* };
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export const NestedModalWithContext = ContextTemplate.bind({});
|
|
170
|
+
NestedModalWithContext.args = {
|
|
171
|
+
children: [
|
|
172
|
+
<ExampleModal id="rootModal" size="large">
|
|
173
|
+
<MiddleModal />
|
|
174
|
+
</ExampleModal>,
|
|
175
|
+
],
|
|
176
|
+
};
|
|
@@ -171,10 +171,14 @@ const canonicalIcons = {
|
|
|
171
171
|
"state-warning": icons.WarningAltFilled,
|
|
172
172
|
|
|
173
173
|
"toggler-caret": icons.CaretSort,
|
|
174
|
+
"toggler-carettop": icons.CaretUp,
|
|
174
175
|
"toggler-caretright": icons.CaretRight,
|
|
175
176
|
"toggler-caretdown": icons.CaretDown,
|
|
177
|
+
"toggler-caretleft": icons.CaretLeft,
|
|
176
178
|
"toggler-maximize": icons.Maximize,
|
|
177
179
|
"toggler-minimize": icons.Minimize,
|
|
180
|
+
"toggler-micon": icons.Microphone,
|
|
181
|
+
"toggler-micoff": icons.MicrophoneOff,
|
|
178
182
|
"toggler-moveleft": icons.ChevronLeft,
|
|
179
183
|
"toggler-moveright": icons.ChevronRight,
|
|
180
184
|
"toggler-list": icons.List,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
.#{$eccgui}-icon {
|
|
2
2
|
vertical-align: text-bottom;
|
|
3
3
|
|
|
4
|
+
&.#{$eccgui}-intent--primary {
|
|
5
|
+
color: $eccgui-color-primary;
|
|
6
|
+
}
|
|
7
|
+
&.#{$eccgui}-intent--accent {
|
|
8
|
+
color: $eccgui-color-accent;
|
|
9
|
+
}
|
|
4
10
|
&.#{$eccgui}-intent--info {
|
|
5
11
|
color: $eccgui-color-info-text;
|
|
6
12
|
}
|