@eccenca/gui-elements 23.7.0-rc.0 → 23.7.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cjs/cmem/markdown/Markdown.js +1 -1
  3. package/dist/cjs/cmem/markdown/Markdown.js.map +1 -1
  4. package/dist/cjs/common/index.js +2 -0
  5. package/dist/cjs/common/index.js.map +1 -1
  6. package/dist/cjs/common/utils/getScrollParent.js +24 -0
  7. package/dist/cjs/common/utils/getScrollParent.js.map +1 -0
  8. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +19 -2
  9. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  10. package/dist/cjs/components/Breadcrumb/BreadcrumbList.js +1 -1
  11. package/dist/cjs/components/Breadcrumb/BreadcrumbList.js.map +1 -1
  12. package/dist/cjs/components/Card/Card.js +3 -1
  13. package/dist/cjs/components/Card/Card.js.map +1 -1
  14. package/dist/cjs/components/MultiSelect/MultiSelect.js +6 -5
  15. package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
  16. package/dist/cjs/components/Sticky/StickyTarget.js +85 -0
  17. package/dist/cjs/components/Sticky/StickyTarget.js.map +1 -0
  18. package/dist/cjs/components/Sticky/index.js +14 -0
  19. package/dist/cjs/components/Sticky/index.js.map +1 -0
  20. package/dist/cjs/components/TextField/TextArea.js +85 -8
  21. package/dist/cjs/components/TextField/TextArea.js.map +1 -1
  22. package/dist/cjs/components/index.js +1 -0
  23. package/dist/cjs/components/index.js.map +1 -1
  24. package/dist/esm/cmem/markdown/Markdown.js +1 -1
  25. package/dist/esm/cmem/markdown/Markdown.js.map +1 -1
  26. package/dist/esm/common/index.js +2 -0
  27. package/dist/esm/common/index.js.map +1 -1
  28. package/dist/esm/common/utils/getScrollParent.js +20 -0
  29. package/dist/esm/common/utils/getScrollParent.js.map +1 -0
  30. package/dist/esm/components/AutocompleteField/AutoCompleteField.js +27 -2
  31. package/dist/esm/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  32. package/dist/esm/components/Breadcrumb/BreadcrumbList.js +1 -1
  33. package/dist/esm/components/Breadcrumb/BreadcrumbList.js.map +1 -1
  34. package/dist/esm/components/Card/Card.js +4 -2
  35. package/dist/esm/components/Card/Card.js.map +1 -1
  36. package/dist/esm/components/MultiSelect/MultiSelect.js +6 -5
  37. package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
  38. package/dist/esm/components/Sticky/StickyTarget.js +89 -0
  39. package/dist/esm/components/Sticky/StickyTarget.js.map +1 -0
  40. package/dist/esm/components/Sticky/index.js +2 -0
  41. package/dist/esm/components/Sticky/index.js.map +1 -0
  42. package/dist/esm/components/TextField/TextArea.js +86 -9
  43. package/dist/esm/components/TextField/TextArea.js.map +1 -1
  44. package/dist/esm/components/index.js +1 -0
  45. package/dist/esm/components/index.js.map +1 -1
  46. package/dist/types/common/index.d.ts +2 -0
  47. package/dist/types/common/utils/getScrollParent.d.ts +9 -0
  48. package/dist/types/components/AutocompleteField/AutoCompleteField.d.ts +2 -0
  49. package/dist/types/components/Breadcrumb/BreadcrumbList.d.ts +2 -1
  50. package/dist/types/components/Card/Card.d.ts +8 -2
  51. package/dist/types/components/Sticky/StickyTarget.d.ts +32 -0
  52. package/dist/types/components/Sticky/index.d.ts +1 -0
  53. package/dist/types/components/TextField/TextArea.d.ts +28 -3
  54. package/dist/types/components/index.d.ts +1 -0
  55. package/package.json +1 -1
  56. package/src/cmem/markdown/Markdown.tsx +1 -1
  57. package/src/common/index.ts +3 -0
  58. package/src/common/utils/getScrollParent.ts +20 -0
  59. package/src/components/Application/application.scss +0 -1
  60. package/src/components/AutocompleteField/AutoCompleteField.tsx +28 -0
  61. package/src/components/AutocompleteField/autocompletefield.scss +1 -1
  62. package/src/components/Breadcrumb/BreadcrumbList.tsx +3 -3
  63. package/src/components/Card/Card.tsx +15 -3
  64. package/src/components/Card/card.scss +6 -1
  65. package/src/components/Icon/stories/Icon.stories.tsx +1 -1
  66. package/src/components/MultiSelect/MultiSelect.tsx +8 -4
  67. package/src/components/MultiSuggestField/MultiSuggestField.stories.tsx +76 -1
  68. package/src/components/MultiSuggestField/tests/MultiSuggestField.test.tsx +297 -109
  69. package/src/components/Sticky/StickyTarget.tsx +119 -0
  70. package/src/components/Sticky/index.ts +1 -0
  71. package/src/components/Sticky/sticky.scss +69 -0
  72. package/src/components/Sticky/stories/StickyTarget.stories.tsx +63 -0
  73. package/src/components/TextField/TextArea.tsx +174 -12
  74. package/src/components/TextField/stories/TextArea.stories.tsx +39 -12
  75. package/src/components/TextField/textfield.scss +78 -11
  76. package/src/components/index.scss +1 -0
  77. package/src/components/index.ts +1 -0
  78. package/src/includes/blueprintjs/_requisits.scss +1 -1
  79. package/src/includes/blueprintjs/_variables.scss +3 -172
  80. package/src/includes/carbon-components/_requisits.scss +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAE5B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAE5B,cAAc,cAAc,CAAC"}
