@xeplr/ui-utils 1.0.0 → 1.0.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/package.json +22 -6
- package/src/confirm/confirm.js +106 -0
- package/src/dataImport/dataImport.js +32 -0
- package/src/dataImport/designs/DataImportSample.jsx +71 -0
- package/src/dataImport/designs/ImportHistorySample.jsx +130 -0
- package/src/dataImport/designs/dataImport.css +98 -0
- package/src/dataImport/designs/index.js +2 -0
- package/src/dataImport/pages.jsx +17 -0
- package/src/dataImport/useDataImportController.js +94 -0
- package/src/dataImport/useImportHistoryController.js +67 -0
- package/src/dataImport/validateDesign.js +49 -0
- package/src/dateField/dateField.js +71 -0
- package/src/dateField/designs/DateFieldSample.jsx +30 -0
- package/src/dateField/designs/dateField.css +60 -0
- package/src/dateField/designs/index.js +1 -0
- package/src/dateField/pages.jsx +10 -0
- package/src/dateField/useDateFieldController.js +77 -0
- package/src/dateField/validateDesign.js +3 -0
- package/src/dropdown/designs/DropdownSample.jsx +151 -0
- package/src/dropdown/designs/dropdown.css +226 -0
- package/src/dropdown/designs/index.js +1 -0
- package/src/dropdown/dropdown.js +75 -0
- package/src/dropdown/pages.jsx +10 -0
- package/src/dropdown/useDropdownController.js +254 -0
- package/src/dropdown/validateDesign.js +4 -0
- package/src/gridDisplayer/designs/GridDisplayerSample.jsx +58 -0
- package/src/gridDisplayer/designs/gridDisplayer.css +89 -0
- package/src/gridDisplayer/designs/index.js +1 -0
- package/src/gridDisplayer/gridDisplayer.js +44 -0
- package/src/gridDisplayer/pages.jsx +10 -0
- package/src/gridDisplayer/useGridDisplayerController.js +59 -0
- package/src/gridDisplayer/validateDesign.js +4 -0
- package/src/index.js +115 -1
- package/src/numberField/designs/NumberFieldSample.jsx +52 -0
- package/src/numberField/designs/index.js +1 -0
- package/src/numberField/designs/numberField.css +66 -0
- package/src/numberField/numberField.js +105 -0
- package/src/numberField/pages.jsx +10 -0
- package/src/numberField/useNumberFieldController.js +116 -0
- package/src/numberField/validateDesign.js +3 -0
- package/src/snackbar/snackbar.js +6 -3
- package/src/theme/default.theme.json +178 -0
- package/src/theme/index.js +75 -0
package/src/index.js
CHANGED
|
@@ -10,12 +10,126 @@ export { useDesignValidator, FILE_UPLOAD_RULES } from './fileUpload/validateDesi
|
|
|
10
10
|
// File Upload — sample design
|
|
11
11
|
export { FileUploadSample } from './fileUpload/designs/index.js';
|
|
12
12
|
|
|
13
|
-
// File Upload — ready-made page
|
|
13
|
+
// File Upload — ready-made page
|
|
14
14
|
export { FileUploadPage } from './fileUpload/pages.jsx';
|
|
15
15
|
|
|
16
|
+
// DataImport — model. Upload a file, map its parts (e.g. Excel sheets — or
|
|
17
|
+
// the whole file, for CSV/JSON) to app-defined named targets, commit. The
|
|
18
|
+
// app supplies onInspect/onCommit (its own upload + load endpoints); this
|
|
19
|
+
// module only owns the file-pick -> mapping -> commit flow and UI.
|
|
20
|
+
export { sanitizeTargetName, buildInitialMappings } from './dataImport/dataImport.js';
|
|
21
|
+
|
|
22
|
+
// DataImport — controller
|
|
23
|
+
export { useDataImportController } from './dataImport/useDataImportController.js';
|
|
24
|
+
|
|
25
|
+
// DataImport — design validation
|
|
26
|
+
export { DATA_IMPORT_RULES } from './dataImport/validateDesign.js';
|
|
27
|
+
|
|
28
|
+
// DataImport — sample design
|
|
29
|
+
export { DataImportSample } from './dataImport/designs/index.js';
|
|
30
|
+
|
|
31
|
+
// DataImport — ready-made page
|
|
32
|
+
export { DataImportPage } from './dataImport/pages.jsx';
|
|
33
|
+
|
|
34
|
+
// ImportHistory — lists past import jobs (app-supplied onList) with a
|
|
35
|
+
// Rollback action per row (app-supplied onRollback) — the read/undo side of
|
|
36
|
+
// DataImportPage's write side, same file/model. Product-agnostic: the app
|
|
37
|
+
// owns what "list" and "rollback" actually call.
|
|
38
|
+
export { useImportHistoryController } from './dataImport/useImportHistoryController.js';
|
|
39
|
+
export { IMPORT_HISTORY_RULES } from './dataImport/validateDesign.js';
|
|
40
|
+
export { ImportHistorySample } from './dataImport/designs/index.js';
|
|
41
|
+
export { ImportHistoryPage } from './dataImport/pages.jsx';
|
|
42
|
+
|
|
43
|
+
// NumberField — model
|
|
44
|
+
export {
|
|
45
|
+
NUMBER_TYPES,
|
|
46
|
+
sanitizeNumberInput,
|
|
47
|
+
toNumeric,
|
|
48
|
+
formatDisplay,
|
|
49
|
+
validateRange,
|
|
50
|
+
resolveAffixState
|
|
51
|
+
} from './numberField/numberField.js';
|
|
52
|
+
|
|
53
|
+
// NumberField — controller
|
|
54
|
+
export { useNumberFieldController } from './numberField/useNumberFieldController.js';
|
|
55
|
+
|
|
56
|
+
// NumberField — design validation
|
|
57
|
+
export { NUMBER_FIELD_RULES } from './numberField/validateDesign.js';
|
|
58
|
+
|
|
59
|
+
// NumberField — sample design
|
|
60
|
+
export { NumberFieldSample } from './numberField/designs/index.js';
|
|
61
|
+
|
|
62
|
+
// NumberField — ready-made page
|
|
63
|
+
export { NumberFieldPage } from './numberField/pages.jsx';
|
|
64
|
+
|
|
65
|
+
// DateField — model
|
|
66
|
+
export {
|
|
67
|
+
DATE_TYPES,
|
|
68
|
+
isValidISODate,
|
|
69
|
+
calculateAge,
|
|
70
|
+
inputTypeFor,
|
|
71
|
+
toInputValue,
|
|
72
|
+
validateBounds
|
|
73
|
+
} from './dateField/dateField.js';
|
|
74
|
+
|
|
75
|
+
// DateField — controller
|
|
76
|
+
export { useDateFieldController } from './dateField/useDateFieldController.js';
|
|
77
|
+
|
|
78
|
+
// DateField — design validation
|
|
79
|
+
export { DATE_FIELD_RULES } from './dateField/validateDesign.js';
|
|
80
|
+
|
|
81
|
+
// DateField — sample design
|
|
82
|
+
export { DateFieldSample } from './dateField/designs/index.js';
|
|
83
|
+
|
|
84
|
+
// DateField — ready-made page
|
|
85
|
+
export { DateFieldPage } from './dateField/pages.jsx';
|
|
86
|
+
|
|
87
|
+
// Dropdown — model
|
|
88
|
+
export {
|
|
89
|
+
DROPDOWN_MODES,
|
|
90
|
+
normalizeKeys,
|
|
91
|
+
sortByKey,
|
|
92
|
+
filterByQuery,
|
|
93
|
+
pickSelected
|
|
94
|
+
} from './dropdown/dropdown.js';
|
|
95
|
+
|
|
96
|
+
// Dropdown — controller
|
|
97
|
+
export { useDropdownController } from './dropdown/useDropdownController.js';
|
|
98
|
+
|
|
99
|
+
// Dropdown — design validation
|
|
100
|
+
export { DROPDOWN_RULES } from './dropdown/validateDesign.js';
|
|
101
|
+
|
|
102
|
+
// Dropdown — sample design
|
|
103
|
+
export { DropdownSample } from './dropdown/designs/index.js';
|
|
104
|
+
|
|
105
|
+
// Dropdown — ready-made page
|
|
106
|
+
export { DropdownPage } from './dropdown/pages.jsx';
|
|
107
|
+
|
|
108
|
+
// GridDisplayer — model
|
|
109
|
+
export { filterRows as filterGridRows, sortRows as sortGridRows } from './gridDisplayer/gridDisplayer.js';
|
|
110
|
+
|
|
111
|
+
// GridDisplayer — controller
|
|
112
|
+
export { useGridDisplayerController } from './gridDisplayer/useGridDisplayerController.js';
|
|
113
|
+
|
|
114
|
+
// GridDisplayer — design validation
|
|
115
|
+
export { GRID_DISPLAYER_RULES } from './gridDisplayer/validateDesign.js';
|
|
116
|
+
|
|
117
|
+
// GridDisplayer — sample design
|
|
118
|
+
export { GridDisplayerSample } from './gridDisplayer/designs/index.js';
|
|
119
|
+
|
|
120
|
+
// GridDisplayer — ready-made page
|
|
121
|
+
export { GridDisplayerPage } from './gridDisplayer/pages.jsx';
|
|
122
|
+
|
|
16
123
|
// Snackbar
|
|
17
124
|
export { raiseSnackbar } from './snackbar/snackbar.js';
|
|
18
125
|
|
|
126
|
+
// Confirm
|
|
127
|
+
export { raiseConfirm } from './confirm/confirm.js';
|
|
128
|
+
|
|
129
|
+
// Theme — brand-neutral DEFAULT design tokens (chart + table), override-friendly.
|
|
130
|
+
// Consumers pass overrides to resolveTheme(); overrides win, rest falls through.
|
|
131
|
+
export { default as DEFAULT_THEME, resolveTheme, getThemeVariant, getChartOptions, deepMerge } from './theme/index.js';
|
|
132
|
+
|
|
19
133
|
// Static data
|
|
20
134
|
export { default as COUNTRIES } from './data/countries.json';
|
|
21
135
|
import _statesData from './data/states.json';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import './numberField.css';
|
|
2
|
+
|
|
3
|
+
export default function NumberFieldSample({
|
|
4
|
+
type, raw, error, placeholder, disabled, affix,
|
|
5
|
+
handleNumberChange, handleAffixChange, handleBlur
|
|
6
|
+
}) {
|
|
7
|
+
function renderAffix() {
|
|
8
|
+
if (!affix) return null;
|
|
9
|
+
if (affix.list.length === 1) {
|
|
10
|
+
return <span className="xeplr-numberfield-affix-static">{affix.selected[affix.labelKey]}</span>;
|
|
11
|
+
}
|
|
12
|
+
return (
|
|
13
|
+
<select
|
|
14
|
+
className="xeplr-numberfield-affix-select"
|
|
15
|
+
value={affix.selected[affix.valueKey]}
|
|
16
|
+
onChange={handleAffixChange}
|
|
17
|
+
disabled={disabled}
|
|
18
|
+
>
|
|
19
|
+
{affix.list.map(function(item) {
|
|
20
|
+
return (
|
|
21
|
+
<option key={item[affix.valueKey]} value={item[affix.valueKey]}>
|
|
22
|
+
{item[affix.labelKey]}
|
|
23
|
+
</option>
|
|
24
|
+
);
|
|
25
|
+
})}
|
|
26
|
+
</select>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var affixBefore = affix && affix.position === 'before';
|
|
31
|
+
var affixAfter = affix && affix.position === 'after';
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={'xeplr-numberfield xeplr-numberfield-' + type + (error ? ' xeplr-numberfield-error' : '')}>
|
|
35
|
+
<div className="xeplr-numberfield-row">
|
|
36
|
+
{affixBefore && renderAffix()}
|
|
37
|
+
<input
|
|
38
|
+
type="text"
|
|
39
|
+
inputMode="decimal"
|
|
40
|
+
className="xeplr-numberfield-input"
|
|
41
|
+
value={raw}
|
|
42
|
+
placeholder={placeholder}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
onChange={handleNumberChange}
|
|
45
|
+
onBlur={handleBlur}
|
|
46
|
+
/>
|
|
47
|
+
{affixAfter && renderAffix()}
|
|
48
|
+
</div>
|
|
49
|
+
{error && <div className="xeplr-numberfield-error-text">{error}</div>}
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as NumberFieldSample } from './NumberFieldSample.jsx';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
.xeplr-numberfield {
|
|
2
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
3
|
+
color: #e0e0e0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.xeplr-numberfield-row {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: stretch;
|
|
9
|
+
border: 1px solid #444;
|
|
10
|
+
border-radius: 6px;
|
|
11
|
+
background: #1a1a1a;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.xeplr-numberfield-row:focus-within {
|
|
16
|
+
border-color: #646cff;
|
|
17
|
+
box-shadow: 0 0 0 2px rgba(100, 108, 255, 0.18);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.xeplr-numberfield-error .xeplr-numberfield-row {
|
|
21
|
+
border-color: #e25555;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.xeplr-numberfield-input {
|
|
25
|
+
flex: 1;
|
|
26
|
+
background: transparent;
|
|
27
|
+
border: none;
|
|
28
|
+
outline: none;
|
|
29
|
+
color: inherit;
|
|
30
|
+
padding: 9px 12px;
|
|
31
|
+
font-size: 14px;
|
|
32
|
+
min-width: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.xeplr-numberfield-affix-select,
|
|
36
|
+
.xeplr-numberfield-affix-static {
|
|
37
|
+
background: #232540;
|
|
38
|
+
border: none;
|
|
39
|
+
color: #b8bbe0;
|
|
40
|
+
padding: 0 12px;
|
|
41
|
+
font-size: 13px;
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
outline: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.xeplr-numberfield-affix-static {
|
|
49
|
+
cursor: default;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.xeplr-numberfield-row > .xeplr-numberfield-affix-select:first-child,
|
|
53
|
+
.xeplr-numberfield-row > .xeplr-numberfield-affix-static:first-child {
|
|
54
|
+
border-right: 1px solid #444;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.xeplr-numberfield-row > .xeplr-numberfield-affix-select:last-child,
|
|
58
|
+
.xeplr-numberfield-row > .xeplr-numberfield-affix-static:last-child {
|
|
59
|
+
border-left: 1px solid #444;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.xeplr-numberfield-error-text {
|
|
63
|
+
margin-top: 4px;
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
color: #e25555;
|
|
66
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NumberField — pure model. No React.
|
|
3
|
+
*
|
|
4
|
+
* Handles the parsing, formatting, and validation of numeric input.
|
|
5
|
+
* Three flavours of value: float, currency, units. Currency and units
|
|
6
|
+
* are numerically the same — they only differ by the affix shown next
|
|
7
|
+
* to the number (a dropdown of currencies or a dropdown of units).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export var NUMBER_TYPES = ['float', 'currency', 'units'];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Strip everything that isn't a digit, decimal point, or leading minus.
|
|
14
|
+
* Collapses multiple decimal points to the first one.
|
|
15
|
+
*/
|
|
16
|
+
export function sanitizeNumberInput(raw) {
|
|
17
|
+
if (raw === null || raw === undefined) return '';
|
|
18
|
+
var str = String(raw);
|
|
19
|
+
var negative = str.charAt(0) === '-';
|
|
20
|
+
str = str.replace(/[^0-9.]/g, '');
|
|
21
|
+
var parts = str.split('.');
|
|
22
|
+
if (parts.length > 2) {
|
|
23
|
+
str = parts[0] + '.' + parts.slice(1).join('');
|
|
24
|
+
}
|
|
25
|
+
return (negative ? '-' : '') + str;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Convert a raw string to a Number. Returns null if not a finite number.
|
|
30
|
+
*/
|
|
31
|
+
export function toNumeric(raw) {
|
|
32
|
+
if (raw === null || raw === undefined || raw === '' || raw === '-') return null;
|
|
33
|
+
var n = Number(raw);
|
|
34
|
+
return isFinite(n) ? n : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Format a numeric value for display. Locale-aware grouping.
|
|
39
|
+
*/
|
|
40
|
+
export function formatDisplay(value, decimals) {
|
|
41
|
+
if (value === null || value === undefined || value === '') return '';
|
|
42
|
+
var n = Number(value);
|
|
43
|
+
if (!isFinite(n)) return '';
|
|
44
|
+
if (typeof decimals === 'number') {
|
|
45
|
+
return n.toLocaleString(undefined, {
|
|
46
|
+
minimumFractionDigits: decimals,
|
|
47
|
+
maximumFractionDigits: decimals
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return n.toLocaleString();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validate a numeric value against optional min/max bounds.
|
|
55
|
+
* Returns { valid: true } or { valid: false, error: string }.
|
|
56
|
+
*/
|
|
57
|
+
export function validateRange(value, min, max) {
|
|
58
|
+
if (value === null) return { valid: true };
|
|
59
|
+
if (typeof min === 'number' && value < min) {
|
|
60
|
+
return { valid: false, error: 'Value must be at least ' + min };
|
|
61
|
+
}
|
|
62
|
+
if (typeof max === 'number' && value > max) {
|
|
63
|
+
return { valid: false, error: 'Value must be at most ' + max };
|
|
64
|
+
}
|
|
65
|
+
return { valid: true };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the affix list and currently-selected affix for a given type.
|
|
70
|
+
* Falls back to a sensible default when the user supplied only one item.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveAffixState(type, options) {
|
|
73
|
+
options = options || {};
|
|
74
|
+
if (type === 'currency') {
|
|
75
|
+
var currencies = options.currencies || [];
|
|
76
|
+
if (currencies.length === 0) {
|
|
77
|
+
throw new Error('[xeplr-ui-utils:NumberField] type="currency" requires a `currencies` prop with at least one entry like { code: "INR", symbol: "₹" }');
|
|
78
|
+
}
|
|
79
|
+
var defaultCode = options.defaultCurrency || currencies[0].code;
|
|
80
|
+
var selected = currencies.find(function(c) { return c.code === defaultCode; }) || currencies[0];
|
|
81
|
+
return {
|
|
82
|
+
list: currencies,
|
|
83
|
+
selected: selected,
|
|
84
|
+
position: options.currencyPosition || 'before',
|
|
85
|
+
labelKey: 'symbol',
|
|
86
|
+
valueKey: 'code'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (type === 'units') {
|
|
90
|
+
var units = options.units || [];
|
|
91
|
+
if (units.length === 0) {
|
|
92
|
+
throw new Error('[xeplr-ui-utils:NumberField] type="units" requires a `units` prop with at least one entry like { value: "kg", label: "kg" }');
|
|
93
|
+
}
|
|
94
|
+
var defaultValue = options.defaultUnit || units[0].value;
|
|
95
|
+
var selectedUnit = units.find(function(u) { return u.value === defaultValue; }) || units[0];
|
|
96
|
+
return {
|
|
97
|
+
list: units,
|
|
98
|
+
selected: selectedUnit,
|
|
99
|
+
position: options.unitPosition || 'after',
|
|
100
|
+
labelKey: 'label',
|
|
101
|
+
valueKey: 'value'
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useNumberFieldController } from './useNumberFieldController.js';
|
|
2
|
+
import NumberFieldSample from './designs/NumberFieldSample.jsx';
|
|
3
|
+
import { useDesignValidator } from '../fileUpload/validateDesign.js';
|
|
4
|
+
import { NUMBER_FIELD_RULES } from './validateDesign.js';
|
|
5
|
+
|
|
6
|
+
export function NumberFieldPage(props) {
|
|
7
|
+
var controller = useNumberFieldController(props);
|
|
8
|
+
var ref = useDesignValidator('NumberFieldPage', NUMBER_FIELD_RULES);
|
|
9
|
+
return <div ref={ref}><NumberFieldSample {...controller} /></div>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
sanitizeNumberInput,
|
|
4
|
+
toNumeric,
|
|
5
|
+
formatDisplay,
|
|
6
|
+
validateRange,
|
|
7
|
+
resolveAffixState,
|
|
8
|
+
NUMBER_TYPES
|
|
9
|
+
} from './numberField.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {object} props
|
|
13
|
+
* @param {'float'|'currency'|'units'} [props.type='float']
|
|
14
|
+
* @param {number|null} [props.value] — controlled numeric value
|
|
15
|
+
* @param {number|null} [props.defaultValue] — uncontrolled initial value
|
|
16
|
+
* @param {(value, meta) => void} [props.onChange] — called with (numericValue, { affix })
|
|
17
|
+
* @param {Array} [props.currencies] — for currency type: [{ code, symbol }]
|
|
18
|
+
* @param {string} [props.defaultCurrency]
|
|
19
|
+
* @param {'before'|'after'} [props.currencyPosition='before']
|
|
20
|
+
* @param {Array} [props.units] — for units type: [{ value, label }]
|
|
21
|
+
* @param {string} [props.defaultUnit]
|
|
22
|
+
* @param {'before'|'after'} [props.unitPosition='after']
|
|
23
|
+
* @param {number} [props.min]
|
|
24
|
+
* @param {number} [props.max]
|
|
25
|
+
* @param {number} [props.decimals] — display decimals on blur
|
|
26
|
+
* @param {string} [props.placeholder]
|
|
27
|
+
* @param {boolean} [props.disabled]
|
|
28
|
+
*/
|
|
29
|
+
export function useNumberFieldController(props) {
|
|
30
|
+
props = props || {};
|
|
31
|
+
var type = props.type || 'float';
|
|
32
|
+
if (NUMBER_TYPES.indexOf(type) === -1) {
|
|
33
|
+
throw new Error('[xeplr-ui-utils:NumberField] Unknown type: ' + type + '. Expected one of ' + NUMBER_TYPES.join(', '));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var affixInit = useMemo(function() {
|
|
37
|
+
return resolveAffixState(type, props);
|
|
38
|
+
}, [
|
|
39
|
+
type,
|
|
40
|
+
props.currencies, props.defaultCurrency, props.currencyPosition,
|
|
41
|
+
props.units, props.defaultUnit, props.unitPosition
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
var isControlled = props.value !== undefined;
|
|
45
|
+
var initial = isControlled ? props.value : (props.defaultValue !== undefined ? props.defaultValue : null);
|
|
46
|
+
|
|
47
|
+
var [raw, setRaw] = useState(initial === null || initial === undefined ? '' : String(initial));
|
|
48
|
+
var [affix, setAffix] = useState(affixInit ? affixInit.selected : null);
|
|
49
|
+
var [error, setError] = useState('');
|
|
50
|
+
|
|
51
|
+
useEffect(function() {
|
|
52
|
+
if (isControlled) {
|
|
53
|
+
var next = props.value === null || props.value === undefined ? '' : String(props.value);
|
|
54
|
+
setRaw(next);
|
|
55
|
+
}
|
|
56
|
+
}, [props.value, isControlled]);
|
|
57
|
+
|
|
58
|
+
useEffect(function() {
|
|
59
|
+
if (affixInit) setAffix(affixInit.selected);
|
|
60
|
+
}, [affixInit]);
|
|
61
|
+
|
|
62
|
+
function emit(nextRaw, nextAffix) {
|
|
63
|
+
var numeric = toNumeric(nextRaw);
|
|
64
|
+
var rangeCheck = validateRange(numeric, props.min, props.max);
|
|
65
|
+
setError(rangeCheck.valid ? '' : rangeCheck.error);
|
|
66
|
+
if (props.onChange) {
|
|
67
|
+
props.onChange(numeric, { affix: nextAffix });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function handleNumberChange(e) {
|
|
72
|
+
var sanitized = sanitizeNumberInput(e.target.value);
|
|
73
|
+
if (!isControlled) setRaw(sanitized);
|
|
74
|
+
emit(sanitized, affix);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function handleAffixChange(e) {
|
|
78
|
+
var nextValue = e.target.value;
|
|
79
|
+
var next = (affixInit.list).find(function(item) {
|
|
80
|
+
return item[affixInit.valueKey] === nextValue;
|
|
81
|
+
});
|
|
82
|
+
if (!next) return;
|
|
83
|
+
setAffix(next);
|
|
84
|
+
emit(raw, next);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function handleBlur() {
|
|
88
|
+
var numeric = toNumeric(raw);
|
|
89
|
+
if (numeric === null) return;
|
|
90
|
+
if (typeof props.decimals === 'number') {
|
|
91
|
+
var fixed = numeric.toFixed(props.decimals);
|
|
92
|
+
if (!isControlled) setRaw(fixed);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
type,
|
|
98
|
+
raw,
|
|
99
|
+
numeric: toNumeric(raw),
|
|
100
|
+
error,
|
|
101
|
+
placeholder: props.placeholder,
|
|
102
|
+
disabled: !!props.disabled,
|
|
103
|
+
decimals: props.decimals,
|
|
104
|
+
formatDisplay: formatDisplay,
|
|
105
|
+
affix: affixInit ? {
|
|
106
|
+
list: affixInit.list,
|
|
107
|
+
selected: affix,
|
|
108
|
+
position: affixInit.position,
|
|
109
|
+
labelKey: affixInit.labelKey,
|
|
110
|
+
valueKey: affixInit.valueKey
|
|
111
|
+
} : null,
|
|
112
|
+
handleNumberChange,
|
|
113
|
+
handleAffixChange,
|
|
114
|
+
handleBlur
|
|
115
|
+
};
|
|
116
|
+
}
|
package/src/snackbar/snackbar.js
CHANGED
|
@@ -2,10 +2,13 @@ var _container = null;
|
|
|
2
2
|
var _queue = [];
|
|
3
3
|
var _timer = null;
|
|
4
4
|
|
|
5
|
+
// var(--xeplr-*, fallback) — picks up @xeplr/ui-account's theme tokens
|
|
6
|
+
// (light/dark) when that stylesheet is loaded, falls back to these original
|
|
7
|
+
// colors otherwise.
|
|
5
8
|
var DESIGNS = {
|
|
6
|
-
success: { bg: '#059669', color: '#fff' },
|
|
7
|
-
error: { bg: '#dc2626', color: '#fff' },
|
|
8
|
-
default: { bg: '#2563eb', color: '#fff' }
|
|
9
|
+
success: { bg: 'var(--xeplr-success, #059669)', color: 'var(--xeplr-accent-text, #fff)' },
|
|
10
|
+
error: { bg: 'var(--xeplr-danger, #dc2626)', color: 'var(--xeplr-accent-text, #fff)' },
|
|
11
|
+
default: { bg: 'var(--xeplr-accent, #2563eb)', color: 'var(--xeplr-accent-text, #fff)' }
|
|
9
12
|
};
|
|
10
13
|
|
|
11
14
|
var DEFAULT_DURATION = 3000;
|