@visns-studio/visns-components 6.1.8 → 6.2.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.
@@ -0,0 +1,153 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { enumOptions } from '../reportSemantics';
4
+ import styles from '../../styles/ReportSemantic.module.scss';
5
+
6
+ const NUMERIC_TYPES = ['number', 'money', 'percent'];
7
+
8
+ /**
9
+ * A single value input typed by the semantic field type.
10
+ *
11
+ * enum -> select of the declared values, date/datetime -> date input,
12
+ * boolean -> yes/no select, money/number/percent -> numeric input,
13
+ * everything else -> text.
14
+ */
15
+ const SemanticValueInput = ({ type, field, value, onChange, ariaLabel }) => {
16
+ const commonProps = {
17
+ className: styles.input,
18
+ 'aria-label': ariaLabel,
19
+ value: value ?? '',
20
+ onChange: (event) => onChange(event.target.value),
21
+ };
22
+
23
+ if (type === 'enum') {
24
+ const options = enumOptions(field);
25
+ return (
26
+ <select
27
+ className={styles.select}
28
+ aria-label={ariaLabel}
29
+ value={value ?? ''}
30
+ onChange={(event) => onChange(event.target.value)}
31
+ >
32
+ <option value="">Choose a value…</option>
33
+ {options.map((option) => (
34
+ <option key={option.value} value={option.value}>
35
+ {option.label}
36
+ </option>
37
+ ))}
38
+ </select>
39
+ );
40
+ }
41
+
42
+ if (type === 'boolean') {
43
+ return (
44
+ <select
45
+ className={styles.select}
46
+ aria-label={ariaLabel}
47
+ value={value === '' || value == null ? '' : String(value)}
48
+ onChange={(event) => onChange(event.target.value)}
49
+ >
50
+ <option value="">Choose…</option>
51
+ <option value="1">Yes</option>
52
+ <option value="0">No</option>
53
+ </select>
54
+ );
55
+ }
56
+
57
+ if (type === 'date') {
58
+ return <input type="date" {...commonProps} />;
59
+ }
60
+
61
+ if (type === 'datetime') {
62
+ return <input type="datetime-local" {...commonProps} />;
63
+ }
64
+
65
+ if (NUMERIC_TYPES.includes(type)) {
66
+ return <input type="number" step="any" {...commonProps} />;
67
+ }
68
+
69
+ return <input type="text" {...commonProps} />;
70
+ };
71
+
72
+ SemanticValueInput.propTypes = {
73
+ type: PropTypes.string,
74
+ field: PropTypes.object,
75
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
76
+ onChange: PropTypes.func.isRequired,
77
+ ariaLabel: PropTypes.string,
78
+ };
79
+
80
+ SemanticValueInput.defaultProps = {
81
+ type: 'text',
82
+ field: null,
83
+ value: '',
84
+ ariaLabel: 'Value',
85
+ };
86
+
87
+ /**
88
+ * Multi-value editor for the `in` / `not_in` operators. Enum fields get a
89
+ * checkbox list of declared values; everything else gets a comma-separated
90
+ * text box, which is the least surprising control for a free-text list.
91
+ */
92
+ export const SemanticValueListInput = ({ type, field, values, onChange }) => {
93
+ const selected = Array.isArray(values) ? values : [];
94
+
95
+ if (type === 'enum') {
96
+ const options = enumOptions(field);
97
+ return (
98
+ <div className={styles.conditionValues}>
99
+ {options.map((option) => (
100
+ <label key={option.value} className={styles.fieldCheckbox}>
101
+ <input
102
+ type="checkbox"
103
+ checked={selected.includes(option.value)}
104
+ onChange={(event) =>
105
+ onChange(
106
+ event.target.checked
107
+ ? [...selected, option.value]
108
+ : selected.filter(
109
+ (entry) => entry !== option.value
110
+ )
111
+ )
112
+ }
113
+ />
114
+ <span>{option.label}</span>
115
+ </label>
116
+ ))}
117
+ </div>
118
+ );
119
+ }
120
+
121
+ return (
122
+ <input
123
+ type="text"
124
+ className={styles.input}
125
+ aria-label="Values, separated by commas"
126
+ placeholder="Separate values with commas"
127
+ value={selected.join(', ')}
128
+ onChange={(event) =>
129
+ onChange(
130
+ event.target.value
131
+ .split(',')
132
+ .map((entry) => entry.trim())
133
+ .filter((entry) => entry !== '')
134
+ )
135
+ }
136
+ />
137
+ );
138
+ };
139
+
140
+ SemanticValueListInput.propTypes = {
141
+ type: PropTypes.string,
142
+ field: PropTypes.object,
143
+ values: PropTypes.array,
144
+ onChange: PropTypes.func.isRequired,
145
+ };
146
+
147
+ SemanticValueListInput.defaultProps = {
148
+ type: 'text',
149
+ field: null,
150
+ values: [],
151
+ };
152
+
153
+ export default SemanticValueInput;