@eccenca/gui-elements 24.4.0 → 24.4.1-featurechatcomponentscmem6775.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/cjs/common/index.js.map +1 -1
  3. package/dist/cjs/components/Chat/ChatArea.js +55 -0
  4. package/dist/cjs/components/Chat/ChatArea.js.map +1 -0
  5. package/dist/cjs/components/Chat/ChatContent.js +120 -0
  6. package/dist/cjs/components/Chat/ChatContent.js.map +1 -0
  7. package/dist/cjs/components/Chat/ChatField.js +45 -0
  8. package/dist/cjs/components/Chat/ChatField.js.map +1 -0
  9. package/dist/cjs/components/Chat/index.js +20 -0
  10. package/dist/cjs/components/Chat/index.js.map +1 -0
  11. package/dist/cjs/components/Icon/IconButton.js +1 -1
  12. package/dist/cjs/components/Icon/IconButton.js.map +1 -1
  13. package/dist/cjs/components/Icon/canonicalIconNames.js +3 -0
  14. package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
  15. package/dist/cjs/components/TextField/TextArea.js +2 -2
  16. package/dist/cjs/components/TextField/TextArea.js.map +1 -1
  17. package/dist/cjs/components/Tooltip/Tooltip.js +5 -1
  18. package/dist/cjs/components/Tooltip/Tooltip.js.map +1 -1
  19. package/dist/cjs/components/index.js +1 -0
  20. package/dist/cjs/components/index.js.map +1 -1
  21. package/dist/esm/common/index.js.map +1 -1
  22. package/dist/esm/components/Chat/ChatArea.js +59 -0
  23. package/dist/esm/components/Chat/ChatArea.js.map +1 -0
  24. package/dist/esm/components/Chat/ChatContent.js +117 -0
  25. package/dist/esm/components/Chat/ChatContent.js.map +1 -0
  26. package/dist/esm/components/Chat/ChatField.js +49 -0
  27. package/dist/esm/components/Chat/ChatField.js.map +1 -0
  28. package/dist/esm/components/Chat/index.js +4 -0
  29. package/dist/esm/components/Chat/index.js.map +1 -0
  30. package/dist/esm/components/Icon/IconButton.js +1 -1
  31. package/dist/esm/components/Icon/IconButton.js.map +1 -1
  32. package/dist/esm/components/Icon/canonicalIconNames.js +3 -0
  33. package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
  34. package/dist/esm/components/TextField/TextArea.js +2 -2
  35. package/dist/esm/components/TextField/TextArea.js.map +1 -1
  36. package/dist/esm/components/Tooltip/Tooltip.js +8 -4
  37. package/dist/esm/components/Tooltip/Tooltip.js.map +1 -1
  38. package/dist/esm/components/index.js +1 -0
  39. package/dist/esm/components/index.js.map +1 -1
  40. package/dist/types/common/index.d.ts +1 -0
  41. package/dist/types/components/Chat/ChatArea.d.ts +34 -0
  42. package/dist/types/components/Chat/ChatContent.d.ts +61 -0
  43. package/dist/types/components/Chat/ChatField.d.ts +19 -0
  44. package/dist/types/components/Chat/index.d.ts +3 -0
  45. package/dist/types/components/Icon/canonicalIconNames.d.ts +3 -0
  46. package/dist/types/components/index.d.ts +1 -0
  47. package/package.json +5 -4
  48. package/src/common/index.ts +1 -0
  49. package/src/components/Chat/ChatArea.tsx +114 -0
  50. package/src/components/Chat/ChatContent.tsx +199 -0
  51. package/src/components/Chat/ChatField.tsx +52 -0
  52. package/src/components/Chat/_chat.scss +86 -0
  53. package/src/components/Chat/index.ts +3 -0
  54. package/src/components/Chat/stories/ChatArea.stories.tsx +36 -0
  55. package/src/components/Chat/stories/ChatContent.stories.tsx +60 -0
  56. package/src/components/Chat/stories/ChatField.stories.tsx +18 -0
  57. package/src/components/Icon/IconButton.tsx +1 -1
  58. package/src/components/Icon/canonicalIconNames.tsx +3 -0
  59. package/src/components/TextField/TextArea.tsx +2 -2
  60. package/src/components/Tooltip/Tooltip.stories.tsx +2 -0
  61. package/src/components/Tooltip/Tooltip.tsx +9 -1
  62. package/src/components/index.scss +1 -0
  63. package/src/components/index.ts +1 -0
  64. package/src/extensions/react-flow/_react-flow_v12.scss +8 -1