@@ -21,6 +21,7 @@ export declare const utils: {
21
21
  };
22
22
  getGlobalVar: (varname: string) => Window;
23
23
  setGlobalVar: (varname: string, value: any) => any;
24
+ getScrollParent: (element: Element) => false | HTMLElement;
24
25
  };
25
26
  export declare const Utilities: {
26
27
  openInNewTab: (event: import("react").MouseEvent<HTMLAnchorElement, MouseEvent>, handler?: ((e: import("react").MouseEvent<HTMLAnchorElement, MouseEvent>) => void) | undefined, url?: string | undefined) => void;
@@ -44,4 +45,5 @@ export declare const Utilities: {
44
45
  };
45
46
  getGlobalVar: (varname: string) => Window;
46
47
  setGlobalVar: (varname: string, value: any) => any;
48
+ getScrollParent: (element: Element) => false | HTMLElement;
47
49
  };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Find the scroll parent of an element, returns `false` if it cannot be found.
3
+ * In case of a `false` return the very probably `document.documentElement` is the parent.
4
+ * In this case `window` object should be used for scroll event listeners.
5
+ * See `src/components/Sticky/StickyTarget.tsx` for an usage example.
6
+ * @param element
7
+ * @returns HTMLElement | false
8
+ */
9
+ export declare const getScrollParent: (element: Element) => HTMLElement | false;
@@ -109,6 +109,8 @@ export interface AutoCompleteFieldProps<T, UPDATE_VALUE> {
109
109
  * Use the full available width of the parent container.
110
110
  */
111
111
  fill?: boolean;
112
+ /** Utility that fetches more options when clicked*/
113
+ loadMoreResults?: () => Promise<T[] | undefined>;
112
114
  }
113
115
  export declare type IAutoCompleteFieldProps<T, UPDATE_VALUE> = AutoCompleteFieldProps<T, UPDATE_VALUE>;
114
116
  /**
@@ -8,7 +8,8 @@ export interface BreadcrumbListProps extends TestableComponent, Omit<BlueprintBr
8
8
  */
9
9
  items: BreadcrumbItemProps[];
10
10
  /**
11
- click handler used on breadcrumb items
11
+ Click handler used on all breadcrumb items using their `href` property.
12
+ Is only used if the breadcrumb item have not defined an own `onClick` handler.
12
13
  */
13
14
  onItemClick?(itemUrl: string | undefined, event: object): boolean | void;
14
15
  /**
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
- import { CardProps as BlueprintCardProps } from "@blueprintjs/core";
3
- export interface CardProps extends BlueprintCardProps {
2
+ import { CardProps as BlueprintCardProps, Elevation as BlueprintCardElevation } from "@blueprintjs/core";
3
+ export interface CardProps extends Omit<BlueprintCardProps, "elevation"> {
4
4
  /**
5
5
  * `<Card />` element is included in DOM as simple `div` element.
6
6
  * By default it is a HTML `section`.
@@ -14,6 +14,12 @@ export interface CardProps extends BlueprintCardProps {
14
14
  * Background color is slightly altered to differ card display from other cards.
15
15
  */
