afformative 0.6.2 → 0.6.3
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/README.md +3 -3
- package/dist/afformative.cjs.js +18 -30
- package/dist/afformative.esm.js +18 -29
- package/dist/afformative.umd.js +19 -63
- package/dist/afformative.umd.min.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/makeFormatter.d.ts +96 -93
- package/dist/types/makeFormatter.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/makeFormatter.test.ts +169 -110
- package/src/makeFormatter.ts +49 -44
package/README.md
CHANGED
|
@@ -81,13 +81,13 @@ Although formatters can render icons or custom translation components, we often
|
|
|
81
81
|
|
|
82
82
|
> Lexicographic sorting of items based on translations is a typical real world example, especially when you are using a custom React component for visualising the translation keys alongside the actual translations.
|
|
83
83
|
|
|
84
|
-
This is where usage suggestions
|
|
84
|
+
This is where usage suggestions come into play. Suggestions can be used to tell formatters that a value needs to be rendered with some special care. For example, pass `"primitive"` to tell a formatter that it should return a primitive value, such as a string.
|
|
85
85
|
|
|
86
86
|
```js
|
|
87
87
|
import { makeFormatter } from "afformative"
|
|
88
88
|
|
|
89
|
-
const booleanFormatter = makeFormatter((value,
|
|
90
|
-
if (
|
|
89
|
+
const booleanFormatter = makeFormatter((value, suggestions) => {
|
|
90
|
+
if (suggestions.includes("primitive")) {
|
|
91
91
|
return value ? "True" : "False"
|
|
92
92
|
}
|
|
93
93
|
|
package/dist/afformative.cjs.js
CHANGED
|
@@ -3,25 +3,18 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var _toConsumableArray = require('@babel/runtime/helpers/toConsumableArray');
|
|
6
|
-
var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
|
|
7
6
|
|
|
8
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
8
|
|
|
10
9
|
var _toConsumableArray__default = /*#__PURE__*/_interopDefaultLegacy(_toConsumableArray);
|
|
11
|
-
var _objectWithoutProperties__default = /*#__PURE__*/_interopDefaultLegacy(_objectWithoutProperties);
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// value: TInput,
|
|
21
|
-
// usageSuggestions: TSuggestion[],
|
|
22
|
-
// dataContext: Partial<TDataContext>,
|
|
23
|
-
// ): PrimitiveSuggestion extends TSuggestion ? TPrimitiveOutput : TOutput
|
|
24
|
-
// }
|
|
11
|
+
var ensureFormatSuggestionIntegrity = function ensureFormatSuggestionIntegrity(suggestions) {
|
|
12
|
+
return suggestions.includes("primitive") || !suggestions.includes("comparable") ? suggestions : ["primitive"].concat(_toConsumableArray__default["default"](suggestions));
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
var ensureFormatAsPrimitiveSuggestionIntegrity = function ensureFormatAsPrimitiveSuggestionIntegrity(suggestions) {
|
|
16
|
+
return suggestions.includes("primitive") ? suggestions : ["primitive"].concat(_toConsumableArray__default["default"](suggestions));
|
|
17
|
+
};
|
|
25
18
|
|
|
26
19
|
/**
|
|
27
20
|
* Creates a new formatter.
|
|
@@ -30,38 +23,33 @@ var _objectWithoutProperties__default = /*#__PURE__*/_interopDefaultLegacy(_obje
|
|
|
30
23
|
* @param formatterOptions Additional options for the formatter.
|
|
31
24
|
*/
|
|
32
25
|
var makeFormatter = function makeFormatter(format, formatterOptions) {
|
|
33
|
-
var formatter = function formatter(
|
|
34
|
-
var _format;
|
|
35
|
-
|
|
36
|
-
var children = _ref.children,
|
|
37
|
-
_ref$suggestions = _ref.suggestions,
|
|
38
|
-
suggestions = _ref$suggestions === void 0 ? [] : _ref$suggestions,
|
|
39
|
-
dataContext = _objectWithoutProperties__default['default'](_ref, ["children", "suggestions"]);
|
|
26
|
+
var formatter = function formatter(props) {
|
|
27
|
+
var _format, _props$suggestions;
|
|
40
28
|
|
|
41
|
-
return (_format = format(children, suggestions,
|
|
29
|
+
return (_format = format(props.children, (_props$suggestions = props.suggestions) !== null && _props$suggestions !== void 0 ? _props$suggestions : [], props)) !== null && _format !== void 0 ? _format : null;
|
|
42
30
|
};
|
|
43
31
|
|
|
44
32
|
formatter.displayName = formatterOptions === null || formatterOptions === void 0 ? void 0 : formatterOptions.displayName;
|
|
45
33
|
|
|
46
34
|
formatter.format = function (value) {
|
|
47
|
-
var
|
|
35
|
+
var suggestions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
48
36
|
var dataContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
49
|
-
return format(value,
|
|
37
|
+
return format(value, ensureFormatSuggestionIntegrity(suggestions), dataContext);
|
|
50
38
|
};
|
|
51
39
|
|
|
52
40
|
formatter.formatAsPrimitive = function (value) {
|
|
53
|
-
var
|
|
41
|
+
var suggestions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
54
42
|
var dataContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
55
|
-
return format(value,
|
|
43
|
+
return format(value, ensureFormatAsPrimitiveSuggestionIntegrity(suggestions), dataContext);
|
|
56
44
|
};
|
|
57
45
|
|
|
58
46
|
formatter.wrap = function (nextFormat, nextFormatterOptions) {
|
|
59
|
-
var nextFormatter = makeFormatter(function (value,
|
|
60
|
-
var delegate = function delegate(delegatedValue,
|
|
61
|
-
return formatter.format(delegatedValue,
|
|
47
|
+
var nextFormatter = makeFormatter(function (value, suggestions, dataContext) {
|
|
48
|
+
var delegate = function delegate(delegatedValue, delegatedSuggestions, delegatedDataContext) {
|
|
49
|
+
return formatter.format(delegatedValue, delegatedSuggestions !== null && delegatedSuggestions !== void 0 ? delegatedSuggestions : suggestions, delegatedDataContext !== null && delegatedDataContext !== void 0 ? delegatedDataContext : dataContext);
|
|
62
50
|
};
|
|
63
51
|
|
|
64
|
-
return nextFormat(delegate, value,
|
|
52
|
+
return nextFormat(delegate, value, suggestions, dataContext);
|
|
65
53
|
}, nextFormatterOptions !== null && nextFormatterOptions !== void 0 ? nextFormatterOptions : formatterOptions);
|
|
66
54
|
nextFormatter.innerFormatter = formatter;
|
|
67
55
|
return nextFormatter;
|
package/dist/afformative.esm.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import _toConsumableArray from '@babel/runtime/helpers/esm/toConsumableArray';
|
|
2
|
-
import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// value: TInput,
|
|
12
|
-
// usageSuggestions: TSuggestion[],
|
|
13
|
-
// dataContext: Partial<TDataContext>,
|
|
14
|
-
// ): PrimitiveSuggestion extends TSuggestion ? TPrimitiveOutput : TOutput
|
|
15
|
-
// }
|
|
3
|
+
var ensureFormatSuggestionIntegrity = function ensureFormatSuggestionIntegrity(suggestions) {
|
|
4
|
+
return suggestions.includes("primitive") || !suggestions.includes("comparable") ? suggestions : ["primitive"].concat(_toConsumableArray(suggestions));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
var ensureFormatAsPrimitiveSuggestionIntegrity = function ensureFormatAsPrimitiveSuggestionIntegrity(suggestions) {
|
|
8
|
+
return suggestions.includes("primitive") ? suggestions : ["primitive"].concat(_toConsumableArray(suggestions));
|
|
9
|
+
};
|
|
16
10
|
|
|
17
11
|
/**
|
|
18
12
|
* Creates a new formatter.
|
|
@@ -21,38 +15,33 @@ import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutPr
|
|
|
21
15
|
* @param formatterOptions Additional options for the formatter.
|
|
22
16
|
*/
|
|
23
17
|
var makeFormatter = function makeFormatter(format, formatterOptions) {
|
|
24
|
-
var formatter = function formatter(
|
|
25
|
-
var _format;
|
|
26
|
-
|
|
27
|
-
var children = _ref.children,
|
|
28
|
-
_ref$suggestions = _ref.suggestions,
|
|
29
|
-
suggestions = _ref$suggestions === void 0 ? [] : _ref$suggestions,
|
|
30
|
-
dataContext = _objectWithoutProperties(_ref, ["children", "suggestions"]);
|
|
18
|
+
var formatter = function formatter(props) {
|
|
19
|
+
var _format, _props$suggestions;
|
|
31
20
|
|
|
32
|
-
return (_format = format(children, suggestions,
|
|
21
|
+
return (_format = format(props.children, (_props$suggestions = props.suggestions) !== null && _props$suggestions !== void 0 ? _props$suggestions : [], props)) !== null && _format !== void 0 ? _format : null;
|
|
33
22
|
};
|
|
34
23
|
|
|
35
24
|
formatter.displayName = formatterOptions === null || formatterOptions === void 0 ? void 0 : formatterOptions.displayName;
|
|
36
25
|
|
|
37
26
|
formatter.format = function (value) {
|
|
38
|
-
var
|
|
27
|
+
var suggestions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
39
28
|
var dataContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
40
|
-
return format(value,
|
|
29
|
+
return format(value, ensureFormatSuggestionIntegrity(suggestions), dataContext);
|
|
41
30
|
};
|
|
42
31
|
|
|
43
32
|
formatter.formatAsPrimitive = function (value) {
|
|
44
|
-
var
|
|
33
|
+
var suggestions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
45
34
|
var dataContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
46
|
-
return format(value,
|
|
35
|
+
return format(value, ensureFormatAsPrimitiveSuggestionIntegrity(suggestions), dataContext);
|
|
47
36
|
};
|
|
48
37
|
|
|
49
38
|
formatter.wrap = function (nextFormat, nextFormatterOptions) {
|
|
50
|
-
var nextFormatter = makeFormatter(function (value,
|
|
51
|
-
var delegate = function delegate(delegatedValue,
|
|
52
|
-
return formatter.format(delegatedValue,
|
|
39
|
+
var nextFormatter = makeFormatter(function (value, suggestions, dataContext) {
|
|
40
|
+
var delegate = function delegate(delegatedValue, delegatedSuggestions, delegatedDataContext) {
|
|
41
|
+
return formatter.format(delegatedValue, delegatedSuggestions !== null && delegatedSuggestions !== void 0 ? delegatedSuggestions : suggestions, delegatedDataContext !== null && delegatedDataContext !== void 0 ? delegatedDataContext : dataContext);
|
|
53
42
|
};
|
|
54
43
|
|
|
55
|
-
return nextFormat(delegate, value,
|
|
44
|
+
return nextFormat(delegate, value, suggestions, dataContext);
|
|
56
45
|
}, nextFormatterOptions !== null && nextFormatterOptions !== void 0 ? nextFormatterOptions : formatterOptions);
|
|
57
46
|
nextFormatter.innerFormatter = formatter;
|
|
58
47
|
return nextFormatter;
|
package/dist/afformative.umd.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
3
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Afformative = {}));
|
|
5
|
-
}(this, (function (exports) { 'use strict';
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _arrayLikeToArray(arr, len) {
|
|
8
8
|
if (len == null || len > arr.length) len = arr.length;
|
|
@@ -39,52 +39,13 @@ function _toConsumableArray(arr) {
|
|
|
39
39
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
var sourceKeys = Object.keys(source);
|
|
46
|
-
var key, i;
|
|
47
|
-
|
|
48
|
-
for (i = 0; i < sourceKeys.length; i++) {
|
|
49
|
-
key = sourceKeys[i];
|
|
50
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
51
|
-
target[key] = source[key];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return target;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function _objectWithoutProperties(source, excluded) {
|
|
58
|
-
if (source == null) return {};
|
|
59
|
-
var target = _objectWithoutPropertiesLoose(source, excluded);
|
|
60
|
-
var key, i;
|
|
61
|
-
|
|
62
|
-
if (Object.getOwnPropertySymbols) {
|
|
63
|
-
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
|
64
|
-
|
|
65
|
-
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
|
66
|
-
key = sourceSymbolKeys[i];
|
|
67
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
68
|
-
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
|
69
|
-
target[key] = source[key];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return target;
|
|
74
|
-
}
|
|
42
|
+
var ensureFormatSuggestionIntegrity = function ensureFormatSuggestionIntegrity(suggestions) {
|
|
43
|
+
return suggestions.includes("primitive") || !suggestions.includes("comparable") ? suggestions : ["primitive"].concat(_toConsumableArray(suggestions));
|
|
44
|
+
};
|
|
75
45
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// which is, of course, nonsense. Same applies to `FormatChainDefinition`.
|
|
80
|
-
//
|
|
81
|
-
// interface FormatDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
82
|
-
// <TSuggestion extends Suggestion>(
|
|
83
|
-
// value: TInput,
|
|
84
|
-
// usageSuggestions: TSuggestion[],
|
|
85
|
-
// dataContext: Partial<TDataContext>,
|
|
86
|
-
// ): PrimitiveSuggestion extends TSuggestion ? TPrimitiveOutput : TOutput
|
|
87
|
-
// }
|
|
46
|
+
var ensureFormatAsPrimitiveSuggestionIntegrity = function ensureFormatAsPrimitiveSuggestionIntegrity(suggestions) {
|
|
47
|
+
return suggestions.includes("primitive") ? suggestions : ["primitive"].concat(_toConsumableArray(suggestions));
|
|
48
|
+
};
|
|
88
49
|
|
|
89
50
|
/**
|
|
90
51
|
* Creates a new formatter.
|
|
@@ -93,38 +54,33 @@ function _objectWithoutProperties(source, excluded) {
|
|
|
93
54
|
* @param formatterOptions Additional options for the formatter.
|
|
94
55
|
*/
|
|
95
56
|
var makeFormatter = function makeFormatter(format, formatterOptions) {
|
|
96
|
-
var formatter = function formatter(
|
|
97
|
-
var _format;
|
|
98
|
-
|
|
99
|
-
var children = _ref.children,
|
|
100
|
-
_ref$suggestions = _ref.suggestions,
|
|
101
|
-
suggestions = _ref$suggestions === void 0 ? [] : _ref$suggestions,
|
|
102
|
-
dataContext = _objectWithoutProperties(_ref, ["children", "suggestions"]);
|
|
57
|
+
var formatter = function formatter(props) {
|
|
58
|
+
var _format, _props$suggestions;
|
|
103
59
|
|
|
104
|
-
return (_format = format(children, suggestions,
|
|
60
|
+
return (_format = format(props.children, (_props$suggestions = props.suggestions) !== null && _props$suggestions !== void 0 ? _props$suggestions : [], props)) !== null && _format !== void 0 ? _format : null;
|
|
105
61
|
};
|
|
106
62
|
|
|
107
63
|
formatter.displayName = formatterOptions === null || formatterOptions === void 0 ? void 0 : formatterOptions.displayName;
|
|
108
64
|
|
|
109
65
|
formatter.format = function (value) {
|
|
110
|
-
var
|
|
66
|
+
var suggestions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
111
67
|
var dataContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
112
|
-
return format(value,
|
|
68
|
+
return format(value, ensureFormatSuggestionIntegrity(suggestions), dataContext);
|
|
113
69
|
};
|
|
114
70
|
|
|
115
71
|
formatter.formatAsPrimitive = function (value) {
|
|
116
|
-
var
|
|
72
|
+
var suggestions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
117
73
|
var dataContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
118
|
-
return format(value,
|
|
74
|
+
return format(value, ensureFormatAsPrimitiveSuggestionIntegrity(suggestions), dataContext);
|
|
119
75
|
};
|
|
120
76
|
|
|
121
77
|
formatter.wrap = function (nextFormat, nextFormatterOptions) {
|
|
122
|
-
var nextFormatter = makeFormatter(function (value,
|
|
123
|
-
var delegate = function delegate(delegatedValue,
|
|
124
|
-
return formatter.format(delegatedValue,
|
|
78
|
+
var nextFormatter = makeFormatter(function (value, suggestions, dataContext) {
|
|
79
|
+
var delegate = function delegate(delegatedValue, delegatedSuggestions, delegatedDataContext) {
|
|
80
|
+
return formatter.format(delegatedValue, delegatedSuggestions !== null && delegatedSuggestions !== void 0 ? delegatedSuggestions : suggestions, delegatedDataContext !== null && delegatedDataContext !== void 0 ? delegatedDataContext : dataContext);
|
|
125
81
|
};
|
|
126
82
|
|
|
127
|
-
return nextFormat(delegate, value,
|
|
83
|
+
return nextFormat(delegate, value, suggestions, dataContext);
|
|
128
84
|
}, nextFormatterOptions !== null && nextFormatterOptions !== void 0 ? nextFormatterOptions : formatterOptions);
|
|
129
85
|
nextFormatter.innerFormatter = formatter;
|
|
130
86
|
return nextFormatter;
|
|
@@ -137,4 +93,4 @@ exports.makeFormatter = makeFormatter;
|
|
|
137
93
|
|
|
138
94
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
139
95
|
|
|
140
|
-
}))
|
|
96
|
+
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,
|
|
1
|
+
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).Afformative={})}(this,(function(t){"use strict";function n(t,n){(null==n||n>t.length)&&(n=t.length);for(var r=0,e=Array(n);n>r;r++)e[r]=t[r];return e}function r(t){return function(t){if(Array.isArray(t))return n(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,r){if(t){if("string"==typeof t)return n(t,r);var e=Object.prototype.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?n(t,r):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}var e=function(t){return t.includes("primitive")||!t.includes("comparable")?t:["primitive"].concat(r(t))},i=function(t){return t.includes("primitive")?t:["primitive"].concat(r(t))};t.makeFormatter=function t(n,r){var o=function(t){var r,e;return null!==(r=n(t.children,null!==(e=t.suggestions)&&void 0!==e?e:[],t))&&void 0!==r?r:null};return o.displayName=null==r?void 0:r.displayName,o.format=function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return n(t,e(r),i)},o.formatAsPrimitive=function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return n(t,i(r),e)},o.wrap=function(n,e){var i=t((function(t,r,e){return n((function(t,n,i){return o.format(t,null!=n?n:r,null!=i?i:e)}),t,r,e)}),null!=e?e:r);return i.innerFormatter=o,i},o},Object.defineProperty(t,"__esModule",{value:!0})}));
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./makeFormatter";
|
|
1
|
+
export * from "./makeFormatter";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,94 +1,97 @@
|
|
|
1
|
-
declare type PrimitiveSuggestion = "primitive";
|
|
2
|
-
declare type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
declare type PrimitiveSuggestion = "primitive" | "comparable";
|
|
2
|
+
declare type Suggestion = PrimitiveSuggestion | "abbreviated" | "as-icon"
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Since v0.6.3. Use `"as-icon"` or `"with-icon"` instead.
|
|
5
|
+
*/
|
|
6
|
+
| "icon" | "verbose" | "with-icon";
|
|
7
|
+
declare type DataContext = Record<string, any>;
|
|
8
|
+
interface FormatterOptions {
|
|
9
|
+
/** Formatter name, useful for debugging or advanced pattern matching. */
|
|
10
|
+
displayName?: string;
|
|
11
|
+
}
|
|
12
|
+
interface FormatDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
13
|
+
(
|
|
14
|
+
/** Value to format. */
|
|
15
|
+
value: TInput,
|
|
16
|
+
/** Suggestions passed by the consumer of a formatter. */
|
|
17
|
+
suggestions: Suggestion[],
|
|
18
|
+
/** Additional data context to be used by the formatter. */
|
|
19
|
+
dataContext: Partial<TDataContext>): TOutput | TPrimitiveOutput;
|
|
20
|
+
}
|
|
21
|
+
interface FormatMethod<TInput, TOutput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
22
|
+
(
|
|
23
|
+
/** Value to format. */
|
|
24
|
+
value: TInput,
|
|
25
|
+
/** Suggestions the formatter should take note of. */
|
|
26
|
+
suggestions?: Suggestion[],
|
|
27
|
+
/** Additional data context the formatter might find useful. */
|
|
28
|
+
dataContext?: Partial<TDataContext>): TOutput | TPrimitiveOutput;
|
|
29
|
+
}
|
|
30
|
+
interface FormatAsPrimitiveMethod<TInput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
31
|
+
(
|
|
32
|
+
/** Value to format. */
|
|
33
|
+
value: TInput,
|
|
34
|
+
/** Suggestions the formatter should take note of in addition to `primitive`. */
|
|
35
|
+
suggestions?: Suggestion[],
|
|
36
|
+
/** Additional data context the formatter might find useful. */
|
|
37
|
+
dataContext?: Partial<TDataContext>): TPrimitiveOutput;
|
|
38
|
+
}
|
|
39
|
+
interface FormatChainDefinition<TInnerInput, TInnerOutput, TInnerPrimitiveInput, TInnerDataContext extends DataContext, TOuterInput, TOuterOutput, TOuterPrimitiveOutput, TOuterDataContext extends DataContext> {
|
|
40
|
+
(
|
|
41
|
+
/**
|
|
42
|
+
* The `formatter.format` method which can be used to delegate the formatting
|
|
43
|
+
* to the wrapped formatter. Delegation is simplified so if no suggestions or contextual
|
|
44
|
+
* props are passed, the original ones are used instead.
|
|
45
|
+
*/
|
|
46
|
+
delegate: FormatMethod<TInnerInput, TInnerOutput, TInnerPrimitiveInput, TInnerDataContext>,
|
|
47
|
+
/** Value to format. */
|
|
48
|
+
value: TOuterInput,
|
|
49
|
+
/** Suggestions the formatter should take note of. */
|
|
50
|
+
suggestions: Suggestion[],
|
|
51
|
+
/** Additional data context the formatter might find useful. */
|
|
52
|
+
dataContext: Partial<TOuterDataContext>): TOuterOutput | TOuterPrimitiveOutput;
|
|
53
|
+
}
|
|
54
|
+
declare type FormatterProps<TInput, TDataContext extends DataContext> = {
|
|
55
|
+
/** Value to format. */
|
|
56
|
+
children: TInput;
|
|
57
|
+
/** Suggestions the formatter should take note of. */
|
|
58
|
+
suggestions?: Suggestion[];
|
|
59
|
+
} & Partial<TDataContext>;
|
|
60
|
+
export interface Formatter<TInput, TOutput, TPrimitiveOutput = TOutput, TDataContext extends DataContext = DataContext> {
|
|
61
|
+
/** Formatter name, useful for debugging or advanced pattern matching. */
|
|
62
|
+
displayName?: string;
|
|
63
|
+
/** Formats a value. */
|
|
64
|
+
format: FormatMethod<TInput, TOutput, TPrimitiveOutput, TDataContext>;
|
|
65
|
+
/** Formats a value with the `primitive` suggestion. */
|
|
66
|
+
formatAsPrimitive: FormatAsPrimitiveMethod<TInput, TPrimitiveOutput, TDataContext>;
|
|
67
|
+
/** The callee of the `wrap` method used to produce this formatter. */
|
|
68
|
+
innerFormatter?: Formatter<any, any, any, any>;
|
|
69
|
+
/**
|
|
70
|
+
* Creates a new formatter from an existing one. Allows overriding of formatter behaviour
|
|
71
|
+
* for certain values.
|
|
72
|
+
*/
|
|
73
|
+
wrap: <TNextInput = TInput, TNextOutput = TOutput, TNextPrimitiveOutput = TPrimitiveOutput, TNextDataContext extends TDataContext = TDataContext>(
|
|
74
|
+
/**
|
|
75
|
+
* Function used to format the value. Has the same signature as the one passed
|
|
76
|
+
* to `makeFormatter`, except a `delegate` function is passed in the first position.
|
|
77
|
+
* This function can be used to delegate formatting to the original (inner) formatter.
|
|
78
|
+
*/
|
|
79
|
+
nextFormat: FormatChainDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext, TNextInput, TNextOutput, TNextPrimitiveOutput, TNextDataContext>,
|
|
80
|
+
/** New formatter options, replacing the original ones. */
|
|
81
|
+
nextFormatterOptions?: FormatterOptions) => Formatter<TNextInput, TNextOutput, TNextPrimitiveOutput, TNextDataContext>;
|
|
82
|
+
/**
|
|
83
|
+
* Backwards-compatible way to use the formatter as a React component.
|
|
84
|
+
*
|
|
85
|
+
* @deprecated Since v0.6.0. Prefer using the `format` method instead.
|
|
86
|
+
*/
|
|
87
|
+
(props: FormatterProps<TInput, TDataContext>): TOutput | TPrimitiveOutput | null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Creates a new formatter.
|
|
91
|
+
*
|
|
92
|
+
* @param format Function used to format the value.
|
|
93
|
+
* @param formatterOptions Additional options for the formatter.
|
|
94
|
+
*/
|
|
95
|
+
export declare const makeFormatter: <TInput, TOutput, TPrimitiveOutput = TOutput, TDataContext extends DataContext = DataContext>(format: FormatDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext>, formatterOptions?: FormatterOptions | undefined) => Formatter<TInput, TOutput, TPrimitiveOutput, TDataContext>;
|
|
96
|
+
export {};
|
|
94
97
|
//# sourceMappingURL=makeFormatter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makeFormatter.d.ts","sourceRoot":"","sources":["../../src/makeFormatter.ts"],"names":[],"mappings":"AAAA,aAAK,mBAAmB,GAAG,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"makeFormatter.d.ts","sourceRoot":"","sources":["../../src/makeFormatter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "afformative",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/afformative.cjs.js",
|
|
6
6
|
"module": "dist/afformative.esm.js",
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"sideEffects": false,
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "ddc8c8682e2777c730108466792b7670d8a43f06"
|
|
38
38
|
}
|
|
@@ -2,158 +2,217 @@ import { makeFormatter } from "./makeFormatter"
|
|
|
2
2
|
|
|
3
3
|
const toUpperCase = (string: string) => string.toUpperCase()
|
|
4
4
|
|
|
5
|
+
const upperCaseFormatter = makeFormatter<string, string>(toUpperCase)
|
|
6
|
+
|
|
7
|
+
interface ValueRecord {
|
|
8
|
+
value: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const valueRecordUpperCaseFormatter = makeFormatter<ValueRecord, ValueRecord, string>(
|
|
12
|
+
({ value }, suggestions) => {
|
|
13
|
+
if (suggestions.includes("primitive")) {
|
|
14
|
+
return toUpperCase(value)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { value: toUpperCase(value) }
|
|
18
|
+
},
|
|
19
|
+
)
|
|
20
|
+
|
|
5
21
|
describe("makeFormatter", () => {
|
|
6
|
-
|
|
7
|
-
const formatter = makeFormatter<string, string>(toUpperCase)
|
|
8
|
-
expect(formatter.format("foo")).toBe("FOO")
|
|
9
|
-
})
|
|
22
|
+
const format = jest.fn()
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
expect(formatter.displayName).toBe("UpperFormatter")
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
jest.clearAllMocks()
|
|
14
26
|
})
|
|
15
27
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
describe("displayName", () => {
|
|
29
|
+
it("accepts a `displayName` option", () => {
|
|
30
|
+
const displayName = "upperCaseFormatter"
|
|
31
|
+
const formatter = makeFormatter<string, string>(toUpperCase, { displayName })
|
|
32
|
+
expect(formatter.displayName).toBe(displayName)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
describe("format", () => {
|
|
37
|
+
it("handles trivial formatting", () => {
|
|
38
|
+
expect(upperCaseFormatter.format("foo")).toBe("FOO")
|
|
23
39
|
})
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
it("passes through non-empty suggestions", () => {
|
|
42
|
+
const formatter = makeFormatter<string, string>(format)
|
|
43
|
+
formatter.format("foo", ["abbreviated"])
|
|
44
|
+
expect(format.mock.calls[0][1]).toEqual(["abbreviated"])
|
|
45
|
+
})
|
|
29
46
|
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
it("passes through empty suggestions", () => {
|
|
48
|
+
const formatter = makeFormatter<string, string>(format)
|
|
49
|
+
formatter.format("foo", [])
|
|
50
|
+
expect(format.mock.calls[0][1]).toEqual([])
|
|
51
|
+
})
|
|
32
52
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
it("defaults suggestions to an empty array", () => {
|
|
54
|
+
const formatter = makeFormatter<string, string>(format)
|
|
55
|
+
formatter.format("foo")
|
|
56
|
+
expect(format.mock.calls[0][1]).toEqual([])
|
|
57
|
+
})
|
|
37
58
|
|
|
38
|
-
|
|
59
|
+
it("passes through the `primitive` suggestion", () => {
|
|
60
|
+
const formatter = makeFormatter<string, string>(format)
|
|
61
|
+
formatter.format("foo", ["primitive"])
|
|
62
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive"])
|
|
39
63
|
})
|
|
40
64
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
65
|
+
it("passes the `primitive` suggestion alongside `comparable` when missing", () => {
|
|
66
|
+
const formatter = makeFormatter<string, string>(format)
|
|
67
|
+
formatter.format("foo", ["comparable"])
|
|
68
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive", "comparable"])
|
|
69
|
+
})
|
|
45
70
|
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
it("does not pass the `primitive` suggestion alongside other suggestions when missing", () => {
|
|
72
|
+
const formatter = makeFormatter<string, string>(format)
|
|
73
|
+
formatter.format("foo", ["abbreviated", "verbose"])
|
|
74
|
+
expect(format.mock.calls[0][1]).toEqual(["abbreviated", "verbose"])
|
|
75
|
+
})
|
|
48
76
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
it("supports data context", () => {
|
|
78
|
+
const formatter = makeFormatter<string, string>(format)
|
|
79
|
+
formatter.format("foo", [], { foo: "bar" })
|
|
80
|
+
expect(format.mock.calls[0][2]).toEqual({ foo: "bar" })
|
|
81
|
+
})
|
|
52
82
|
|
|
53
|
-
|
|
54
|
-
|
|
83
|
+
it("defaults data context to an empty object", () => {
|
|
84
|
+
const formatter = makeFormatter<string, string>(format)
|
|
85
|
+
formatter.format("foo")
|
|
86
|
+
expect(format.mock.calls[0][2]).toEqual({})
|
|
87
|
+
})
|
|
55
88
|
})
|
|
56
89
|
|
|
57
|
-
|
|
58
|
-
|
|
90
|
+
describe("formatAsPrimitive", () => {
|
|
91
|
+
it("handles trivial formatting", () => {
|
|
92
|
+
expect(upperCaseFormatter.formatAsPrimitive("foo")).toBe("FOO")
|
|
93
|
+
})
|
|
59
94
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
95
|
+
it("passes the `primitive` suggestion when no suggestions are passed", () => {
|
|
96
|
+
const formatter = makeFormatter<string, string>(format)
|
|
97
|
+
formatter.formatAsPrimitive("foo")
|
|
98
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive"])
|
|
99
|
+
})
|
|
63
100
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
101
|
+
it("passes the `primitive` suggestion when empty suggestions are passed", () => {
|
|
102
|
+
const formatter = makeFormatter<string, string>(format)
|
|
103
|
+
formatter.formatAsPrimitive("foo", [])
|
|
104
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive"])
|
|
105
|
+
})
|
|
68
106
|
|
|
69
|
-
|
|
70
|
-
|
|
107
|
+
it("passes the `primitive` suggestion when a different suggestion is passed", () => {
|
|
108
|
+
const formatter = makeFormatter<string, string>(format)
|
|
109
|
+
formatter.formatAsPrimitive("foo", ["abbreviated"])
|
|
110
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive", "abbreviated"])
|
|
111
|
+
})
|
|
71
112
|
|
|
72
|
-
|
|
113
|
+
it("passes through the `primitive` suggestion", () => {
|
|
114
|
+
const formatter = makeFormatter<string, string>(format)
|
|
115
|
+
formatter.formatAsPrimitive("foo", ["primitive"])
|
|
116
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive"])
|
|
117
|
+
})
|
|
73
118
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
119
|
+
it("passes the `primitive` suggestion alongside `comparable`", () => {
|
|
120
|
+
const formatter = makeFormatter<string, string>(format)
|
|
121
|
+
formatter.formatAsPrimitive("foo", ["comparable"])
|
|
122
|
+
expect(format.mock.calls[0][1]).toEqual(["primitive", "comparable"])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it("supports data context", () => {
|
|
126
|
+
const formatter = makeFormatter<string, string>(format)
|
|
127
|
+
formatter.formatAsPrimitive("foo", [], { foo: "bar" })
|
|
128
|
+
expect(format.mock.calls[0][2]).toEqual({ foo: "bar" })
|
|
129
|
+
})
|
|
77
130
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
131
|
+
it("defaults data context to an empty object", () => {
|
|
132
|
+
const formatter = makeFormatter<string, string>(format)
|
|
133
|
+
formatter.formatAsPrimitive("foo")
|
|
134
|
+
expect(format.mock.calls[0][2]).toEqual({})
|
|
135
|
+
})
|
|
81
136
|
})
|
|
82
137
|
|
|
83
|
-
|
|
84
|
-
|
|
138
|
+
describe("wrap", () => {
|
|
139
|
+
it("handles trivial wrapping", () => {
|
|
140
|
+
const formatter = makeFormatter<string, string>(format).wrap(() => "bar")
|
|
141
|
+
expect(formatter.format("foo")).toBe("bar")
|
|
142
|
+
})
|
|
85
143
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
144
|
+
it("handles single-value mapping", () => {
|
|
145
|
+
const formatter = makeFormatter<string, string>(format).wrap((delegate, value) =>
|
|
146
|
+
value === "ping" ? "pong" : delegate(value),
|
|
147
|
+
)
|
|
89
148
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
forcedValue ?? delegate(value),
|
|
93
|
-
)
|
|
149
|
+
expect(formatter.format("ping")).toBe("pong")
|
|
150
|
+
})
|
|
94
151
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
152
|
+
it("handles delegation to the original formatter", () => {
|
|
153
|
+
const formatter = upperCaseFormatter.wrap((delegate, value) =>
|
|
154
|
+
value === "ping" ? "pong" : delegate(value),
|
|
155
|
+
)
|
|
98
156
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (suggestions.includes("abbreviated")) {
|
|
102
|
-
return value[0]
|
|
103
|
-
}
|
|
157
|
+
expect(formatter.format("foo")).toBe("FOO")
|
|
158
|
+
})
|
|
104
159
|
|
|
105
|
-
|
|
160
|
+
it("sets the `innerFormatter` property", () => {
|
|
161
|
+
const wrappedFormatter = upperCaseFormatter.wrap((delegate, value) => delegate(value))
|
|
162
|
+
expect(wrappedFormatter.innerFormatter).toBe(upperCaseFormatter)
|
|
106
163
|
})
|
|
107
164
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
165
|
+
it("passes the `primitive` suggestion to `delegate` when using `formatAsPrimitive`", () => {
|
|
166
|
+
const formatter = valueRecordUpperCaseFormatter.wrap((delegate, value) => delegate(value))
|
|
167
|
+
expect(formatter.formatAsPrimitive({ value: "foo" })).toBe("FOO")
|
|
168
|
+
})
|
|
111
169
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
})
|
|
170
|
+
it("passes suggestions through to `delegate` when not overriden", () => {
|
|
171
|
+
const formatter = valueRecordUpperCaseFormatter.wrap((delegate, value) => delegate(value))
|
|
172
|
+
expect(formatter.format({ value: "foo" }, ["primitive"])).toBe("FOO")
|
|
173
|
+
})
|
|
117
174
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
175
|
+
it("supports overriding of suggestions for `delegate`", () => {
|
|
176
|
+
const formatter = valueRecordUpperCaseFormatter.wrap((delegate, value) => delegate(value, []))
|
|
177
|
+
expect(formatter.format({ value: "foo" }, ["primitive"])).toEqual({ value: "FOO" })
|
|
178
|
+
})
|
|
122
179
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
value
|
|
126
|
-
|
|
180
|
+
it("supports modifying the input structure", () => {
|
|
181
|
+
const formatter = upperCaseFormatter.wrap<ValueRecord>((delegate, { value }) =>
|
|
182
|
+
delegate(value),
|
|
183
|
+
)
|
|
127
184
|
|
|
128
|
-
|
|
185
|
+
expect(formatter.format({ value: "foo" })).toBe("FOO")
|
|
186
|
+
})
|
|
129
187
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
188
|
+
it("supports modifying the output structure", () => {
|
|
189
|
+
const formatter = upperCaseFormatter.wrap<ValueRecord, ValueRecord>(
|
|
190
|
+
(delegate, { value }) => ({ value: delegate(value) }),
|
|
191
|
+
)
|
|
134
192
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
193
|
+
expect(formatter.format({ value: "foo" })).toEqual({ value: "FOO" })
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it("supports modifying the data context structure", () => {
|
|
197
|
+
interface ListContext {
|
|
198
|
+
row: string
|
|
199
|
+
}
|
|
138
200
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)
|
|
201
|
+
const listFormatter = upperCaseFormatter.wrap<string, string, string, ListContext>(
|
|
202
|
+
(delegate, value, suggestions, { row }) => `${row}: ${delegate(value)}`,
|
|
203
|
+
)
|
|
143
204
|
|
|
144
|
-
|
|
205
|
+
interface GridContext extends ListContext {
|
|
206
|
+
column: string
|
|
207
|
+
}
|
|
145
208
|
|
|
146
|
-
|
|
147
|
-
complexFormatter.format({ index: 3, value: "procrastination" }, [], {
|
|
148
|
-
getIndex: ({ index }) => index,
|
|
149
|
-
}),
|
|
150
|
-
).toBe("c")
|
|
209
|
+
expect(listFormatter.format("foo", [], { row: "A" })).toBe("A: FOO")
|
|
151
210
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
211
|
+
const gridFormatter = listFormatter.wrap<string, string, string, GridContext>(
|
|
212
|
+
(delegate, value, suggestions, { column }) => delegate(value).replace(":", `${column}:`),
|
|
213
|
+
)
|
|
156
214
|
|
|
157
|
-
|
|
215
|
+
expect(gridFormatter.format("foo", [], { row: "B", column: "1" })).toBe("B1: FOO")
|
|
216
|
+
})
|
|
158
217
|
})
|
|
159
218
|
})
|
package/src/makeFormatter.ts
CHANGED
|
@@ -1,47 +1,51 @@
|
|
|
1
|
-
type PrimitiveSuggestion = "primitive"
|
|
2
|
-
|
|
3
|
-
type Suggestion =
|
|
1
|
+
type PrimitiveSuggestion = "primitive" | "comparable"
|
|
2
|
+
|
|
3
|
+
type Suggestion =
|
|
4
|
+
| PrimitiveSuggestion
|
|
5
|
+
| "abbreviated"
|
|
6
|
+
| "as-icon"
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Since v0.6.3. Use `"as-icon"` or `"with-icon"` instead.
|
|
9
|
+
*/
|
|
10
|
+
| "icon"
|
|
11
|
+
| "verbose"
|
|
12
|
+
| "with-icon"
|
|
13
|
+
|
|
14
|
+
const ensureFormatSuggestionIntegrity = (suggestions: Suggestion[]): Suggestion[] =>
|
|
15
|
+
suggestions.includes("primitive") || !suggestions.includes("comparable")
|
|
16
|
+
? suggestions
|
|
17
|
+
: ["primitive", ...suggestions]
|
|
18
|
+
|
|
19
|
+
const ensureFormatAsPrimitiveSuggestionIntegrity = (suggestions: Suggestion[]): Suggestion[] =>
|
|
20
|
+
suggestions.includes("primitive") ? suggestions : ["primitive", ...suggestions]
|
|
21
|
+
|
|
22
|
+
type DataContext = Record<string, any>
|
|
4
23
|
|
|
5
24
|
interface FormatterOptions {
|
|
6
25
|
/** Formatter name, useful for debugging or advanced pattern matching. */
|
|
7
26
|
displayName?: string
|
|
8
27
|
}
|
|
9
28
|
|
|
10
|
-
type DataContext = Record<string, any>
|
|
11
|
-
|
|
12
|
-
// TODO: Update `FormatDefinition` type based on this snippet. Sadly, TypeScript cannot correctly
|
|
13
|
-
// verify the return type of the format definition based on the suggestions, printing errors such
|
|
14
|
-
// as `Type 'string' is not assignable to type '"primitive" extends TSuggestion ? string : string'`
|
|
15
|
-
// which is, of course, nonsense. Same applies to `FormatChainDefinition`.
|
|
16
|
-
//
|
|
17
|
-
// interface FormatDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
18
|
-
// <TSuggestion extends Suggestion>(
|
|
19
|
-
// value: TInput,
|
|
20
|
-
// usageSuggestions: TSuggestion[],
|
|
21
|
-
// dataContext: Partial<TDataContext>,
|
|
22
|
-
// ): PrimitiveSuggestion extends TSuggestion ? TPrimitiveOutput : TOutput
|
|
23
|
-
// }
|
|
24
|
-
|
|
25
29
|
interface FormatDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
26
30
|
(
|
|
27
31
|
/** Value to format. */
|
|
28
32
|
value: TInput,
|
|
29
33
|
/** Suggestions passed by the consumer of a formatter. */
|
|
30
|
-
|
|
34
|
+
suggestions: Suggestion[],
|
|
31
35
|
/** Additional data context to be used by the formatter. */
|
|
32
36
|
dataContext: Partial<TDataContext>,
|
|
33
37
|
): TOutput | TPrimitiveOutput
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
interface FormatMethod<TInput, TOutput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
37
|
-
|
|
41
|
+
(
|
|
38
42
|
/** Value to format. */
|
|
39
43
|
value: TInput,
|
|
40
44
|
/** Suggestions the formatter should take note of. */
|
|
41
|
-
|
|
45
|
+
suggestions?: Suggestion[],
|
|
42
46
|
/** Additional data context the formatter might find useful. */
|
|
43
47
|
dataContext?: Partial<TDataContext>,
|
|
44
|
-
):
|
|
48
|
+
): TOutput | TPrimitiveOutput
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
interface FormatAsPrimitiveMethod<TInput, TPrimitiveOutput, TDataContext extends DataContext> {
|
|
@@ -49,7 +53,7 @@ interface FormatAsPrimitiveMethod<TInput, TPrimitiveOutput, TDataContext extends
|
|
|
49
53
|
/** Value to format. */
|
|
50
54
|
value: TInput,
|
|
51
55
|
/** Suggestions the formatter should take note of in addition to `primitive`. */
|
|
52
|
-
|
|
56
|
+
suggestions?: Suggestion[],
|
|
53
57
|
/** Additional data context the formatter might find useful. */
|
|
54
58
|
dataContext?: Partial<TDataContext>,
|
|
55
59
|
): TPrimitiveOutput
|
|
@@ -75,7 +79,7 @@ interface FormatChainDefinition<
|
|
|
75
79
|
/** Value to format. */
|
|
76
80
|
value: TOuterInput,
|
|
77
81
|
/** Suggestions the formatter should take note of. */
|
|
78
|
-
|
|
82
|
+
suggestions: Suggestion[],
|
|
79
83
|
/** Additional data context the formatter might find useful. */
|
|
80
84
|
dataContext: Partial<TOuterDataContext>,
|
|
81
85
|
): TOuterOutput | TOuterPrimitiveOutput
|
|
@@ -100,7 +104,7 @@ export interface Formatter<
|
|
|
100
104
|
format: FormatMethod<TInput, TOutput, TPrimitiveOutput, TDataContext>
|
|
101
105
|
/** Formats a value with the `primitive` suggestion. */
|
|
102
106
|
formatAsPrimitive: FormatAsPrimitiveMethod<TInput, TPrimitiveOutput, TDataContext>
|
|
103
|
-
/** The callee of the
|
|
107
|
+
/** The callee of the `wrap` method used to produce this formatter. */
|
|
104
108
|
innerFormatter?: Formatter<any, any, any, any>
|
|
105
109
|
/**
|
|
106
110
|
* Creates a new formatter from an existing one. Allows overriding of formatter behaviour
|
|
@@ -110,7 +114,7 @@ export interface Formatter<
|
|
|
110
114
|
TNextInput = TInput,
|
|
111
115
|
TNextOutput = TOutput,
|
|
112
116
|
TNextPrimitiveOutput = TPrimitiveOutput,
|
|
113
|
-
TNextDataContext extends
|
|
117
|
+
TNextDataContext extends TDataContext = TDataContext
|
|
114
118
|
>(
|
|
115
119
|
/**
|
|
116
120
|
* Function used to format the value. Has the same signature as the one passed
|
|
@@ -133,7 +137,7 @@ export interface Formatter<
|
|
|
133
137
|
/**
|
|
134
138
|
* Backwards-compatible way to use the formatter as a React component.
|
|
135
139
|
*
|
|
136
|
-
* @deprecated Since v0.6.0. Prefer using the `
|
|
140
|
+
* @deprecated Since v0.6.0. Prefer using the `format` method instead.
|
|
137
141
|
*/
|
|
138
142
|
(props: FormatterProps<TInput, TDataContext>): TOutput | TPrimitiveOutput | null
|
|
139
143
|
}
|
|
@@ -153,25 +157,26 @@ export const makeFormatter = <
|
|
|
153
157
|
format: FormatDefinition<TInput, TOutput, TPrimitiveOutput, TDataContext>,
|
|
154
158
|
formatterOptions?: FormatterOptions,
|
|
155
159
|
): Formatter<TInput, TOutput, TPrimitiveOutput, TDataContext> => {
|
|
156
|
-
const formatter: Formatter<TInput, TOutput, TPrimitiveOutput, TDataContext> =
|
|
157
|
-
children,
|
|
158
|
-
suggestions = [],
|
|
159
|
-
...dataContext
|
|
160
|
-
}) => format(children, suggestions, dataContext as any) ?? null
|
|
160
|
+
const formatter: Formatter<TInput, TOutput, TPrimitiveOutput, TDataContext> = props =>
|
|
161
|
+
format(props.children, props.suggestions ?? [], props) ?? null
|
|
161
162
|
|
|
162
163
|
formatter.displayName = formatterOptions?.displayName
|
|
163
164
|
|
|
164
|
-
formatter.format = (value,
|
|
165
|
-
format(value,
|
|
165
|
+
formatter.format = (value, suggestions = [], dataContext = {}) =>
|
|
166
|
+
format(value, ensureFormatSuggestionIntegrity(suggestions), dataContext)
|
|
166
167
|
|
|
167
|
-
formatter.formatAsPrimitive = (value,
|
|
168
|
-
format(
|
|
168
|
+
formatter.formatAsPrimitive = (value, suggestions = [], dataContext = {}) =>
|
|
169
|
+
format(
|
|
170
|
+
value,
|
|
171
|
+
ensureFormatAsPrimitiveSuggestionIntegrity(suggestions),
|
|
172
|
+
dataContext,
|
|
173
|
+
) as TPrimitiveOutput
|
|
169
174
|
|
|
170
175
|
formatter.wrap = <
|
|
171
176
|
TNextInput,
|
|
172
177
|
TNextOutput,
|
|
173
178
|
TNextPrimitiveOutput,
|
|
174
|
-
TNextDataContext extends
|
|
179
|
+
TNextDataContext extends TDataContext
|
|
175
180
|
>(
|
|
176
181
|
nextFormat: FormatChainDefinition<
|
|
177
182
|
TInput,
|
|
@@ -185,24 +190,24 @@ export const makeFormatter = <
|
|
|
185
190
|
>,
|
|
186
191
|
nextFormatterOptions?: FormatterOptions,
|
|
187
192
|
) => {
|
|
188
|
-
const nextFormatter
|
|
193
|
+
const nextFormatter = makeFormatter<
|
|
189
194
|
TNextInput,
|
|
190
195
|
TNextOutput,
|
|
191
196
|
TNextPrimitiveOutput,
|
|
192
197
|
TNextDataContext
|
|
193
|
-
>
|
|
198
|
+
>((value, suggestions, dataContext) => {
|
|
194
199
|
const delegate: FormatMethod<TInput, TOutput, TPrimitiveOutput, TDataContext> = (
|
|
195
200
|
delegatedValue,
|
|
196
|
-
|
|
201
|
+
delegatedSuggestions,
|
|
197
202
|
delegatedDataContext,
|
|
198
203
|
) =>
|
|
199
204
|
formatter.format(
|
|
200
205
|
delegatedValue,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
206
|
+
delegatedSuggestions ?? suggestions,
|
|
207
|
+
delegatedDataContext ?? dataContext,
|
|
208
|
+
)
|
|
204
209
|
|
|
205
|
-
return nextFormat(delegate, value,
|
|
210
|
+
return nextFormat(delegate, value, suggestions, dataContext)
|
|
206
211
|
}, nextFormatterOptions ?? formatterOptions)
|
|
207
212
|
|
|
208
213
|
nextFormatter.innerFormatter = formatter
|