@pareto-engineering/design-system 4.0.0-alpha.41 → 4.0.0-alpha.43

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 (30) hide show
  1. package/dist/cjs/b/Button/Button.js +2 -1
  2. package/dist/cjs/b/Button/styles.scss +3 -3
  3. package/dist/cjs/f/FormInput/FormInput.js +6 -0
  4. package/dist/cjs/f/common/Description/Description.js +5 -4
  5. package/dist/cjs/f/fields/ChoicesInput/styles.scss +1 -1
  6. package/dist/cjs/f/fields/EditorInput/EditorInput.js +32 -1
  7. package/dist/cjs/f/fields/EditorInput/common/Toolbar.js +64 -0
  8. package/dist/cjs/f/fields/EditorInput/styles.scss +28 -0
  9. package/dist/es/b/Button/Button.js +2 -1
  10. package/dist/es/b/Button/styles.scss +3 -3
  11. package/dist/es/f/FormInput/FormInput.js +7 -1
  12. package/dist/es/f/common/Description/Description.js +5 -4
  13. package/dist/es/f/fields/ChoicesInput/styles.scss +1 -1
  14. package/dist/es/f/fields/EditorInput/EditorInput.js +32 -1
  15. package/dist/es/f/fields/EditorInput/common/Toolbar.js +66 -2
  16. package/dist/es/f/fields/EditorInput/styles.scss +28 -0
  17. package/package.json +3 -3
  18. package/src/stories/a/AppContext.stories.jsx +2 -2
  19. package/src/stories/b/SocialMediaButton.stories.jsx +2 -2
  20. package/src/stories/colors.js +2 -0
  21. package/src/stories/f/EditorInput.stories.jsx +1 -1
  22. package/src/ui/b/Button/Button.jsx +2 -0
  23. package/src/ui/b/Button/styles.scss +3 -3
  24. package/src/ui/f/FormInput/FormInput.jsx +11 -0
  25. package/src/ui/f/common/Description/Description.jsx +5 -4
  26. package/src/ui/f/fields/ChoicesInput/styles.scss +1 -1
  27. package/src/ui/f/fields/EditorInput/EditorInput.jsx +36 -1
  28. package/src/ui/f/fields/EditorInput/common/Toolbar.jsx +103 -1
  29. package/src/ui/f/fields/EditorInput/styles.scss +28 -0
  30. package/tests/__snapshots__/Storyshots.test.js.snap +1008 -11
@@ -25,6 +25,7 @@ const Button = _ref => {
25
25
  children,
26
26
  isLoading,
27
27
  color,
28
+ textColor,
28
29
  isCompact,
29
30
  isGhost,
30
31
  isSimple,
@@ -41,7 +42,7 @@ const Button = _ref => {
41
42
  }, children) : children;
