@formant/ava 3.5.0-formant → 3.5.2-formant
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/dist/ava.min.js +1 -1
- package/esm/insight/chart/strategy/augmentedMarks/changePoint.js +2 -2
- package/esm/insight/narrative/strategy/changePoint.js +2 -1
- package/esm/insight/narrative/strategy/timeSeriesOutlier.js +3 -4
- package/esm/insight/narrative/strategy/trend.js +2 -2
- package/esm/utils/dateFormat.js +44 -0
- package/esm/utils/index.js +1 -0
- package/lib/insight/chart/strategy/augmentedMarks/changePoint.js +1 -1
- package/lib/insight/narrative/strategy/changePoint.js +2 -1
- package/lib/insight/narrative/strategy/timeSeriesOutlier.js +3 -4
- package/lib/insight/narrative/strategy/trend.js +2 -2
- package/lib/utils/dateFormat.d.ts +10 -0
- package/lib/utils/dateFormat.js +50 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +5 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __read, __spreadArray } from "tslib";
|
|
2
2
|
import { size } from 'lodash';
|
|
3
|
-
import { dataFormat } from '../../../../utils';
|
|
3
|
+
import { dataFormat, formatInsightDate } from '../../../../utils';
|
|
4
4
|
import { pointMarkStrategy } from '../commonMarks';
|
|
5
5
|
import { insight2ChartStrategy } from '../chart';
|
|
6
6
|
import { augmentedMarks2Marks } from '../../utils';
|
|
@@ -14,7 +14,7 @@ export var changePointAugmentedMarksStrategy = function (insight) {
|
|
|
14
14
|
pointMarkStrategy([pattern], {
|
|
15
15
|
role: 'highlight',
|
|
16
16
|
name: 'change point',
|
|
17
|
-
label: function (pt) { return "".concat(pt.x, "
|
|
17
|
+
label: function (pt) { return "".concat(formatInsightDate(pt.x), " \u00B7 ").concat(measure, ": ").concat(dataFormat(pt.y)); },
|
|
18
18
|
}),
|
|
19
19
|
],
|
|
20
20
|
}); });
|
|
@@ -2,6 +2,7 @@ import { __extends } from "tslib";
|
|
|
2
2
|
/* eslint-disable no-template-curly-in-string */
|
|
3
3
|
import { first, last } from 'lodash';
|
|
4
4
|
import { generateTextSpec } from '../../../ntv';
|
|
5
|
+
import { formatInsightDateRange } from '../../../utils/dateFormat';
|
|
5
6
|
import { InsightNarrativeStrategy } from './base';
|
|
6
7
|
var variableMetaMap = {
|
|
7
8
|
dateRange: {
|
|
@@ -25,7 +26,7 @@ var ChangePointNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
25
26
|
var spec = generateTextSpec({
|
|
26
27
|
structures: ChangePointNarrativeStrategy.structures[lang],
|
|
27
28
|
variable: {
|
|
28
|
-
dateRange:
|
|
29
|
+
dateRange: formatInsightDateRange(first(data)[dimension], last(data)[dimension]),
|
|
29
30
|
measure: patterns[0].measure,
|
|
30
31
|
total: patterns.length,
|
|
31
32
|
},
|
|
@@ -4,8 +4,7 @@ import { first, last, maxBy, minBy } from 'lodash';
|
|
|
4
4
|
import { generateTextSpec } from '../../../ntv';
|
|
5
5
|
import { InsightNarrativeStrategy } from './base';
|
|
6
6
|
import { getDiffDesc } from './helpers';
|
|
7
|
-
import {
|
|
8
|
-
import { format } from 'date-fns';
|
|
7
|
+
import { formatInsightDate, formatInsightDateRange } from '../../../utils/dateFormat';
|
|
9
8
|
var variableMetaMap = {
|
|
10
9
|
dateRange: {
|
|
11
10
|
varType: 'time_desc',
|
|
@@ -46,7 +45,7 @@ var TimeSeriesOutlierNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
46
45
|
var spec = generateTextSpec({
|
|
47
46
|
structures: TimeSeriesOutlierNarrativeStrategy.structures[lang],
|
|
48
47
|
variable: {
|
|
49
|
-
dateRange:
|
|
48
|
+
dateRange: formatInsightDateRange(first(data)[dimension], last(data)[dimension]),
|
|
50
49
|
total: patterns.length,
|
|
51
50
|
measure: measure,
|
|
52
51
|
max: maxBy(data, measure)[measure],
|
|
@@ -54,7 +53,7 @@ var TimeSeriesOutlierNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
54
53
|
outliers: patterns.map(function (point) {
|
|
55
54
|
var base = point.baselines[point.index];
|
|
56
55
|
var diff = point.y - base;
|
|
57
|
-
return __assign(__assign({}, point), { base: base, diffDesc: getDiffDesc(diff, lang), x:
|
|
56
|
+
return __assign(__assign({}, point), { base: base, diffDesc: getDiffDesc(diff, lang), x: formatInsightDate(point.x), diff: diff });
|
|
58
57
|
}),
|
|
59
58
|
},
|
|
60
59
|
});
|
|
@@ -2,8 +2,8 @@ import { __extends } from "tslib";
|
|
|
2
2
|
/* eslint-disable no-template-curly-in-string */
|
|
3
3
|
import { first, last } from 'lodash';
|
|
4
4
|
import { generateTextSpec } from '../../../ntv';
|
|
5
|
+
import { formatInsightDateRange } from '../../../utils/dateFormat';
|
|
5
6
|
import { InsightNarrativeStrategy } from './base';
|
|
6
|
-
import { formatDateRange } from "little-date";
|
|
7
7
|
var trendMapping = {
|
|
8
8
|
decreasing: '下降',
|
|
9
9
|
increasing: '上升',
|
|
@@ -32,7 +32,7 @@ var TrendNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
32
32
|
var spec = generateTextSpec({
|
|
33
33
|
structures: TrendNarrativeStrategy.structures[lang],
|
|
34
34
|
variable: {
|
|
35
|
-
dateRange:
|
|
35
|
+
dateRange: formatInsightDateRange(first(data)[dimension], last(data)[dimension]),
|
|
36
36
|
measure: measure,
|
|
37
37
|
trend: lang === 'en-US' ? trend : trendMapping[trend],
|
|
38
38
|
slope: regression.equation[0],
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { format } from 'date-fns';
|
|
2
|
+
import { formatDateRange } from 'little-date';
|
|
3
|
+
// 2025-03-19, 2025-03-19 00:00:00, 2025-03-19T00:00:00.000Z, 2025-03-19T00:00:00+02:00
|
|
4
|
+
var ISO_LIKE = /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
5
|
+
/**
|
|
6
|
+
* Parse an insight dimension value as a date. Only ISO-like strings and Date objects count;
|
|
7
|
+
* zone-less values are local time. `new Date('2025-03-19 00:00:00')` is non-standard (older
|
|
8
|
+
* Safari rejects it), so the space is normalised to `T` first.
|
|
9
|
+
*/
|
|
10
|
+
export var parseInsightDate = function (value) {
|
|
11
|
+
if (value instanceof Date)
|
|
12
|
+
return Number.isNaN(value.getTime()) ? undefined : value;
|
|
13
|
+
if (typeof value !== 'string' || !ISO_LIKE.test(value))
|
|
14
|
+
return undefined;
|
|
15
|
+
var date = new Date(value.length === 10 ? "".concat(value, "T00:00:00") : value.replace(' ', 'T'));
|
|
16
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
17
|
+
};
|
|
18
|
+
// a zone designator means an absolute instant (e.g. an hourly UTC bucket): always show its time
|
|
19
|
+
var ZONED = /\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})$/;
|
|
20
|
+
/**
|
|
21
|
+
* Whether to print a time of day. Zoned instants always do, so an hourly series reads
|
|
22
|
+
* `Oct 6, 23:00`, `Oct 7, 00:00` rather than switching to a date at local midnight. Zone-less
|
|
23
|
+
* values only do when the clock is not midnight (`2025-03-19 00:00:00` is a daily bucket).
|
|
24
|
+
*/
|
|
25
|
+
var showsTime = function (value, date) {
|
|
26
|
+
return value instanceof Date || (typeof value === 'string' && ZONED.test(value))
|
|
27
|
+
? true
|
|
28
|
+
: date.getHours() !== 0 || date.getMinutes() !== 0;
|
|
29
|
+
};
|
|
30
|
+
/** `Oct 7, 2026`, or `Oct 7, 14:00` when the value carries a time of day; non-dates pass through */
|
|
31
|
+
export var formatInsightDate = function (value) {
|
|
32
|
+
var date = parseInsightDate(value);
|
|
33
|
+
if (!date)
|
|
34
|
+
return String(value);
|
|
35
|
+
return format(date, showsTime(value, date) ? 'MMM d, HH:mm' : 'MMM d, yyyy');
|
|
36
|
+
};
|
|
37
|
+
/** `Oct 6 - 7, 2026` style range (little-date); non-dates fall back to `start ~ end` */
|
|
38
|
+
export var formatInsightDateRange = function (start, end) {
|
|
39
|
+
var from = parseInsightDate(start);
|
|
40
|
+
var to = parseInsightDate(end);
|
|
41
|
+
if (!from || !to)
|
|
42
|
+
return "".concat(start, "~").concat(end);
|
|
43
|
+
return formatDateRange(from, to, { includeTime: false });
|
|
44
|
+
};
|
package/esm/utils/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var changePointAugmentedMarksStrategy = function (insight) {
|
|
|
17
17
|
(0, commonMarks_1.pointMarkStrategy)([pattern], {
|
|
18
18
|
role: 'highlight',
|
|
19
19
|
name: 'change point',
|
|
20
|
-
label: function (pt) { return "".concat(pt.x, "
|
|
20
|
+
label: function (pt) { return "".concat((0, utils_1.formatInsightDate)(pt.x), " \u00B7 ").concat(measure, ": ").concat((0, utils_1.dataFormat)(pt.y)); },
|
|
21
21
|
}),
|
|
22
22
|
],
|
|
23
23
|
}); });
|
|
@@ -4,6 +4,7 @@ var tslib_1 = require("tslib");
|
|
|
4
4
|
/* eslint-disable no-template-curly-in-string */
|
|
5
5
|
var lodash_1 = require("lodash");
|
|
6
6
|
var ntv_1 = require("../../../ntv");
|
|
7
|
+
var dateFormat_1 = require("../../../utils/dateFormat");
|
|
7
8
|
var base_1 = require("./base");
|
|
8
9
|
var variableMetaMap = {
|
|
9
10
|
dateRange: {
|
|
@@ -27,7 +28,7 @@ var ChangePointNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
27
28
|
var spec = (0, ntv_1.generateTextSpec)({
|
|
28
29
|
structures: ChangePointNarrativeStrategy.structures[lang],
|
|
29
30
|
variable: {
|
|
30
|
-
dateRange:
|
|
31
|
+
dateRange: (0, dateFormat_1.formatInsightDateRange)((0, lodash_1.first)(data)[dimension], (0, lodash_1.last)(data)[dimension]),
|
|
31
32
|
measure: patterns[0].measure,
|
|
32
33
|
total: patterns.length,
|
|
33
34
|
},
|
|
@@ -6,8 +6,7 @@ var lodash_1 = require("lodash");
|
|
|
6
6
|
var ntv_1 = require("../../../ntv");
|
|
7
7
|
var base_1 = require("./base");
|
|
8
8
|
var helpers_1 = require("./helpers");
|
|
9
|
-
var
|
|
10
|
-
var date_fns_1 = require("date-fns");
|
|
9
|
+
var dateFormat_1 = require("../../../utils/dateFormat");
|
|
11
10
|
var variableMetaMap = {
|
|
12
11
|
dateRange: {
|
|
13
12
|
varType: 'time_desc',
|
|
@@ -48,7 +47,7 @@ var TimeSeriesOutlierNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
48
47
|
var spec = (0, ntv_1.generateTextSpec)({
|
|
49
48
|
structures: TimeSeriesOutlierNarrativeStrategy.structures[lang],
|
|
50
49
|
variable: {
|
|
51
|
-
dateRange:
|
|
50
|
+
dateRange: (0, dateFormat_1.formatInsightDateRange)((0, lodash_1.first)(data)[dimension], (0, lodash_1.last)(data)[dimension]),
|
|
52
51
|
total: patterns.length,
|
|
53
52
|
measure: measure,
|
|
54
53
|
max: (0, lodash_1.maxBy)(data, measure)[measure],
|
|
@@ -56,7 +55,7 @@ var TimeSeriesOutlierNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
56
55
|
outliers: patterns.map(function (point) {
|
|
57
56
|
var base = point.baselines[point.index];
|
|
58
57
|
var diff = point.y - base;
|
|
59
|
-
return tslib_1.__assign(tslib_1.__assign({}, point), { base: base, diffDesc: (0, helpers_1.getDiffDesc)(diff, lang), x: (0,
|
|
58
|
+
return tslib_1.__assign(tslib_1.__assign({}, point), { base: base, diffDesc: (0, helpers_1.getDiffDesc)(diff, lang), x: (0, dateFormat_1.formatInsightDate)(point.x), diff: diff });
|
|
60
59
|
}),
|
|
61
60
|
},
|
|
62
61
|
});
|
|
@@ -4,8 +4,8 @@ var tslib_1 = require("tslib");
|
|
|
4
4
|
/* eslint-disable no-template-curly-in-string */
|
|
5
5
|
var lodash_1 = require("lodash");
|
|
6
6
|
var ntv_1 = require("../../../ntv");
|
|
7
|
+
var dateFormat_1 = require("../../../utils/dateFormat");
|
|
7
8
|
var base_1 = require("./base");
|
|
8
|
-
var little_date_1 = require("little-date");
|
|
9
9
|
var trendMapping = {
|
|
10
10
|
decreasing: '下降',
|
|
11
11
|
increasing: '上升',
|
|
@@ -34,7 +34,7 @@ var TrendNarrativeStrategy = /** @class */ (function (_super) {
|
|
|
34
34
|
var spec = (0, ntv_1.generateTextSpec)({
|
|
35
35
|
structures: TrendNarrativeStrategy.structures[lang],
|
|
36
36
|
variable: {
|
|
37
|
-
dateRange:
|
|
37
|
+
dateRange: (0, dateFormat_1.formatInsightDateRange)((0, lodash_1.first)(data)[dimension], (0, lodash_1.last)(data)[dimension]),
|
|
38
38
|
measure: measure,
|
|
39
39
|
trend: lang === 'en-US' ? trend : trendMapping[trend],
|
|
40
40
|
slope: regression.equation[0],
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse an insight dimension value as a date. Only ISO-like strings and Date objects count;
|
|
3
|
+
* zone-less values are local time. `new Date('2025-03-19 00:00:00')` is non-standard (older
|
|
4
|
+
* Safari rejects it), so the space is normalised to `T` first.
|
|
5
|
+
*/
|
|
6
|
+
export declare const parseInsightDate: (value: unknown) => Date | undefined;
|
|
7
|
+
/** `Oct 7, 2026`, or `Oct 7, 14:00` when the value carries a time of day; non-dates pass through */
|
|
8
|
+
export declare const formatInsightDate: (value: unknown) => string;
|
|
9
|
+
/** `Oct 6 - 7, 2026` style range (little-date); non-dates fall back to `start ~ end` */
|
|
10
|
+
export declare const formatInsightDateRange: (start: unknown, end: unknown) => string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatInsightDateRange = exports.formatInsightDate = exports.parseInsightDate = void 0;
|
|
4
|
+
var date_fns_1 = require("date-fns");
|
|
5
|
+
var little_date_1 = require("little-date");
|
|
6
|
+
// 2025-03-19, 2025-03-19 00:00:00, 2025-03-19T00:00:00.000Z, 2025-03-19T00:00:00+02:00
|
|
7
|
+
var ISO_LIKE = /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
8
|
+
/**
|
|
9
|
+
* Parse an insight dimension value as a date. Only ISO-like strings and Date objects count;
|
|
10
|
+
* zone-less values are local time. `new Date('2025-03-19 00:00:00')` is non-standard (older
|
|
11
|
+
* Safari rejects it), so the space is normalised to `T` first.
|
|
12
|
+
*/
|
|
13
|
+
var parseInsightDate = function (value) {
|
|
14
|
+
if (value instanceof Date)
|
|
15
|
+
return Number.isNaN(value.getTime()) ? undefined : value;
|
|
16
|
+
if (typeof value !== 'string' || !ISO_LIKE.test(value))
|
|
17
|
+
return undefined;
|
|
18
|
+
var date = new Date(value.length === 10 ? "".concat(value, "T00:00:00") : value.replace(' ', 'T'));
|
|
19
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
20
|
+
};
|
|
21
|
+
exports.parseInsightDate = parseInsightDate;
|
|
22
|
+
// a zone designator means an absolute instant (e.g. an hourly UTC bucket): always show its time
|
|
23
|
+
var ZONED = /\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})$/;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to print a time of day. Zoned instants always do, so an hourly series reads
|
|
26
|
+
* `Oct 6, 23:00`, `Oct 7, 00:00` rather than switching to a date at local midnight. Zone-less
|
|
27
|
+
* values only do when the clock is not midnight (`2025-03-19 00:00:00` is a daily bucket).
|
|
28
|
+
*/
|
|
29
|
+
var showsTime = function (value, date) {
|
|
30
|
+
return value instanceof Date || (typeof value === 'string' && ZONED.test(value))
|
|
31
|
+
? true
|
|
32
|
+
: date.getHours() !== 0 || date.getMinutes() !== 0;
|
|
33
|
+
};
|
|
34
|
+
/** `Oct 7, 2026`, or `Oct 7, 14:00` when the value carries a time of day; non-dates pass through */
|
|
35
|
+
var formatInsightDate = function (value) {
|
|
36
|
+
var date = (0, exports.parseInsightDate)(value);
|
|
37
|
+
if (!date)
|
|
38
|
+
return String(value);
|
|
39
|
+
return (0, date_fns_1.format)(date, showsTime(value, date) ? 'MMM d, HH:mm' : 'MMM d, yyyy');
|
|
40
|
+
};
|
|
41
|
+
exports.formatInsightDate = formatInsightDate;
|
|
42
|
+
/** `Oct 6 - 7, 2026` style range (little-date); non-dates fall back to `start ~ end` */
|
|
43
|
+
var formatInsightDateRange = function (start, end) {
|
|
44
|
+
var from = (0, exports.parseInsightDate)(start);
|
|
45
|
+
var to = (0, exports.parseInsightDate)(end);
|
|
46
|
+
if (!from || !to)
|
|
47
|
+
return "".concat(start, "~").concat(end);
|
|
48
|
+
return (0, little_date_1.formatDateRange)(from, to, { includeTime: false });
|
|
49
|
+
};
|
|
50
|
+
exports.formatInsightDateRange = formatInsightDateRange;
|
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dataFormat = void 0;
|
|
3
|
+
exports.formatInsightDateRange = exports.formatInsightDate = exports.parseInsightDate = exports.dataFormat = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
tslib_1.__exportStar(require("./isType"), exports);
|
|
6
6
|
tslib_1.__exportStar(require("./arr2map"), exports);
|
|
7
7
|
var dataFormat_1 = require("./dataFormat");
|
|
8
8
|
Object.defineProperty(exports, "dataFormat", { enumerable: true, get: function () { return tslib_1.__importDefault(dataFormat_1).default; } });
|
|
9
|
+
var dateFormat_1 = require("./dateFormat");
|
|
10
|
+
Object.defineProperty(exports, "parseInsightDate", { enumerable: true, get: function () { return dateFormat_1.parseInsightDate; } });
|
|
11
|
+
Object.defineProperty(exports, "formatInsightDate", { enumerable: true, get: function () { return dateFormat_1.formatInsightDate; } });
|
|
12
|
+
Object.defineProperty(exports, "formatInsightDateRange", { enumerable: true, get: function () { return dateFormat_1.formatInsightDateRange; } });
|