@pareto-engineering/design-system 5.0.5 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/a/Charts/AreaChart/AreaChart.js +3 -13
- package/dist/cjs/a/Charts/BarChart/BarChart.js +6 -4
- package/dist/cjs/a/Charts/Common/CustomLegend/CustomLegend.js +26 -7
- package/dist/cjs/a/Charts/Common/CustomLegend/styles.scss +41 -14
- package/dist/cjs/a/Charts/Common/CustomTooltipContent/CustomTooltipContent.js +18 -7
- package/dist/cjs/a/Charts/Common/YLabelsDropDown/YlabelsDropDown.js +3 -4
- package/dist/cjs/a/Charts/Common/YLabelsDropDown/styles.scss +7 -6
- package/dist/cjs/a/Charts/PieChart/PieChart.js +99 -0
- package/dist/cjs/a/Charts/PieChart/index.js +13 -0
- package/dist/cjs/a/Charts/PieChart/styles.scss +48 -0
- package/dist/cjs/a/Charts/index.js +8 -1
- package/dist/cjs/a/Notification/styles.scss +17 -5
- package/dist/cjs/a/index.js +6 -0
- package/dist/cjs/f/FormInput/FormInput.js +1 -1
- package/dist/cjs/f/fields/EditorInput/EditorInput.js +7 -3
- package/dist/cjs/f/fields/EditorInput/common/ExposePlainTextPlugin.js +40 -0
- package/dist/cjs/f/fields/EditorInput/common/ToolbarPlugin/ToolbarPlugin.js +1 -1
- package/dist/cjs/f/fields/EditorInput/common/index.js +7 -0
- package/dist/cjs/f/fields/LatexPreviewInput/LatexPreviewInput.js +1 -1
- package/dist/cjs/f/fields/LatexPreviewInput/styles.scss +1 -0
- package/dist/cjs/f/fields/TextareaInput/TextareaInput.js +4 -2
- package/dist/cjs/g/ExpandableLexicalPreview/ExpandableLexicalPreview.js +4 -2
- package/dist/cjs/g/ExpandableLexicalPreview/styles.scss +0 -1
- package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +27 -1
- package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +15 -0
- package/dist/cjs/g/FormBuilder/common/Builder/common/Section/Section.js +6 -2
- package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +6 -0
- package/dist/cjs/utils/applyCharacterLimit.js +75 -0
- package/dist/cjs/utils/formatting.js +10 -2
- package/dist/cjs/utils/index.js +14 -1
- package/dist/es/a/Charts/AreaChart/AreaChart.js +3 -13
- package/dist/es/a/Charts/BarChart/BarChart.js +6 -4
- package/dist/es/a/Charts/Common/CustomLegend/CustomLegend.js +38 -21
- package/dist/es/a/Charts/Common/CustomLegend/styles.scss +41 -14
- package/dist/es/a/Charts/Common/CustomTooltipContent/CustomTooltipContent.js +19 -8
- package/dist/es/a/Charts/Common/YLabelsDropDown/YlabelsDropDown.js +3 -5
- package/dist/es/a/Charts/Common/YLabelsDropDown/styles.scss +7 -6
- package/dist/es/a/Charts/PieChart/PieChart.js +89 -0
- package/dist/es/a/Charts/PieChart/index.js +1 -0
- package/dist/es/a/Charts/PieChart/styles.scss +48 -0
- package/dist/es/a/Charts/index.js +2 -1
- package/dist/es/a/Notification/styles.scss +17 -5
- package/dist/es/a/index.js +1 -1
- package/dist/es/f/FormInput/FormInput.js +1 -1
- package/dist/es/f/fields/EditorInput/EditorInput.js +8 -4
- package/dist/es/f/fields/EditorInput/common/ExposePlainTextPlugin.js +32 -0
- package/dist/es/f/fields/EditorInput/common/ToolbarPlugin/ToolbarPlugin.js +1 -1
- package/dist/es/f/fields/EditorInput/common/index.js +2 -1
- package/dist/es/f/fields/LatexPreviewInput/LatexPreviewInput.js +1 -1
- package/dist/es/f/fields/LatexPreviewInput/styles.scss +1 -0
- package/dist/es/f/fields/TextareaInput/TextareaInput.js +4 -2
- package/dist/es/g/ExpandableLexicalPreview/ExpandableLexicalPreview.js +4 -2
- package/dist/es/g/ExpandableLexicalPreview/styles.scss +0 -1
- package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +27 -1
- package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +15 -0
- package/dist/es/g/FormBuilder/common/Builder/common/Section/Section.js +6 -2
- package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +6 -0
- package/dist/es/utils/applyCharacterLimit.js +67 -0
- package/dist/es/utils/formatting.js +7 -0
- package/dist/es/utils/index.js +2 -1
- package/package.json +3 -3
- package/src/stories/a/AreaChart.stories.jsx +1 -1
- package/src/stories/a/BarChart.stories.jsx +1 -1
- package/src/stories/a/PieChart.stories.jsx +53 -0
- package/src/ui/a/Charts/AreaChart/AreaChart.jsx +8 -14
- package/src/ui/a/Charts/BarChart/BarChart.jsx +4 -2
- package/src/ui/a/Charts/Common/CustomLegend/CustomLegend.jsx +54 -29
- package/src/ui/a/Charts/Common/CustomLegend/styles.scss +41 -14
- package/src/ui/a/Charts/Common/CustomTooltipContent/CustomTooltipContent.jsx +25 -13
- package/src/ui/a/Charts/Common/YLabelsDropDown/YlabelsDropDown.jsx +4 -4
- package/src/ui/a/Charts/Common/YLabelsDropDown/styles.scss +7 -6
- package/src/ui/a/Charts/PieChart/PieChart.jsx +125 -0
- package/src/ui/a/Charts/PieChart/index.js +1 -0
- package/src/ui/a/Charts/PieChart/styles.scss +48 -0
- package/src/ui/a/Charts/index.js +1 -0
- package/src/ui/a/Notification/styles.scss +17 -5
- package/src/ui/a/index.js +1 -1
- package/src/ui/f/FormInput/FormInput.jsx +1 -0
- package/src/ui/f/fields/EditorInput/EditorInput.jsx +24 -9
- package/src/ui/f/fields/EditorInput/common/ExposePlainTextPlugin.jsx +42 -0
- package/src/ui/f/fields/EditorInput/common/ToolbarPlugin/ToolbarPlugin.jsx +1 -1
- package/src/ui/f/fields/EditorInput/common/index.js +1 -0
- package/src/ui/f/fields/LatexPreviewInput/LatexPreviewInput.jsx +1 -0
- package/src/ui/f/fields/LatexPreviewInput/styles.scss +1 -0
- package/src/ui/f/fields/TextareaInput/TextareaInput.jsx +2 -0
- package/src/ui/g/ExpandableLexicalPreview/ExpandableLexicalPreview.jsx +3 -3
- package/src/ui/g/ExpandableLexicalPreview/styles.scss +0 -1
- package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.jsx +34 -0
- package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +15 -0
- package/src/ui/g/FormBuilder/common/Builder/common/Section/Section.jsx +10 -2
- package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +5 -0
- package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +0 -1
- package/src/ui/utils/applyCharacterLimit.js +80 -0
- package/src/ui/utils/formatting.js +8 -0
- package/src/ui/utils/index.js +4 -1
- package/tests/__snapshots__/Storyshots.test.js.snap +1167 -447
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
PieChart as RechartsPieChart,
|
|
7
|
+
Pie,
|
|
8
|
+
Cell,
|
|
9
|
+
ResponsiveContainer,
|
|
10
|
+
Tooltip,
|
|
11
|
+
} from 'recharts'
|
|
12
|
+
|
|
13
|
+
import styleNames from '@pareto-engineering/bem/exports'
|
|
14
|
+
|
|
15
|
+
import { CustomLegend, CustomTooltipContent } from '../Common'
|
|
16
|
+
|
|
17
|
+
import './styles.scss'
|
|
18
|
+
|
|
19
|
+
const baseClassName = styleNames.base
|
|
20
|
+
|
|
21
|
+
const componentClassName = 'pie-chart'
|
|
22
|
+
|
|
23
|
+
const PieChart = ({
|
|
24
|
+
id,
|
|
25
|
+
className: userClassName,
|
|
26
|
+
data,
|
|
27
|
+
title,
|
|
28
|
+
valueKey,
|
|
29
|
+
labelKey,
|
|
30
|
+
colors,
|
|
31
|
+
height,
|
|
32
|
+
width,
|
|
33
|
+
innerRadius,
|
|
34
|
+
outerRadius,
|
|
35
|
+
}) => {
|
|
36
|
+
const total = data.reduce((sum, item) => sum + item[valueKey], 0)
|
|
37
|
+
|
|
38
|
+
const formattedData = data.map((item) => ({
|
|
39
|
+
...item,
|
|
40
|
+
label :item[labelKey],
|
|
41
|
+
color :colors[data.indexOf(item)],
|
|
42
|
+
percentage:((item[valueKey] / total) * 100).toFixed(0),
|
|
43
|
+
}))
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
id={id}
|
|
48
|
+
className={[
|
|
49
|
+
baseClassName,
|
|
50
|
+
componentClassName,
|
|
51
|
+
userClassName,
|
|
52
|
+
]
|
|
53
|
+
.filter((e) => e)
|
|
54
|
+
.join(' ')}
|
|
55
|
+
>
|
|
56
|
+
<div className="chart-header">
|
|
57
|
+
<h3>{title}</h3>
|
|
58
|
+
</div>
|
|
59
|
+
<div className="chart-content">
|
|
60
|
+
<ResponsiveContainer width={width} height={height}>
|
|
61
|
+
<RechartsPieChart>
|
|
62
|
+
<Pie
|
|
63
|
+
data={formattedData}
|
|
64
|
+
dataKey={valueKey}
|
|
65
|
+
nameKey={labelKey}
|
|
66
|
+
cx="50%"
|
|
67
|
+
cy="50%"
|
|
68
|
+
innerRadius={innerRadius}
|
|
69
|
+
outerRadius={outerRadius}
|
|
70
|
+
label={false}
|
|
71
|
+
paddingAngle={0}
|
|
72
|
+
>
|
|
73
|
+
{formattedData.map((entry, index) => (
|
|
74
|
+
<Cell
|
|
75
|
+
key={entry[labelKey]}
|
|
76
|
+
fill={colors[index]}
|
|
77
|
+
strokeWidth={0}
|
|
78
|
+
/>
|
|
79
|
+
))}
|
|
80
|
+
</Pie>
|
|
81
|
+
<Tooltip content={<CustomTooltipContent isDateValue={false} />} />
|
|
82
|
+
</RechartsPieChart>
|
|
83
|
+
</ResponsiveContainer>
|
|
84
|
+
<CustomLegend
|
|
85
|
+
colorsArray={colors}
|
|
86
|
+
yKeysArray={formattedData}
|
|
87
|
+
capitalizedLegend
|
|
88
|
+
orientation="vertical"
|
|
89
|
+
getLegendItemTitle={(entry) => entry[labelKey]}
|
|
90
|
+
getLegendItemSubtitle={(entry) => entry[valueKey]}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
PieChart.propTypes = {
|
|
98
|
+
id :PropTypes.string,
|
|
99
|
+
className:PropTypes.string,
|
|
100
|
+
data :PropTypes.arrayOf(PropTypes.shape({
|
|
101
|
+
[PropTypes.string]:PropTypes.oneOfType([
|
|
102
|
+
PropTypes.string,
|
|
103
|
+
PropTypes.number,
|
|
104
|
+
]),
|
|
105
|
+
})).isRequired,
|
|
106
|
+
title :PropTypes.string.isRequired,
|
|
107
|
+
valueKey :PropTypes.string.isRequired,
|
|
108
|
+
labelKey :PropTypes.string.isRequired,
|
|
109
|
+
colors :PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
110
|
+
height :PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
111
|
+
width :PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
112
|
+
innerRadius:PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
113
|
+
outerRadius:PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
PieChart.defaultProps = {
|
|
117
|
+
id :undefined,
|
|
118
|
+
className :undefined,
|
|
119
|
+
width :'100%',
|
|
120
|
+
height :300,
|
|
121
|
+
innerRadius:'0%',
|
|
122
|
+
outerRadius:'100%',
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default PieChart
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PieChart } from './PieChart'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
@use "@pareto-engineering/bem";
|
|
2
|
+
|
|
3
|
+
$default-margin: 1rem;
|
|
4
|
+
$default-padding: 1rem;
|
|
5
|
+
$default-box-shadow: 0 .25rem .75rem var(--ui-lines);
|
|
6
|
+
$default-text-font-size: calc(var(--s-1) * 1rem);
|
|
7
|
+
|
|
8
|
+
.#{bem.$base} {
|
|
9
|
+
&.pie-chart {
|
|
10
|
+
background-color: var(--background-far);
|
|
11
|
+
border-radius: var(--theme-default-border-radius);
|
|
12
|
+
box-shadow: $default-box-shadow;
|
|
13
|
+
margin: $default-margin 0;
|
|
14
|
+
padding: $default-padding;
|
|
15
|
+
|
|
16
|
+
.chart-header {
|
|
17
|
+
align-items: center;
|
|
18
|
+
display: flex;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
margin-bottom: $default-margin;
|
|
21
|
+
|
|
22
|
+
h3 {
|
|
23
|
+
color: var(--subtitle);
|
|
24
|
+
margin: calc($default-margin / 5);
|
|
25
|
+
text-align: left;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.chart-content {
|
|
30
|
+
align-items: flex-start;
|
|
31
|
+
display: flex;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.recharts-wrapper {
|
|
35
|
+
.recharts-surface {
|
|
36
|
+
.recharts-text {
|
|
37
|
+
fill: var(--soft-paragraph);
|
|
38
|
+
font-size: calc($default-text-font-size * .75);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.recharts-text.recharts-label {
|
|
42
|
+
fill: var(--paragraph);
|
|
43
|
+
font-size: $default-text-font-size;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/ui/a/Charts/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
@use "@pareto-engineering/styles/src/mixins";
|
|
5
5
|
@use "@pareto-engineering/styles/src/globals" as *;
|
|
6
6
|
|
|
7
|
+
$default-border: 1px solid var(--x);
|
|
7
8
|
$default-padding: 1rem;
|
|
8
9
|
$default-margin: 1rem;
|
|
9
10
|
$default-border-radius: 1.5rem;
|
|
@@ -12,10 +13,11 @@ $default-height: var(--notification-height, 5rem);
|
|
|
12
13
|
|
|
13
14
|
.#{bem.$base}.notification {
|
|
14
15
|
align-items: center;
|
|
15
|
-
background-color: var(--
|
|
16
|
+
background-color: var(--background-far);
|
|
17
|
+
border: $default-border;
|
|
16
18
|
border-radius: $default-border-radius;
|
|
17
19
|
bottom: 0;
|
|
18
|
-
color: var(--
|
|
20
|
+
color: var(--paragraph);
|
|
19
21
|
display: flex;
|
|
20
22
|
justify-content: space-between;
|
|
21
23
|
margin-bottom: $default-margin;
|
|
@@ -31,8 +33,16 @@ $default-height: var(--notification-height, 5rem);
|
|
|
31
33
|
> .message-container {
|
|
32
34
|
align-items: center;
|
|
33
35
|
display: flex;
|
|
36
|
+
gap: $default-padding;
|
|
34
37
|
overflow: auto;
|
|
35
38
|
|
|
39
|
+
> .icon {
|
|
40
|
+
background-color: var(--x);
|
|
41
|
+
border-radius: 50%;
|
|
42
|
+
color: var(--white);
|
|
43
|
+
padding: calc($default-padding / 2);
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
> .message {
|
|
37
47
|
font-size: calc(var(--s0) * 1rem);
|
|
38
48
|
margin-left: calc($default-margin / 2);
|
|
@@ -44,12 +54,14 @@ $default-height: var(--notification-height, 5rem);
|
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
.#{bem.$base}.button {
|
|
57
|
+
background-color: transparent;
|
|
58
|
+
color: var(--paragraph);
|
|
47
59
|
padding: calc($default-padding / 2);
|
|
48
60
|
|
|
49
|
-
&:focus
|
|
61
|
+
&:focus,
|
|
62
|
+
&:hover {
|
|
50
63
|
background-color: transparent;
|
|
64
|
+
color: var(--hard-paragraph);
|
|
51
65
|
}
|
|
52
66
|
}
|
|
53
67
|
}
|
|
54
|
-
|
|
55
|
-
|
package/src/ui/a/index.js
CHANGED
|
@@ -30,4 +30,4 @@ export { ToggleSwitch } from './ToggleSwitch'
|
|
|
30
30
|
export { XMLEditor } from './XMLEditor'
|
|
31
31
|
export { DatePicker } from './DatePicker'
|
|
32
32
|
export { Tooltip } from './Tooltip'
|
|
33
|
-
export { AreaChart, BarChart } from './Charts'
|
|
33
|
+
export { AreaChart, BarChart, PieChart } from './Charts'
|
|
@@ -26,7 +26,12 @@ import styleNames from '@pareto-engineering/bem/exports'
|
|
|
26
26
|
// Local Definitions
|
|
27
27
|
|
|
28
28
|
import { FormLabel, FormDescription } from '../../common'
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
ToolbarPlugin,
|
|
31
|
+
TreeViewPlugin,
|
|
32
|
+
StopPropagationPlugin,
|
|
33
|
+
ExposePlainTextPlugin,
|
|
34
|
+
} from './common'
|
|
30
35
|
|
|
31
36
|
import './styles.scss'
|
|
32
37
|
|
|
@@ -60,6 +65,7 @@ const EditorInput = ({
|
|
|
60
65
|
disabled,
|
|
61
66
|
showDebugger,
|
|
62
67
|
setEditorState,
|
|
68
|
+
setPlainTextKey,
|
|
63
69
|
// ...otherProps
|
|
64
70
|
}) => {
|
|
65
71
|
const [field, ,] = useField(name)
|
|
@@ -157,14 +163,17 @@ const EditorInput = ({
|
|
|
157
163
|
'--rows' :`${rows}em`,
|
|
158
164
|
}}
|
|
159
165
|
>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
{
|
|
167
|
+
label && (
|
|
168
|
+
<FormLabel
|
|
169
|
+
name={name}
|
|
170
|
+
color={labelColor}
|
|
171
|
+
optional={optional}
|
|
172
|
+
>
|
|
173
|
+
{label}
|
|
174
|
+
</FormLabel>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
168
177
|
{ !disabled && <ToolbarPlugin /> }
|
|
169
178
|
<RichTextPlugin
|
|
170
179
|
contentEditable={(
|
|
@@ -187,6 +196,12 @@ const EditorInput = ({
|
|
|
187
196
|
<StopPropagationPlugin />
|
|
188
197
|
<FormDescription className="s-1" description={description} name={name} />
|
|
189
198
|
{ showDebugger && <TreeViewPlugin /> }
|
|
199
|
+
{setPlainTextKey && (
|
|
200
|
+
<ExposePlainTextPlugin
|
|
201
|
+
setFieldValue={setFieldValue}
|
|
202
|
+
setPlainTextKey={setPlainTextKey}
|
|
203
|
+
/>
|
|
204
|
+
)}
|
|
190
205
|
</div>
|
|
191
206
|
</LexicalComposer>
|
|
192
207
|
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
$getRoot,
|
|
7
|
+
} from 'lexical'
|
|
8
|
+
|
|
9
|
+
import PropTypes from 'prop-types'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This is the component description
|
|
13
|
+
*/
|
|
14
|
+
const ExposePlainTextPlugin = ({
|
|
15
|
+
setFieldValue,
|
|
16
|
+
setPlainTextKey,
|
|
17
|
+
}) => {
|
|
18
|
+
const [editor] = useLexicalComposerContext()
|
|
19
|
+
|
|
20
|
+
useEffect(() => (
|
|
21
|
+
editor.registerUpdateListener(({ editorState }) => {
|
|
22
|
+
editorState.read(() => {
|
|
23
|
+
const root = $getRoot()
|
|
24
|
+
const textContent = root.getTextContent()
|
|
25
|
+
setFieldValue(setPlainTextKey, textContent)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
), [editor])
|
|
29
|
+
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ExposePlainTextPlugin.propTypes = {
|
|
34
|
+
setFieldValue :PropTypes.func.isRequired,
|
|
35
|
+
setPlainTextKey:PropTypes.string.isRequired,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ExposePlainTextPlugin.defaultProps = {
|
|
39
|
+
//
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default ExposePlainTextPlugin
|
|
@@ -4,3 +4,4 @@ export { default as StopPropagationPlugin } from './StopPropagationPlugin'
|
|
|
4
4
|
export { ToolbarPlugin } from './ToolbarPlugin'
|
|
5
5
|
export { ColorPicker } from './ColorPicker'
|
|
6
6
|
export { FontSizeDropDown } from './FontSizeDropDown'
|
|
7
|
+
export { default as ExposePlainTextPlugin } from './ExposePlainTextPlugin'
|
|
@@ -38,6 +38,7 @@ const TextareaInput = ({
|
|
|
38
38
|
placeholder,
|
|
39
39
|
autoComplete,
|
|
40
40
|
resize,
|
|
41
|
+
maxLength,
|
|
41
42
|
// ...otherProps
|
|
42
43
|
}) => {
|
|
43
44
|
const [field] = useField({ name, validate })
|
|
@@ -73,6 +74,7 @@ const TextareaInput = ({
|
|
|
73
74
|
rows={rows}
|
|
74
75
|
disabled={disabled}
|
|
75
76
|
autoComplete={autoComplete}
|
|
77
|
+
maxLength={maxLength}
|
|
76
78
|
>
|
|
77
79
|
{/* It was a dark and stormy night... */}
|
|
78
80
|
</textarea>
|
|
@@ -37,6 +37,7 @@ const ExpandableLexicalPreview = ({
|
|
|
37
37
|
onBlock,
|
|
38
38
|
onOpen,
|
|
39
39
|
header,
|
|
40
|
+
rows,
|
|
40
41
|
// ...otherProps
|
|
41
42
|
}) => {
|
|
42
43
|
const [isCollapsed, setIsCollapsed] = useState(false)
|
|
@@ -54,9 +55,7 @@ const ExpandableLexicalPreview = ({
|
|
|
54
55
|
<div
|
|
55
56
|
id={id}
|
|
56
57
|
className={[
|
|
57
|
-
|
|
58
58
|
baseClassName,
|
|
59
|
-
|
|
60
59
|
componentClassName,
|
|
61
60
|
userClassName,
|
|
62
61
|
`y-${color}`,
|
|
@@ -102,7 +101,8 @@ const ExpandableLexicalPreview = ({
|
|
|
102
101
|
name={name}
|
|
103
102
|
resize={resize}
|
|
104
103
|
disabled
|
|
105
|
-
id=
|
|
104
|
+
id={`${id}-editor-input`}
|
|
105
|
+
rows={rows}
|
|
106
106
|
/>
|
|
107
107
|
<Button
|
|
108
108
|
id={id}
|
|
@@ -70,6 +70,8 @@ const InputBuilder = ({
|
|
|
70
70
|
setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.showSpecificFileTypes`, !input.showSpecificFileTypes)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
const textChoices = ['text', 'number', 'textarea', 'latex-preview-input']
|
|
74
|
+
|
|
73
75
|
return (
|
|
74
76
|
<div
|
|
75
77
|
id={id}
|
|
@@ -179,6 +181,38 @@ const InputBuilder = ({
|
|
|
179
181
|
/>
|
|
180
182
|
)}
|
|
181
183
|
</div>
|
|
184
|
+
{textChoices.includes(input?.type) && (
|
|
185
|
+
<div className="character-limit-container">
|
|
186
|
+
<div className="is-required">
|
|
187
|
+
<span className="s0">
|
|
188
|
+
Limit number of characters permitted for this input
|
|
189
|
+
</span>
|
|
190
|
+
<ToggleSwitch
|
|
191
|
+
handleOnChange={() => {
|
|
192
|
+
setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.hasCharacterLimit`, !input?.hasCharacterLimit)
|
|
193
|
+
if (!input?.hasCharacterLimit) {
|
|
194
|
+
setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.maxLength`, '')
|
|
195
|
+
}
|
|
196
|
+
}}
|
|
197
|
+
checked={input?.hasCharacterLimit}
|
|
198
|
+
style={getToggleSwitchStyles(!input?.hasCharacterLimit)}
|
|
199
|
+
inputId={`sections_${sectionIndex}_inputs.${inputIndex}_character_limit_toggle`}
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
<div className="character-limit-input">
|
|
203
|
+
{input?.hasCharacterLimit && (
|
|
204
|
+
<TextInput
|
|
205
|
+
label="Enter the maximum number of characters permitted."
|
|
206
|
+
name={`sections.${sectionIndex}.inputs.${inputIndex}.maxLength`}
|
|
207
|
+
placeholder=""
|
|
208
|
+
type="number"
|
|
209
|
+
validate={integerAndGreaterThanZero}
|
|
210
|
+
value={input?.maxLength}
|
|
211
|
+
/>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
182
216
|
{shouldRenderOptions && (
|
|
183
217
|
<FieldArray name={`sections.${sectionIndex}.inputs.${inputIndex}.options`}>
|
|
184
218
|
{({ push, remove }) => (
|
|
@@ -64,6 +64,21 @@ $default-list-width: var(--action-button-width, 18rem);
|
|
|
64
64
|
margin-bottom: $default-margin;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
> .character-limit-container {
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
gap: var(--gap);
|
|
71
|
+
|
|
72
|
+
> .is-required {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: row;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
> .character-limit-input {
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
67
82
|
> .input-options {
|
|
68
83
|
display: flex;
|
|
69
84
|
flex-direction: column;
|
|
@@ -62,7 +62,13 @@ const Section = ({
|
|
|
62
62
|
const { inputs } = section
|
|
63
63
|
|
|
64
64
|
useEffect(() => {
|
|
65
|
-
const
|
|
65
|
+
const orderMap = new Map()
|
|
66
|
+
section.orderedInputDragIds.forEach(
|
|
67
|
+
(identifier, indx) => orderMap.set(identifier, indx),
|
|
68
|
+
)
|
|
69
|
+
const orderedInputs = inputs.sort((a, b) => orderMap.get(a.name) - orderMap.get(b.name))
|
|
70
|
+
|
|
71
|
+
const items = orderedInputs.map((input, indx) => ({
|
|
66
72
|
Content:<InputBuilder
|
|
67
73
|
key={input.name}
|
|
68
74
|
sectionIndex={index}
|
|
@@ -75,6 +81,8 @@ const Section = ({
|
|
|
75
81
|
setDraggableInputs(items)
|
|
76
82
|
}, [inputs.length, index])
|
|
77
83
|
|
|
84
|
+
const dragAndDropKey = draggableInputs.map((e) => e.identifier).join(',')
|
|
85
|
+
|
|
78
86
|
return (
|
|
79
87
|
<div
|
|
80
88
|
id={id}
|
|
@@ -128,7 +136,7 @@ const Section = ({
|
|
|
128
136
|
const ids = reOrderedItems.map((e) => e.identifier)
|
|
129
137
|
setFieldValue(`sections.${index}.orderedInputDragIds`, ids)
|
|
130
138
|
}}
|
|
131
|
-
key={
|
|
139
|
+
key={dragAndDropKey}
|
|
132
140
|
/>
|
|
133
141
|
<button
|
|
134
142
|
type="button"
|
|
@@ -15,6 +15,7 @@ import './styles.scss'
|
|
|
15
15
|
|
|
16
16
|
// Local Definitions
|
|
17
17
|
|
|
18
|
+
import { applyCharacterLimit } from 'ui/utils'
|
|
18
19
|
import { Section } from './common'
|
|
19
20
|
|
|
20
21
|
const baseClassName = styleNames.base
|
|
@@ -165,6 +166,10 @@ const Renderer = ({
|
|
|
165
166
|
}
|
|
166
167
|
}, [values])
|
|
167
168
|
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
applyCharacterLimit({ setMaxLength: false })
|
|
171
|
+
}, [values])
|
|
172
|
+
|
|
168
173
|
const hasErrors = Object.keys(errors).length > 0
|
|
169
174
|
return (
|
|
170
175
|
<Form>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const applyCharacterLimit = (setMaxLength = true) => {
|
|
2
|
+
setTimeout(() => {
|
|
3
|
+
document.querySelectorAll("[class*='limit-character-count-']").forEach((parent) => {
|
|
4
|
+
const match = parent.className.match(/limit-character-count-(\d+)/)
|
|
5
|
+
if (!match) return
|
|
6
|
+
|
|
7
|
+
const maxLength = parseInt(match[1], 10)
|
|
8
|
+
|
|
9
|
+
const fields = parent.matches("input[type='text'], input[type='email'], input[type='password'], input[type='search'], textarea")
|
|
10
|
+
? [parent]
|
|
11
|
+
: Array.from(parent.querySelectorAll("input[type='text'], input[type='email'], input[type='password'], input[type='search'], textarea"))
|
|
12
|
+
|
|
13
|
+
if (!fields.length) return
|
|
14
|
+
|
|
15
|
+
const characterCounterBaseClassName = 's-2 character-counter'
|
|
16
|
+
const characterCounterClassName = `${characterCounterBaseClassName} x-paragraph c-x`
|
|
17
|
+
const characterCounterWarningClassName = `${characterCounterBaseClassName} x-orange c-x`
|
|
18
|
+
const characterCounterErrorClassName = `${characterCounterBaseClassName} x-error c-x`
|
|
19
|
+
|
|
20
|
+
fields.forEach((field) => {
|
|
21
|
+
if (field.parentNode.querySelector('.character-counter')) return
|
|
22
|
+
|
|
23
|
+
if (setMaxLength) {
|
|
24
|
+
field.setAttribute('maxlength', maxLength)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const counter = document.createElement('span')
|
|
28
|
+
counter.className = characterCounterClassName
|
|
29
|
+
counter.style.position = 'absolute'
|
|
30
|
+
counter.style.right = '10px'
|
|
31
|
+
counter.style.bottom = '-20px'
|
|
32
|
+
counter.style.pointerEvents = 'none'
|
|
33
|
+
|
|
34
|
+
let wrapper
|
|
35
|
+
const { parentNode } = field
|
|
36
|
+
const computedStyle = window.getComputedStyle(parentNode)
|
|
37
|
+
if ((parentNode.style && parentNode.style.position === 'relative') || computedStyle.getPropertyValue('position') === 'relative') {
|
|
38
|
+
wrapper = parentNode
|
|
39
|
+
} else {
|
|
40
|
+
wrapper = document.createElement('div')
|
|
41
|
+
wrapper.style.position = 'relative'
|
|
42
|
+
wrapper.style.display = 'inline-block'
|
|
43
|
+
wrapper.style.width = '100%'
|
|
44
|
+
field.parentNode.insertBefore(wrapper, field)
|
|
45
|
+
wrapper.appendChild(field)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
wrapper.appendChild(counter)
|
|
49
|
+
|
|
50
|
+
function updateCounter() {
|
|
51
|
+
if (field.value.length > maxLength) {
|
|
52
|
+
// eslint-disable-next-line no-param-reassign
|
|
53
|
+
field.value = field.value.substring(0, maxLength)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { length } = field.value
|
|
57
|
+
counter.textContent = `${length}/${maxLength}`
|
|
58
|
+
|
|
59
|
+
if (length >= maxLength) {
|
|
60
|
+
counter.className = characterCounterErrorClassName
|
|
61
|
+
} else if (length >= maxLength * 0.9) {
|
|
62
|
+
counter.className = characterCounterWarningClassName
|
|
63
|
+
} else {
|
|
64
|
+
counter.className = characterCounterClassName
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
field.removeEventListener('input', updateCounter)
|
|
69
|
+
field.removeEventListener('paste', updateCounter)
|
|
70
|
+
|
|
71
|
+
field.addEventListener('input', updateCounter)
|
|
72
|
+
field.addEventListener('paste', () => {
|
|
73
|
+
setTimeout(updateCounter, 0)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
updateCounter()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
}, 100) // Small delay to ensure DOM is ready
|
|
80
|
+
}
|
|
@@ -46,6 +46,9 @@ export const formatTime = (seconds) => {
|
|
|
46
46
|
if (minutes > 0 || (hours > 0 && remainingSeconds > 0)) parts.push(`${minutes}m`)
|
|
47
47
|
if (remainingSeconds > 0) parts.push(`${remainingSeconds}s`)
|
|
48
48
|
|
|
49
|
+
// Cater for decimal seconds
|
|
50
|
+
if (parts.length === 0) return '0s'
|
|
51
|
+
|
|
49
52
|
return parts.join(' ')
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -123,3 +126,8 @@ export const formatDate = (input, format = DATE_FORMATS.HUMAN_READABLE) => {
|
|
|
123
126
|
return 'Invalid Date'
|
|
124
127
|
}
|
|
125
128
|
}
|
|
129
|
+
|
|
130
|
+
export const snakeCaseToTitleCase = (word) => {
|
|
131
|
+
const result = word.replace(/([A-Z])/g, ' $1')
|
|
132
|
+
return result.charAt(0).toUpperCase() + result.slice(1)
|
|
133
|
+
}
|
package/src/ui/utils/index.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export { useWindowSize, useDynamicPosition, useOutsideClick } from './hooks'
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
formatTime, formatDate, DATE_FORMATS, snakeCaseToTitleCase,
|
|
4
|
+
} from './formatting'
|
|
5
|
+
export { applyCharacterLimit } from './applyCharacterLimit'
|