@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.
Files changed (43) hide show
  1. package/package.json +22 -6
  2. package/src/confirm/confirm.js +106 -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 +49 -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/dropdown/designs/DropdownSample.jsx +151 -0
  20. package/src/dropdown/designs/dropdown.css +226 -0
  21. package/src/dropdown/designs/index.js +1 -0
  22. package/src/dropdown/dropdown.js +75 -0
  23. package/src/dropdown/pages.jsx +10 -0
  24. package/src/dropdown/useDropdownController.js +254 -0
  25. package/src/dropdown/validateDesign.js +4 -0
  26. package/src/gridDisplayer/designs/GridDisplayerSample.jsx +58 -0
  27. package/src/gridDisplayer/designs/gridDisplayer.css +89 -0
  28. package/src/gridDisplayer/designs/index.js +1 -0
  29. package/src/gridDisplayer/gridDisplayer.js +44 -0
  30. package/src/gridDisplayer/pages.jsx +10 -0
  31. package/src/gridDisplayer/useGridDisplayerController.js +59 -0
  32. package/src/gridDisplayer/validateDesign.js +4 -0
  33. package/src/index.js +115 -1
  34. package/src/numberField/designs/NumberFieldSample.jsx +52 -0
  35. package/src/numberField/designs/index.js +1 -0
  36. package/src/numberField/designs/numberField.css +66 -0
  37. package/src/numberField/numberField.js +105 -0
  38. package/src/numberField/pages.jsx +10 -0
  39. package/src/numberField/useNumberFieldController.js +116 -0
  40. package/src/numberField/validateDesign.js +3 -0
  41. package/src/snackbar/snackbar.js +6 -3
  42. package/src/theme/default.theme.json +178 -0
  43. package/src/theme/index.js +75 -0