16
16
  elevated?: boolean;
17
+ /**
18
+ * Controls the intensity of the drop shadow beneath the card.
19
+ * At elevation `0`, no drop shadow is applied.
20
+ * At elevation `-1`, the card is even borderless.
21
+ */
22
+ elevation?: -1 | BlueprintCardElevation;
17
23
  /**
18
24
  * When card (or its children) get focus the card is scrolled into the viewport.
19
25
  * Property value defined which part of the card is always scrolled in, this may important when the card is larger than the viewport.
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ export interface StickyTargetProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ /**
4
+ * Set the side the element need to be sticky on.
5
+ */
6
+ to?: "top" | "bottom";
7
+ /**
8
+ * The sticky area is positioned relatively to a local scroll area.
9
+ * The application header is not taken into offset calculation
10
+ */
11
+ local?: boolean;
12
+ /**
13
+ * Set the background color used for the sticky area.
14
+ * As it can overlay other content readability could be harmed if the overlayed content is shining through.
15
+ */
16
+ background?: "card" | "application" | "transparent";
17
+ /**
18
+ * Set additional distance to original sticky position.
19
+ */
20
+ offset?: `${number}${string}`;
21
+ /**
22
+ * Callback that returns an DOM element.
23
+ * The position of `StickyTarget` is then calculated relative to that element.
24
+ */
25
+ getConnectedElement?: (ref: React.MutableRefObject<HTMLDivElement | null>) => Element | false;
26
+ }
27
+ /**
28
+ * Element wraps the content that need to be displayed sticky.
29
+ * The content then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor).
30
+ */
31
+ export declare const StickyTarget: ({ className, to, local, background, offset, style, getConnectedElement, ...otherDivProps }: StickyTargetProps) => React.JSX.Element;
32
+ export default StickyTarget;
@@ -0,0 +1 @@
1
+ export * from "./StickyTarget";
@@ -1,27 +1,52 @@
1
1
  import React from "react";
2
- import { TextAreaProps as BlueprintTextAreaProps } from "@blueprintjs/core";
2
+ import { MaybeElement, TextAreaProps as BlueprintTextAreaProps } from "@blueprintjs/core";
3
+ import { IntentTypes } from "../../common/Intent";
4
+ import { ValidIconName } from "../Icon/canonicalIconNames";
3
5
  import { InvisibleCharacterWarningProps } from "./useTextValidation";
