@eccenca/gui-elements 25.0.0-bugfixcenterednodecontentmenuitemscmem7184.0 → 25.0.0-featurecolorwheelinputcmem7327.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -4
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +17 -13
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/cjs/common/index.js +1 -0
- package/dist/cjs/common/index.js.map +1 -1
- package/dist/cjs/common/utils/colorHash.js +25 -11
- package/dist/cjs/common/utils/colorHash.js.map +1 -1
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js +6 -6
- package/dist/cjs/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/cjs/components/DecoupledOverlay/DecoupledOverlay.js +47 -0
- package/dist/cjs/components/DecoupledOverlay/DecoupledOverlay.js.map +1 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js +3 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/Icon/transformIcon.js +14 -0
- package/dist/cjs/components/Icon/transformIcon.js.map +1 -0
- package/dist/cjs/components/MultiSelect/MultiSelect.js +2 -1
- package/dist/cjs/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/cjs/components/RadioButton/RadioButton.js +5 -2
- package/dist/cjs/components/RadioButton/RadioButton.js.map +1 -1
- package/dist/cjs/components/VisualTour/VisualTour.js +24 -32
- package/dist/cjs/components/VisualTour/VisualTour.js.map +1 -1
- package/dist/cjs/components/index.js +1 -0
- package/dist/cjs/components/index.js.map +1 -1
- package/dist/cjs/extensions/codemirror/CodeMirror.js +10 -1
- package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeLabel.js +1 -1
- package/dist/cjs/extensions/react-flow/edges/EdgeLabel.js.map +1 -1
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +19 -14
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/esm/common/index.js +2 -1
- package/dist/esm/common/index.js.map +1 -1
- package/dist/esm/common/utils/colorHash.js +25 -12
- package/dist/esm/common/utils/colorHash.js.map +1 -1
- package/dist/esm/components/ContextOverlay/ContextOverlay.js +3 -3
- package/dist/esm/components/ContextOverlay/ContextOverlay.js.map +1 -1
- package/dist/esm/components/DecoupledOverlay/DecoupledOverlay.js +41 -0
- package/dist/esm/components/DecoupledOverlay/DecoupledOverlay.js.map +1 -0
- package/dist/esm/components/Icon/canonicalIconNames.js +3 -0
- package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/esm/components/Icon/transformIcon.js +21 -0
- package/dist/esm/components/Icon/transformIcon.js.map +1 -0
- package/dist/esm/components/MultiSelect/MultiSelect.js +3 -2
- package/dist/esm/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/RadioButton/RadioButton.js +6 -2
- package/dist/esm/components/RadioButton/RadioButton.js.map +1 -1
- package/dist/esm/components/VisualTour/VisualTour.js +25 -33
- package/dist/esm/components/VisualTour/VisualTour.js.map +1 -1
- package/dist/esm/components/index.js +1 -0
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/extensions/codemirror/CodeMirror.js +10 -1
- package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeLabel.js +1 -1
- package/dist/esm/extensions/react-flow/edges/EdgeLabel.js.map +1 -1
- package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +9 -0
- package/dist/types/common/index.d.ts +2 -1
- package/dist/types/common/utils/colorHash.d.ts +4 -3
- package/dist/types/components/ContextOverlay/ContextOverlay.d.ts +7 -1
- package/dist/types/components/DecoupledOverlay/DecoupledOverlay.d.ts +20 -0
- package/dist/types/components/Icon/canonicalIconNames.d.ts +2 -0
- package/dist/types/components/Icon/transformIcon.d.ts +2 -0
- package/dist/types/components/MultiSelect/MultiSelect.d.ts +1 -1
- package/dist/types/components/RadioButton/RadioButton.d.ts +8 -2
- package/dist/types/components/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/cmem/ActivityControl/ActivityControlWidget.tsx +68 -35
- package/src/common/index.ts +2 -1
- package/src/common/utils/colorHash.ts +36 -18
- package/src/components/Application/_colors.scss +15 -0
- package/src/components/ColorField/ColorField.stories.tsx +69 -0
- package/src/components/ColorField/ColorField.tsx +200 -0
- package/src/components/ColorField/_colorfield.scss +56 -0
- package/src/components/ContextOverlay/ContextOverlay.tsx +20 -1
- package/src/components/DecoupledOverlay/DecoupledOverlay.stories.tsx +30 -0
- package/src/components/DecoupledOverlay/DecoupledOverlay.tsx +97 -0
- package/src/components/DecoupledOverlay/_decoupledoverlay.scss +46 -0
- package/src/components/Icon/canonicalIconNames.tsx +3 -0
- package/src/components/Icon/transformIcon.tsx +17 -0
- package/src/components/Link/Link.stories.tsx +30 -0
- package/src/components/Link/link.scss +28 -2
- package/src/components/MultiSelect/MultiSelect.tsx +12 -3
- package/src/components/RadioButton/RadioButton.tsx +15 -3
- package/src/components/RadioButton/radiobutton.scss +13 -0
- package/src/components/Tabs/stories/TabTitle.stories.tsx +7 -2
- package/src/components/Typography/_reset.scss +1 -0
- package/src/components/Typography/typography.scss +5 -0
- package/src/components/VisualTour/VisualTour.tsx +30 -50
- package/src/components/VisualTour/visualTour.scss +0 -34
- package/src/components/index.scss +2 -0
- package/src/components/index.ts +1 -0
- package/src/configuration/_customproperties.scss +32 -0
- package/src/configuration/stories/customproperties.stories.tsx +118 -0
- package/src/extensions/codemirror/CodeMirror.tsx +14 -6
- package/src/extensions/react-flow/_config.scss +3 -3
- package/src/extensions/react-flow/edges/EdgeLabel.tsx +5 -3
- package/src/extensions/react-flow/edges/_edges.scss +3 -2
- package/src/index.scss +1 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { RadioProps as BlueprintRadioProps } from "@blueprintjs/core";
|
|
3
|
-
export
|
|
4
|
-
|
|
3
|
+
export interface RadioButtonProps extends BlueprintRadioProps {
|
|
4
|
+
/**
|
|
5
|
+
* Hide the indicator.
|
|
6
|
+
* The element cannot be identified as radio input then but a click on the children can be easily processed via `onChange` event.
|
|
7
|
+
*/
|
|
8
|
+
hideIndicator?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const RadioButton: ({ children, className, hideIndicator, ...restProps }: RadioButtonProps) => React.JSX.Element;
|
|
5
11
|
export default RadioButton;
|
|
@@ -9,6 +9,7 @@ export * from "./Checkbox/Checkbox";
|
|
|
9
9
|
export * from "./CodeAutocompleteField";
|
|
10
10
|
export * from "./ContentGroup/ContentGroup";
|
|
11
11
|
export * from "./ContextOverlay";
|
|
12
|
+
export * from "./DecoupledOverlay/DecoupledOverlay";
|
|
12
13
|
export * from "./Depiction/Depiction";
|
|
13
14
|
export * from "./Dialog";
|
|
14
15
|
export * from "./FlexibleLayout";
|
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": "25.0.0-
|
|
4
|
+
"version": "25.0.0-featurecolorwheelinputcmem7327.0",
|
|
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",
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
3
|
+
import {ValidIconName} from "../../components/Icon/canonicalIconNames";
|
|
4
|
+
import {IconProps} from "../../components/Icon/Icon";
|
|
5
|
+
import {TestIconProps} from "../../components/Icon/TestIcon";
|
|
6
|
+
import {TestableComponent} from "../../components/interfaces";
|
|
7
|
+
import {ProgressBarProps} from "../../components/ProgressBar/ProgressBar";
|
|
8
|
+
import {SpinnerProps} from "../../components/Spinner/Spinner";
|
|
9
|
+
import {CLASSPREFIX as eccgui} from "../../configuration/constants";
|
|
10
10
|
import {
|
|
11
11
|
Card,
|
|
12
12
|
ContextMenu,
|
|
13
|
+
DecoupledOverlay,
|
|
13
14
|
IconButton,
|
|
14
15
|
MenuItem,
|
|
16
|
+
Notification,
|
|
17
|
+
NotificationProps,
|
|
15
18
|
OverflowText,
|
|
16
19
|
OverviewItem,
|
|
17
20
|
OverviewItemActions,
|
|
@@ -97,7 +100,7 @@ interface IActivityContextMenu extends TestableComponent {
|
|
|
97
100
|
export interface ActivityControlWidgetAction extends TestableComponent {
|
|
98
101
|
// The action that should be triggered
|
|
99
102
|
action: () => void;
|
|
100
|
-
// The tooltip that should be shown over the action icon
|
|
103
|
+
// The tooltip that should be shown over the action icon on hover
|
|
101
104
|
tooltip?: string;
|
|
102
105
|
// The icon of the action button
|
|
103
106
|
icon: ValidIconName | React.ReactElement<TestIconProps>;
|
|
@@ -105,6 +108,16 @@ export interface ActivityControlWidgetAction extends TestableComponent {
|
|
|
105
108
|
disabled?: boolean;
|
|
106
109
|
// Warning state
|
|
107
110
|
hasStateWarning?: boolean;
|
|
111
|
+
// Active state
|
|
112
|
+
active?: boolean
|
|
113
|
+
/** A notification that is shown in an overlay pointing at the activity action button. */
|
|
114
|
+
notification?: {
|
|
115
|
+
message: string
|
|
116
|
+
onClose: () => void
|
|
117
|
+
intent?: NotificationProps["intent"]
|
|
118
|
+
// Timeout in ms before notification is closed. Default: none
|
|
119
|
+
timeout?: number
|
|
120
|
+
}
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
interface IActivityMenuAction extends ActivityControlWidgetAction {
|
|
@@ -209,28 +222,11 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
|
|
|
209
222
|
data-test-id={dataTestIdLegacy ? `${dataTestIdLegacy}-actions` : undefined}
|
|
210
223
|
>
|
|
211
224
|
{activityActions &&
|
|
212
|
-
activityActions.map((action, idx) =>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
? action.icon
|
|
218
|
-
: action["data-test-id"] ?? action["data-testid"] ?? idx
|
|
219
|
-
}
|
|
220
|
-
data-test-id={action["data-test-id"]}
|
|
221
|
-
data-testid={action["data-testid"]}
|
|
222
|
-
name={action.icon}
|
|
223
|
-
text={action.tooltip}
|
|
224
|
-
onClick={action.action}
|
|
225
|
-
disabled={action.disabled}
|
|
226
|
-
intent={action.hasStateWarning ? "warning" : undefined}
|
|
227
|
-
tooltipProps={{
|
|
228
|
-
hoverOpenDelay: 200,
|
|
229
|
-
placement: "bottom",
|
|
230
|
-
}}
|
|
231
|
-
/>
|
|
232
|
-
);
|
|
233
|
-
})}
|
|
225
|
+
activityActions.map((action, idx) => <ActivityActionButton
|
|
226
|
+
key={idx}
|
|
227
|
+
action={action}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
234
230
|
{additionalActions}
|
|
235
231
|
{activityContextMenu && activityContextMenu.menuItems.length > 0 && (
|
|
236
232
|
<ContextMenu
|
|
@@ -241,11 +237,7 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
|
|
|
241
237
|
return (
|
|
242
238
|
<MenuItem
|
|
243
239
|
icon={menuAction.icon}
|
|
244
|
-
key={
|
|
245
|
-
typeof menuAction.icon === "string"
|
|
246
|
-
? menuAction.icon
|
|
247
|
-
: menuAction["data-test-id"] ?? idx
|
|
248
|
-
}
|
|
240
|
+
key={idx}
|
|
249
241
|
onClick={menuAction.action}
|
|
250
242
|
text={menuAction.tooltip}
|
|
251
243
|
/>
|
|
@@ -267,3 +259,44 @@ export function ActivityControlWidget(props: ActivityControlWidgetProps) {
|
|
|
267
259
|
<div className={classname}>{widget}</div>
|
|
268
260
|
);
|
|
269
261
|
}
|
|
262
|
+
|
|
263
|
+
interface ActivityActionButtonProps {
|
|
264
|
+
action: ActivityControlWidgetAction
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const ActivityActionButton = ({action}: ActivityActionButtonProps) => {
|
|
268
|
+
const actionButtonRef = React.useRef(null);
|
|
269
|
+
const ActionButton = () => (
|
|
270
|
+
<IconButton
|
|
271
|
+
data-test-id={action["data-test-id"]}
|
|
272
|
+
data-testid={action["data-testid"]}
|
|
273
|
+
name={action.icon}
|
|
274
|
+
text={action.tooltip}
|
|
275
|
+
onClick={action.action}
|
|
276
|
+
disabled={action.disabled}
|
|
277
|
+
intent={action.hasStateWarning ? "warning" : undefined}
|
|
278
|
+
tooltipProps={{
|
|
279
|
+
hoverOpenDelay: 200,
|
|
280
|
+
placement: "bottom"
|
|
281
|
+
}}
|
|
282
|
+
active={action.active}
|
|
283
|
+
/>
|
|
284
|
+
)
|
|
285
|
+
return action.notification ?
|
|
286
|
+
<>
|
|
287
|
+
<span ref={actionButtonRef}>
|
|
288
|
+
<ActionButton/>
|
|
289
|
+
</span>
|
|
290
|
+
{actionButtonRef.current && (
|
|
291
|
+
<DecoupledOverlay targetSelectorOrElement={actionButtonRef.current} paddingSize={"small"}>
|
|
292
|
+
<Notification
|
|
293
|
+
message={action.notification.message}
|
|
294
|
+
intent={action.notification.intent ?? "neutral"}
|
|
295
|
+
onDismiss={action.notification.onClose}
|
|
296
|
+
timeout={action.notification.timeout}
|
|
297
|
+
/>
|
|
298
|
+
</DecoupledOverlay>
|
|
299
|
+
)}
|
|
300
|
+
</> :
|
|
301
|
+
<ActionButton/>
|
|
302
|
+
}
|
package/src/common/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { decode } from "he";
|
|
|
3
3
|
import { invisibleZeroWidthCharacters } from "./utils/characters";
|
|
4
4
|
import { colorCalculateDistance } from "./utils/colorCalculateDistance";
|
|
5
5
|
import decideContrastColorValue from "./utils/colorDecideContrastvalue";
|
|
6
|
-
import { getEnabledColorsFromPalette, textToColorHash } from "./utils/colorHash";
|
|
6
|
+
import { getEnabledColorPropertiesFromPalette, getEnabledColorsFromPalette, textToColorHash } from "./utils/colorHash";
|
|
7
7
|
import getColorConfiguration from "./utils/getColorConfiguration";
|
|
8
8
|
import { getScrollParent } from "./utils/getScrollParent";
|
|
9
9
|
import { getGlobalVar, setGlobalVar } from "./utils/globalVars";
|
|
@@ -22,6 +22,7 @@ export const utils = {
|
|
|
22
22
|
setGlobalVar,
|
|
23
23
|
getScrollParent,
|
|
24
24
|
getEnabledColorsFromPalette,
|
|
25
|
+
getEnabledColorPropertiesFromPalette,
|
|
25
26
|
textToColorHash,
|
|
26
27
|
reduceToText,
|
|
27
28
|
decodeHtmlEntities: decode,
|
|
@@ -6,8 +6,8 @@ import { colorCalculateDistance } from "./colorCalculateDistance";
|
|
|
6
6
|
import CssCustomProperties from "./CssCustomProperties";
|
|
7
7
|
|
|
8
8
|
type ColorOrFalse = Color | false;
|
|
9
|
-
type ColorWeight = 100 | 300 | 500 | 700 | 900;
|
|
10
|
-
type PaletteGroup = "identity" | "semantic" | "layout" | "extra";
|
|
9
|
+
export type ColorWeight = 100 | 300 | 500 | 700 | 900;
|
|
10
|
+
export type PaletteGroup = "identity" | "semantic" | "layout" | "extra";
|
|
11
11
|
|
|
12
12
|
interface getEnabledColorsProps {
|
|
13
13
|
/** Specify the palette groups used to define the set of colors. */
|
|
@@ -21,20 +21,43 @@ interface getEnabledColorsProps {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const getEnabledColorsFromPaletteCache = new Map<string, Color[]>();
|
|
24
|
+
const getEnabledColorPropertiesFromPaletteCache = new Map<string, string[][]>();
|
|
24
25
|
|
|
25
|
-
export function getEnabledColorsFromPalette({
|
|
26
|
+
export function getEnabledColorsFromPalette(props: getEnabledColorsProps): Color[] {
|
|
27
|
+
const configId = JSON.stringify({
|
|
28
|
+
includePaletteGroup: props.includePaletteGroup,
|
|
29
|
+
includeColorWeight: props.includeColorWeight,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (getEnabledColorsFromPaletteCache.has(configId)) {
|
|
33
|
+
return getEnabledColorsFromPaletteCache.get(configId)!;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const colorPropertiesFromPalette = Object.values(getEnabledColorPropertiesFromPalette(props));
|
|
37
|
+
|
|
38
|
+
getEnabledColorsFromPaletteCache.set(
|
|
39
|
+
configId,
|
|
40
|
+
colorPropertiesFromPalette.map((color) => {
|
|
41
|
+
return Color(color[1]);
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return getEnabledColorsFromPaletteCache.get(configId)!;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getEnabledColorPropertiesFromPalette({
|
|
26
49
|
includePaletteGroup = ["layout"],
|
|
27
50
|
includeColorWeight = [100, 300, 500, 700, 900],
|
|
28
|
-
//
|
|
51
|
+
// (planned for later): includeMixedColors = false,
|
|
29
52
|
minimalColorDistance = COLORMINDISTANCE,
|
|
30
|
-
}: getEnabledColorsProps):
|
|
53
|
+
}: getEnabledColorsProps): string[][] {
|
|
31
54
|
const configId = JSON.stringify({
|
|
32
55
|
includePaletteGroup,
|
|
33
56
|
includeColorWeight,
|
|
34
57
|
});
|
|
35
58
|
|
|
36
|
-
if (
|
|
37
|
-
return
|
|
59
|
+
if (getEnabledColorPropertiesFromPaletteCache.has(configId)) {
|
|
60
|
+
return getEnabledColorPropertiesFromPaletteCache.get(configId)!;
|
|
38
61
|
}
|
|
39
62
|
|
|
40
63
|
const colorsFromPalette = new CssCustomProperties({
|
|
@@ -50,18 +73,18 @@ export function getEnabledColorsFromPalette({
|
|
|
50
73
|
const weight = parseInt(tint[2], 10) as ColorWeight;
|
|
51
74
|
return includePaletteGroup.includes(group) && includeColorWeight.includes(weight);
|
|
52
75
|
},
|
|
53
|
-
removeDashPrefix:
|
|
76
|
+
removeDashPrefix: true,
|
|
54
77
|
returnObject: true,
|
|
55
78
|
}).customProperties();
|
|
56
79
|
|
|
57
|
-
const colorsFromPaletteValues = Object.
|
|
80
|
+
const colorsFromPaletteValues = Object.entries(colorsFromPalette) as [string, string][];
|
|
58
81
|
|
|
59
82
|
const colorsFromPaletteWithEnoughDistance =
|
|
60
83
|
minimalColorDistance > 0
|
|
61
|
-
? colorsFromPaletteValues.reduce((enoughDistance: string[], color: string) => {
|
|
84
|
+
? colorsFromPaletteValues.reduce((enoughDistance: [string, string][], color: [string, string]) => {
|
|
62
85
|
if (enoughDistance.includes(color)) {
|
|
63
86
|
return enoughDistance.filter((checkColor) => {
|
|
64
|
-
const distance = colorCalculateDistance({ color1: color, color2: checkColor });
|
|
87
|
+
const distance = colorCalculateDistance({ color1: color[1], color2: checkColor[1] });
|
|
65
88
|
return checkColor === color || (distance && minimalColorDistance <= distance);
|
|
66
89
|
});
|
|
67
90
|
} else {
|
|
@@ -70,14 +93,9 @@ export function getEnabledColorsFromPalette({
|
|
|
70
93
|
}, colorsFromPaletteValues)
|
|
71
94
|
: colorsFromPaletteValues;
|
|
72
95
|
|
|
73
|
-
|
|
74
|
-
configId,
|
|
75
|
-
colorsFromPaletteWithEnoughDistance.map((color: string) => {
|
|
76
|
-
return Color(color);
|
|
77
|
-
})
|
|
78
|
-
);
|
|
96
|
+
getEnabledColorPropertiesFromPaletteCache.set(configId, colorsFromPaletteWithEnoughDistance);
|
|
79
97
|
|
|
80
|
-
return
|
|
98
|
+
return getEnabledColorPropertiesFromPaletteCache.get(configId)!;
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
function getColorcode(text: string): ColorOrFalse {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
@use "sass:list";
|
|
3
3
|
|
|
4
4
|
:root {
|
|
5
|
+
// creating css custom properties from palette colors
|
|
5
6
|
@each $palette-group-name, $palette-group-tints in $eccgui-color-palette-light {
|
|
6
7
|
@each $palette-tint-name, $palette-tint-colors in $palette-group-tints {
|
|
7
8
|
@for $i from 1 through list.length($palette-tint-colors) {
|
|
@@ -12,4 +13,18 @@
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
// set aliases for base colors
|
|
18
|
+
--#{$eccgui}-color-primary: #{$eccgui-color-primary};
|
|
19
|
+
--#{$eccgui}-color-primary-contrast: #{$eccgui-color-primary-contrast};
|
|
20
|
+
--#{$eccgui}-color-accent: #{$eccgui-color-accent};
|
|
21
|
+
--#{$eccgui}-color-accent-contrast: #{$eccgui-color-accent-contrast};
|
|
22
|
+
--#{$eccgui}-color-success-foreground: #{$eccgui-color-success-text};
|
|
23
|
+
--#{$eccgui}-color-success-background: #{$eccgui-color-success-background};
|
|
24
|
+
--#{$eccgui}-color-info-foreground: #{$eccgui-color-info-text};
|
|
25
|
+
--#{$eccgui}-color-info-background: #{$eccgui-color-info-background};
|
|
26
|
+
--#{$eccgui}-color-warning-foreground: #{$eccgui-color-warning-text};
|
|
27
|
+
--#{$eccgui}-color-warning-background: #{$eccgui-color-warning-background};
|
|
28
|
+
--#{$eccgui}-color-danger-foreground: #{$eccgui-color-danger-text};
|
|
29
|
+
--#{$eccgui}-color-danger-background: #{$eccgui-color-danger-background};
|
|
15
30
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Meta, StoryFn } from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import textFieldTest from "../TextField/stories/TextField.stories";
|
|
5
|
+
|
|
6
|
+
import { ColorField, ColorFieldProps } from "./ColorField";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: "Forms/ColorField",
|
|
10
|
+
component: ColorField,
|
|
11
|
+
argTypes: {
|
|
12
|
+
...textFieldTest.argTypes,
|
|
13
|
+
},
|
|
14
|
+
} as Meta<typeof ColorField>;
|
|
15
|
+
|
|
16
|
+
const Template: StoryFn<typeof ColorField> = (args) => <ColorField {...args}></ColorField>;
|
|
17
|
+
|
|
18
|
+
export const Default = Template.bind({});
|
|
19
|
+
Default.args = {
|
|
20
|
+
onChange: (e) => {
|
|
21
|
+
alert(e.target.value);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const NoPalettePresets = Template.bind({});
|
|
26
|
+
NoPalettePresets.args = {
|
|
27
|
+
...Default.args,
|
|
28
|
+
colorWeightFilter: [],
|
|
29
|
+
paletteGroupFilter: [],
|
|
30
|
+
allowCustomColor: true,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
interface TemplateColorHashProps
|
|
34
|
+
extends Pick<ColorFieldProps, "onChange" | "allowCustomColor" | "colorWeightFilter" | "paletteGroupFilter"> {
|
|
35
|
+
stringForColorHashValue: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const TemplateColorHash: StoryFn<TemplateColorHashProps> = (args: TemplateColorHashProps) => (
|
|
39
|
+
<ColorField
|
|
40
|
+
allowCustomColor={args.allowCustomColor}
|
|
41
|
+
colorWeightFilter={args.colorWeightFilter}
|
|
42
|
+
paletteGroupFilter={args.paletteGroupFilter}
|
|
43
|
+
value={ColorField.calculateColorHashValue(args.stringForColorHashValue, {
|
|
44
|
+
allowCustomColor: args.allowCustomColor,
|
|
45
|
+
colorWeightFilter: args.colorWeightFilter,
|
|
46
|
+
paletteGroupFilter: args.paletteGroupFilter,
|
|
47
|
+
})}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Component provides a helper function to calculate a color hash from a text,
|
|
53
|
+
* that can be used as `value` or `defaultValue`.
|
|
54
|
+
*
|
|
55
|
+
* ```
|
|
56
|
+
* <ColorField value={ColorField.calculateColorHashValue("MyText")} />
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* You can add `options` to set the config for the color palette filters.
|
|
60
|
+
* The same default values like on `ColorField` are used for them.
|
|
61
|
+
*/
|
|
62
|
+
export const ColorHashValue = TemplateColorHash.bind({});
|
|
63
|
+
ColorHashValue.args = {
|
|
64
|
+
...Default.args,
|
|
65
|
+
allowCustomColor: true,
|
|
66
|
+
colorWeightFilter: [300, 500, 700],
|
|
67
|
+
paletteGroupFilter: ["layout", "extra"],
|
|
68
|
+
stringForColorHashValue: "My text that will used to create a color hash as initial value.",
|
|
69
|
+
};
|
|
@@ -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
|
+
colorWeightFilter?: ColorWeight[];
|
|
25
|
+
/**
|
|
26
|
+
* What palette groups should be included in the set of allowed colors.
|
|
27
|
+
*/
|
|
28
|
+
paletteGroupFilter?: PaletteGroup[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Color input field that provides resets from the configured color palette.
|
|
33
|
+
* Use `colorWeightFilter` and `paletteGroupFilter` to filter them.
|
|
34
|
+
*/
|
|
35
|
+
export const ColorField = ({
|
|
36
|
+
className = "",
|
|
37
|
+
allowCustomColor = false,
|
|
38
|
+
colorWeightFilter = [100, 300, 700, 900],
|
|
39
|
+
paletteGroupFilter = ["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: paletteGroupFilter,
|
|
53
|
+
includeColorWeight: colorWeightFilter,
|
|
54
|
+
minimalColorDistance: 0, // we use all allowed colors, and do not check distances between them
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
disableNativePicker =
|
|
58
|
+
colorWeightFilter.length > 0 && paletteGroupFilter.length > 0 && allowedPaletteColors.length > 0;
|
|
59
|
+
disabled = (!disableNativePicker && !allowCustomColor) || otherTextFieldProps.disabled;
|
|
60
|
+
};
|
|
61
|
+
updateConfig();
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
updateConfig();
|
|
64
|
+
}, [allowCustomColor, colorWeightFilter, paletteGroupFilter, 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
|
+
colorWeightFilter.length >= 3 ? colorWeightFilter.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) % (colorWeightFilter.length >= 3 ? colorWeightFilter.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" | "colorWeightFilter" | "paletteGroupFilter"
|
|
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
|
+
colorWeightFilter: [100, 300, 700, 900],
|
|
182
|
+
paletteGroupFilter: ["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.paletteGroupFilter,
|
|
191
|
+
includeColorWeight: options.colorWeightFilter,
|
|
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
|
+
}
|