42
43
  return /*#__PURE__*/React.createElement("button", _extends({
43
44
  id: id,
44
- className: [baseClassName, componentClassName, userClassName, `x-${color}`, isGhost && _exports.default.modifierGhost, isCompact && _exports.default.modifierCompact, isSimple && _exports.default.modifierSimple, isGradient && _exports.default.modifierGradient, isSpaced && _exports.default.modifierSpaced, !isLoading && arrowDirection && `arrow-${arrowDirection}`].filter(e => e).join(' '),
45
+ className: [baseClassName, componentClassName, userClassName, `x-${color}`, textColor && `y-${textColor}`, isGhost && _exports.default.modifierGhost, isCompact && _exports.default.modifierCompact, isSimple && _exports.default.modifierSimple, isGradient && _exports.default.modifierGradient, isSpaced && _exports.default.modifierSpaced, !isLoading && arrowDirection && `arrow-${arrowDirection}`].filter(e => e).join(' '),
45
46
  style: style,
46
47
  type: "button"
47
48
  }, otherProps), isLoading ? /*#__PURE__*/React.createElement(_.LoadingCircle, {
@@ -92,7 +92,7 @@ $default-animation-time: .31s;
92
92
  &.#{bem.$modifier-ghost} {
93
93
  background: transparent;
94
94
  border: 1px solid var(--x, var(--#{$default-color}));
95
- color: var(--x, var(--#{$default-color}));
95
+ color: var(--y, var(--x, var(--#{$default-color})));
96
96
 
97
97
  &:hover,
98
98
  &:focus,
@@ -103,12 +103,12 @@ $default-animation-time: .31s;
103
103
  &:not(:disabled) {
104
104
  &:hover {
105
105
  border: 1px solid var(--soft-x, var(--soft-#{$default-color}));
106
- color: var(--soft-x, var(--soft-#{$default-color}));
106
+ color: var(--soft-y, var(--soft-x, var(--soft-#{$default-color})));
107
107
  }
108
108
 
109
109
  &:focus {
110
110
  border: 1px solid var(--hard-x, var(--hard-#{$default-color}));
111
- color: var(--hard-x, var(--hard-#{$default-color}));
111
+ color: var(--hard-y, var(--hard-x, var(--hard-#{$default-color})));
112
112
  }
113
113
  }
114
114
 
@@ -83,6 +83,12 @@ const FormInput = _ref => {
83
83
  disabled: disabled
84
84
  }, otherProps));
85
85
  }
86
+ if (type === 'editor') {
87
+ return /*#__PURE__*/React.createElement(_fields.EditorInput, _extends({
88
+ className: newClassName,
89
+ disabled: disabled
90
+ }, otherProps));
91
+ }
86
92
  if (extraTypes?.[type]) {
87
93
  const Component = extraTypes[type];
88
94
  return /*#__PURE__*/React.createElement(Component, _extends({
@@ -32,13 +32,14 @@ const Description = _ref => {
32
32
  (0, React.useInsertionEffect)(() => {
33
33
  Promise.resolve().then(() => _interopRequireWildcard(require("./styles.scss")));
34
34
  }, []);
35
- const [field, meta] = (0, _formik.useField)(name);
36
- if (description || (meta.touched || field.value) && meta.error) {
35
+ const [, meta] = (0, _formik.useField)(name);
36
+ const hasError = meta.touched && meta.error;
37
+ if (hasError || description) {
37
38
  return /*#__PURE__*/React.createElement("div", {
38
39
  id: id,
39
- className: [baseClassName, componentClassName, userClassName, meta.error ? 'x-error' : `x-${color}`].filter(e => e).join(' '),
40
+ className: [baseClassName, componentClassName, userClassName, hasError ? 'x-error' : `x-${color}`].filter(e => e).join(' '),
40
41
  style: style
41
- }, meta.error || description);
42
+ }, hasError ? meta.error : description);
42
43
  }
43
44
  return null;
44
45
  };
@@ -36,7 +36,7 @@ $disabled-background: var(--background-inputs-30);
36
36
 
37
37
  input {
38
38
  opacity: 0;
39
- position: absolute;
39
+ position: fixed;
40
40
  visibility: none;
41
41
  z-index: -1;
42
42
  }
@@ -57,7 +57,38 @@ const EditorInput = _ref => {
57
57
  const formik = (0, _formik.useFormikContext)();
58
58
  const setInitialValue = () => {
59
59
  const value = formik.values[name];
60
- return value || undefined;
60
+ try {
61
+ JSON.parse(value);
62
+ return value;
63
+ } catch {
64
+ const defaultValue = {
65
+ root: {
66
+ children: [{
67
+ children: [{
68
+ detail: 0,
69
+ format: 0,
70
+ mode: 'normal',
71
+ style: '',
72
+ text: '',
73
+ type: 'text',
74
+ version: 1
75
+ }],
76
+ direction: 'ltr',
77
+ format: '',
78
+ indent: 0,
79
+ type: 'paragraph',
80
+ version: 1
81
+ }],
82
+ direction: 'ltr',
83
+ format: '',
84
+ indent: 0,
85
+ type: 'root',
86
+ version: 1
87
+ }
88
+ };
89
+ defaultValue.root.children[0].children[0].text = value;
90
+ return JSON.stringify(defaultValue);
91
+ }
61
92
  };
62
93
  const onChange = state => {
63
94
  formik.setValues({
@@ -11,6 +11,7 @@ var _selection = require("@lexical/selection");
11
11
  var _link = require("@lexical/link");
12
12
  var _LexicalComposerContext = require("@lexical/react/LexicalComposerContext");
13
13
  var _utils = require("@lexical/utils");
14
+ var _ = require("../../../..");
14
15
  var _exports = _interopRequireDefault(require("@pareto-engineering/bem/exports"));
15
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
17
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -35,6 +36,8 @@ const getSelectedNode = selection => {
35
36
  }
36
37
  return (0, _selection.$isAtNodeEnd)(anchor) ? focusNode : anchorNode;
37
38
  };
39
+ const defaultColor = 'var(--paragraph)';
40
+ const colorOptions = ['red', 'blue', 'green', 'yellow', 'orange', 'purple', 'pink', 'brown'];
38
41
  const Toolbar = () => {
39
42
  const [editor] = (0, _LexicalComposerContext.useLexicalComposerContext)();
40
43
  const [isBold, setIsBold] = (0, React.useState)(false);
@@ -43,6 +46,8 @@ const Toolbar = () => {
43
46
  const [blockType, setBlockType] = (0, React.useState)('paragraph');
44
47
  const [isLink, setIsLink] = (0, React.useState)(false);
45
48
  const [isUnderline, setIsUnderline] = (0, React.useState)(false);
49
+ const [color, setColor] = (0, React.useState)(defaultColor);
50
+ const colorMenuRef = (0, React.useRef)(false);
46
51
  const formatBulletList = () => {
47
52
  if (blockType !== 'ul') {
48
53
  editor.dispatchCommand(_list.INSERT_UNORDERED_LIST_COMMAND);
@@ -81,6 +86,18 @@ const Toolbar = () => {
81
86
  setBlockType(element);
82
87
  }
83
88
 
89
+ // Check nodes for color
90
+ const nodes = selection.getNodes().filter(node => node.getType() === 'text');
91
+ nodes.forEach(node => {
92
+ const style = node.getStyle();
93
+ const colorProperty = style.match(/color: ([^;]+)/);
94
+ if (colorProperty) {
95
+ setColor(colorProperty[1]);
96
+ return;
97
+ }
98
+ setColor(false);
99
+ });
100
+
84
101
  // Check selection text styles
85
102
  setIsBold(selection.hasFormat('bold'));
86
103
  setIsItalic(selection.hasFormat('italic'));
@@ -98,6 +115,22 @@ const Toolbar = () => {
98
115
  }
99
116
  }
100
117
  }, [editor]);
118
+ const UPDATE_COLOR_COMMAND = (0, _lexical.createCommand)();
119
+ editor.registerCommand(UPDATE_COLOR_COMMAND, payload => {
120
+ const selection = (0, _lexical.$getSelection)();
121
+ const nodes = selection?.extract().filter(node => node.getType() === 'text');
122
+ nodes?.forEach(node => {
123
+ const style = node.getStyle();
124
+ const colorProperty = style?.match(/color: ([^;]+)/);
125
+ if (colorProperty && color !== payload) {
126
+ node.setStyle(style.replace(colorProperty[0], `color: ${payload}`));
127
+ } else if (colorProperty) {
128
+ node.setStyle(`color: ${defaultColor}`);
129
+ } else {
130
+ node.setStyle(`color: ${payload}`);
131
+ }
132
+ });
133
+ }, _lexical.COMMAND_PRIORITY_NORMAL);
101
134
  (0, React.useEffect)(() => (0, _utils.mergeRegister)(editor.registerUpdateListener(_ref => {
102
135
  let {
103
136
  editorState
@@ -106,6 +139,10 @@ const Toolbar = () => {
106
139
  updateToolbar();
107
140
  });
108
141
  })), [updateToolbar, editor]);
142
+ const dispatchUpdateColor = (0, React.useCallback)((e, payload) => {
143
+ e.stopPropagation();
144
+ editor.dispatchCommand(UPDATE_COLOR_COMMAND, payload);
145
+ }, [editor]);
109
146
  return /*#__PURE__*/React.createElement("div", {
110
147
  className: `${baseClassName} ${componentClassName}`
111
148
  }, /*#__PURE__*/React.createElement("div", {
@@ -135,6 +172,33 @@ const Toolbar = () => {
135
172
  }, /*#__PURE__*/React.createElement("span", {
136
173
  className: "icon"
137
174
  }, "?")), /*#__PURE__*/React.createElement("button", {
175
+ type: "button",
176
+ className: color && color !== defaultColor ? 'active color-menu-button' : 'color-menu-button',
177
+ onClick: () => editor.dispatchCommand(UPDATE_COLOR_COMMAND, color !== defaultColor ? defaultColor : color),
178
+ ref: colorMenuRef,
179
+ style: {
180
+ position: 'relative'
181
+ }
182
+ }, /*#__PURE__*/React.createElement("span", {
183
+ className: "icon",
184
+ style: {
185
+ color
186
+ }
187
+ }, "Q"), /*#__PURE__*/React.createElement(_.Popover, {
188
+ parentRef: colorMenuRef
189
+ }, /*#__PURE__*/React.createElement("div", {
190
+ className: "color-menu"
191
+ }, colorOptions.map(option => /*#__PURE__*/React.createElement("span", {
192
+ role: "button",
193
+ className: "icon color-option",
194
+ style: {
195
+ color: option
196
+ },
197
+ onClick: e => dispatchUpdateColor(e, option),
198
+ onKeyDown: e => dispatchUpdateColor(e, option),
199
+ tabIndex: 0,
200
+ key: option
201
+ }, "o"))))), /*#__PURE__*/React.createElement("button", {
138
202
  type: "button",
139
203
  className: isLink ? 'active' : undefined,
140
204
  onClick: () => formatLink()
@@ -13,7 +13,9 @@ $default-border: var(--theme-default-input-border);
13
13
  $focus-border: var(--theme-focus-input-border);
14
14
  $active-background: var(--hard-background-inputs);
15
15
  $default-background: var(--background-inputs);
16
+ $default-icon-color: var(--on-background-inputs);
16
17
  $disabled-background: var(--background-inputs-30);
18
+ $default-color-menu-padding: .5em .25em;
17
19
 
18
20
  .#{bem.$base}.editor-input {
19
21
  &.#{bem.$base}.input-wrapper {
@@ -29,6 +31,7 @@ $disabled-background: var(--background-inputs-30);
29
31
  > button {
30
32
  background: $default-background;
31
33
  border: $default-border;
34
+ color: $default-icon-color;
32
35
  padding: $default-padding;
33
36
 
34
37
  &.active {
@@ -60,6 +63,31 @@ $disabled-background: var(--background-inputs-30);
60
63
  }
61
64
  }
62
65
 
66
+ .color-menu-button {
67
+ &:hover {
68
+ > .#{bem.$base}.popover {
69
+ display: block;
70
+ }
71
+ }
72
+
73
+ > .#{bem.$base}.popover {
74
+ padding: $default-color-menu-padding;
75
+
76
+ .color-menu {
77
+ display: flex;
78
+ flex-wrap: wrap;
79
+ gap: calc($default-gap / 2);
80
+ justify-content: center;
81
+ max-width: 10em;
82
+ min-width: 5em;
83
+ }
84
+
85
+ .color-option:hover {
86
+ opacity: .5;
87
+ }
88
+ }
89
+ }
90
+
63
91
  > .content-editable {
64
92
  background: $default-background;
65
93
  border: $default-border;
@@ -20,6 +20,7 @@ const Button = ({
20
20
  children,
21
21
  isLoading,
22
22
  color,
23
+ textColor,
23
24
  isCompact,
24
25
  isGhost,
25
26
  isSimple,
@@ -36,7 +37,7 @@ const Button = ({
36
37
  }, children) : children;
37
38
  return /*#__PURE__*/React.createElement("button", _extends({
38
39
  id: id,
39
- className: [baseClassName, componentClassName, userClassName, `x-${color}`, isGhost && styleNames.modifierGhost, isCompact && styleNames.modifierCompact, isSimple && styleNames.modifierSimple, isGradient && styleNames.modifierGradient, isSpaced && styleNames.modifierSpaced, !isLoading && arrowDirection && `arrow-${arrowDirection}`].filter(e => e).join(' '),
40
+ className: [baseClassName, componentClassName, userClassName, `x-${color}`, textColor && `y-${textColor}`, isGhost && styleNames.modifierGhost, isCompact && styleNames.modifierCompact, isSimple && styleNames.modifierSimple, isGradient && styleNames.modifierGradient, isSpaced && styleNames.modifierSpaced, !isLoading && arrowDirection && `arrow-${arrowDirection}`].filter(e => e).join(' '),
40
41
  style: style,
41
42
  type: "button"
42
43
  }, otherProps), isLoading ? /*#__PURE__*/React.createElement(LoadingCircle, {
@@ -92,7 +92,7 @@ $default-animation-time: .31s;
92
92
  &.#{bem.$modifier-ghost} {
93
93
  background: transparent;
94
94
  border: 1px solid var(--x, var(--#{$default-color}));
95
- color: var(--x, var(--#{$default-color}));
95
+ color: var(--y, var(--x, var(--#{$default-color})));
96
96
 
97
97
  &:hover,
98
98
  &:focus,
@@ -103,12 +103,12 @@ $default-animation-time: .31s;
103
103
  &:not(:disabled) {
104
104
  &:hover {
105
105
  border: 1px solid var(--soft-x, var(--soft-#{$default-color}));
106
- color: var(--soft-x, var(--soft-#{$default-color}));
106
+ color: var(--soft-y, var(--soft-x, var(--soft-#{$default-color})));
107
107
  }
108
108
 
109
109
  &:focus {
110
110
  border: 1px solid var(--hard-x, var(--hard-#{$default-color}));
111
- color: var(--hard-x, var(--hard-#{$default-color}));
111
+ color: var(--hard-y, var(--hard-x, var(--hard-#{$default-color})));
112
112
  }
113
113
  }
114
114
 
@@ -3,7 +3,7 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
3
3
  import * as React from 'react';
4
4
  import { memo, useInsertionEffect } from 'react';
5
5
  import PropTypes from 'prop-types';
6
- import { TextInput, TextareaInput, ChoicesInput, SelectInput, QueryCombobox, QuerySelect, RatingsInput, Checkbox, QueryChoices, LinkInput } from "../fields";
6
+ import { TextInput, TextareaInput, ChoicesInput, SelectInput, QueryCombobox, QuerySelect, RatingsInput, Checkbox, QueryChoices, LinkInput, EditorInput } from "../fields";
7
7
 
8
8
  // Local Definitions
9
9
 
@@ -78,6 +78,12 @@ const FormInput = ({
78
78
  disabled: disabled
79
79
  }, otherProps));
80
80
  }
81
+ if (type === 'editor') {
82
+ return /*#__PURE__*/React.createElement(EditorInput, _extends({
83
+ className: newClassName,
84
+ disabled: disabled
85
+ }, otherProps));
86
+ }
81
87
  if (extraTypes?.[type]) {
82
88
  const Component = extraTypes[type];
83
89
  return /*#__PURE__*/React.createElement(Component, _extends({
@@ -25,13 +25,14 @@ const Description = ({
25
25
  useInsertionEffect(() => {
26
26
  import("./styles.scss");
27
27
  }, []);
28
- const [field, meta] = useField(name);
29
- if (description || (meta.touched || field.value) && meta.error) {
28
+ const [, meta] = useField(name);
29
+ const hasError = meta.touched && meta.error;
30
+ if (hasError || description) {
30
31
  return /*#__PURE__*/React.createElement("div", {
31
32
  id: id,
32
- className: [baseClassName, componentClassName, userClassName, meta.error ? 'x-error' : `x-${color}`].filter(e => e).join(' '),
33
+ className: [baseClassName, componentClassName, userClassName, hasError ? 'x-error' : `x-${color}`].filter(e => e).join(' '),
33
34
  style: style
34
- }, meta.error || description);
35
+ }, hasError ? meta.error : description);
35
36
  }
36
37
  return null;
37
38
  };
@@ -36,7 +36,7 @@ $disabled-background: var(--background-inputs-30);
36
36
 
37
37
  input {
38
38
  opacity: 0;
39
- position: absolute;
39
+ position: fixed;
40
40
  visibility: none;
41
41
  z-index: -1;
42
42
  }
@@ -53,7 +53,38 @@ const EditorInput = ({
53
53
  const formik = useFormikContext();
54
54
  const setInitialValue = () => {
55
55
  const value = formik.values[name];
56
- return value || undefined;
56
+ try {
57
+ JSON.parse(value);
58
+ return value;
59
+ } catch {
60
+ const defaultValue = {
61
+ root: {
62
+ children: [{
63
+ children: [{
64
+ detail: 0,
65
+ format: 0,
66
+ mode: 'normal',
67
+ style: '',
68
+ text: '',
69
+ type: 'text',
70
+ version: 1
71
+ }],
72
+ direction: 'ltr',
73
+ format: '',
74
+ indent: 0,
75
+ type: 'paragraph',
76
+ version: 1
77
+ }],
78
+ direction: 'ltr',
79
+ format: '',
80
+ indent: 0,
81
+ type: 'root',
82
+ version: 1
83
+ }
84
+ };
85
+ defaultValue.root.children[0].children[0].text = value;
86
+ return JSON.stringify(defaultValue);
87
+ }
57
88
  };
58
89
  const onChange = state => {
59
90
  formik.setValues({
@@ -1,12 +1,13 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies -- required here */
2
2
  import * as React from 'react';
3
- import { useEffect, useState, useCallback } from 'react';
4
- import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, UNDO_COMMAND, REDO_COMMAND } from 'lexical';
3
+ import { useEffect, useState, useCallback, useRef } from 'react';
4
+ import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND, FORMAT_ELEMENT_COMMAND, UNDO_COMMAND, REDO_COMMAND, COMMAND_PRIORITY_NORMAL, createCommand } from 'lexical';
5
5
  import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, REMOVE_LIST_COMMAND, $isListNode, ListNode } from '@lexical/list';
6
6
  import { $isAtNodeEnd } from '@lexical/selection';
7
7
  import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
8
8
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
9
9
  import { mergeRegister, $getNearestNodeOfType } from '@lexical/utils';
10
+ import { Popover } from "../../../..";
10
11
  import styleNames from '@pareto-engineering/bem/exports';
11
12
  const baseClassName = styleNames.base;
12
13
  const componentClassName = 'toolbar';
@@ -26,6 +27,8 @@ const getSelectedNode = selection => {
26
27
  }
27
28
  return $isAtNodeEnd(anchor) ? focusNode : anchorNode;
28
29
  };
30
+ const defaultColor = 'var(--paragraph)';
31
+ const colorOptions = ['red', 'blue', 'green', 'yellow', 'orange', 'purple', 'pink', 'brown'];
29
32
  const Toolbar = () => {
30
33
  const [editor] = useLexicalComposerContext();
31
34
  const [isBold, setIsBold] = useState(false);
@@ -34,6 +37,8 @@ const Toolbar = () => {
34
37
  const [blockType, setBlockType] = useState('paragraph');
35
38
  const [isLink, setIsLink] = useState(false);
36
39
  const [isUnderline, setIsUnderline] = useState(false);
40
+ const [color, setColor] = useState(defaultColor);
41
+ const colorMenuRef = useRef(false);
37
42
  const formatBulletList = () => {
38
43
  if (blockType !== 'ul') {
39
44
  editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND);
@@ -72,6 +77,18 @@ const Toolbar = () => {
72
77
  setBlockType(element);
73
78
  }
74
79
 
80
+ // Check nodes for color
81
+ const nodes = selection.getNodes().filter(node => node.getType() === 'text');
82
+ nodes.forEach(node => {
83
+ const style = node.getStyle();
84
+ const colorProperty = style.match(/color: ([^;]+)/);
85
+ if (colorProperty) {
86
+ setColor(colorProperty[1]);
87
+ return;
88
+ }
89
+ setColor(false);
90
+ });
91
+
75
92
  // Check selection text styles
76
93
  setIsBold(selection.hasFormat('bold'));
77
94
  setIsItalic(selection.hasFormat('italic'));
@@ -89,6 +106,22 @@ const Toolbar = () => {
89
106
  }
90
107
  }
91
108
  }, [editor]);
109
+ const UPDATE_COLOR_COMMAND = createCommand();
110
+ editor.registerCommand(UPDATE_COLOR_COMMAND, payload => {
111
+ const selection = $getSelection();
112
+ const nodes = selection?.extract().filter(node => node.getType() === 'text');
113
+ nodes?.forEach(node => {
114
+ const style = node.getStyle();
115
+ const colorProperty = style?.match(/color: ([^;]+)/);
116
+ if (colorProperty && color !== payload) {
117
+ node.setStyle(style.replace(colorProperty[0], `color: ${payload}`));
118
+ } else if (colorProperty) {
119
+ node.setStyle(`color: ${defaultColor}`);
120
+ } else {
121
+ node.setStyle(`color: ${payload}`);
122
+ }
123
+ });
124
+ }, COMMAND_PRIORITY_NORMAL);
92
125
  useEffect(() => mergeRegister(editor.registerUpdateListener(({
93
126
  editorState
94
127
  }) => {
@@ -96,6 +129,10 @@ const Toolbar = () => {
96
129
  updateToolbar();
97
130
  });
98
131
  })), [updateToolbar, editor]);
132
+ const dispatchUpdateColor = useCallback((e, payload) => {
133
+ e.stopPropagation();
134
+ editor.dispatchCommand(UPDATE_COLOR_COMMAND, payload);
135
+ }, [editor]);
99
136
  return /*#__PURE__*/React.createElement("div", {
100
137
  className: `${baseClassName} ${componentClassName}`
101
138
  }, /*#__PURE__*/React.createElement("div", {
@@ -125,6 +162,33 @@ const Toolbar = () => {
125
162
  }, /*#__PURE__*/React.createElement("span", {
126
163
  className: "icon"
127
164
  }, "?")), /*#__PURE__*/React.createElement("button", {
165
+ type: "button",
166
+ className: color && color !== defaultColor ? 'active color-menu-button' : 'color-menu-button',
167
+ onClick: () => editor.dispatchCommand(UPDATE_COLOR_COMMAND, color !== defaultColor ? defaultColor : color),
168
+ ref: colorMenuRef,
169
+ style: {
170
+ position: 'relative'
171
+ }
172
+ }, /*#__PURE__*/React.createElement("span", {
173
+ className: "icon",
174
+ style: {
175
+ color
176
+ }
177
+ }, "Q"), /*#__PURE__*/React.createElement(Popover, {
178
+ parentRef: colorMenuRef
179
+ }, /*#__PURE__*/React.createElement("div", {
180
+ className: "color-menu"
181
+ }, colorOptions.map(option => /*#__PURE__*/React.createElement("span", {
182
+ role: "button",
183
+ className: "icon color-option",
184
+ style: {
185
+ color: option
186
+ },
187
+ onClick: e => dispatchUpdateColor(e, option),
188
+ onKeyDown: e => dispatchUpdateColor(e, option),
189
+ tabIndex: 0,
190
+ key: option
191
+ }, "o"))))), /*#__PURE__*/React.createElement("button", {
128
192
  type: "button",
129
193
  className: isLink ? 'active' : undefined,
130
194
  onClick: () => formatLink()
@@ -13,7 +13,9 @@ $default-border: var(--theme-default-input-border);
13
13
  $focus-border: var(--theme-focus-input-border);
14
14
  $active-background: var(--hard-background-inputs);
15
15
  $default-background: var(--background-inputs);
16
+ $default-icon-color: var(--on-background-inputs);
16
17
  $disabled-background: var(--background-inputs-30);
18
+ $default-color-menu-padding: .5em .25em;
17
19
 
18
20
  .#{bem.$base}.editor-input {
19
21
  &.#{bem.$base}.input-wrapper {
@@ -29,6 +31,7 @@ $disabled-background: var(--background-inputs-30);
29
31
  > button {
30
32
  background: $default-background;
31
33
  border: $default-border;
34
+ color: $default-icon-color;
32
35
  padding: $default-padding;
33
36
 
34
37
  &.active {
@@ -60,6 +63,31 @@ $disabled-background: var(--background-inputs-30);
60
63
  }
61
64
  }
62
65
 
66
+ .color-menu-button {
67
+ &:hover {
68
+ > .#{bem.$base}.popover {
69
+ display: block;
70
+ }
71
+ }
72
+
73
+ > .#{bem.$base}.popover {
74
+ padding: $default-color-menu-padding;
75
+
76
+ .color-menu {
77
+ display: flex;
78
+ flex-wrap: wrap;
79
+ gap: calc($default-gap / 2);
80
+ justify-content: center;
81
+ max-width: 10em;
82
+ min-width: 5em;
83
+ }
84
+
85
+ .color-option:hover {
86
+ opacity: .5;
87
+ }
88
+ }
89
+ }
90
+
63
91
  > .content-editable {
64
92
  background: $default-background;
65
93
  border: $default-border;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pareto-engineering/design-system",
3
- "version": "4.0.0-alpha.41",
3
+ "version": "4.0.0-alpha.43",
4
4
  "description": "",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/es/index.js",
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@lexical/react": "^0.11.3",
54
- "@pareto-engineering/assets": "^4.0.0-alpha.40",
54
+ "@pareto-engineering/assets": "^4.0.0-alpha.43",
55
55
  "@pareto-engineering/bem": "^4.0.0-alpha.20",
56
56
  "@pareto-engineering/styles": "^4.0.0-alpha.39",
57
57
  "@pareto-engineering/utils": "^4.0.0-alpha.33",
@@ -70,5 +70,5 @@
70
70
  "relay-test-utils": "^15.0.0"
71
71
  },
72
72
  "browserslist": "> 2%",
73
- "gitHead": "d02bcd514202331d31637ec62653fb2ce4f33c93"
73
+ "gitHead": "7923a68d7f59211ebbac6f90efaaaf89f744fb17"
74
74
  }
@@ -22,7 +22,7 @@ export const Base = () => (
22
22
  config={{
23
23
  APP:{
24
24
  NAME :'Pareto',
25
- CANONICAL :'https://hellopareto.com',
25
+ CANONICAL :'https://pareto.ai',
26
26
  SUPPORT_EMAIL:'support@hellopareto.com',
27
27
  TITLE_SUFFIX :'| Pareto',
28
28
  },
@@ -32,7 +32,7 @@ export const Base = () => (
32
32
  TWITTER :'https://www.twitter.com/hellopareto',
33
33
  },
34
34
  EXTRA:{
35
- SURVEY:'https://survey.hellopareto.com',
35
+ SURVEY:'https://survey.pareto.ai',
36
36
  },
37
37
  }}
38
38
 
@@ -33,7 +33,7 @@ Base.args = {
33
33
  config:{
34
34
  SITE:{
35
35
  NAME :'Pareto',
36
- CANONICAL :'https://hellopareto.com',
36
+ CANONICAL :'https://pareto.ai',
37
37
  SUPPORT_EMAIL:'support@hellopareto.com',
38
38
  TITLE_SUFFIX :'| Pareto',
39
39
  },
@@ -44,7 +44,7 @@ Base.args = {
44
44
  LINKEDIN :'https://www.linkedin.com/company/hellopareto',
45
45
  },
46
46
  EXTRA:{
47
- SURVEY:'https://survey.hellopareto.com',
47
+ SURVEY:'https://survey.pareto.ai',
48
48
  },
49
49
  },
50
50
  }
@@ -51,6 +51,8 @@ const UI = [
51
51
  'transparent',
52
52
  'highlighted',
53
53
  'disabled',
54
+ 'ui-main',
55
+ 'ui-main-2',
54
56
  ]
55
57
 
56
58
  const SM = [
@@ -14,7 +14,7 @@ export default {
14
14
  decorators:[
15
15
  (storyfn) => (
16
16
  <Formik
17
- initialValues={{ feedback: '' }}
17
+ initialValues={{ instructions: '' }}
18
18
  >
19
19
  <Form>
20
20
  { storyfn() }