4
- export interface TextAreaProps extends Partial<BlueprintTextAreaProps> {
6
+ export interface TextAreaProps extends Omit<BlueprintTextAreaProps, "intent"> {
5
7
  /**
6
8
  * when set to true the input takes a blue border color
9
+ * @deprecated Use the `intent` property.
7
10
  */
8
11
  hasStatePrimary?: boolean;
9
12
  /**
10
13
  * when set to true the input takes a green border color
14
+ * @deprecated Use the `intent` property.
11
15
  */
12
16
  hasStateSuccess?: boolean;
13
17
  /**
14
18
  * when set to true the input takes an orange border color
19
+ * @deprecated Use the `intent` property.
15
20
  */
16
21
  hasStateWarning?: boolean;
17
22
  /**
18
23
  * when set to true the input takes a red border color
24
+ * @deprecated Use the `intent` property.
19
25
  */
20
26
  hasStateDanger?: boolean;
27
+ /**
28
+ * Intent state of the text area.
29
+ */
30
+ intent?: IntentTypes | "edited" | "removed";
21
31
  /**
22
32
  * If set, allows to be informed of invisible, hard to spot characters in the string value.
23
33
  */
24
34
  invisibleCharacterWarning?: InvisibleCharacterWarningProps;
35
+ /**
36
+ * Left aligned icon, can be a canonical icon name or an `Icon` element.
37
+ * This will update left padding on the text area.
38
+ */
39
+ leftIcon?: ValidIconName | MaybeElement;
40
+ /**
41
+ * Element to render on right side of text area. Should be not too large.
42
+ * This will update right padding on the text area.
43
+ */
44
+ rightElement?: JSX.Element;
45
+ /**
46
+ * Add HTML properties to the wrapper element.
47
+ * The element wraps `TextArea` in case of a given `wrapperDivProps`, `leftIcon` or `rightElement` property.
48
+ */
49
+ wrapperDivProps?: Omit<React.HTMLAttributes<HTMLDivElement>, "children">;
25
50
  }
26
- export declare const TextArea: ({ className, hasStatePrimary, hasStateSuccess, hasStateWarning, hasStateDanger, rows, invisibleCharacterWarning, ...otherProps }: TextAreaProps) => React.JSX.Element;
51
+ export declare const TextArea: ({ className, hasStatePrimary, hasStateSuccess, hasStateWarning, hasStateDanger, rows, invisibleCharacterWarning, leftIcon, rightElement, wrapperDivProps, ...otherProps }: TextAreaProps) => React.JSX.Element;
27
52
  export default TextArea;
@@ -36,6 +36,7 @@ export * from "./Separation/Divider";
36
36
  export * from "./Separation/Spacing";
37
37
  export * from "./Skeleton/Skeleton";
38
38
  export * from "./Spinner/Spinner";
39
+ export * from "./Sticky";
39
40
  export * from "./Structure";
40
41
  export * from "./Switch/Switch";
41
42
  export * from "./Table";
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.7.0-rc.0",
4
+ "version": "23.7.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",
@@ -3,7 +3,7 @@ import ReactMarkdown from "react-markdown";
3
3
  import { PluggableList } from "react-markdown/lib/react-markdown";
4
4
  import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
5
5
  import { materialLight } from "react-syntax-highlighter/dist/esm/styles/prism";
6
- // @ts-expect-error: No declaration file for module
6
+ // @ts-ignore: No declaration file for module (TODO: should be @ts-expect-error but GUI elements is used inside project with `noImplicitAny=false`)
7
7
  import remarkTypograf from "@mavrin/remark-typograf";
8
8
  import rehypeRaw from "rehype-raw";
9
9
  import { remarkDefinitionList } from "remark-definition-list";
@@ -1,6 +1,7 @@
1
1
  import { invisibleZeroWidthCharacters } from "./utils/characters";
2
2
  import decideContrastColorValue from "./utils/colorDecideContrastvalue";
3
3
  import getColorConfiguration from "./utils/getColorConfiguration";
4
+ import { getScrollParent } from "./utils/getScrollParent";
4
5
  import { getGlobalVar, setGlobalVar } from "./utils/globalVars";
5
6
  import { openInNewTab } from "./utils/openInNewTab";
6
7
 
@@ -11,6 +12,8 @@ export const utils = {
11
12
  invisibleZeroWidthCharacters,
12
13
  getGlobalVar,
13
14
  setGlobalVar,
15
+ getScrollParent,
14
16
  };
17
+
15
18
  // @deprecated use `utils`
16
19
  export const Utilities = utils;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Find the scroll parent of an element, returns `false` if it cannot be found.
3
+ * In case of a `false` return the very probably `document.documentElement` is the parent.
4
+ * In this case `window` object should be used for scroll event listeners.
5
+ * See `src/components/Sticky/StickyTarget.tsx` for an usage example.
6
+ * @param element
7
+ * @returns HTMLElement | false
8
+ */
9
+ export const getScrollParent = (element: Element): HTMLElement | false => {
10
+ let scrollParent = element.parentElement;
11
+ while (scrollParent) {
12
+ const { overflow } = window.getComputedStyle(scrollParent);
13
+ if (overflow.split(" ").every((value) => value === "auto" || value === "scroll")) {
14
+ return scrollParent;
15
+ }
16
+ scrollParent = scrollParent.parentElement;
17
+ }
18
+
19
+ return false;
20
+ };
@@ -1,5 +1,4 @@
1
1
  // @import 'config';
2
- @import "~@carbon/styles/scss/components/ui-shell/functions";
3
2
  @import "header";
4
3
  @import "toolbar";
5
4
 
@@ -156,6 +156,8 @@ export interface AutoCompleteFieldProps<T, UPDATE_VALUE> {
156
156
  * Use the full available width of the parent container.
157
157
  */
158
158
  fill?: boolean;
159
+ /** Utility that fetches more options when clicked*/
160
+ loadMoreResults?: () => Promise<T[] | undefined>;
159
161
  }
160
162
 
161
163
  export type IAutoCompleteFieldProps<T, UPDATE_VALUE> = AutoCompleteFieldProps<T, UPDATE_VALUE>;
@@ -195,6 +197,7 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
195
197
  requestErrorPrefix,
196
198
  hasBackDrop,
197
199
  fill,
200
+ loadMoreResults,
198
201
  ...otherProps
199
202
  } = props;
