@object-ui/core 6.0.4 → 6.1.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @object-ui/core
2
2
 
3
+ ## 6.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 991b62d: Add `compareTo` field to dashboard widgets for period-over-period
8
+ comparison. Supports `'previousPeriod'`, `'previousYear'`, and
9
+ `{ offset: '7d' | '4w' | '1M' | '1y' }`.
10
+ - **Metric / gauge widgets** now compute a delta percentage when `compareTo`
11
+ is set and surface it as a derived `trend` (auto-labelled via
12
+ `dashboard.trend.vsLast*` i18n keys sniffed from the filter macros).
13
+ - **Chart widgets** (line / area / bar / horizontal-bar / scatter / combo)
14
+ overlay a muted comparison-period series (dashed line, lower fill opacity).
15
+ Pie / donut / funnel ignore `compareTo`.
16
+ - New core utilities: `shiftFilterByCompareTo`, `compareToTrendLabelKey`,
17
+ `computeMetricDelta`, and `CompareToConfig` type.
18
+ - `ChartSeries` now accepts `variant: 'comparison'`, `dashArray`, and
19
+ `opacity` overrides for visual treatment.
20
+
21
+ See `packages/plugin-dashboard/SKILL.md` for usage examples.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [991b62d]
26
+ - @object-ui/types@6.1.0
27
+
3
28
  ## 6.0.4
4
29
 
5
30
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -37,3 +37,4 @@ export * from './protocols/index.js';
37
37
  export { composeStacks } from '@objectstack/spec';
38
38
  export * from './utils/drill-down.js';
39
39
  export * from './utils/date-macros.js';
40
+ export * from './utils/compare-to.js';
package/dist/index.js CHANGED
@@ -36,3 +36,4 @@ export * from './protocols/index.js';
36
36
  export { composeStacks } from '@objectstack/spec';
37
37
  export * from './utils/drill-down.js';
38
38
  export * from './utils/date-macros.js';