@@ -0,0 +1,178 @@
1
+ {
2
+ "name": "xeplr-default",
3
+ "description": "Brand-neutral DEFAULT design theme for the @xeplr UI packages (upstream — NO consumer branding). The `chart` section IS a ChartOptions styling object (see @xeplr/ui-charts type.js): camelCase keys, CSS-value strings, Style.background/Style.border, Positioning — so theme→ChartOptions is a plain deep-merge, no remapping. It carries styling/chrome only (no data, no axis.labels, no title.text, no chartType — those are per-chart) plus two theme extras ChartOptions consumes: `palette` (→ option.color) and `marks` (per-type series defaults). The `table` section holds @xeplr/ui-table tokens. Consumers pass overrides to resolveTheme(); overrides win, rest falls through. Sizes are CSS values; rem scales with the container.",
4
+ "default": "light",
5
+ "font": {
6
+ "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif"
7
+ },
8
+ "themes": {
9
+ "light": {
10
+ "chart": {
11
+ "title": {
12
+ "positioning": { "position": "top", "align": "start" },
13
+ "font": { "size": "1rem", "weight": 600, "color": "#0b0b0b", "align": "left" }
14
+ },
15
+ "axes": {
16
+ "x": {
17
+ "line": { "show": true, "color": "#c3c2b7", "width": "1px" },
18
+ "grid": { "show": false, "color": "#e1e0d9", "width": "1px" },
19
+ "ticks": { "show": true, "length": "0", "color": "#898781", "font": { "size": "0.8rem", "color": "#898781" } },
20
+ "title": { "font": { "size": "0.9rem", "weight": 600, "color": "#52514e" } }
21
+ },
22
+ "y": {
23
+ "line": { "show": true, "color": "#c3c2b7", "width": "1px" },
24
+ "grid": { "show": true, "color": "#e1e0d9", "width": "1px" },
25
+ "ticks": { "show": true, "length": "0", "color": "#898781", "font": { "size": "0.8rem", "color": "#898781" } },
26
+ "title": { "font": { "size": "0.9rem", "weight": 600, "color": "#52514e" } }
27
+ }
28
+ },
29
+ "legend": {
30
+ "show": true,
31
+ "positioning": { "position": "bottom", "align": "center" },
32
+ "font": { "size": "0.85rem", "color": "#52514e" }
33
+ },
34
+ "tooltip": {
35
+ "show": true,
36
+ "font": { "size": "0.8rem", "color": "#0b0b0b" },
37
+ "style": {
38
+ "background": { "color": "#fcfcfb" },
39
+ "border": { "color": "#e1e0d9", "width": "1px" }
40
+ }
41
+ },
42
+ "dataLabel": {
43
+ "show": false,
44
+ "positioning": { "position": "top" },
45
+ "font": { "size": "0.75rem", "color": "#52514e" }
46
+ },
47
+ "chartArea": {
48
+ "margin": { "top": "8px", "right": "12px", "bottom": "8px", "left": "8px" },
49
+ "style": { "background": { "color": "transparent" } }
50
+ },
51
+ "palette": ["#2a78d6", "#1baf7a", "#eda100", "#008300", "#4a3aa7", "#e34948", "#e87ba4", "#eb6834"],
52
+ "marks": {
53
+ "line": { "width": "2px", "symbol": "circle", "symbolSize": 8, "smooth": false },
54
+ "bar": { "border": { "radius": "4px" }, "gap": { "color": "#fcfcfb", "width": "2px" } },
55
+ "scatter": { "symbolSize": 10 },
56
+ "pie": { "border": { "color": "#fcfcfb", "width": "2px" }, "label": { "font": { "color": "#52514e" } } }
57
+ }
58
+ },
59
+ "table": {
60
+ "container": {
61
+ "colors": { "background": "#ffffff", "color": "#0b0b0b" },
62
+ "border": { "color": "#e1e0d9", "width": "1px", "radius": "8px" }
63
+ },
64
+ "toolbar": { "colors": { "background": "#f4f4f2", "border_bottom": "#e1e0d9" } },
65
+ "header": {
66
+ "colors": { "background": "#f4f4f2", "color": "#52514e", "sort_active": "#2a78d6" },
67
+ "font": { "size": "0.875rem", "weight": "600", "text_align": "left", "text_transform": "none" },
68
+ "border": { "bottom": { "color": "#c3c2b7", "width": "2px" } },
69
+ "padding": { "top": "10px", "right": "12px", "bottom": "10px", "left": "12px" }
70
+ },
71
+ "body": {
72
+ "row": {
73
+ "colors": { "background": "#ffffff", "hover_background": "#f5f5ff", "selected_background": "#eaf1fb", "stripe_background": "#fbfbfa" },
74
+ "status": { "new": "#1baf7a", "edited": "#eda100" }
75
+ },
76
+ "cell": {
77
+ "font": { "size": "0.875rem", "color": "#0b0b0b", "weight": "normal" },
78
+ "border": { "bottom": { "color": "#eeeeea", "width": "1px" } },
79
+ "padding": { "top": "8px", "right": "12px", "bottom": "8px", "left": "12px" }
80
+ }
81
+ },
82
+ "accent": { "default": "#2a78d6", "hover": "#1f5fb0", "soft": "#eaf1fb" },
83
+ "pagination": {
84
+ "colors": { "background": "#f4f4f2", "border_top": "#e1e0d9", "color": "#52514e" },
85
+ "font": { "size": "0.8rem" }
86
+ },
87
+ "overlay": {
88
+ "colors": { "background": "rgba(0,0,0,0.3)" },
89
+ "panel": { "background": "#ffffff", "border": "#c3c2b7", "radius": "10px", "shadow": "0 12px 40px rgba(0,0,0,0.15)" }
90
+ }
91
+ }
92
+ },
93
+ "dark": {
94
+ "chart": {
95
+ "title": {
96
+ "positioning": { "position": "top", "align": "start" },
97
+ "font": { "size": "1rem", "weight": 600, "color": "#ffffff", "align": "left" }
98
+ },
99
+ "axes": {
100
+ "x": {
101
+ "line": { "show": true, "color": "#383835", "width": "1px" },
102
+ "grid": { "show": false, "color": "#2c2c2a", "width": "1px" },
103
+ "ticks": { "show": true, "length": "0", "color": "#898781", "font": { "size": "0.8rem", "color": "#898781" } },
104
+ "title": { "font": { "size": "0.9rem", "weight": 600, "color": "#c3c2b7" } }
105
+ },
106
+ "y": {
107
+ "line": { "show": true, "color": "#383835", "width": "1px" },
108
+ "grid": { "show": true, "color": "#2c2c2a", "width": "1px" },
109
+ "ticks": { "show": true, "length": "0", "color": "#898781", "font": { "size": "0.8rem", "color": "#898781" } },
110
+ "title": { "font": { "size": "0.9rem", "weight": 600, "color": "#c3c2b7" } }
111
+ }
112
+ },
113
+ "legend": {
114
+ "show": true,
115
+ "positioning": { "position": "bottom", "align": "center" },
116
+ "font": { "size": "0.85rem", "color": "#c3c2b7" }
117
+ },
118
+ "tooltip": {
119
+ "show": true,
120
+ "font": { "size": "0.8rem", "color": "#ffffff" },
121
+ "style": {
122
+ "background": { "color": "#1a1a19" },
123
+ "border": { "color": "#2c2c2a", "width": "1px" }
124
+ }
125
+ },
126
+ "dataLabel": {
127
+ "show": false,
128
+ "positioning": { "position": "top" },
129
+ "font": { "size": "0.75rem", "color": "#c3c2b7" }
130
+ },
131
+ "chartArea": {
132
+ "margin": { "top": "8px", "right": "12px", "bottom": "8px", "left": "8px" },
133
+ "style": { "background": { "color": "transparent" } }
134
+ },
135
+ "palette": ["#3987e5", "#199e70", "#c98500", "#008300", "#9085e9", "#e66767", "#d55181", "#d95926"],
136
+ "marks": {
137
+ "line": { "width": "2px", "symbol": "circle", "symbolSize": 8, "smooth": false },
138
+ "bar": { "border": { "radius": "4px" }, "gap": { "color": "#1a1a19", "width": "2px" } },
139
+ "scatter": { "symbolSize": 10 },
140
+ "pie": { "border": { "color": "#1a1a19", "width": "2px" }, "label": { "font": { "color": "#c3c2b7" } } }
141
+ }
142
+ },
143
+ "table": {
144
+ "container": {
145
+ "colors": { "background": "#1a1a19", "color": "#c3c2b7" },
146
+ "border": { "color": "#2c2c2a", "width": "1px", "radius": "8px" }
147
+ },
148
+ "toolbar": { "colors": { "background": "#141413", "border_bottom": "#2c2c2a" } },
149
+ "header": {
150
+ "colors": { "background": "#141413", "color": "#898781", "sort_active": "#3987e5" },
151
+ "font": { "size": "0.875rem", "weight": "600", "text_align": "left", "text_transform": "none" },
152
+ "border": { "bottom": { "color": "#383835", "width": "2px" } },
153
+ "padding": { "top": "10px", "right": "12px", "bottom": "10px", "left": "12px" }
154
+ },
155
+ "body": {
156
+ "row": {
157
+ "colors": { "background": "#1a1a19", "hover_background": "#242422", "selected_background": "#1f2b3a", "stripe_background": "#1d1d1b" },
158
+ "status": { "new": "#199e70", "edited": "#c98500" }
159
+ },
160
+ "cell": {
161
+ "font": { "size": "0.875rem", "color": "#c3c2b7", "weight": "normal" },
162
+ "border": { "bottom": { "color": "#2c2c2a", "width": "1px" } },
163
+ "padding": { "top": "8px", "right": "12px", "bottom": "8px", "left": "12px" }
164
+ }
165
+ },
166
+ "accent": { "default": "#3987e5", "hover": "#5ba0ee", "soft": "#1f2b3a" },
167
+ "pagination": {
168
+ "colors": { "background": "#141413", "border_top": "#2c2c2a", "color": "#c3c2b7" },
169
+ "font": { "size": "0.8rem" }
170
+ },
171
+ "overlay": {
172
+ "colors": { "background": "rgba(0,0,0,0.6)" },
173
+ "panel": { "background": "#242422", "border": "#383835", "radius": "10px", "shadow": "0 12px 40px rgba(0,0,0,0.5)" }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,75 @@
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
+ export { defaultTheme };
75
+ export default defaultTheme;