@eccenca/gui-elements 25.0.0-rc.2 → 25.1.0-featurev2510colorfield.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.
Files changed (244) hide show
  1. package/CHANGELOG.md +94 -18
  2. package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +17 -13
  3. package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
  4. package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js +1 -3
  5. package/dist/cjs/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
  6. package/dist/cjs/cmem/ContentBlobToggler/ContentBlobToggler.js +1 -0
  7. package/dist/cjs/cmem/ContentBlobToggler/ContentBlobToggler.js.map +1 -1
  8. package/dist/cjs/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js +34 -11
  9. package/dist/cjs/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js.map +1 -1
  10. package/dist/cjs/cmem/react-flow/ReactFlow/ReactFlowV12.js.map +1 -1
  11. package/dist/cjs/common/Intent/index.js +1 -1
  12. package/dist/cjs/common/Intent/index.js.map +1 -1
  13. package/dist/cjs/common/index.js +4 -1
  14. package/dist/cjs/common/index.js.map +1 -1
  15. package/dist/cjs/common/utils/colorHash.js +25 -11
  16. package/dist/cjs/common/utils/colorHash.js.map +1 -1
  17. package/dist/cjs/common/utils/reduceToText.js +26 -1
  18. package/dist/cjs/common/utils/reduceToText.js.map +1 -1
  19. package/dist/cjs/components/Application/ApplicationViewability.js +33 -0
  20. package/dist/cjs/components/Application/ApplicationViewability.js.map +1 -0
  21. package/dist/cjs/components/Application/index.js +1 -0
  22. package/dist/cjs/components/Application/index.js.map +1 -1
  23. package/dist/cjs/components/ColorField/ColorField.js +115 -0
  24. package/dist/cjs/components/ColorField/ColorField.js.map +1 -0
  25. package/dist/cjs/components/ContextOverlay/ContextMenu.js +2 -2
  26. package/dist/cjs/components/ContextOverlay/ContextMenu.js.map +1 -1
  27. package/dist/cjs/components/ContextOverlay/ContextOverlay.js +67 -29
  28. package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
  29. package/dist/cjs/components/DecoupledOverlay/DecoupledOverlay.js +47 -0
  30. package/dist/cjs/components/DecoupledOverlay/DecoupledOverlay.js.map +1 -0
  31. package/dist/cjs/components/Icon/IconButton.js.map +1 -1
  32. package/dist/cjs/components/Icon/canonicalIconNames.js +3 -0
  33. package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
  34. package/dist/cjs/components/Icon/transformIcon.js +14 -0
  35. package/dist/cjs/components/Icon/transformIcon.js.map +1 -0
  36. package/dist/cjs/components/MultiSelect/MultiSelect.js +2 -1
  37. package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
  38. package/dist/cjs/components/RadioButton/RadioButton.js +5 -2
  39. package/dist/cjs/components/RadioButton/RadioButton.js.map +1 -1
  40. package/dist/cjs/components/TextReducer/TextReducer.js +15 -3
  41. package/dist/cjs/components/TextReducer/TextReducer.js.map +1 -1
  42. package/dist/cjs/components/Typography/InlineText.js +29 -0
  43. package/dist/cjs/components/Typography/InlineText.js.map +1 -0
  44. package/dist/cjs/components/Typography/index.js +1 -0
  45. package/dist/cjs/components/Typography/index.js.map +1 -1
  46. package/dist/cjs/components/VisualTour/VisualTour.js +24 -32
  47. package/dist/cjs/components/VisualTour/VisualTour.js.map +1 -1
  48. package/dist/cjs/components/index.js +2 -0
  49. package/dist/cjs/components/index.js.map +1 -1
  50. package/dist/cjs/extensions/codemirror/CodeMirror.js +18 -6
  51. package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
  52. package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +1 -1
  53. package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
  54. package/dist/cjs/extensions/codemirror/tests/codemirrorTestHelper.js +2 -2
  55. package/dist/cjs/extensions/codemirror/tests/codemirrorTestHelper.js.map +1 -1
  56. package/dist/cjs/extensions/react-flow/edges/EdgeLabel.js +1 -1
  57. package/dist/cjs/extensions/react-flow/edges/EdgeLabel.js.map +1 -1
  58. package/dist/cjs/extensions/react-flow/edges/EdgeNew.js +1 -1
  59. package/dist/cjs/extensions/react-flow/edges/EdgeNew.js.map +1 -1
  60. package/dist/cjs/extensions/react-flow/handles/HandleDefault.js +1 -1
  61. package/dist/cjs/extensions/react-flow/handles/HandleDefault.js.map +1 -1
  62. package/dist/cjs/extensions/react-flow/minimap/MiniMap.js +1 -1
  63. package/dist/cjs/extensions/react-flow/minimap/MiniMap.js.map +1 -1
  64. package/dist/cjs/extensions/react-flow/minimap/MiniMapV12.js.map +1 -1
  65. package/dist/cjs/extensions/react-flow/nodes/nodeUtils.js.map +1 -1
  66. package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +19 -14
  67. package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
  68. package/dist/esm/cmem/ActivityControl/SilkActivityControl.js +1 -3
  69. package/dist/esm/cmem/ActivityControl/SilkActivityControl.js.map +1 -1
  70. package/dist/esm/cmem/ContentBlobToggler/ContentBlobToggler.js +1 -0
  71. package/dist/esm/cmem/ContentBlobToggler/ContentBlobToggler.js.map +1 -1
  72. package/dist/esm/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js +32 -9
  73. package/dist/esm/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.js.map +1 -1
  74. package/dist/esm/cmem/react-flow/ReactFlow/ReactFlowV12.js.map +1 -1
  75. package/dist/esm/common/Intent/index.js +1 -1
  76. package/dist/esm/common/Intent/index.js.map +1 -1
  77. package/dist/esm/common/index.js +5 -2
  78. package/dist/esm/common/index.js.map +1 -1
  79. package/dist/esm/common/utils/colorHash.js +25 -12
  80. package/dist/esm/common/utils/colorHash.js.map +1 -1
  81. package/dist/esm/common/utils/reduceToText.js +37 -1
  82. package/dist/esm/common/utils/reduceToText.js.map +1 -1
  83. package/dist/esm/components/Application/ApplicationViewability.js +28 -0
  84. package/dist/esm/components/Application/ApplicationViewability.js.map +1 -0
  85. package/dist/esm/components/Application/index.js +1 -0
  86. package/dist/esm/components/Application/index.js.map +1 -1
  87. package/dist/esm/components/ColorField/ColorField.js +141 -0
  88. package/dist/esm/components/ColorField/ColorField.js.map +1 -0
  89. package/dist/esm/components/ContextOverlay/ContextMenu.js +2 -2
  90. package/dist/esm/components/ContextOverlay/ContextMenu.js.map +1 -1
  91. package/dist/esm/components/ContextOverlay/ContextOverlay.js +67 -29
  92. package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
  93. package/dist/esm/components/DecoupledOverlay/DecoupledOverlay.js +41 -0
  94. package/dist/esm/components/DecoupledOverlay/DecoupledOverlay.js.map +1 -0
  95. package/dist/esm/components/Icon/IconButton.js.map +1 -1
  96. package/dist/esm/components/Icon/canonicalIconNames.js +3 -0
  97. package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
  98. package/dist/esm/components/Icon/transformIcon.js +21 -0
  99. package/dist/esm/components/Icon/transformIcon.js.map +1 -0
  100. package/dist/esm/components/MultiSelect/MultiSelect.js +3 -2
  101. package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
  102. package/dist/esm/components/RadioButton/RadioButton.js +6 -2
  103. package/dist/esm/components/RadioButton/RadioButton.js.map +1 -1
  104. package/dist/esm/components/TextReducer/TextReducer.js +14 -3
  105. package/dist/esm/components/TextReducer/TextReducer.js.map +1 -1
  106. package/dist/esm/components/Typography/InlineText.js +33 -0
  107. package/dist/esm/components/Typography/InlineText.js.map +1 -0
  108. package/dist/esm/components/Typography/index.js +1 -0
  109. package/dist/esm/components/Typography/index.js.map +1 -1
  110. package/dist/esm/components/VisualTour/VisualTour.js +25 -33
  111. package/dist/esm/components/VisualTour/VisualTour.js.map +1 -1
  112. package/dist/esm/components/index.js +2 -0
  113. package/dist/esm/components/index.js.map +1 -1
  114. package/dist/esm/extensions/codemirror/CodeMirror.js +19 -7
  115. package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
  116. package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +1 -1
  117. package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
  118. package/dist/esm/extensions/codemirror/tests/codemirrorTestHelper.js +3 -3
  119. package/dist/esm/extensions/codemirror/tests/codemirrorTestHelper.js.map +1 -1
  120. package/dist/esm/extensions/react-flow/edges/EdgeLabel.js +1 -1
  121. package/dist/esm/extensions/react-flow/edges/EdgeLabel.js.map +1 -1
  122. package/dist/esm/extensions/react-flow/edges/EdgeNew.js +1 -1
  123. package/dist/esm/extensions/react-flow/edges/EdgeNew.js.map +1 -1
  124. package/dist/esm/extensions/react-flow/handles/HandleDefault.js +1 -1
  125. package/dist/esm/extensions/react-flow/handles/HandleDefault.js.map +1 -1
  126. package/dist/esm/extensions/react-flow/minimap/MiniMap.js +1 -1
  127. package/dist/esm/extensions/react-flow/minimap/MiniMap.js.map +1 -1
  128. package/dist/esm/extensions/react-flow/minimap/MiniMapV12.js.map +1 -1
  129. package/dist/esm/extensions/react-flow/nodes/nodeUtils.js.map +1 -1
  130. package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +9 -0
  131. package/dist/types/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.d.ts +26 -10
  132. package/dist/types/common/index.d.ts +4 -1
  133. package/dist/types/common/utils/colorHash.d.ts +4 -3
  134. package/dist/types/common/utils/reduceToText.d.ts +1 -1
  135. package/dist/types/components/Application/ApplicationViewability.d.ts +36 -0
  136. package/dist/types/components/Application/index.d.ts +1 -0
  137. package/dist/types/components/ColorField/ColorField.d.ts +31 -0
  138. package/dist/types/components/ContextOverlay/ContextMenu.d.ts +1 -1
  139. package/dist/types/components/ContextOverlay/ContextOverlay.d.ts +7 -1
  140. package/dist/types/components/DecoupledOverlay/DecoupledOverlay.d.ts +20 -0
  141. package/dist/types/components/Icon/IconButton.d.ts +1 -1
  142. package/dist/types/components/Icon/canonicalIconNames.d.ts +2 -0
  143. package/dist/types/components/Icon/transformIcon.d.ts +2 -0
  144. package/dist/types/components/MultiSelect/MultiSelect.d.ts +1 -1
  145. package/dist/types/components/RadioButton/RadioButton.d.ts +8 -2
  146. package/dist/types/components/Structure/TitleSubsection.d.ts +1 -1
  147. package/dist/types/components/Tabs/Tab.d.ts +2 -2
  148. package/dist/types/components/TextReducer/TextReducer.d.ts +13 -1
  149. package/dist/types/components/Typography/InlineText.d.ts +13 -0
  150. package/dist/types/components/Typography/index.d.ts +1 -0
  151. package/dist/types/components/index.d.ts +2 -0
  152. package/dist/types/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.d.ts +1 -1
  153. package/dist/types/extensions/codemirror/tests/codemirrorTestHelper.d.ts +1 -1
  154. package/package.json +54 -53
  155. package/src/cmem/ActivityControl/ActivityControlWidget.tsx +68 -35
  156. package/src/cmem/ActivityControl/SilkActivityControl.tsx +1 -1
  157. package/src/cmem/ContentBlobToggler/ContentBlobToggler.tsx +1 -1
  158. package/src/cmem/ContentBlobToggler/StringPreviewContentBlobToggler.tsx +66 -18
  159. package/src/cmem/ContentBlobToggler/stories/StringPreviewContentBlobToggler.stories.tsx +27 -0
  160. package/src/cmem/ContentBlobToggler/tests/StringPreviewContentBlobToggler.test.tsx +98 -0
  161. package/src/cmem/react-flow/ReactFlow/ReactFlowV12.tsx +1 -0
  162. package/src/common/Intent/index.ts +2 -1
  163. package/src/common/index.ts +8 -3
  164. package/src/common/utils/colorHash.ts +36 -18
  165. package/src/common/utils/reduceToText.tsx +30 -2
  166. package/src/components/Application/ApplicationViewability.tsx +61 -0
  167. package/src/components/Application/_colors.scss +15 -0
  168. package/src/components/Application/_content.scss +7 -0
  169. package/src/components/Application/_header.scss +12 -3
  170. package/src/components/Application/_viewability.scss +13 -0
  171. package/src/components/Application/application.scss +1 -0
  172. package/src/components/Application/index.ts +1 -0
  173. package/src/components/Application/stories/ApplicationViewability.stories.tsx +37 -0
  174. package/src/components/Application/tests/ApplicationViewability.test.tsx +43 -0
  175. package/src/components/AutoSuggestion/tests/ExtendedCodeEditor.test.tsx +1 -1
  176. package/src/components/Card/card.scss +6 -0
  177. package/src/components/Checkbox/checkbox.scss +14 -2
  178. package/src/components/ColorField/ColorField.stories.tsx +69 -0
  179. package/src/components/ColorField/ColorField.test.tsx +125 -0
  180. package/src/components/ColorField/ColorField.tsx +200 -0
  181. package/src/components/ColorField/_colorfield.scss +56 -0
  182. package/src/components/ContentGroup/_contentgroup.scss +9 -0
  183. package/src/components/ContextOverlay/ContextMenu.tsx +4 -1
  184. package/src/components/ContextOverlay/ContextOverlay.tsx +94 -25
  185. package/src/components/ContextOverlay/tests/ContextMenu.test.tsx +43 -0
  186. package/src/components/ContextOverlay/tests/ContextOverlay.test.tsx +71 -0
  187. package/src/components/DecoupledOverlay/DecoupledOverlay.stories.tsx +30 -0
  188. package/src/components/DecoupledOverlay/DecoupledOverlay.tsx +97 -0
  189. package/src/components/DecoupledOverlay/_decoupledoverlay.scss +46 -0
  190. package/src/components/Depiction/depiction.scss +6 -0
  191. package/src/components/Dialog/stories/Modal.stories.tsx +12 -150
  192. package/src/components/Dialog/stories/ModalContext.stories.tsx +153 -0
  193. package/src/components/FlexibleLayout/flexiblelayout.scss +16 -0
  194. package/src/components/Grid/grid.scss +17 -0
  195. package/src/components/Grid/stories/Grid.stories.tsx +10 -7
  196. package/src/components/Grid/stories/GridRow.stories.tsx +13 -7
  197. package/src/components/Icon/IconButton.tsx +1 -1
  198. package/src/components/Icon/canonicalIconNames.tsx +3 -0
  199. package/src/components/Icon/transformIcon.tsx +17 -0
  200. package/src/components/Link/Link.stories.tsx +30 -0
  201. package/src/components/Link/link.scss +28 -2
  202. package/src/components/MultiSelect/MultiSelect.tsx +12 -3
  203. package/src/components/Notification/notification.scss +6 -0
  204. package/src/components/OverviewItem/overviewitem.scss +9 -0
  205. package/src/components/OverviewItem/stories/OverviewItem.stories.tsx +28 -0
  206. package/src/components/OverviewItem/stories/OverviewItemActions.stories.tsx +2 -2
  207. package/src/components/OverviewItem/stories/OverviewItemDescription.stories.tsx +1 -1
  208. package/src/components/OverviewItem/stories/OverviewItemLine.stories.tsx +1 -1
  209. package/src/components/PropertyValuePair/propertyvalue.scss +23 -1
  210. package/src/components/RadioButton/RadioButton.tsx +15 -3
  211. package/src/components/RadioButton/radiobutton.scss +13 -0
  212. package/src/components/Separation/separation.scss +6 -0
  213. package/src/components/Table/table.scss +22 -0
  214. package/src/components/Tabs/stories/TabTitle.stories.tsx +7 -2
  215. package/src/components/Tag/stories/TagList.stories.tsx +2 -2
  216. package/src/components/Tag/tag.scss +19 -9
  217. package/src/components/TextReducer/TextReducer.stories.tsx +2 -1
  218. package/src/components/TextReducer/TextReducer.test.tsx +44 -0
  219. package/src/components/TextReducer/TextReducer.tsx +14 -4
  220. package/src/components/Typography/InlineText.tsx +24 -0
  221. package/src/components/Typography/_reset.scss +1 -0
  222. package/src/components/Typography/index.ts +1 -0
  223. package/src/components/Typography/stories/InlineText.stories.tsx +27 -0
  224. package/src/components/Typography/typography.scss +28 -2
  225. package/src/components/VisualTour/VisualTour.tsx +30 -50
  226. package/src/components/VisualTour/visualTour.scss +0 -34
  227. package/src/components/index.scss +2 -0
  228. package/src/components/index.ts +2 -0
  229. package/src/configuration/_customproperties.scss +32 -0
  230. package/src/configuration/stories/customproperties.stories.tsx +118 -0
  231. package/src/extensions/codemirror/CodeMirror.tsx +20 -9
  232. package/src/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.ts +1 -2
  233. package/src/extensions/codemirror/tests/codemirrorTestHelper.ts +3 -3
  234. package/src/extensions/react-flow/_config.scss +3 -3
  235. package/src/extensions/react-flow/edges/EdgeLabel.tsx +5 -3
  236. package/src/extensions/react-flow/edges/EdgeNew.tsx +2 -1
  237. package/src/extensions/react-flow/edges/_edges.scss +3 -2
  238. package/src/extensions/react-flow/handles/HandleDefault.tsx +2 -2
  239. package/src/extensions/react-flow/minimap/MiniMap.tsx +2 -1
  240. package/src/extensions/react-flow/minimap/MiniMapV12.tsx +1 -1
  241. package/src/extensions/react-flow/nodes/_nodes.scss +4 -3
  242. package/src/extensions/react-flow/nodes/nodeUtils.tsx +1 -0
  243. package/src/extensions/react-flow/nodes/stories/NodeContent.stories.tsx +2 -2
  244. package/src/index.scss +1 -0