39
+ export * from './utils/compare-to.js';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * Period-over-period comparison config for dashboard widgets.
10
+ *
11
+ * - `'previousPeriod'` — substitute `current_*` / `today` date macro tokens
12
+ * with their `last_*` / `yesterday` counterparts (e.g. `current_quarter_start`
13
+ * → `last_quarter_start`). Best when the filter uses date macros.
14
+ * - `'previousYear'` — re-resolve date macros against a `now` shifted back
15
+ * one calendar year (same week / month / quarter, one year earlier).
16
+ * - `{ offset: '7d' | '4w' | '1M' | '1y' }` — re-resolve macros against a
17
+ * `now` shifted by the given duration. Units: `d` (day), `w` (week),
18
+ * `M` (month), `y` (year).
19
+ */
20
+ export type CompareToConfig = 'previousPeriod' | 'previousYear' | {
21
+ offset: string;
22
+ };
23
+ /**
24
+ * Resolve a filter for a period-over-period comparison query.
25
+ *
26
+ * - `previousYear` — re-resolve date macros against a `now` shifted back by one year.
27
+ * - `{ offset }` — re-resolve date macros against a `now` shifted by the given duration.
28
+ * - `previousPeriod` — substitute `current_*` / `today` tokens with `last_*` / `yesterday`
29
+ * before resolving.
30
+ */
31
+ export declare function shiftFilterByCompareTo<T = any>(filter: T, compareTo: CompareToConfig, now?: Date): T;
32
+ /**
33
+ * Derive a translation key suffix (under `dashboard.trend.*`) describing the
34
+ * comparison window. Used by metric widgets to label the trend delta.
35
+ */
36
+ export declare function compareToTrendLabelKey(compareTo: CompareToConfig, filter?: unknown): string;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ import { resolveDateMacros } from './date-macros.js';
9
+ const CURRENT_TO_LAST_TOKENS = {
10
+ current_week_start: 'last_week_start',
11
+ current_week_end: 'last_week_end',
12
+ current_month_start: 'last_month_start',
13
+ current_month_end: 'last_month_end',
14
+ current_quarter_start: 'last_quarter_start',
15
+ current_quarter_end: 'last_quarter_end',
16
+ current_year_start: 'last_year_start',
17
+ current_year_end: 'last_year_end',
18
+ week_start: 'last_week_start',
19
+ week_end: 'last_week_end',
20
+ month_start: 'last_month_start',
21
+ month_end: 'last_month_end',
22
+ quarter_start: 'last_quarter_start',
23
+ quarter_end: 'last_quarter_end',
24
+ year_start: 'last_year_start',
25
+ year_end: 'last_year_end',
26
+ today: 'yesterday',
27
+ };
28
+ function substituteTokens(value, map) {
29
+ if (value == null)
30
+ return value;
31
+ if (typeof value === 'string') {
32
+ return value.replace(/\$?\{([a-zA-Z0-9_]+)\}/g, (m, tok) => {
33
+ const target = map[tok];
34
+ return target ? `{${target}}` : m;
35
+ });
36
+ }
37
+ if (Array.isArray(value))
38
+ return value.map((v) => substituteTokens(v, map));
39
+ if (typeof value === 'object') {
40
+ const out = {};
41
+ for (const k of Object.keys(value))
42
+ out[k] = substituteTokens(value[k], map);
43
+ return out;
44
+ }
45
+ return value;
46
+ }
47
+ function shiftNow(now, offset) {
48
+ const m = offset.match(/^(\d+)([dwMy])$/);
49
+ if (!m)
50
+ return null;
51
+ const n = parseInt(m[1], 10);
52
+ const unit = m[2];
53
+ const x = new Date(now);
54
+ if (unit === 'd')
55
+ x.setDate(x.getDate() - n);
56
+ else if (unit === 'w')
57
+ x.setDate(x.getDate() - n * 7);
58
+ else if (unit === 'M')
59
+ x.setMonth(x.getMonth() - n);
60
+ else if (unit === 'y')
61
+ x.setFullYear(x.getFullYear() - n);
62
+ else
63
+ return null;
64
+ return x;
65
+ }
66
+ /**
67
+ * Resolve a filter for a period-over-period comparison query.
68
+ *
69
+ * - `previousYear` — re-resolve date macros against a `now` shifted back by one year.
70
+ * - `{ offset }` — re-resolve date macros against a `now` shifted by the given duration.
71
+ * - `previousPeriod` — substitute `current_*` / `today` tokens with `last_*` / `yesterday`
72
+ * before resolving.
73
+ */
74
+ export function shiftFilterByCompareTo(filter, compareTo, now = new Date()) {
75
+ if (compareTo === 'previousYear') {
76
+ const shifted = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
77
+ return resolveDateMacros(filter, shifted);
78
+ }
79
+ if (typeof compareTo === 'object' && compareTo && 'offset' in compareTo) {
80
+ const shifted = shiftNow(now, compareTo.offset);
81
+ if (!shifted)
82
+ return resolveDateMacros(filter, now);
83
+ return resolveDateMacros(filter, shifted);
84
+ }
85
+ // previousPeriod
86
+ const substituted = substituteTokens(filter, CURRENT_TO_LAST_TOKENS);
87
+ return resolveDateMacros(substituted, now);
88
+ }
89
+ /**
90
+ * Derive a translation key suffix (under `dashboard.trend.*`) describing the
91
+ * comparison window. Used by metric widgets to label the trend delta.
92
+ */
93
+ export function compareToTrendLabelKey(compareTo, filter) {
94
+ if (compareTo === 'previousYear')
95
+ return 'vsLastYear';
96
+ if (typeof compareTo === 'object' && compareTo && 'offset' in compareTo) {
97
+ return 'vsPreviousPeriod';
98
+ }
99
+ // previousPeriod — sniff the dominant token in the filter
100
+ const json = filter ? JSON.stringify(filter) : '';
101
+ if (/current_year_|year_start|year_end/.test(json))
102
+ return 'vsLastYear';
103
+ if (/current_quarter_|quarter_start|quarter_end/.test(json))
104
+ return 'vsLastQuarter';
105
+ if (/current_month_|month_start|month_end/.test(json))
106
+ return 'vsLastMonth';
107
+ if (/current_week_|week_start|week_end/.test(json))
108
+ return 'vsLastWeek';
109
+ if (/\btoday\b/.test(json))
110
+ return 'vsYesterday';
111
+ return 'vsPreviousPeriod';
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/core",
3
- "version": "6.0.4",
3
+ "version": "6.1.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -28,11 +28,11 @@
28
28
  "@objectstack/spec": "^5.2.0",
29
29
  "lodash": "^4.18.1",
30
30
  "zod": "^4.4.3",
31
- "@object-ui/types": "6.0.4"
31
+ "@object-ui/types": "6.1.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "^6.0.3",
35
- "vitest": "^4.1.6"
35
+ "vitest": "^4.1.7"
36
36
  },
37
37
  "keywords": [
38
38
  "objectui",