@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.
Files changed (68) hide show
  1. package/package.json +25 -6
  2. package/src/confirm/confirm.js +175 -0
  3. package/src/dataImport/dataImport.js +32 -0
  4. package/src/dataImport/designs/DataImportSample.jsx +71 -0
  5. package/src/dataImport/designs/ImportHistorySample.jsx +130 -0
  6. package/src/dataImport/designs/dataImport.css +98 -0
  7. package/src/dataImport/designs/index.js +2 -0
  8. package/src/dataImport/pages.jsx +17 -0
  9. package/src/dataImport/useDataImportController.js +94 -0
  10. package/src/dataImport/useImportHistoryController.js +67 -0
  11. package/src/dataImport/validateDesign.js +68 -0
  12. package/src/dateField/dateField.js +71 -0
  13. package/src/dateField/designs/DateFieldSample.jsx +30 -0
  14. package/src/dateField/designs/dateField.css +60 -0
  15. package/src/dateField/designs/index.js +1 -0
  16. package/src/dateField/pages.jsx +10 -0
  17. package/src/dateField/useDateFieldController.js +77 -0
  18. package/src/dateField/validateDesign.js +3 -0
  19. package/src/dateRange/dateRange.js +273 -0
  20. package/src/dateRange/designs/DateRangeSample.jsx +138 -0
  21. package/src/dateRange/designs/dateRange.css +69 -0
  22. package/src/dateRange/designs/index.js +1 -0
  23. package/src/dateRange/pages.jsx +10 -0
  24. package/src/dateRange/useDateRangeController.js +118 -0
  25. package/src/dateRange/validateDesign.js +20 -0
  26. package/src/dropdown/designs/DropdownSample.jsx +151 -0
  27. package/src/dropdown/designs/dropdown.css +226 -0
  28. package/src/dropdown/designs/index.js +1 -0
  29. package/src/dropdown/dropdown.js +75 -0
  30. package/src/dropdown/pages.jsx +10 -0
  31. package/src/dropdown/useDropdownController.js +254 -0
  32. package/src/dropdown/validateDesign.js +4 -0
  33. package/src/fileUpload/validateDesign.js +27 -8
  34. package/src/gridDisplayer/designs/GridDisplayerSample.jsx +58 -0
  35. package/src/gridDisplayer/designs/gridDisplayer.css +89 -0
  36. package/src/gridDisplayer/designs/index.js +1 -0
  37. package/src/gridDisplayer/gridDisplayer.js +44 -0
  38. package/src/gridDisplayer/pages.jsx +10 -0
  39. package/src/gridDisplayer/useGridDisplayerController.js +59 -0
  40. package/src/gridDisplayer/validateDesign.js +4 -0
  41. package/src/index.js +177 -1
  42. package/src/numberField/designs/NumberFieldSample.jsx +52 -0
  43. package/src/numberField/designs/index.js +1 -0
  44. package/src/numberField/designs/numberField.css +66 -0
  45. package/src/numberField/numberField.js +105 -0
  46. package/src/numberField/pages.jsx +10 -0
  47. package/src/numberField/useNumberFieldController.js +116 -0
  48. package/src/numberField/validateDesign.js +3 -0
  49. package/src/progress/ProgressNotifier.jsx +118 -0
  50. package/src/progress/designs/progressNotifier.css +73 -0
  51. package/src/progress/progressSource.js +168 -0
  52. package/src/rangeField/designs/RangeFieldSample.jsx +25 -0
  53. package/src/rangeField/designs/index.js +1 -0
  54. package/src/rangeField/designs/rangeField.css +26 -0
  55. package/src/rangeField/pages.jsx +10 -0
  56. package/src/rangeField/rangeField.js +29 -0
  57. package/src/rangeField/useRangeFieldController.js +52 -0
  58. package/src/rangeField/validateDesign.js +3 -0
  59. package/src/snackbar/snackbar.js +6 -3
  60. package/src/textField/designs/TextFieldSample.jsx +75 -0
  61. package/src/textField/designs/index.js +1 -0
  62. package/src/textField/designs/textField.css +92 -0
  63. package/src/textField/pages.jsx +10 -0
  64. package/src/textField/textField.js +65 -0
  65. package/src/textField/useTextFieldController.js +101 -0
  66. package/src/textField/validateDesign.js +3 -0
  67. package/src/theme/default.theme.json +934 -0
  68. 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;