@@ -0,0 +1,114 @@
1
+ import React from "react";
2
+
3
+ import { TestableComponent } from "../../components/interfaces";
4
+ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
+
6
+ import { FlexibleLayoutContainer, FlexibleLayoutContainerProps, FlexibleLayoutItem } from "./../FlexibleLayout";
7
+ import { Spacing, SpacingProps } from "./../Separation/Spacing";
8
+ import { ChatFieldProps } from "./ChatField";
9
+
10
+ export interface ChatAreaProps
11
+ extends Omit<FlexibleLayoutContainerProps, "vertical" | "noEqualItemSpace">,
12
+ TestableComponent {
13
+ /**
14
+ * The inut field for the chat.
15
+ */
16
+ chatField?: React.ReactElement<ChatFieldProps>;
17
+ /**
18
+ * Set the position of the chat field.
19
+ */
20
+ chatFieldPosition?: "top" | "bottom";
21
+ /**
22
+ * Sets the maximum width for chat contents and input.
23
+ */
24
+ contentWidth?: "small" | "medium" | "large" | "full";
25
+ /**
26
+ * Put chat content in a list and add spacings automatically.
27
+ * Works best if each `ChatArea` child represents one chat content item.
28
+ */
29
+ autoSpacingSize?: SpacingProps["size"];
30
+ /**
31
+ * Scrolls content to the first or last child automatically.
32
+ * The correct value depends on the place where you insert the most recent chat item.
33
+ */
34
+ autoScrollTo?: "first" | "last";
35
+ }
36
+
37
+ /**
38
+ * Component to display a full chat, containing chat content bubbles and text input.
39
+ */
40
+ export const ChatArea = ({
41
+ children,
42
+ className,
43
+ chatField,
44
+ chatFieldPosition = "bottom",
45
+ contentWidth = "medium",
46
+ autoSpacingSize,
47
+ gapSize = "medium",
48
+ autoScrollTo,
49
+ ...otherFlexibleLayoutContainerProps
50
+ }: ChatAreaProps) => {
51
+ const chatcontents = React.useRef<HTMLDivElement>(null);
52
+
53
+ React.useEffect(() => {
54
+ if (chatcontents.current && children && autoScrollTo) {
55
+ const chatitems = chatcontents.current.getElementsByClassName(`${eccgui}-chat__content`);
56
+ if (chatitems.length > 0) {
57
+ chatitems[autoScrollTo === "first" ? 0 : chatitems.length - 1].scrollIntoView({
58
+ behavior: "instant",
59
+ block: autoScrollTo === "first" ? "start" : "end",
60
+ });
61
+ }
62
+ }
63
+ }, [chatcontents, children, autoScrollTo]);
64
+
65
+ return (
66
+ <FlexibleLayoutContainer
67
+ className={
68
+ `${eccgui}-chat__area` + ` ${eccgui}-chat__area--${contentWidth}` + (className ? ` ${className}` : "")
69
+ }
70
+ vertical
71
+ noEqualItemSpace
72
+ gapSize={gapSize}
73
+ {...otherFlexibleLayoutContainerProps}
74
+ >
75
+ {chatField && (
76
+ <FlexibleLayoutItem
77
+ growFactor={0}
78
+ shrinkFactor={0}
79
+ style={chatFieldPosition === "bottom" ? { order: 1 } : undefined}
80
+ >
81
+ <div className={`${eccgui}-chat__area-contentwidth`}>{chatField}</div>
82
+ </FlexibleLayoutItem>
83
+ )}
84
+ <FlexibleLayoutItem
85
+ style={
86
+ otherFlexibleLayoutContainerProps.useAbsoluteSpace
87
+ ? {
88
+ overflow: "auto",
89
+ minHeight: 0,
90
+ padding: "2px 0",
91
+ }
92
+ : undefined
93
+ }
94
+ >
95
+ <div className={`${eccgui}-chat__area-contentwidth`} ref={chatcontents}>
96
+ {autoSpacingSize && children ? (
97
+ <ul>
98
+ {React.Children.toArray(children).map((child) => (
99
+ <li>
100
+ {child}
101
+ <Spacing size={autoSpacingSize} />
102
+ </li>
103
+ ))}
104
+ </ul>
105
+ ) : (
106
+ children
107
+ )}
108
+ </div>
109
+ </FlexibleLayoutItem>
110
+ </FlexibleLayoutContainer>
111
+ );
112
+ };
113
+
114
+ export default ChatArea;
@@ -0,0 +1,199 @@
1
+ import React from "react";
2
+ import { renderToString } from "react-dom/server";
3
+ import * as ReactIs from "react-is";
4
+
5
+ import { TestableComponent } from "../../components/interfaces";
6
+ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
7
+
8
+ import { Markdown, MarkdownProps } from "./../../cmem/markdown/Markdown";
9
+ import { DepictionProps } from "./../Depiction/Depiction";
10
+ import { FlexibleLayoutContainer, FlexibleLayoutItem } from "./../FlexibleLayout";
11
+ import { IconButton } from "./../Icon/IconButton";
12
+ import { Spacing } from "./../Separation/Spacing";
13
+ import { HtmlContentBlock, OverflowText, OverflowTextProps } from "./../Typography";
14
+
15
+ export interface ChatContentProps extends React.HTMLAttributes<HTMLDivElement>, TestableComponent {
16
+ /**
17
+ * Should be a line of text, e.g. username, timestamp, ...
18
+ */
19
+ statusLine?: React.ReactElement<OverflowTextProps>;
20
+ /**
21
+ * How the chat content box is displayed.
22
+ */
23
+ displayType?: "free" | "simple" | "bubble";
24
+ /**
25
+ * A depiction used as avatar next to the content box.
26
+ */
27
+ avatar?: React.ReactElement<DepictionProps>;
28
+ /**
29
+ * If indented then the content box has some white space on the opposite side to the alignment
30
+ */
31
+ indentationSize?: "small" | "medium" | "large";
32
+ /**
33
+ * How the content box and avatar is aligned.
34
+ * If `left` is set then the avatar is on the left side, and the indentation on the right side.
35
+ */
36
+ alignment?: "left" | "right";
37
+ /**
38
+ * If set then the chat bubble only grows to a height of 50% of the viewport.
39
+ * In case you need to set other maximum heights then use the `style` property directly.
40
+ */
41
+ limitHeight?: React.ReactChild;
42
+ /**
43
+ * If given then the content is automatically parsed and displayed by our `<Markdown />` component.
44
+ * `children` need to a `string` then, otherwise it cannot be parsed.
45
+ */
46
+ markdownProps?: Omit<MarkdownProps, "children">;
47
+ /**
48
+ * Callback handler if content should be expanded.
49
+ * Button to shrink/expand is displayed, depending on `shrinked` value.
50
+ * If this handler is given then the component never will change the `shrinked` state automatically.
51
+ */
52
+ onToggleSize?: () => void;
53
+ /**
54
+ * Content should dislayed shrinked.
55
+ * Button to expand content is displayed.
56
+ * Component can reduce content automatically to one line if `autoShrink` is set to `true`.
57
+ * If `onToggleSize` handler is not given then `autoShrink=true` is inferred and size toggling is automatically provided.
58
+ */
59
+ shrinked?: boolean;
60
+ /**
61
+ * Children elements are automatically shrinked to one line.
62
+ * If `shrinked` are not given then `shrinked=true` is infered.
63
+ */
64
+ autoShrink?: boolean;
65
+ }
66
+
67
+ /**
68
+ * Component to display singe chat contents, including avatar and status line.
69
+ */
70
+ export const ChatContent = ({
71
+ className,
72
+ children,
73
+ statusLine,
74
+ avatar,
75
+ displayType = "bubble",
76
+ indentationSize,
77
+ alignment = "left",
78
+ limitHeight,
79
+ markdownProps,
80
+ shrinked,
81
+ autoShrink,
82
+ onToggleSize,
83
+ ...otherDivProps
84
+ }: ChatContentProps) => {
85
+ const [displayShrinked, setDispayShrinked] = React.useState<boolean>(
86
+ shrinked === true || (autoShrink === true && typeof shrinked === "undefined")
87
+ );
88
+
89
+ const toggleSize = () => {
90
+ if (onToggleSize) {
91
+ onToggleSize();
92
+ } else {
93
+ setDispayShrinked(!displayShrinked);
94
+ }
95
+ };
96
+
97
+ const content =
98
+ markdownProps && typeof children === "string" ? <Markdown {...markdownProps}>{children}</Markdown> : children;
99
+
100
+ const onlyText = (children: React.ReactNode | React.ReactNode[]): string => {
101
+ if (children instanceof Array) {
102
+ return children
103
+ .map((child: React.ReactNode) => {
104
+ return onlyText(child);
105
+ })
106
+ .join(" ");
107
+ }
108
+
109
+ return React.Children.toArray(children)
110
+ .map((child) => {
111
+ if (ReactIs.isFragment(child)) {
112
+ return onlyText(child.props?.children);
113
+ }
114
+ if (typeof child === "string") {
115
+ return child;
116
+ }
117
+ if (typeof child === "number") {
118
+ return child.toString();
119
+ }
120
+ if (ReactIs.isElement(child)) {
121
+ // for some reasons `renderToString` returns empty string if not wrappe in a `span`
122
+ return renderToString(<span>{child}</span>);
123
+ }
124
+ return "";
125
+ })
126
+ .join(" ");
127
+ };
128
+
129
+ const chatitem = (
130
+ <div
131
+ className={
132
+ `${eccgui}-chat__content` +
133
+ ` ${eccgui}-chat__content--display-${displayType}` +
134
+ ` ${eccgui}-chat__content--align-${alignment}` +
135
+ (limitHeight ? ` ${eccgui}-chat__content--limitheight` : "") +
136
+ (className ? ` ${className}` : "")
137
+ }
138
+ {...otherDivProps}
139
+ >
140
+ {statusLine && (
141
+ <HtmlContentBlock small>
142
+ {statusLine}
143
+ <Spacing size="tiny" />
144
+ </HtmlContentBlock>
145
+ )}
146
+ {displayShrinked && autoShrink ? (
147
+ <OverflowText passDown>
148
+ <Markdown removeMarkup>{onlyText(content)}</Markdown>
149
+ </OverflowText>
150
+ ) : (
151
+ content
152
+ )}
153
+ </div>
154
+ );
155
+
156
+ const indentationSizes = {
157
+ small: "8%",
158
+ medium: "21%",
159
+ large: "34%",
160
+ };
161
+
162
+ return (
163
+ <div
164
+ style={{
165
+ marginLeft: alignment === "right" && indentationSize ? indentationSizes[indentationSize] : undefined,
166
+ marginRight: alignment === "left" && indentationSize ? indentationSizes[indentationSize] : undefined,
167
+ }}
168
+ >
169
+ <FlexibleLayoutContainer noEqualItemSpace gapSize="tiny">
170
+ {avatar && (
171
+ <FlexibleLayoutItem
172
+ className={`${eccgui}-chat__content-avatar`}
173
+ growFactor={0}
174
+ shrinkFactor={0}
175
+ style={alignment === "right" ? { order: 1 } : undefined}
176
+ >
177
+ {React.cloneElement(avatar, { size: "small", ratio: "1:1", rounded: true, resizing: "cover" })}
178
+ </FlexibleLayoutItem>
179
+ )}
180
+ <FlexibleLayoutItem className={`${eccgui}-chat__content-wrapper`}>{chatitem}</FlexibleLayoutItem>
181
+ {(displayShrinked || onToggleSize || autoShrink) && (
182
+ <FlexibleLayoutItem
183
+ className={`${eccgui}-chat__content-sizetoggle`}
184
+ growFactor={0}
185
+ shrinkFactor={0}
186
+ style={alignment === "right" ? { order: -1 } : undefined}
187
+ >
188
+ <IconButton
189
+ name={displayShrinked ? "toggler-showmore" : "toggler-showless"}
190
+ onClick={() => toggleSize()}
191
+ />
192
+ </FlexibleLayoutItem>
193
+ )}
194
+ </FlexibleLayoutContainer>
195
+ </div>
196
+ );
197
+ };
198
+
199
+ export default ChatContent;
@@ -0,0 +1,52 @@
1
+ import React from "react";
2
+
3
+ import { TestableComponent } from "../../components/interfaces";
4
+ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
+ import { IconButton } from "../Icon/IconButton";
6
+ import { TextArea, TextAreaProps } from "../TextField/TextArea";
7
+
8
+ export interface ChatFieldProps extends Pick<TextAreaProps, "className">, TestableComponent {
9
+ /**
10
+ * Default input to start with.
11
+ */
12
+ children?: string;
13
+ /**
14
+ * Callback handler to process the input of the field when `Enter` is pressed or the submit button is clicked.
15
+ */
16
+ onSubmit: (value: string) => void;
17
+ }
18
+
19
+ /**
20
+ * Component to input chat text.
21
+ * Based on `TextArea` component.
22
+ */
23
+ export const ChatField = ({ className, onSubmit, ...otherTextAreaProps }: ChatFieldProps) => {
24
+ const chatvalue = React.useRef<string>(otherTextAreaProps.children ?? "");
25
+
26
+ const onContentChange = (value: string) => {
27
+ chatvalue.current = value;
28
+ };
29
+
30
+ const onEnter = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
31
+ if (e.keyCode === 13 && e.shiftKey === false) {
32
+ e.preventDefault();
33
+ onSubmit(chatvalue.current);
34
+ }
35
+ };
36
+
37
+ return (
38
+ <TextArea
39
+ fill
40
+ autoResize
41
+ className={`${eccgui}-chat__inputfield` + (className ? ` ${className}` : "")}
42
+ onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
43
+ onContentChange(e.target.value);
44
+ }}
45
+ onKeyDown={onEnter}
46
+ rightElement={<IconButton name={"operation-send"} onClick={() => onSubmit(chatvalue.current)} />}
47
+ {...otherTextAreaProps}
48
+ />
49
+ );
50
+ };
51
+
52
+ export default ChatField;
@@ -0,0 +1,86 @@
1
+ .#{$eccgui}-chat__content {
2
+ @extend .bp5-elevation-1;
3
+
4
+ position: relative;
5
+ z-index: 0;
6
+ min-height: $button-height;
7
+ padding: $eccgui-size-inline-whitespace;
8
+ overflow: auto;
9
+ background-color: var(--#{$eccgui}-chat__content-background);
10
+ border-radius: 3 * $pt-border-radius;
11
+ }
12
+
13
+ .#{$eccgui}-chat__content--display-free {
14
+ --#{$eccgui}-chat__content-background: transparent;
15
+
16
+ padding: 1px 0;
17
+ box-shadow: none;
18
+ }
19
+
20
+ .#{$eccgui}-chat__content--display-bubble {
21
+ margin-left: 0.75 * $eccgui-size-block-whitespace;
22
+
23
+ &.#{$eccgui}-chat__content--align-right {
24
+ margin-right: 0.75 * $eccgui-size-block-whitespace;
25
+ margin-left: none;
26
+ }
27
+ }
28
+
29
+ .#{$eccgui}-chat__content-wrapper {
30
+ --#{$eccgui}-chat__content-background: #{$light-gray5};
31
+ &:has(.#{$eccgui}-chat__content--display-bubble) {
32
+ position: relative;
33
+
34
+ &::before {
35
+ @extend .bp5-elevation-1;
36
+
37
+ position: absolute;
38
+ top: calc(#{mini-units(3)} - #{0.5 * $eccgui-size-block-whitespace});
39
+ left: 0.25 * $eccgui-size-block-whitespace;
40
+ z-index: 1;
41
+ width: $eccgui-size-block-whitespace;
42
+ height: $eccgui-size-block-whitespace;
43
+ content: " ";
44
+ background-color: var(--#{$eccgui}-chat__content-background);
45
+ clip-path: polygon(-5px calc(100% + 5px), -5px -5px, calc(100% + 5px) calc(100% + 5px));
46
+ transform: rotate(45deg);
47
+ }
48
+ }
49
+ &:has(.#{$eccgui}-chat__content--display-bubble.#{$eccgui}-chat__content--align-right) {
50
+ &::before {
51
+ right: 0.25 * $eccgui-size-block-whitespace;
52
+ left: auto;
53
+ transform: rotate(225deg);
54
+ }
55
+ }
56
+ }
57
+
58
+ .#{$eccgui}-chat__content--limitheight {
59
+ max-height: 50vh;
60
+ }
61
+
62
+ .#{$eccgui}-chat__inputfield {
63
+ min-height: mini-units(9);
64
+ max-height: 39vh;
65
+ resize: none !important;
66
+ }
67
+
68
+ .#{$eccgui}-chat__area {
69
+ padding: 0.25rem;
70
+ background-color: $card-background-color;
71
+ }
72
+
73
+ .#{$eccgui}-chat__area-contentwidth {
74
+ width: 100%;
75
+ margin: 0 auto;
76
+
77
+ .#{$eccgui}-chat__area--small & {
78
+ max-width: 40rem;
79
+ }
80
+ .#{$eccgui}-chat__area--medium & {
81
+ max-width: 60rem;
82
+ }
83
+ .#{$eccgui}-chat__area--large & {
84
+ max-width: 80rem;
85
+ }
86
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./ChatArea";
2
+ export * from "./ChatContent";
3
+ export * from "./ChatField";
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { Meta, StoryFn } from "@storybook/react";
3
+
4
+ import { ChatArea, ChatContent, ChatField } from "../../../index";
5
+
6
+ import { Default as ShortChatBubble, LongChatBubble } from "./ChatContent.stories";
7
+ import { Default as ChatFieldExample } from "./ChatField.stories";
8
+
9
+ export default {
10
+ title: "Components/Chat/ChatArea",
11
+ component: ChatArea,
12
+ argTypes: {},
13
+ } as Meta<typeof ChatArea>;
14
+
15
+ let forceupdate = 0;
16
+ const TemplateFull: StoryFn<typeof ChatArea> = (args) => (
17
+ <div style={args.useAbsoluteSpace ? { position: "relative", height: "75vh" } : undefined}>
18
+ <ChatArea {...args} key={forceupdate++} />
19
+ </div>
20
+ );
21
+
22
+ export const Default = TemplateFull.bind({});
23
+ Default.args = {
24
+ chatField: <ChatField {...ChatFieldExample.args} />,
25
+ children: [
26
+ <ChatContent {...ShortChatBubble.args} alignment="right" indentationSize="medium" />,
27
+ <ChatContent {...ShortChatBubble.args} avatar={undefined} displayType="free" />,
28
+ <ChatContent {...ShortChatBubble.args} alignment="right" indentationSize="medium" />,
29
+ <ChatContent {...LongChatBubble.args} autoShrink />,
30
+ <ChatContent {...ShortChatBubble.args} alignment="right" indentationSize="medium" />,
31
+ <ChatContent {...ShortChatBubble.args} />,
32
+ ],
33
+ autoSpacingSize: "medium",
34
+ autoScrollTo: "last",
35
+ useAbsoluteSpace: true,
36
+ };
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { LoremIpsum } from "react-lorem-ipsum";
3
+ import { Meta, StoryFn } from "@storybook/react";
4
+
5
+ import { ChatContent, Depiction, HtmlContentBlock, Icon, OverflowText } from "../../../index";
6
+
7
+ import canonicalIcons from "./../../Icon/canonicalIconNames";
8
+
9
+ const allIcons = new Map([
10
+ ...Object.keys(canonicalIcons).map((keyId) => {
11
+ return [`Icon: ${keyId}`, <Depiction image={<Icon name={keyId} />} />];
12
+ }),
13
+ ]);
14
+
15
+ const exampleImages = {
16
+ None: undefined,
17
+ ...Object.fromEntries(allIcons),
18
+ };
19
+
20
+ export default {
21
+ title: "Components/Chat/ChatContent",
22
+ component: ChatContent,
23
+ argTypes: {
24
+ avatar: {
25
+ control: "select",
26
+ options: Object.keys(exampleImages),
27
+ mapping: exampleImages,
28
+ },
29
+ },
30
+ } as Meta<typeof ChatContent>;
31
+
32
+ let update = 0;
33
+ const TemplateFull: StoryFn<typeof ChatContent> = (args) => <ChatContent {...args} key={update++} />;
34
+
35
+ export const Default = TemplateFull.bind({});
36
+ Default.args = {
37
+ children: (
38
+ <HtmlContentBlock>
39
+ <LoremIpsum p={1} avgSentencesPerParagraph={5} random={false} />
40
+ </HtmlContentBlock>
41
+ ),
42
+ avatar: <Depiction image={<Icon name={"application-useraccount"} />} />,
43
+ statusLine: (
44
+ <OverflowText>
45
+ <strong>Username</strong> 25 minutes ago
46
+ </OverflowText>
47
+ ),
48
+ onToggleSize: undefined,
49
+ };
50
+
51
+ export const LongChatBubble = TemplateFull.bind({});
52
+ LongChatBubble.args = {
53
+ ...Default.args,
54
+ children: (
55
+ <HtmlContentBlock>
56
+ <LoremIpsum p={10} avgSentencesPerParagraph={10} random={false} />
57
+ </HtmlContentBlock>
58
+ ),
59
+ limitHeight: true,
60
+ };
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { Meta, StoryFn } from "@storybook/react";
3
+
4
+ import { ChatField } from "../../../index";
5
+
6
+ export default {
7
+ title: "Components/Chat/ChatField",
8
+ component: ChatField,
9
+ argTypes: {},
10
+ } as Meta<typeof ChatField>;
11
+
12
+ let forceupdate = 0;
13
+ const TemplateFull: StoryFn<typeof ChatField> = (args) => <ChatField {...args} key={forceupdate++} />;
14
+
15
+ export const Default = TemplateFull.bind({});
16
+ Default.args = {
17
+ onSubmit: (value) => alert(value),
18
+ };
@@ -50,7 +50,7 @@ export const IconButton = ({
50
50
  const defaultIconTooltipProps = {
51
51
  hoverOpenDelay: 1000,
52
52
  openOnTargetFocus: restProps.disabled || (restProps.tabIndex ?? 0) < 0 ? false : undefined,
53
- swapPlaceholderDelay: 1,
53
+ swapPlaceholderDelay: 10,
54
54
  };
55
55
  const iconProps = {
56
56
  small: restProps.small,
@@ -8,6 +8,7 @@ const canonicalIcons = {
8
8
  "application-homepage": icons.Workspace,
9
9
  "application-legacygui": icons.ResetAlt,
10
10
  "application-mapping": icons.ModelBuilder,
11
+ "application-colors": icons.ColorPalette,
11
12
  "application-queries": icons.DataView,
12
13
  "application-useraccount": icons.UserAvatar,
13
14
  "application-vocabularies": icons.Catalog,
@@ -50,6 +51,7 @@ const canonicalIcons = {
50
51
  "artefact-workflow": icons.ModelBuilder,
51
52
 
52
53
  "data-boolean": icons.Boolean,
54
+ "data-color": icons.ColorPalette,
53
55
  "data-sourcepath": icons.Data_2,
54
56
  "data-targetpath": icons.Data_1,
55
57
  "data-string": icons.StringText,
@@ -145,6 +147,7 @@ const canonicalIcons = {
145
147
  "operation-merge": icons.DirectionMerge,
146
148
  "operation-redo": icons.Redo,
147
149
  "operation-search": icons.Search,
150
+ "operation-send": icons.Send,
148
151
  "operation-sharelink": icons.CopyLink,
149
152
  "operation-transform": icons.Calculation,
150
153
  "operation-translate": icons.Translate,
@@ -91,7 +91,7 @@ export const TextArea = ({
91
91
  leftIconElementRect.width ?? 0
92
92
  }px)`
93
93
  );
94
- leftIconElement.addEventListener("click", (_event: MouseEvent) => {
94
+ leftIconElement.addEventListener("click", () => {
95
95
  textAreaElement.focus();
96
96
  });
97
97
  }
@@ -157,7 +157,7 @@ export const TextArea = ({
157
157
  rows={
158
158
  rows && !otherBlueprintTextAreaProps.autoResize && !otherBlueprintTextAreaProps.growVertically
159
159
  ? rows
160
- : 1
160
+ : 2
161
161
  }
162
162
  {...otherBlueprintTextAreaProps}
163
163
  dir={"auto"}
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { loremIpsum } from "react-lorem-ipsum";
3
3
  import { OverlaysProvider } from "@blueprintjs/core";
4
4
  import { Meta, StoryFn } from "@storybook/react";
5
+ import { fn } from "@storybook/test";
5
6
 
6
7
  import { Tooltip } from "../../index";
7
8
 
@@ -34,6 +35,7 @@ Default.args = {
34
35
  children: "hover me",
35
36
  content: testContent,
36
37
  addIndicator: true,
38
+ onOpening: fn(),
37
39
  };
38
40
 
39
41
  export const MarkdownSupport = Template.bind({});
@@ -134,7 +134,15 @@ export const Tooltip = ({
134
134
  (target as HTMLElement).focus();
135
135
  break;
136
136
  case "afterhover":
137
- (target as HTMLElement).dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
137
+ // re-check if the cursor is still over the element after swapping the placeholder before triggering the event to bubble up
138
+ (target as HTMLElement).addEventListener(
139
+ "mouseover",
140
+ () => (target as HTMLElement).dispatchEvent(new MouseEvent("mouseover", { bubbles: true })),
141
+ {
142
+ capture: true,
143
+ once: true,
144
+ }
145
+ );
138
146
  break;
139
147
  }
140
148
  }
@@ -3,6 +3,7 @@
3
3
  @import "./Breadcrumb/breadcrumb";
4
4
  @import "./Button/button";
5
5
  @import "./Card/card";
6
+ @import "./Chat/chat";
6
7
  @import "./Checkbox/checkbox";
7
8
  @import "./Depiction/depiction";
8
9
  @import "./Dialog/dialog";
@@ -8,6 +8,7 @@ export * from "./Badge/Badge";
8
8
  export * from "./Breadcrumb";
9
9
  export * from "./Button/Button";
10
10
  export * from "./Card";
11
+ export * from "./Chat";
11
12
  export * from "./Checkbox/Checkbox";
12
13
  export * from "./ContextOverlay";
13
14
  export * from "./Depiction/Depiction";