@@ -0,0 +1,200 @@
1
+ import React, { CSSProperties } from "react";
2
+ import classNames from "classnames";
3
+
4
+ import { utils } from "../../common";
5
+ import { ColorWeight, PaletteGroup } from "../../common/utils/colorHash";
6
+ import { CLASSPREFIX as eccgui } from "../../configuration/constants";
7
+ import { ContextOverlay } from "../ContextOverlay";
8
+ import { FieldSet } from "../Form";
9
+ import { RadioButton } from "../RadioButton/RadioButton";
10
+ import { Spacing } from "../Separation/Spacing";
11
+ import { Tag, TagList } from "../Tag";
12
+ import { TextField, TextFieldProps } from "../TextField";
13
+ import { Tooltip } from "../Tooltip/Tooltip";
14
+ import { WhiteSpaceContainer } from "../Typography";
15
+
16
+ export interface ColorFieldProps extends Omit<TextFieldProps, "invisibleCharacterWarning"> {
17
+ /**
18
+ * Any color can be selected, not only from the configured color palette.
19
+ */
20
+ allowCustomColor?: boolean;
21
+ /**
22
+ * What color weights should be included in the set of allowed colors.
23
+ */
24
+ includeColorWeight?: ColorWeight[];
25
+ /**
26
+ * What palette groups should be included in the set of allowed colors.
27
+ */
28
+ includePaletteGroup?: PaletteGroup[];
29
+ }
30
+
31
+ /**
32
+ * Color input field that provides resets from the configured color palette.
33
+ * Use `includeColorWeight` and `includePaletteGroup` to filter them.
34
+ */
35
+ export const ColorField = ({
36
+ className = "",
37
+ allowCustomColor = false,
38
+ includeColorWeight = [100, 300, 700, 900], // on default, we only include color weights that can have enough contrasts to black/white
39
+ includePaletteGroup = ["layout"],
40
+ defaultValue,
41
+ value,
42
+ onChange,
43
+ fullWidth = false,
44
+ ...otherTextFieldProps
45
+ }: ColorFieldProps) => {
46
+ const ref = React.useRef(null);
47
+ const [colorValue, setColorValue] = React.useState<string>(defaultValue || value || "#000000");
48
+
49
+ let allowedPaletteColors, disableNativePicker, disabled;
50
+ const updateConfig = () => {
51
+ allowedPaletteColors = utils.getEnabledColorPropertiesFromPalette({
52
+ includePaletteGroup: includePaletteGroup,
53
+ includeColorWeight: includeColorWeight,
54
+ minimalColorDistance: 0, // we use all allowed colors, and do not check distances between them
55
+ });
56
+
57
+ disableNativePicker =
58
+ includeColorWeight.length > 0 && includePaletteGroup.length > 0 && allowedPaletteColors.length > 0;
59
+ disabled = (!disableNativePicker && !allowCustomColor) || otherTextFieldProps.disabled;
60
+ };
61
+ updateConfig();
62
+ React.useEffect(() => {
63
+ updateConfig();
64
+ }, [allowCustomColor, includeColorWeight, includePaletteGroup, otherTextFieldProps]);
65
+
66
+ React.useEffect(() => {
67
+ setColorValue(defaultValue || value || "#000000");
68
+ }, [defaultValue, value]);
69
+
70
+ const forwardOnChange = (forwardedEvent: React.ChangeEvent<HTMLInputElement>) => {
71
+ setColorValue(forwardedEvent.target.value);
72
+ if (onChange) {
73
+ onChange(forwardedEvent);
74
+ }
75
+ };
76
+
77
+ const colorInput = (
78
+ <TextField
79
+ inputRef={ref}
80
+ className={classNames(`${eccgui}-colorfield`, className, {
81
+ [`${eccgui}-colorfield--custom-picker`]: disableNativePicker,
82
+ })}
83
+ // we cannot use `color` type for the custom picker because we do not have control over it then
84
+ type={!disableNativePicker ? "color" : "text"}
85
+ readOnly={disableNativePicker}
86
+ disabled={disabled}
87
+ value={colorValue}
88
+ fullWidth={fullWidth}
89
+ {...otherTextFieldProps}
90
+ onChange={
91
+ !disableNativePicker
92
+ ? (e: React.ChangeEvent<HTMLInputElement>) => {
93
+ forwardOnChange(e);
94
+ }
95
+ : undefined
96
+ }
97
+ style={{ ...otherTextFieldProps.style, [`--eccgui-colorfield-background`]: colorValue } as CSSProperties}
98
+ />
99
+ );
100
+
101
+ return disableNativePicker && !disabled ? (
102
+ <ContextOverlay
103
+ fill={fullWidth}
104
+ content={
105
+ <WhiteSpaceContainer
106
+ paddingTop={"small"}
107
+ paddingRight={"small"}
108
+ paddingBottom={"small"}
109
+ paddingLeft={"small"}
110
+ className={`${eccgui}-colorfield__picker`}
111
+ >
112
+ {allowCustomColor && (
113
+ <>
114
+ <TextField
115
+ type={"color"}
116
+ value={colorValue}
117
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
118
+ forwardOnChange(e);
119
+ }}
120
+ />
121
+ <Spacing size={"small"} />
122
+ </>
123
+ )}
124
+ <FieldSet>
125
+ <TagList
126
+ className={`${eccgui}-colorfield__palette ${eccgui}-colorfield__palette--${
127
+ includeColorWeight.length >= 3 ? includeColorWeight.length * 2 : "8"
128
+ }col`}
129
+ >
130
+ {allowedPaletteColors!.map((color: [string, string], idx: number) => [
131
+ <RadioButton
132
+ className={`${eccgui}-colorfield__palette__color`}
133
+ hideIndicator
134
+ value={color[1]}
135
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
136
+ forwardOnChange(e);
137
+ }}
138
+ >
139
+ <Tooltip key={idx} content={color[0].replace(`${eccgui}-color-palette-`, "")}>
140
+ <Tag
141
+ large
142
+ style={{ [`--eccgui-colorfield-palette-color`]: color[1] } as CSSProperties}
143
+ >
144
+ {color[1]}
145
+ </Tag>
146
+ </Tooltip>
147
+ </RadioButton>,
148
+ // Looks like we cannot force some type of line break in the tag list via CSS only
149
+ (idx + 1) % (includeColorWeight.length >= 3 ? includeColorWeight.length * 2 : 8) ===
150
+ 0 && (
151
+ <>
152
+ <br className={`${eccgui}-colorfield__palette-linebreak`} />
153
+ </>
154
+ ),
155
+ ])}
156
+ </TagList>
157
+ </FieldSet>
158
+ </WhiteSpaceContainer>
159
+ }
160
+ >
161
+ {colorInput}
162
+ </ContextOverlay>
163
+ ) : (
164
+ colorInput
165
+ );
166
+ };
167
+
168
+ type calculateColorHashValueProps = Pick<
169
+ ColorFieldProps,
170
+ "allowCustomColor" | "includeColorWeight" | "includePaletteGroup"
171
+ >;
172
+
173
+ /**
174
+ * Simple helper function that provide simple access to color hash calculation.
175
+ * Using the same default values for the color palette filter.
176
+ */
177
+ ColorField.calculateColorHashValue = (
178
+ text: string,
179
+ options: calculateColorHashValueProps = {
180
+ allowCustomColor: false,
181
+ includeColorWeight: [100, 300, 700, 900],
182
+ includePaletteGroup: ["layout"],
183
+ }
184
+ ) => {
185
+ const hash = utils.textToColorHash({
186
+ text,
187
+ options: {
188
+ returnValidColorsDirectly: options.allowCustomColor as boolean,
189
+ enabledColors: utils.getEnabledColorsFromPalette({
190
+ includePaletteGroup: options.includePaletteGroup,
191
+ includeColorWeight: options.includeColorWeight,
192
+ minimalColorDistance: 0,
193
+ }),
194
+ },
195
+ });
196
+
197
+ return hash ? hash : undefined;
198
+ };
199
+
200
+ export default ColorField;
@@ -0,0 +1,56 @@
1
+ .#{$eccgui}-colorfield {
2
+ cursor: default;
3
+
4
+ &:not(.#{$ns}-fill) {
5
+ width: 100%;
6
+ max-width: 4 * $eccgui-size-textfield-height-regular;
7
+ }
8
+
9
+ .#{$ns}-input {
10
+ color: var(--#{$eccgui}-colorfield-background);
11
+ cursor: inherit;
12
+ background-color: var(--#{$eccgui}-colorfield-background);
13
+
14
+ &[type="color"] {
15
+ &::-webkit-color-swatch-wrapper {
16
+ display: none;
17
+ }
18
+
19
+ &::-moz-color-swatch {
20
+ display: none;
21
+ }
22
+ }
23
+ }
24
+
25
+ .#{$ns}-input-left-container {
26
+ top: 1px;
27
+ left: 1px !important;
28
+ height: calc(100% - 2px);
29
+ background-color: $eccgui-color-textfield-background;
30
+ }
31
+ .#{$ns}-input-action {
32
+ top: 1px;
33
+ right: 1px !important;
34
+ height: calc(100% - 2px);
35
+ background-color: $eccgui-color-textfield-background;
36
+ }
37
+ }
38
+
39
+ .#{$eccgui}-colorfield__palette {
40
+ & > li:has(.#{$eccgui}-colorfield__palette-linebreak) {
41
+ display: block;
42
+ width: 100%;
43
+ height: 0;
44
+ margin: 0;
45
+ overflow: hidden;
46
+ }
47
+ }
48
+
49
+ .#{$eccgui}-colorfield__palette__color {
50
+ margin: 0;
51
+ .#{$eccgui}-tag__item {
52
+ width: 3rem;
53
+ color: var(--#{$eccgui}-colorfield-palette-color) !important;
54
+ background-color: var(--#{$eccgui}-colorfield-palette-color) !important;
55
+ }
56
+ }
@@ -60,3 +60,12 @@ $eccgui-color-scontentgroup-border-sub: eccgui-color-rgba(
60
60
  flex-shrink: 1;
61
61
  width: 100%;
62
62
  }
