@xeplr/ui-utils 1.0.0 → 1.0.2
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 +25 -6
- package/src/confirm/confirm.js +175 -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 +68 -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/dateRange/dateRange.js +273 -0
- package/src/dateRange/designs/DateRangeSample.jsx +138 -0
- package/src/dateRange/designs/dateRange.css +69 -0
- package/src/dateRange/designs/index.js +1 -0
- package/src/dateRange/pages.jsx +10 -0
- package/src/dateRange/useDateRangeController.js +118 -0
- package/src/dateRange/validateDesign.js +20 -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/fileUpload/validateDesign.js +27 -8
- 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 +177 -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/progress/ProgressNotifier.jsx +118 -0
- package/src/progress/designs/progressNotifier.css +73 -0
- package/src/progress/progressSource.js +168 -0
- package/src/rangeField/designs/RangeFieldSample.jsx +25 -0
- package/src/rangeField/designs/index.js +1 -0
- package/src/rangeField/designs/rangeField.css +26 -0
- package/src/rangeField/pages.jsx +10 -0
- package/src/rangeField/rangeField.js +29 -0
- package/src/rangeField/useRangeFieldController.js +52 -0
- package/src/rangeField/validateDesign.js +3 -0
- package/src/snackbar/snackbar.js +6 -3
- package/src/textField/designs/TextFieldSample.jsx +75 -0
- package/src/textField/designs/index.js +1 -0
- package/src/textField/designs/textField.css +92 -0
- package/src/textField/pages.jsx +10 -0
- package/src/textField/textField.js +65 -0
- package/src/textField/useTextFieldController.js +101 -0
- package/src/textField/validateDesign.js +3 -0
- package/src/theme/default.theme.json +934 -0
- package/src/theme/index.js +155 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Theme resolution for the @xeplr UI packages.
|
|
2
|
+
//
|
|
3
|
+
// The model is two layers:
|
|
4
|
+
// 1. default.theme.json — the brand-neutral BASE. Holds the design for every
|
|
5
|
+
// themed component (chart, table, …). Upstream; nobody edits it.
|
|
6
|
+
// 2. an override object the CONSUMER supplies. deepMerge lays it over the base;
|
|
7
|
+
// overrides WIN, and anything not overridden falls through to the default.
|
|
8
|
+
//
|
|
9
|
+
// resolved = resolveTheme(appOverrides) // whole theme, both variants
|
|
10
|
+
// variant = getThemeVariant('dark', overrides) // one variant's { colorset, chart, table }
|
|
11
|
+
//
|
|
12
|
+
// Then @xeplr/ui-charts reads variant.chart, @xeplr/ui-table reads variant.table,
|
|
13
|
+
// both over the shared variant.colorset foundation.
|
|
14
|
+
|
|
15
|
+
import defaultTheme from './default.theme.json' with { type: 'json' };
|
|
16
|
+
|
|
17
|
+
function isPlainObject(v) {
|
|
18
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Deep-merge `override` onto `base` (immutably). Objects merge key-by-key;
|
|
23
|
+
* everything else (including ARRAYS — e.g. a palette) is REPLACED, not merged,
|
|
24
|
+
* so an override palette wholly supersedes the default instead of blending
|
|
25
|
+
* index-by-index. `undefined` in the override never clobbers a base value.
|
|
26
|
+
*/
|
|
27
|
+
export function deepMerge(base, override) {
|
|
28
|
+
if (override === undefined) return base;
|
|
29
|
+
if (!isPlainObject(override) || !isPlainObject(base)) return override;
|
|
30
|
+
const out = { ...base };
|
|
31
|
+
for (const key of Object.keys(override)) {
|
|
32
|
+
const o = override[key];
|
|
33
|
+
if (o === undefined) continue;
|
|
34
|
+
out[key] = isPlainObject(o) && isPlainObject(base[key]) ? deepMerge(base[key], o) : o;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Resolve the full theme: default base with consumer overrides merged on top. */
|
|
40
|
+
export function resolveTheme(overrides) {
|
|
41
|
+
return deepMerge(defaultTheme, overrides || {});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve one variant ('light' | 'dark'), overrides applied. Falls back to the
|
|
46
|
+
* theme's declared `default` variant when the name is unknown.
|
|
47
|
+
* @returns {{ colorset: object, chart: object, table: object }}
|
|
48
|
+
*/
|
|
49
|
+
export function getThemeVariant(name, overrides) {
|
|
50
|
+
const theme = resolveTheme(overrides);
|
|
51
|
+
return theme.themes[name] || theme.themes[theme.default];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Theme → ChartOptions. Returns the variant's `chart` section (already a
|
|
56
|
+
* ChartOptions styling object) with the top-level font `family` attached as
|
|
57
|
+
* `fontFamily`, ready to deep-merge a per-chart spec (data, chartType,
|
|
58
|
+
* axes.x/y.labels, title.text) onto and hand to <XeplrChart> / chartOptionsToOption.
|
|
59
|
+
*
|
|
60
|
+
* const base = getChartOptions('light', appOverrides);
|
|
61
|
+
* const chartOptions = deepMerge(base, { data, chartType: 'bar',
|
|
62
|
+
* axes: { x: { labels: ['month'] }, y: { labels: ['revenue'] } } });
|
|
63
|
+
*
|
|
64
|
+
* @returns {object} a ChartOptions object (styling only + palette + marks + fontFamily)
|
|
65
|
+
*/
|
|
66
|
+
export function getChartOptions(variantName, overrides) {
|
|
67
|
+
const theme = resolveTheme(overrides);
|
|
68
|
+
const variant = theme.themes[variantName] || theme.themes[theme.default];
|
|
69
|
+
const chart = (variant && variant.chart) || {};
|
|
70
|
+
const family = theme.font && theme.font.family;
|
|
71
|
+
return family ? { ...chart, fontFamily: family } : { ...chart };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Theme → the CSS custom properties an input control reads.
|
|
76
|
+
*
|
|
77
|
+
* One control at a time, because the sections are separate: `input.text` and
|
|
78
|
+
* `input.select` are the same shape and need not hold the same values.
|
|
79
|
+
*
|
|
80
|
+
* Every edge is emitted on its own — border-top-width, -right, -bottom, -left
|
|
81
|
+
* — so "bottom rule only" is `{ border: { top: false, left: false, right:
|
|
82
|
+
* false, bottom: true } }` and not a fork of the stylesheet.
|
|
83
|
+
*
|
|
84
|
+
* <div style={inputStyleVars('text', overrides)}>…</div>
|
|
85
|
+
*
|
|
86
|
+
* @param {'text'|'select'|'number'|'date'} control
|
|
87
|
+
* @param {object} [overrides] consumer theme overrides
|
|
88
|
+
* @param {string} [variantName]
|
|
89
|
+
* @returns {object} { '--xeplr-input-text-…': value }
|
|
90
|
+
*/
|
|
91
|
+
export function inputStyleVars(control, overrides, variantName) {
|
|
92
|
+
const theme = resolveTheme(overrides);
|
|
93
|
+
const variant = theme.themes[variantName] || theme.themes[theme.default];
|
|
94
|
+
const input = (variant && variant.input && variant.input[control]) || {};
|
|
95
|
+
const p = '--xeplr-input-' + control;
|
|
96
|
+
const border = input.border || {};
|
|
97
|
+
const font = input.font || {};
|
|
98
|
+
const label = input.label || {};
|
|
99
|
+
const ring = input.focusRing || {};
|
|
100
|
+
const invalid = input.invalid || {};
|
|
101
|
+
|
|
102
|
+
// An edge that is false is 0 — not "absent", which would fall through to the
|
|
103
|
+
// stylesheet's own default and quietly put the border back.
|
|
104
|
+
const edge = (on) => (on === false ? '0' : (border.width || '1px'));
|
|
105
|
+
|
|
106
|
+
const out = {
|
|
107
|
+
[p + '-gap']: input.gap,
|
|
108
|
+
[p + '-padding']: input.padding,
|
|
109
|
+
[p + '-bg']: input.background,
|
|
110
|
+
[p + '-color']: font.color,
|
|
111
|
+
[p + '-font-size']: font.size,
|
|
112
|
+
[p + '-placeholder']: font.placeholder,
|
|
113
|
+
[p + '-border-style']: border.style,
|
|
114
|
+
[p + '-border-color']: border.color,
|
|
115
|
+
[p + '-border-hover']: border.hover,
|
|
116
|
+
[p + '-border-focus']: border.focus,
|
|
117
|
+
[p + '-border-top']: edge(border.top),
|
|
118
|
+
[p + '-border-right']: edge(border.right),
|
|
119
|
+
[p + '-border-bottom']: edge(border.bottom),
|
|
120
|
+
[p + '-border-left']: edge(border.left),
|
|
121
|
+
[p + '-radius']: border.radius,
|
|
122
|
+
// The label carries the FULL Font block — the same shape
|
|
123
|
+
// @xeplr/ui-charts' type.js defines, so `underline` means the same thing
|
|
124
|
+
// on a field label as on a chart's. Every one of them is emitted, because
|
|
125
|
+
// a property the theme accepts and the stylesheet ignores is a setting
|
|
126
|
+
// that silently does nothing.
|
|
127
|
+
[p + '-label-family']: label.family,
|
|
128
|
+
[p + '-label-size']: label.size,
|
|
129
|
+
[p + '-label-weight']: label.weight,
|
|
130
|
+
[p + '-label-style']: label.style,
|
|
131
|
+
[p + '-label-variant']: label.variant,
|
|
132
|
+
[p + '-label-color']: label.color,
|
|
133
|
+
[p + '-label-line-height']: label.lineHeight,
|
|
134
|
+
[p + '-label-spacing']: label.letterSpacing,
|
|
135
|
+
[p + '-label-align']: label.align,
|
|
136
|
+
[p + '-label-transform']: label.transform,
|
|
137
|
+
[p + '-label-decoration']: label.decoration,
|
|
138
|
+
[p + '-label-opacity']: label.opacity,
|
|
139
|
+
[p + '-help-family']: (input.help || {}).family,
|
|
140
|
+
[p + '-help-size']: (input.help || {}).size,
|
|
141
|
+
[p + '-help-color']: (input.help || {}).color,
|
|
142
|
+
[p + '-help-style']: (input.help || {}).style,
|
|
143
|
+
[p + '-help-decoration']: (input.help || {}).decoration,
|
|
144
|
+
[p + '-ring']: ring.width,
|
|
145
|
+
[p + '-ring-color']: ring.color,
|
|
146
|
+
[p + '-invalid-color']: invalid.color,
|
|
147
|
+
[p + '-invalid-ring']: invalid.ring
|
|
148
|
+
};
|
|
149
|
+
// Undefined would render as the string "undefined" in a style object.
|
|
150
|
+
Object.keys(out).forEach((k) => { if (out[k] === undefined) delete out[k]; });
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export { defaultTheme };
|
|
155
|
+
export default defaultTheme;
|