200
203
  const [selectedItem, setSelectedItem] = useState<T | undefined>(initialValue);
@@ -334,6 +337,7 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
334
337
  disabled: modifiers.disabled,
335
338
  highlightingEnabled: highlightingEnabled,
336
339
  };
340
+
337
341
  const renderedItem = itemRenderer(item, query, relevantModifiers, handleClick);
338
342
  if (typeof renderedItem === "string") {
339
343
  return (
@@ -442,6 +446,26 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
442
446
  createNewItemPosition,
443
447
  }
444
448
  : {};
449
+
450
+ const handleMenuScroll = React.useCallback(
451
+ async (event: any) => {
452
+ const menu = event.target;
453
+ const { scrollTop, scrollHeight, clientHeight } = menu;
454
+ // Check if scrolled to the bottom of the list
455
+ if (Math.round(scrollTop + clientHeight) >= scrollHeight && loadMoreResults) {
456
+ const results = await loadMoreResults();
457
+ if (results) {
458
+ setFiltered((prev) => [...prev, ...results]);
459
+ setTimeout(() => {
460
+ menu.scrollTop = scrollTop; //safari adaptation
461
+ menu.scrollTo({ left: 0, top: scrollTop, behavior: "auto" });
462
+ });
463
+ }
464
+ }
465
+ },
466
+ [loadMoreResults]
467
+ );
468
+
445
469
  return (
446
470
  <BlueprintSuggest<T>
447
471
  className={`${eccgui}-autocompletefield__input` + (className ? ` ${className}` : "")}
@@ -455,7 +479,11 @@ export function AutoCompleteField<T, UPDATE_VALUE>(props: AutoCompleteFieldProps
455
479
  noResults={<MenuItem disabled={true} text={noResultText} />}
456
480
  onItemSelect={onSelectionChange}
457
481
  onQueryChange={(q) => setQuery(q)}
482
+ resetOnQuery={false}
458
483
  closeOnSelect={true}
484
+ menuProps={{
485
+ onScroll: handleMenuScroll,
486
+ }}
459
487
  query={query}
460
488
  // This leads to odd compile errors without "as any"
461
489
  popoverProps={updatedContextOverlayProps as any}
@@ -1,5 +1,5 @@
1
1
  .#{$eccgui}-autocompletefield__options {
2
- .#{$ns}-popover2-content {
2
+ .#{$ns}-popover-content {
3
3
  .#{$ns}-menu {
4
4
  max-height: 45vh;
5
5
  overflow: auto;
@@ -10,7 +10,6 @@ import { TestableComponent } from "../interfaces";
10
10
  import BreadcrumbItem from "./BreadcrumbItem";
11
11
  import { BreadcrumbItemProps } from "./BreadcrumbItem";
12
12
 
13
- // FIXME: enforce onItemClick later when href value can always be routed correctly
14
13
  export interface BreadcrumbListProps
15
14
  extends TestableComponent,
16
15
  Omit<
@@ -28,7 +27,8 @@ export interface BreadcrumbListProps
28
27
  */
29
28
  items: BreadcrumbItemProps[];
30
29
  /**
31
- click handler used on breadcrumb items
30
+ Click handler used on all breadcrumb items using their `href` property.
31
+ Is only used if the breadcrumb item have not defined an own `onClick` handler.
32
32
  */
33
33
  onItemClick?(itemUrl: string | undefined, event: object): boolean | void;
34
34
  /**
@@ -80,7 +80,7 @@ export const BreadcrumbList = ({
80
80
  /*itemDivider="/"*/
81
81
  {...otherProps}
82
82
  onClick={
83
- onItemClick
83
+ onItemClick && propsBreadcrumb.href && !onClick
84
84
  ? (e) => {
85
85
  onItemClick(propsBreadcrumb.href, e);
86
86
  }
@@ -1,9 +1,13 @@
1
1
  import React from "react";
2
- import { Card as BlueprintCard, CardProps as BlueprintCardProps } from "@blueprintjs/core";
2
+ import {
3
+ Card as BlueprintCard,
4
+ CardProps as BlueprintCardProps,
5
+ Elevation as BlueprintCardElevation,
6
+ } from "@blueprintjs/core";
3
7
 
4
8
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
5
9
 
6
- export interface CardProps extends BlueprintCardProps {
10
+ export interface CardProps extends Omit<BlueprintCardProps, "elevation"> {
7
11
  /**
8
12
  * `<Card />` element is included in DOM as simple `div` element.
9
13
  * By default it is a HTML `section`.
@@ -17,6 +21,12 @@ export interface CardProps extends BlueprintCardProps {
17
21
  * Background color is slightly altered to differ card display from other cards.
18
22
  */
19
23
  elevated?: boolean;
24
+ /**
25
+ * Controls the intensity of the drop shadow beneath the card.
26
+ * At elevation `0`, no drop shadow is applied.
27
+ * At elevation `-1`, the card is even borderless.
28
+ */
29
+ elevation?: -1 | BlueprintCardElevation;
20
30
  /**
21
31
  * When card (or its children) get focus the card is scrolled into the viewport.
22
32
  * Property value defined which part of the card is always scrolled in, this may important when the card is larger than the viewport.
@@ -48,6 +58,7 @@ export const Card = ({
48
58
  ? {
49
59
  tabIndex: 0,
50
60
  onFocus: (e: any) => {
61
+ // FIXME: we should not have any hard relations to apps that using this lib
51
62
  const el = e.target.closest(".diapp-iframewindow__content");
52
63
  setTimeout(() => {
53
64
  if (el)
@@ -68,9 +79,10 @@ export const Card = ({
68
79
  (elevated ? ` ${eccgui}-card--elevated` : "") +
69
80
  (scrollinOnFocus ? ` ${eccgui}-card--scrollonfocus` : "") +
70
81
  (whitespaceAmount !== "medium" ? ` ${eccgui}-card--whitespace-${whitespaceAmount}` : "") +
82
+ (elevation < 0 ? ` ${eccgui}-card--whitespace-borderless` : "") +
71
83
  (className ? ` ${className}` : "")
72
84
  }
73
- elevation={elevation}
85
+ elevation={Math.max(0, elevation) as BlueprintCardElevation}
74
86
  interactive={otherProps.onClick ? true : interactive}
75
87
  {...scrollIn}
76
88
  {...otherProps}
@@ -1,9 +1,10 @@
1
+ @use "sass:color";
2
+
1
3
  $card-padding: 0 !default;
2
4
  $card-background-color: $white !default;
3
5
  $card-selected-background-color: rgba($blue3, 0.1);
4
6
  $eccgui-size-card-spacing: $eccgui-size-typo-base !default;
5
7
 
6
- @use "sass:color";
7
8
  @import "~@blueprintjs/core/src/components/card/card";
8
9
 
9
10
  .#{$eccgui}-card {
@@ -61,6 +62,10 @@ $eccgui-size-card-spacing: $eccgui-size-typo-base !default;
61
62
  outline: none;
62
63
  }
63
64
 
65
+ .#{$eccgui}-card--whitespace-borderless {
66
+ box-shadow: none;
67
+ }
68
+
64
69
  .#{$eccgui}-card__header {
65
70
  box-sizing: content-box;
66
71
  flex-grow: 0;
@@ -24,7 +24,7 @@ export default {
24
24
 
25
25
  const Template: StoryFn<typeof Icon> = (args) => (
26
26
  <OverlaysProvider>
27
- <Icon {...args} tooltipText={args.name?.toString()} />
27
+ <Icon tooltipText={args.name?.toString()} {...args} />
28
28
  </OverlaysProvider>
29
29
  );
30
30
 
@@ -229,16 +229,15 @@ export function MultiSelect<T>({
229
229
  ]);
230
230
 
231
231
  /**
232
- * Update selected items if the component is controlled and we get
233
- * new selected items from outside
232
+ * Update selected items if we get new selected items from outside
234
233
  */
235
234
  React.useEffect(() => {
236
- if (!isControlled) {
235
+ if (!externalSelectedItems) {
237
236
  return;
238
237
  }
239
238
 
240
239
  setSelectedItems(externalSelectedItems);
241
- }, [isControlled, externalSelectedItems]);
240
+ }, [externalSelectedItems]);
242
241
 
243
242
  /**
244
243
  * using the equality prop specified checks if an item has already been selected
@@ -461,6 +460,11 @@ export function MultiSelect<T>({
461
460
 
462
461
  const contentMultiSelect = (
463
462
  <BlueprintMultiSelect<T>
463
+ placeholder={
464
+ !otherMultiSelectProps.placeholder && createNewItemFromQuery
465
+ ? "Search for item, or enter term to create new one..."
466
+ : undefined
467
+ }
464
468
  {...otherMultiSelectProps}
465
469
  query={requestState.current.query}
466
470
  onQueryChange={onQueryChange}
@@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from "react";
2
2
  import { loremIpsum } from "react-lorem-ipsum";
3
3
  import { Meta, StoryFn } from "@storybook/react";
4
4
 
5
- import { MultiSelectSelectionProps, MultiSuggestField } from "./../../../index";
5
+ import { MultiSelectSelectionProps, MultiSuggestField, SimpleDialog } from "./../../../index";
6
6
 
7
7
  const testLabels = loremIpsum({
8
8
  p: 1,
@@ -78,6 +78,40 @@ predefinedNotControlledValues.args = {
78
78
  itemLabel: (item) => item.testLabel,
79
79
  };
80
80
 
81
+ const DeferredSelectionTemplate: StoryFn = () => {
82
+ const initialSelected: Array<{ testId: string; testLabel: string }> = [];
83
+ const [loaded, setLoaded] = useState(false);
84
+
85
+ const selected = loaded ? selectedItems : initialSelected;
86
+
87
+ const identity = useCallback((item: string): string => item, []);
88
+
89
+ return (
90
+ <>
91
+ <div>Selected items loaded: {loaded.toString()}</div>
92
+
93
+ <br />
94
+
95
+ <MultiSuggestField<string>
96
+ items={items.map(({ testId }) => testId)}
97
+ selectedItems={selected.map(({ testId }) => testId)}
98
+ itemId={identity}
99
+ itemLabel={(itemId) => items.find(({ testId }) => testId === itemId)?.testLabel ?? itemId}
100
+ createNewItemFromQuery={(query) => query}
101
+ />
102
+
103
+ <br />
104
+
105
+ <button onClick={() => setLoaded((prev) => !prev)}>Toggle selected</button>
106
+ </>
107
+ );
108
+ };
109
+
110
+ /**
111
+ *
112
+ */
113
+ export const deferredSelection = DeferredSelectionTemplate.bind({});
114
+
81
115
  /**
82
116
  * New item creation, add to a existing list
83
117
  */
@@ -160,3 +194,44 @@ const WithResetButton: StoryFn = () => {
160
194
  * Reset values
161
195
  */
162
196
  export const withResetItemAndCreation = WithResetButton.bind({});
197
+
198
+ const WithinModal = (): JSX.Element => {
199
+ const [isOpen, setIsOpen] = useState(false);
200
+
201
+ const copy: Array<{ testLabel: string; testId: string }> = [items[2]];
202
+
203
+ const [selected, setSelected] = useState(copy);
204
+
205
+ const handleOnSelect = useCallback((params) => {
206
+ const items = params.selectedItems;
207
+ setSelected(items);
208
+ }, []);
209
+
210
+ const handleReset = (): void => {
211
+ setSelected(copy);
212
+ };
213
+
214
+ return (
215
+ <>
216
+ <button onClick={() => setIsOpen(true)}>open modal</button>
217
+
218
+ <SimpleDialog isOpen={isOpen} onClose={() => setIsOpen(false)} canOutsideClickClose>
219
+ <div>
220
+ <button onClick={handleReset}>Reset</button>
221
+ <br />
222
+ <br />
223
+ <MultiSuggestField<{ testLabel: string; testId: string }>
224
+ items={items}
225
+ selectedItems={selected}
226
+ onSelection={handleOnSelect}
227
+ itemId={({ testId }) => testId}
228
+ itemLabel={({ testLabel }) => testLabel}
229
+ createNewItemFromQuery={(query) => ({ testId: `${query}-id`, testLabel: query })}
230
+ />
231
+ </div>
232
+ </SimpleDialog>
233
+ </>
234
+ );
235
+ };
236
+
237
+ export const withinModal = WithinModal.bind({});