63
+
64
+ @media print {
65
+ .#{$eccgui}-contentgroup__header__options {
66
+ display: none;
67
+ }
68
+ .#{$eccgui}-contentgroup--border-sub::after {
69
+ print-color-adjust: exact;
70
+ }
71
+ }
@@ -66,6 +66,8 @@ export const ContextMenu = ({
66
66
  so by default we use the title attribute instead of Tooltip. */
67
67
  tooltipAsTitle = true,
68
68
  preventPlaceholder = false,
69
+ "data-test-id": dataTestId,
70
+ "data-testid": dataTestid,
69
71
  ...restProps
70
72
  }: ContextMenuProps) => {
71
73
  const toggleButton =
@@ -76,7 +78,8 @@ export const ContextMenu = ({
76
78
  text={togglerText}
77
79
  large={togglerLarge}
78
80
  disabled={!!disabled}
79
- data-test-id={restProps["data-test-id"]}
81
+ data-test-id={dataTestId ?? undefined}
82
+ data-testid={dataTestid ?? undefined}
80
83
  />
81
84
  ) : (
82
85
  (togglerElement as ReactElement)
@@ -2,11 +2,12 @@ import React from "react";
2
2
  import {
3
3
  Classes as BlueprintClasses,
4
4
  Popover as BlueprintPopover,
5
+ PopoverInteractionKind as InteractionKind,
5
6
  PopoverProps as BlueprintPopoverProps,
6
7
  Utils as BlueprintUtils,
7
8
  } from "@blueprintjs/core";
8
9
 
9
- import { CLASSPREFIX as eccgui } from "../../configuration/constants";
10
+ import { CLASSPREFIX as eccgui, WhiteSpaceContainer, WhiteSpaceContainerProps } from "../../index";
10
11
 
11
12
  export interface ContextOverlayProps extends Omit<BlueprintPopoverProps, "position"> {
12
13
  /**
@@ -23,6 +24,11 @@ export interface ContextOverlayProps extends Omit<BlueprintPopoverProps, "positi
23
24
  * Currently experimental.
24
25
  */
25
26
  usePlaceholder?: boolean;
27
+ /**
28
+ * Adds white space to each side of the overlay content.
29
+ * For more control use `WhiteSpaceContainer` directly as wrapper for the content children.
30
+ */
31
+ paddingSize?: WhiteSpaceContainerProps["paddingTop"];
26
32
  }
27
33
 
28
34
  /**
@@ -35,11 +41,14 @@ export const ContextOverlay = ({
35
41
  preventTopPosition,
36
42
  className = "",
37
43
  usePlaceholder = false,
44
+ paddingSize,
45
+ content,
38
46
  ...otherPopoverProps
39
47
  }: ContextOverlayProps) => {
40
- const placeholderRef = React.useRef(null);
41
- const eventMemory = React.useRef<undefined | "afterhover" | "afterfocus">(undefined);
48
+ const placeholderRef = React.useRef<HTMLElement>(null);
49
+ const eventMemory = React.useRef<undefined | "mouseenter" | "focusin" | "click">(undefined);
42
50
  const swapDelay = React.useRef<null | NodeJS.Timeout>(null);
51
+ const interactionKind = React.useRef<InteractionKind>(otherPopoverProps.interactionKind ?? InteractionKind.CLICK);
43
52
  const swapDelayTime = 15;
44
53
  const [placeholder, setPlaceholder] = React.useState<boolean>(
45
54
  // use placeholder only for "simple" overlays without special states
@@ -50,37 +59,85 @@ export const ContextOverlay = ({
50
59
  usePlaceholder
51
60
  );
52
61
 
62
+ const swap = (ev: MouseEvent | globalThis.FocusEvent) => {
63
+ const waitForClick =
64
+ interactionKind.current === InteractionKind.CLICK ||
65
+ interactionKind.current === InteractionKind.CLICK_TARGET_ONLY;
66
+
67
+ if (swapDelay.current) {
68
+ clearTimeout(swapDelay.current);
69
+ }
70
+
71
+ const replacePlaceholder = () => {
72
+ eventMemory.current = ev.type as "mouseenter" | "focusin" | "click";
73
+ setPlaceholder(false);
74
+ };
75
+
76
+ if (waitForClick) {
77
+ ev.stopImmediatePropagation();
78
+ replacePlaceholder();
79
+ return;
80
+ }
81
+
82
+ swapDelay.current = setTimeout(
83
+ replacePlaceholder,
84
+ // we delay the swap for hover/focus to prevent unwanted effects
85
+ // (e.g. event hickup after replacing elements when it is not really necessary)
86
+ swapDelayTime
87
+ );
88
+ };
89
+
53
90
  React.useEffect(() => {
91
+ interactionKind.current = otherPopoverProps.interactionKind ?? InteractionKind.CLICK;
92
+ const waitForClick =
93
+ interactionKind.current === InteractionKind.CLICK ||
94
+ interactionKind.current === InteractionKind.CLICK_TARGET_ONLY;
95
+ const removeEvents = () => {
96
+ if (placeholderRef.current) {
97
+ placeholderRef.current.removeEventListener("click", swap);
98
+ placeholderRef.current.removeEventListener("mouseenter", swap);
99
+ placeholderRef.current.removeEventListener("focusin", swap);
100
+ }
101
+ return;
102
+ };
54
103
  if (placeholderRef.current) {
55
- const swap = (ev: MouseEvent | globalThis.FocusEvent) => {
56
- if (swapDelay.current) {
57
- clearTimeout(swapDelay.current);
58
- }
59
- swapDelay.current = setTimeout(() => {
60
- // we delay the swap to prevent unwanted effects
61
- // (e.g. event hickup after replacing elements when it is not really necessary)
62
- eventMemory.current = ev.type === "focusin" ? "afterfocus" : "afterhover";
63
- setPlaceholder(false);
64
- }, swapDelayTime);
65
- };
66
- (placeholderRef.current as HTMLElement).addEventListener("mouseenter", swap);
67
- (placeholderRef.current as HTMLElement).addEventListener("focusin", swap);
104
+ removeEvents(); // remove events in case of interaction kind changed during existence
105
+ if (waitForClick) {
106
+ placeholderRef.current.addEventListener("click", swap);
107
+ } else {
108
+ placeholderRef.current.addEventListener("mouseenter", swap);
109
+ placeholderRef.current.addEventListener("focusin", swap);
110
+ }
68
111
  return () => {
69
- if (placeholderRef.current) {
70
- (placeholderRef.current as HTMLElement).removeEventListener("mouseenter", swap);
71
- (placeholderRef.current as HTMLElement).removeEventListener("focusin", swap);
72
- }
112
+ removeEvents();
73
113
  };
74
114
  }
75
115
  return () => {};
76
- }, [!!placeholderRef.current]);
116
+ }, [!!placeholderRef.current, otherPopoverProps.interactionKind]);
77
117
 
78
118
  const refocus = React.useCallback((node) => {
79
- if (eventMemory.current === "afterfocus" && node) {
80
- const target = node.targetRef.current.children[0];
81
- if (target) {
119
+ const target = node?.targetRef.current.children[0];
120
+ if (!eventMemory.current || !target) {
121
+ return;
122
+ }
123
+ switch (eventMemory.current) {
124
+ case "focusin":
82
125
  target.focus();
83
- }
126
+ break;
127
+ case "click":
128
+ target.click();
129
+ break;
130
+ case "mouseenter":
131
+ // re-check if the cursor is still over the element after swapping the placeholder before triggering the event to bubble up
132
+ (target as HTMLElement).addEventListener(
133
+ "mouseover",
134
+ () => (target as HTMLElement).dispatchEvent(new MouseEvent("mouseover", { bubbles: true })),
135
+ {
136
+ capture: true,
137
+ once: true,
138
+ }
139
+ );
140
+ break;
84
141
  }
85
142
  }, []);
86
143
 
@@ -119,6 +176,18 @@ export const ContextOverlay = ({
119
176
  ) : (
120
177
  <BlueprintPopover
121
178
  placement="bottom"
179
+ content={content ? (
180
+ paddingSize ? (
181
+ <WhiteSpaceContainer
182
+ paddingTop={paddingSize}
183
+ paddingRight={paddingSize}
184
+ paddingBottom={paddingSize}
185
+ paddingLeft={paddingSize}
186
+ >
187
+ {content}
188
+ </WhiteSpaceContainer>
189
+ ) : content
190
+ ) : undefined}
122
191
  {...otherPopoverProps}
123
192
  className={targetClassName}
124
193
  portalClassName={portalClassNameFinal.trim() ?? undefined}
@@ -0,0 +1,43 @@
1
+ import React from "react";
2
+ import { fireEvent, render, screen } from "@testing-library/react";
3
+
4
+ import "@testing-library/jest-dom";
5
+
6
+ import { CLASSPREFIX as eccgui } from "../../../configuration/constants";
7
+
8
+ import ContextMenu from "./../ContextMenu";
9
+ import { Default as ContextMenuStory } from "./../ContextMenu.stories";
10
+
11
+ const overlayWrapper = `${eccgui}-contextoverlay`;
12
+ const placeholderClass = `${overlayWrapper}__wrapper--placeholder`;
13
+
14
+ const checkForPlaceholderClass = (container: HTMLElement, tobe: number) => {
15
+ expect(container.getElementsByClassName(placeholderClass).length).toBe(tobe);
16
+ };
17
+
18
+ describe("ContextMenu", () => {
19
+ it("should render placeholder automatically", () => {
20
+ const { container } = render(<ContextMenu {...ContextMenuStory.args} />);
21
+ checkForPlaceholderClass(container, 1);
22
+ });
23
+ it("should not render placeholder when `preventPlaceholder===true`", () => {
24
+ const { container } = render(<ContextMenu {...ContextMenuStory.args} preventPlaceholder={true} />);
25
+ checkForPlaceholderClass(container, 0);
26
+ });
27
+ it("should render placeholder when `preventPlaceholder===false`", () => {
28
+ const { container } = render(<ContextMenu {...ContextMenuStory.args} preventPlaceholder={false} />);
29
+ checkForPlaceholderClass(container, 1);
30
+ });
31
+ it("if no placeholder is used the menu should be displayed on click", async () => {
32
+ const { container } = render(<ContextMenu {...ContextMenuStory.args} preventPlaceholder={true} />);
33
+ checkForPlaceholderClass(container, 0);
34
+ fireEvent.click(container.getElementsByClassName(overlayWrapper)[0]);
35
+ expect(await screen.findByText("First option")).toBeVisible();
36
+ });
37
+ it("if placeholder is used the menu should be displayed on click", async () => {
38
+ const { container } = render(<ContextMenu {...ContextMenuStory.args} preventPlaceholder={false} />);
39
+ checkForPlaceholderClass(container, 1);
40
+ fireEvent.click(container.getElementsByClassName(overlayWrapper)[0]);
41
+ expect(await screen.findByText("First option")).toBeVisible();
42
+ });
43
+ });
@@ -0,0 +1,71 @@
1
+ import React from "react";
2
+ import { PopoverInteractionKind } from "@blueprintjs/core";
3
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+
5
+ import "@testing-library/jest-dom";
6
+
7
+ import { CLASSPREFIX as eccgui } from "../../../configuration/constants";
8
+
9
+ import ContextOverlay from "./../ContextOverlay";
10
+ import { Default as ContextOverlayStory } from "./../ContextOverlay.stories";
11
+
12
+ const overlayWrapper = `${eccgui}-contextoverlay`;
13
+ const placeholderClass = `${overlayWrapper}__wrapper--placeholder`;
14
+
15
+ const checkForPlaceholderClass = (container: HTMLElement, tobe: number) => {
16
+ expect(container.getElementsByClassName(placeholderClass).length).toBe(tobe);
17
+ };
18
+
19
+ describe("ContextOverlay", () => {
20
+ it("should not render placeholder automatically", () => {
21
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} />);
22
+ checkForPlaceholderClass(container, 0);
23
+ });
24
+ it("should render placeholder when `usePlaceholder===true`", () => {
25
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={true} />);
26
+ checkForPlaceholderClass(container, 1);
27
+ });
28
+ it("should render no placeholder when `usePlaceholder===false`", () => {
29
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={false} />);
30
+ checkForPlaceholderClass(container, 0);
31
+ });
32
+ it("if no placeholder is used the overlay should be displayed on click", async () => {
33
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={false} />);
34
+ fireEvent.click(container.getElementsByClassName(overlayWrapper)[0]);
35
+ expect(await screen.findByText("Overlay:")).toBeVisible();
36
+ });
37
+ it("if no placeholder is used the overlay should be displayed on hover (hover interactionKind)", async () => {
38
+ const { container } = render(
39
+ <ContextOverlay
40
+ {...ContextOverlayStory.args}
41
+ usePlaceholder={false}
42
+ interactionKind={PopoverInteractionKind.HOVER}
43
+ />
44
+ );
45
+ fireEvent.mouseEnter(container.getElementsByClassName(overlayWrapper)[0]);
46
+ expect(await screen.findByText("Overlay:")).toBeVisible();
47
+ });
48
+ it("if placeholder is used the overlay should be displayed on click", async () => {
49
+ const { container } = render(<ContextOverlay {...ContextOverlayStory.args} usePlaceholder={true} />);
50
+ fireEvent.click(container.getElementsByClassName(overlayWrapper)[0]);
51
+ expect(await screen.findByText("Overlay:")).toBeVisible();
52
+ });
53
+ it("if placeholder is used the overlay should be displayed on hover (hover interactionKind)", async () => {
54
+ const { container } = render(
55
+ <ContextOverlay
56
+ {...ContextOverlayStory.args}
57
+ usePlaceholder={true}
58
+ interactionKind={PopoverInteractionKind.HOVER}
59
+ />
60
+ );
61
+ checkForPlaceholderClass(container, 1);
62
+ fireEvent.mouseEnter(container.getElementsByClassName(overlayWrapper)[0]);
63
+ await waitFor(async () => {
64
+ expect(screen.queryByDisplayValue("Overlay:")).toBeNull();
65
+ checkForPlaceholderClass(container, 0);
66
+ // we need to emulate another mouseover to simulate real user behaviour
67
+ fireEvent.mouseOver(container.getElementsByClassName(overlayWrapper)[0]);
68
+ expect(await screen.findByText("Overlay:")).toBeVisible();
69
+ });
70
+ });
71
+ });
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import { Meta, StoryFn } from "@storybook/react";
3
+
4
+ import { DecoupledOverlay, DecoupledOverlayProps, Tag, WhiteSpaceContainer } from "../../../index";
5
+
6
+ export default {
7
+ title: "Components/DecoupledOverlay",
8
+ component: DecoupledOverlay,
9
+ argTypes: {},
10
+ } as Meta<typeof DecoupledOverlay>;
11
+
12
+ const Template: StoryFn<typeof DecoupledOverlay> = (args: DecoupledOverlayProps) => {
13
+ return (
14
+ <>
15
+ <Tag id={"decoupledTarget"}>Decoupled target</Tag>
16
+ <DecoupledOverlay {...args} />
17
+ </>
18
+ );
19
+ };
20
+
21
+ export const Default = Template.bind({});
22
+
23
+ Default.args = {
24
+ children: (
25
+ <WhiteSpaceContainer marginTop={"small"} marginRight={"small"} marginBottom={"small"} marginLeft={"small"}>
26
+ Decoupled overlay
27
+ </WhiteSpaceContainer>
28
+ ),
29
+ targetSelectorOrElement: "#decoupledTarget",
30
+ };