@vuu-ui/vuu-utils 4.0.0-beta.6 → 4.0.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/package.json +7 -7
- package/src/ScaledDecimal.js +0 -1
- package/src/filters/filterAsQuery.js +12 -3
- package/src/filters/index.js +1 -0
- package/src/filters/scaled-decimal-filter.js +130 -0
- package/src/form-utils.js +10 -4
- package/src/formatting-utils.js +2 -2
- package/src/round-decimal.js +35 -6
- package/types/component-registry.d.ts +6 -1
- package/types/filters/filterAsQuery.d.ts +3 -2
- package/types/filters/index.d.ts +1 -0
- package/types/filters/scaled-decimal-filter.d.ts +58 -0
- package/types/src/Clock.d.ts +20 -0
- package/types/src/PageVisibilityObserver.d.ts +36 -0
- package/types/src/ScaledDecimal.d.ts +12 -0
- package/types/src/ShellContext.d.ts +11 -0
- package/types/src/ThemeProvider.d.ts +32 -0
- package/types/src/array-utils.d.ts +16 -0
- package/types/src/box-utils.d.ts +24 -0
- package/types/src/broadcast-channel.d.ts +16 -0
- package/types/src/column-utils.d.ts +243 -0
- package/types/src/common-types.d.ts +6 -0
- package/types/src/component-registry.d.ts +65 -0
- package/types/src/context-definitions/DataContext.d.ts +21 -0
- package/types/src/context-definitions/DataProvider.d.ts +7 -0
- package/types/src/context-definitions/DataSourceProvider.d.ts +12 -0
- package/types/src/context-definitions/WorkspaceContext.d.ts +17 -0
- package/types/src/cookie-utils.d.ts +9 -0
- package/types/src/css-utils.d.ts +1 -0
- package/types/src/data-utils.d.ts +33 -0
- package/types/src/datasource/BaseDataSource.d.ts +63 -0
- package/types/src/datasource/datasource-action-utils.d.ts +7 -0
- package/types/src/datasource/datasource-filter-utils.d.ts +2 -0
- package/types/src/datasource/datasource-utils.d.ts +61 -0
- package/types/src/date/date-utils.d.ts +39 -0
- package/types/src/date/dateTimePattern.d.ts +8 -0
- package/types/src/date/formatter.d.ts +9 -0
- package/types/src/date/index.d.ts +4 -0
- package/types/src/date/types.d.ts +29 -0
- package/types/src/debug-utils.d.ts +9 -0
- package/types/src/edit-utils.d.ts +2 -0
- package/types/src/errors.d.ts +2 -0
- package/types/src/event-emitter.d.ts +21 -0
- package/types/src/feature-utils.d.ts +110 -0
- package/types/src/filters/filter-utils.d.ts +75 -0
- package/types/src/filters/filterAsQuery.d.ts +8 -0
- package/types/src/filters/index.d.ts +2 -0
- package/types/src/form-utils.d.ts +37 -0
- package/types/src/formatting-utils.d.ts +14 -0
- package/types/src/getUniqueId.d.ts +1 -0
- package/types/src/group-utils.d.ts +10 -0
- package/types/src/html-utils.d.ts +21 -0
- package/types/src/index.d.ts +76 -0
- package/types/src/input-utils.d.ts +2 -0
- package/types/src/invariant.d.ts +1 -0
- package/types/src/itemToString.d.ts +2 -0
- package/types/src/json-types.d.ts +52 -0
- package/types/src/json-utils.d.ts +6 -0
- package/types/src/keyboard-utils.d.ts +15 -0
- package/types/src/keyset.d.ts +16 -0
- package/types/src/layout-types.d.ts +22 -0
- package/types/src/list-utils.d.ts +2 -0
- package/types/src/local-storage-utils.d.ts +4 -0
- package/types/src/logging-utils.d.ts +45 -0
- package/types/src/module-utils.d.ts +8 -0
- package/types/src/nanoid/index.d.ts +1 -0
- package/types/src/perf-utils.d.ts +5 -0
- package/types/src/promise-utils.d.ts +8 -0
- package/types/src/protocol-message-utils.d.ts +43 -0
- package/types/src/range-utils.d.ts +29 -0
- package/types/src/react-utils.d.ts +15 -0
- package/types/src/round-decimal.d.ts +4 -0
- package/types/src/row-utils.d.ts +28 -0
- package/types/src/selection-utils.d.ts +9 -0
- package/types/src/shell-layout-types.d.ts +15 -0
- package/types/src/sort-utils.d.ts +11 -0
- package/types/src/table-schema-utils.d.ts +5 -0
- package/types/src/text-utils.d.ts +10 -0
- package/types/src/theme-utils.d.ts +14 -0
- package/types/src/tree-types.d.ts +9 -0
- package/types/src/tree-utils.d.ts +9 -0
- package/types/src/ts-utils.d.ts +20 -0
- package/types/src/typeahead-utils.d.ts +1 -0
- package/types/src/url-utils.d.ts +3 -0
- package/types/src/useId.d.ts +1 -0
- package/types/src/useLayoutEffectSkipFirst.d.ts +2 -0
- package/types/src/useResizeObserver.d.ts +15 -0
- package/types/src/useStateRef.d.ts +2 -0
- package/types/src/user-types.d.ts +3 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.0.0
|
|
2
|
+
"version": "4.0.0",
|
|
3
3
|
"author": "heswell",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
@@ -11,18 +11,18 @@
|
|
|
11
11
|
"type-defs": "node ../../scripts/build-type-defs.mjs"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@salt-ds/core": "1.
|
|
14
|
+
"@salt-ds/core": "1.74.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@dnd-kit/react": "^0.1.21",
|
|
18
|
-
"@vuu-ui/vuu-data-types": "4.0.0
|
|
19
|
-
"@vuu-ui/vuu-table-types": "4.0.0
|
|
20
|
-
"@vuu-ui/vuu-filter-types": "4.0.0
|
|
21
|
-
"@vuu-ui/vuu-protocol-types": "4.0.0
|
|
18
|
+
"@vuu-ui/vuu-data-types": "4.0.0",
|
|
19
|
+
"@vuu-ui/vuu-table-types": "4.0.0",
|
|
20
|
+
"@vuu-ui/vuu-filter-types": "4.0.0",
|
|
21
|
+
"@vuu-ui/vuu-protocol-types": "4.0.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@internationalized/date": "^3.0.0",
|
|
25
|
-
"@vuu-ui/vuu-filter-parser": "4.0.0
|
|
25
|
+
"@vuu-ui/vuu-filter-parser": "4.0.0",
|
|
26
26
|
"clsx": "^2.0.0",
|
|
27
27
|
"react": "^19.2.3",
|
|
28
28
|
"react-dom": "^19.2.3"
|
package/src/ScaledDecimal.js
CHANGED
|
@@ -4,6 +4,7 @@ import { isStartOfDay, startOfDay, startOfNextDay, timeOfDayToEpochMillis } from
|
|
|
4
4
|
import { stringIsValidLong } from "../data-utils.js";
|
|
5
5
|
import { isMultiClauseFilter, isMultiValueFilter, isSerializableFilter } from "./filter-utils.js";
|
|
6
6
|
import { ScaledDecimal } from "../ScaledDecimal.js";
|
|
7
|
+
import { getScaledDecimalScale, isScaledDecimalFilterOptions, scaledDecimalFilterAsQuery } from "./scaled-decimal-filter.js";
|
|
7
8
|
const filterValue = (value, columnType)=>{
|
|
8
9
|
if ("string" == typeof value) {
|
|
9
10
|
if (void 0 !== columnType) {
|
|
@@ -12,7 +13,11 @@ const filterValue = (value, columnType)=>{
|
|
|
12
13
|
"long",
|
|
13
14
|
"double",
|
|
14
15
|
"epochtimestamp",
|
|
15
|
-
"epochtimestampnano"
|
|
16
|
+
"epochtimestampnano",
|
|
17
|
+
"scaleddecimal2",
|
|
18
|
+
"scaleddecimal4",
|
|
19
|
+
"scaleddecimal6",
|
|
20
|
+
"scaleddecimal8"
|
|
16
21
|
].includes(columnType);
|
|
17
22
|
return isNumericType ? value : `"${value}"`;
|
|
18
23
|
}
|
|
@@ -43,6 +48,7 @@ const filterAsQueryCore = (f, opts)=>{
|
|
|
43
48
|
function singleValueFilterAsQuery(f, opts) {
|
|
44
49
|
if (isSerializableFilter(f)) return f.asQuery();
|
|
45
50
|
{
|
|
51
|
+
if (isScaledDecimalFilterOptions(f.extendedOptions)) return scaledDecimalFilterAsQuery(f, f.extendedOptions);
|
|
46
52
|
const column = opts?.columnsByName?.[f.column];
|
|
47
53
|
const temporalInfo = getTemporalInfo(column);
|
|
48
54
|
if (temporalInfo) return temporalFilterAsQuery(f, temporalInfo);
|
|
@@ -143,7 +149,10 @@ const getColumnsByNameForFilter = (tableSchema, columnsByName)=>{
|
|
|
143
149
|
};
|
|
144
150
|
const filterRequiresResolution = (filter, columnsByName)=>{
|
|
145
151
|
if (isMultiClauseFilter(filter)) return filter.filters.some((f)=>filterRequiresResolution(f, columnsByName));
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
{
|
|
153
|
+
if (isSerializableFilter(filter) || isScaledDecimalFilterOptions(filter.extendedOptions)) return true;
|
|
154
|
+
const column = columnsByName?.[filter.column];
|
|
155
|
+
return void 0 !== getTemporalInfo(column) || void 0 !== getScaledDecimalScale(column?.serverDataType);
|
|
156
|
+
}
|
|
148
157
|
};
|
|
149
158
|
export { ONE_DAY_IN_MILLIS, dateFilterAsQuery, filterAsQuery, filterRequiresResolution, getColumnsByName, getColumnsByNameForFilter, temporalFilterAsQuery };
|
package/src/filters/index.js
CHANGED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { isTypeDescriptor } from "../column-utils.js";
|
|
2
|
+
import { roundScaledDecimal } from "../round-decimal.js";
|
|
3
|
+
const DEFAULT_DISPLAY_DECIMALS = 4;
|
|
4
|
+
const scales = {
|
|
5
|
+
scaleddecimal2: 2,
|
|
6
|
+
scaleddecimal4: 4,
|
|
7
|
+
scaleddecimal6: 6,
|
|
8
|
+
scaleddecimal8: 8
|
|
9
|
+
};
|
|
10
|
+
const getScaledDecimalScale = (serverDataType)=>void 0 === serverDataType ? void 0 : scales[serverDataType];
|
|
11
|
+
const decimalPattern = /^-?(\d+(\.\d*)?|\.\d+)$/;
|
|
12
|
+
const isValidDecimalString = (value)=>decimalPattern.test(value);
|
|
13
|
+
const isScaledDecimalFilterOptions = (options)=>options?.type === "ScaledDecimal";
|
|
14
|
+
const toDecimalString = (value, scale)=>{
|
|
15
|
+
if ("number" == typeof value) {
|
|
16
|
+
const text = `${value}`;
|
|
17
|
+
return /e/i.test(text) ? value.toFixed(scale) : text;
|
|
18
|
+
}
|
|
19
|
+
return `${value}`;
|
|
20
|
+
};
|
|
21
|
+
const toScaledInteger = (value, scale)=>{
|
|
22
|
+
if (!isValidDecimalString(value)) throw Error(`[scaled-decimal-filter] invalid decimal value '${value}'`);
|
|
23
|
+
const negative = value.startsWith("-");
|
|
24
|
+
const [integral, fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
25
|
+
const magnitude = BigInt((integral || "0") + fraction.slice(0, scale).padEnd(scale, "0"));
|
|
26
|
+
return negative ? -magnitude : magnitude;
|
|
27
|
+
};
|
|
28
|
+
const fromScaledInteger = (value, scale)=>{
|
|
29
|
+
const negative = value < 0n;
|
|
30
|
+
const digits = (negative ? -value : value).toString().padStart(scale + 1, "0");
|
|
31
|
+
const literal = `${digits.slice(0, -scale)}.${digits.slice(-scale)}`;
|
|
32
|
+
return negative ? `-${literal}` : literal;
|
|
33
|
+
};
|
|
34
|
+
const getScaledDecimalFilterOptions = (column)=>{
|
|
35
|
+
const scale = getScaledDecimalScale(column.serverDataType);
|
|
36
|
+
if (void 0 === scale) return;
|
|
37
|
+
if (isTypeDescriptor(column.type)) {
|
|
38
|
+
const { decimals = DEFAULT_DISPLAY_DECIMALS, roundingRule = "round" } = column.type.formatting ?? {};
|
|
39
|
+
return decimals < scale ? {
|
|
40
|
+
type: "ScaledDecimal",
|
|
41
|
+
scale,
|
|
42
|
+
decimals,
|
|
43
|
+
roundingRule
|
|
44
|
+
} : {
|
|
45
|
+
type: "ScaledDecimal",
|
|
46
|
+
scale
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
type: "ScaledDecimal",
|
|
51
|
+
scale
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
const countDecimals = (value)=>value.split(".")[1]?.length ?? 0;
|
|
55
|
+
const getScaledDecimalMatchRange = (value, { scale, decimals, roundingRule = "round" })=>{
|
|
56
|
+
const scaledValue = toScaledInteger(value, scale);
|
|
57
|
+
const precision = void 0 === decimals ? scale : Math.max(decimals, countDecimals(value));
|
|
58
|
+
if (precision >= scale) return [
|
|
59
|
+
scaledValue,
|
|
60
|
+
scaledValue
|
|
61
|
+
];
|
|
62
|
+
const step = 10n ** BigInt(scale - precision);
|
|
63
|
+
if ("truncate" === roundingRule) if (scaledValue > 0n) return [
|
|
64
|
+
scaledValue,
|
|
65
|
+
scaledValue + step - 1n
|
|
66
|
+
];
|
|
67
|
+
else if (scaledValue < 0n) return [
|
|
68
|
+
scaledValue - step + 1n,
|
|
69
|
+
scaledValue
|
|
70
|
+
];
|
|
71
|
+
else return [
|
|
72
|
+
-step + 1n,
|
|
73
|
+
step - 1n
|
|
74
|
+
];
|
|
75
|
+
{
|
|
76
|
+
const half = step / 2n;
|
|
77
|
+
if (scaledValue > 0n) return [
|
|
78
|
+
scaledValue - half,
|
|
79
|
+
scaledValue + half - 1n
|
|
80
|
+
];
|
|
81
|
+
if (scaledValue < 0n) return [
|
|
82
|
+
scaledValue - half + 1n,
|
|
83
|
+
scaledValue + half
|
|
84
|
+
];
|
|
85
|
+
return [
|
|
86
|
+
-half + 1n,
|
|
87
|
+
half - 1n
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const scaledDecimalFilterAsQuery = ({ column, op, value }, options)=>{
|
|
92
|
+
const { scale } = options;
|
|
93
|
+
const [from, to] = getScaledDecimalMatchRange(toDecimalString(value, scale), options);
|
|
94
|
+
const lo = fromScaledInteger(from, scale);
|
|
95
|
+
const hi = fromScaledInteger(to, scale);
|
|
96
|
+
if (from === to) return `${column} ${op} ${lo}`;
|
|
97
|
+
switch(op){
|
|
98
|
+
case "=":
|
|
99
|
+
return `(${column} >= ${lo} and ${column} <= ${hi})`;
|
|
100
|
+
case "!=":
|
|
101
|
+
return `(${column} < ${lo} or ${column} > ${hi})`;
|
|
102
|
+
case ">":
|
|
103
|
+
return `${column} > ${hi}`;
|
|
104
|
+
case ">=":
|
|
105
|
+
return `${column} >= ${lo}`;
|
|
106
|
+
case "<":
|
|
107
|
+
return `${column} < ${lo}`;
|
|
108
|
+
case "<=":
|
|
109
|
+
return `${column} <= ${hi}`;
|
|
110
|
+
default:
|
|
111
|
+
throw Error(`[scaled-decimal-filter] operator ${op} is not supported for scaled decimals`);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const getScaledDecimalCellFilter = (column, value)=>{
|
|
115
|
+
const options = getScaledDecimalFilterOptions(column);
|
|
116
|
+
if (void 0 === options || "string" != typeof value || "" === value) return;
|
|
117
|
+
const { decimals, roundingRule } = options;
|
|
118
|
+
if (void 0 === decimals) return {
|
|
119
|
+
label: value,
|
|
120
|
+
op: "=",
|
|
121
|
+
value
|
|
122
|
+
};
|
|
123
|
+
const display = (useLocaleString)=>roundScaledDecimal(value, void 0, decimals, true, false, useLocaleString, roundingRule);
|
|
124
|
+
return {
|
|
125
|
+
label: display(true),
|
|
126
|
+
op: "=",
|
|
127
|
+
value: display(false)
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
export { fromScaledInteger, getScaledDecimalCellFilter, getScaledDecimalFilterOptions, getScaledDecimalMatchRange, getScaledDecimalScale, isScaledDecimalFilterOptions, isValidDecimalString, scaledDecimalFilterAsQuery, toDecimalString, toScaledInteger };
|
package/src/form-utils.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getTemporalInfo, parseTemporalInput } from "./date/temporal.js";
|
|
|
4
4
|
import { getDefaultTimeZone } from "./date/time-zone.js";
|
|
5
5
|
import { isTypeDescriptor } from "./column-utils.js";
|
|
6
6
|
import { queryClosest } from "./html-utils.js";
|
|
7
|
+
import { isValidDecimalString } from "./filters/scaled-decimal-filter.js";
|
|
7
8
|
import { ScaledDecimal2, ScaledDecimal4, ScaledDecimal6, ScaledDecimal8 } from "./ScaledDecimal.js";
|
|
8
9
|
class DataValidationError extends Error {
|
|
9
10
|
actualType;
|
|
@@ -33,6 +34,12 @@ function getTypedRange([value1, value2], dataType, options) {
|
|
|
33
34
|
getTypedValue(value2, dataType, false, options)
|
|
34
35
|
];
|
|
35
36
|
}
|
|
37
|
+
const ScaledDecimalFactory = {
|
|
38
|
+
scaleddecimal2: ScaledDecimal2,
|
|
39
|
+
scaleddecimal4: ScaledDecimal4,
|
|
40
|
+
scaleddecimal6: ScaledDecimal6,
|
|
41
|
+
scaleddecimal8: ScaledDecimal8
|
|
42
|
+
};
|
|
36
43
|
const getActualType = (value)=>{
|
|
37
44
|
if ("boolean" == typeof value) return "boolean";
|
|
38
45
|
if ("number" == typeof value) return "number";
|
|
@@ -53,13 +60,12 @@ function getTypedValue(value, type, throwIfInvalid = false, options) {
|
|
|
53
60
|
if (!throwIfInvalid) return;
|
|
54
61
|
throw new DataValidationError(`value ${value} is not a valid ${type}`, "single character", getActualType(value));
|
|
55
62
|
case "scaleddecimal2":
|
|
56
|
-
return ScaledDecimal2(value);
|
|
57
63
|
case "scaleddecimal4":
|
|
58
|
-
return ScaledDecimal4(value);
|
|
59
64
|
case "scaleddecimal6":
|
|
60
|
-
return ScaledDecimal6(value);
|
|
61
65
|
case "scaleddecimal8":
|
|
62
|
-
return
|
|
66
|
+
if ("" === value || isValidDecimalString(value)) return ScaledDecimalFactory[type](value);
|
|
67
|
+
if (!throwIfInvalid) return;
|
|
68
|
+
throw new DataValidationError(`value ${value} is not a valid ${type}`, "decimal", getActualType(value));
|
|
63
69
|
case "double":
|
|
64
70
|
case "number":
|
|
65
71
|
if (stringIsValidDecimal(value)) return parseFloat(value);
|
package/src/formatting-utils.js
CHANGED
|
@@ -52,8 +52,8 @@ const numericFormatter = ({ align = "right", serverDataType, type })=>{
|
|
|
52
52
|
{
|
|
53
53
|
const { alignOnDecimals = false, decimals, roundingRule, useLocaleString, zeroPad = false } = type.formatting ?? DEFAULT_NUMERIC_FORMAT;
|
|
54
54
|
return (value)=>{
|
|
55
|
-
if (serverDataType?.startsWith("scaleddecimal")) if (null == value) return "";
|
|
56
|
-
else if ("string" == typeof value) return roundScaledDecimal(value, align, decimals, zeroPad, alignOnDecimals, useLocaleString, roundingRule);
|
|
55
|
+
if ("long" === serverDataType || serverDataType?.startsWith("scaleddecimal")) if (null == value) return "";
|
|
56
|
+
else if ("string" == typeof value || "long" === serverDataType && "number" == typeof value) return roundScaledDecimal("number" == typeof value ? value.toString() : value, align, decimals, zeroPad, alignOnDecimals, useLocaleString, roundingRule);
|
|
57
57
|
else throw Error(`[formatting-utils] numericFormatter, invalid data for ${serverDataType}: '${value}'`);
|
|
58
58
|
if ("string" == typeof value && (value.startsWith("Σ") || value.startsWith("["))) return value;
|
|
59
59
|
const number = "number" == typeof value ? value : "string" == typeof value ? parseFloat(value) : void 0;
|
package/src/round-decimal.js
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
const PUNCTUATION_STR = String.fromCharCode(8200);
|
|
2
2
|
const DIGIT_STR = String.fromCharCode(8199);
|
|
3
3
|
const DECIMALS_AUTO = -1;
|
|
4
|
+
const integerFormat = new Intl.NumberFormat();
|
|
4
5
|
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER.toString();
|
|
5
6
|
const MAX_INTEGER_DIGITS = MAX_SAFE_INTEGER.length;
|
|
6
|
-
const exceedsMaxSafeInteger = (value)=>
|
|
7
|
-
const
|
|
7
|
+
const exceedsMaxSafeInteger = (value)=>{
|
|
8
|
+
const digits = value.startsWith("-") ? value.slice(1) : value;
|
|
9
|
+
return digits.length > MAX_INTEGER_DIGITS || digits.length === MAX_INTEGER_DIGITS && digits > MAX_SAFE_INTEGER;
|
|
10
|
+
};
|
|
11
|
+
const roundFraction = (fraction, decimals)=>{
|
|
12
|
+
const kept = fraction.slice(0, decimals);
|
|
13
|
+
if (fraction.length <= decimals || fraction[decimals] < "5") return [
|
|
14
|
+
kept,
|
|
15
|
+
false
|
|
16
|
+
];
|
|
17
|
+
if (0 === decimals) return [
|
|
18
|
+
"",
|
|
19
|
+
true
|
|
20
|
+
];
|
|
21
|
+
const rounded = (BigInt(kept) + 1n).toString().padStart(decimals, "0");
|
|
22
|
+
return rounded.length > decimals ? [
|
|
23
|
+
rounded.slice(1),
|
|
24
|
+
true
|
|
25
|
+
] : [
|
|
26
|
+
rounded,
|
|
27
|
+
false
|
|
28
|
+
];
|
|
29
|
+
};
|
|
30
|
+
const carriesIntoIntegral = (fraction, decimals, roundingRule)=>"round" === roundingRule && decimals !== DECIMALS_AUTO && fraction.length > decimals && roundFraction(fraction, decimals)[1];
|
|
8
31
|
const incrementIntegral = (integral)=>{
|
|
9
32
|
const negative = integral.startsWith("-");
|
|
10
33
|
const magnitude = BigInt(negative ? integral.slice(1) : integral) + 1n;
|
|
@@ -70,9 +93,12 @@ function roundDecimal(value, align = Align.Right, decimals = 4, zeroPad, alignOn
|
|
|
70
93
|
const [whole, part2 = ""] = value.toString().split(".");
|
|
71
94
|
const part1 = carriesIntoIntegral(part2, decimals, roundingRule) ? incrementIntegral(whole) : whole;
|
|
72
95
|
const actualDecimals = part2.length;
|
|
73
|
-
integral = "-0" === part1 ? "-0" : useLocaleString ? parseFloat(part1)
|
|
96
|
+
integral = "-0" === part1 ? "-0" : useLocaleString ? integerFormat.format(parseFloat(part1)) : parseFloat(part1).toString();
|
|
74
97
|
if (align === Align.Left && alignOnDecimals) integral = padLeft(integral);
|
|
75
|
-
|
|
98
|
+
if (decimals === DECIMALS_AUTO || actualDecimals === decimals) fraction = part2;
|
|
99
|
+
else if (actualDecimals > decimals) if ("round" === roundingRule) [fraction] = roundFraction(part2, decimals);
|
|
100
|
+
else fraction = part2.slice(0, decimals);
|
|
101
|
+
else fraction = (Pad = zeroPad ? Zero : alignOnDecimals && align !== Align.Left ? Space : null) ? 0 === actualDecimals ? Pad.FULL_PADDING[decimals] ?? "" : pad(part2, decimals, Pad) : part2;
|
|
76
102
|
return integral + (fraction ? "." + fraction : "");
|
|
77
103
|
}
|
|
78
104
|
function roundScaledDecimal(value, align = Align.Right, decimals = 4, zeroPad, alignOnDecimals, useLocaleString = true, roundingRule = "round") {
|
|
@@ -83,9 +109,12 @@ function roundScaledDecimal(value, align = Align.Right, decimals = 4, zeroPad, a
|
|
|
83
109
|
const [whole, part2 = ""] = value.split(".");
|
|
84
110
|
const part1 = carriesIntoIntegral(part2, decimals, roundingRule) ? incrementIntegral(whole) : whole;
|
|
85
111
|
const actualDecimals = part2.length;
|
|
86
|
-
integral = "" === part1 ? "0" : "-0" === part1 ? "-0" : useLocaleString ? exceedsMaxSafeInteger(value) ? BigInt(part1)
|
|
112
|
+
integral = "" === part1 ? "0" : "-0" === part1 ? "-0" : useLocaleString ? exceedsMaxSafeInteger(value) ? integerFormat.format(BigInt(part1)) : integerFormat.format(parseFloat(part1)) : part1;
|
|
87
113
|
if (align === Align.Left && alignOnDecimals) integral = padLeft(integral);
|
|
88
|
-
|
|
114
|
+
if (decimals === DECIMALS_AUTO || actualDecimals === decimals) fraction = part2;
|
|
115
|
+
else if (actualDecimals > decimals) if ("round" === roundingRule) [fraction] = roundFraction(part2, decimals);
|
|
116
|
+
else fraction = part2.slice(0, decimals);
|
|
117
|
+
else fraction = (Pad = zeroPad ? Zero : alignOnDecimals && align !== Align.Left ? Space : null) ? 0 === actualDecimals ? Pad.FULL_PADDING[decimals] ?? "" : pad(part2, decimals, Pad) : part2;
|
|
89
118
|
return integral + (fraction ? "." + fraction : "");
|
|
90
119
|
}
|
|
91
120
|
export { exceedsMaxSafeInteger, roundDecimal, roundScaledDecimal };
|
|
@@ -11,7 +11,12 @@ export interface ConfigurationEditorProps {
|
|
|
11
11
|
column: ColumnDescriptorCustomRenderer;
|
|
12
12
|
onChangeRendering: ColumnRenderPropsChangeHandler;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Returns a class name for a row, or undefined. previousDataRow and nextDataRow
|
|
16
|
+
* are the rows immediately before and after dataRow in data order. Either is
|
|
17
|
+
* undefined at the start or end of the data, or when that row is not loaded.
|
|
18
|
+
*/
|
|
19
|
+
export type RowClassNameGenerator = (dataRow: DataRow, previousDataRow?: DataRow, nextDataRow?: DataRow) => string | undefined;
|
|
15
20
|
export type RowClassGenerator = {
|
|
16
21
|
id: string;
|
|
17
22
|
fn: RowClassNameGenerator;
|
|
@@ -43,7 +43,8 @@ export declare const getColumnsByName: (columns: readonly ColumnDescriptor[]) =>
|
|
|
43
43
|
export declare const getColumnsByNameForFilter: (tableSchema?: Pick<TableSchema, "columns">, columnsByName?: ColumnDescriptorsByName) => ColumnDescriptorsByName | undefined;
|
|
44
44
|
/**
|
|
45
45
|
* Returns true if the filter includes clauses whose meaning depends on the
|
|
46
|
-
* column type (temporal columns) or which serialize
|
|
47
|
-
* must be resolved (see resolveFilterQuery) before
|
|
46
|
+
* column type (temporal and scaled decimal columns) or which serialize
|
|
47
|
+
* themselves. Such filters must be resolved (see resolveFilterQuery) before
|
|
48
|
+
* they can be evaluated locally.
|
|
48
49
|
*/
|
|
49
50
|
export declare const filterRequiresResolution: (filter: Filter, columnsByName?: ColumnDescriptorsByName) => boolean;
|
package/types/filters/index.d.ts
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ExtendedFilterOptions, ScaledDecimalFilterOptions, SingleValueFilterClause } from "@vuu-ui/vuu-filter-types";
|
|
2
|
+
import type { DataValueDescriptor } from "@vuu-ui/vuu-data-types";
|
|
3
|
+
export type ScaledDecimalScale = ScaledDecimalFilterOptions["scale"];
|
|
4
|
+
export declare const getScaledDecimalScale: (serverDataType?: string) => ScaledDecimalScale | undefined;
|
|
5
|
+
export declare const isValidDecimalString: (value: string) => boolean;
|
|
6
|
+
export declare const isScaledDecimalFilterOptions: (options?: ExtendedFilterOptions) => options is ScaledDecimalFilterOptions;
|
|
7
|
+
/**
|
|
8
|
+
* A decimal (not exponential) string representation of a filter value.
|
|
9
|
+
*/
|
|
10
|
+
export declare const toDecimalString: (value: unknown, scale: ScaledDecimalScale) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Convert a decimal string to its scaled integer representation. Digits beyond
|
|
13
|
+
* the scale are truncated, as the Vuu server does.
|
|
14
|
+
*/
|
|
15
|
+
export declare const toScaledInteger: (value: string, scale: ScaledDecimalScale) => bigint;
|
|
16
|
+
/**
|
|
17
|
+
* Convert a scaled integer to a decimal string with 'scale' decimal places,
|
|
18
|
+
* the literal form in which scaled decimals are expressed in a filter query.
|
|
19
|
+
*/
|
|
20
|
+
export declare const fromScaledInteger: (value: bigint, scale: ScaledDecimalScale) => string;
|
|
21
|
+
/**
|
|
22
|
+
* The filter options for a clause on a scaled decimal column, describing the
|
|
23
|
+
* precision with which the column displays values. These mirror the formatting
|
|
24
|
+
* rules of numericFormatter: a column with no type descriptor displays the full
|
|
25
|
+
* value, a type descriptor with no decimals displays 4 decimal places.
|
|
26
|
+
*/
|
|
27
|
+
export declare const getScaledDecimalFilterOptions: (column: Pick<DataValueDescriptor, "serverDataType" | "type">) => ScaledDecimalFilterOptions | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* The range of scaled integer values, inclusive at both ends, matched by a
|
|
30
|
+
* filter value. A value with no more decimal places than are displayed matches
|
|
31
|
+
* every value that displays as that value, e.g 1.23, displayed with 2 decimals,
|
|
32
|
+
* rounded, matches [1.225, 1.235). Display rounding applies to the magnitude,
|
|
33
|
+
* so -1.23 matches (-1.235, -1.225]. A value with more decimal places than are
|
|
34
|
+
* displayed is matched at its own precision, by the same rounding rule, e.g
|
|
35
|
+
* 1.234 matches [1.2335, 1.2345). A value with as many decimal places as the
|
|
36
|
+
* column holds, or any value where every decimal is displayed, is matched exactly.
|
|
37
|
+
*/
|
|
38
|
+
export declare const getScaledDecimalMatchRange: (value: string, { scale, decimals, roundingRule }: ScaledDecimalFilterOptions) => [bigint, bigint];
|
|
39
|
+
/**
|
|
40
|
+
* Serialize a filter clause on a scaled decimal column. Where the value matches
|
|
41
|
+
* a range of values (see getScaledDecimalMatchRange), each operator applies to
|
|
42
|
+
* that range, e.g '>' matches values greater than every value in the range.
|
|
43
|
+
* Note: '!=' on a range is expressed as '< from or > to', which, unlike an
|
|
44
|
+
* exact '!=', does not match a null value.
|
|
45
|
+
*/
|
|
46
|
+
export declare const scaledDecimalFilterAsQuery: ({ column, op, value, }: Pick<SingleValueFilterClause, "column" | "op" | "value">, options: ScaledDecimalFilterOptions) => string;
|
|
47
|
+
export interface ScaledDecimalCellFilter {
|
|
48
|
+
/** The value as displayed */
|
|
49
|
+
label: string;
|
|
50
|
+
op: "=";
|
|
51
|
+
value: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The filter for a cell value on a scaled decimal column, matching the value as
|
|
55
|
+
* displayed, e.g a cell displaying 1.23 (underlying value 1.234567) creates a
|
|
56
|
+
* filter matching every value that displays as 1.23.
|
|
57
|
+
*/
|
|
58
|
+
export declare const getScaledDecimalCellFilter: (column: Pick<DataValueDescriptor, "serverDataType" | "type">, value: unknown) => ScaledDecimalCellFilter | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ClockProps {
|
|
2
|
+
year?: number;
|
|
3
|
+
month?: number;
|
|
4
|
+
day?: number;
|
|
5
|
+
hours?: number;
|
|
6
|
+
minutes?: number;
|
|
7
|
+
seconds?: number;
|
|
8
|
+
milliseconds?: number;
|
|
9
|
+
}
|
|
10
|
+
export type ClockTimeUnit = "minutes" | "seconds" | "ms";
|
|
11
|
+
export declare class Clock {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(props?: ClockProps);
|
|
14
|
+
goBack(value: number, units?: ClockTimeUnit): this;
|
|
15
|
+
advance(value: number, units?: ClockTimeUnit): this;
|
|
16
|
+
set showCurrentTime(value: boolean);
|
|
17
|
+
toString(): string;
|
|
18
|
+
get now(): number;
|
|
19
|
+
get date(): Date;
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { EventEmitter } from "./event-emitter";
|
|
2
|
+
export type PageVisibilityEvents = {
|
|
3
|
+
/**
|
|
4
|
+
* emits when page has been hidden for this period of time
|
|
5
|
+
*/
|
|
6
|
+
"inactive-timeout": () => void;
|
|
7
|
+
/**
|
|
8
|
+
* emits when page is hidden
|
|
9
|
+
*/
|
|
10
|
+
hidden: () => void;
|
|
11
|
+
/**
|
|
12
|
+
* emits when page is made visible
|
|
13
|
+
*/
|
|
14
|
+
visible: () => void;
|
|
15
|
+
/**
|
|
16
|
+
* emits when page visibility status changes
|
|
17
|
+
*/
|
|
18
|
+
visibilityChange: (hidden: boolean) => void;
|
|
19
|
+
};
|
|
20
|
+
export interface PageVisibilityObserverConstructorProps {
|
|
21
|
+
/**
|
|
22
|
+
* timeout period in seconds. Defaults to zero which will never timeout
|
|
23
|
+
*/
|
|
24
|
+
inactiveTimeout?: number;
|
|
25
|
+
onHidden?: () => void;
|
|
26
|
+
onInactiveTimeout?: () => void;
|
|
27
|
+
onVisible?: () => void;
|
|
28
|
+
onVisibilityChange?: () => void;
|
|
29
|
+
}
|
|
30
|
+
export declare class PageVisibilityObserver extends EventEmitter<PageVisibilityEvents> {
|
|
31
|
+
#private;
|
|
32
|
+
visibilityChangeHandler: () => void;
|
|
33
|
+
constructor({ inactiveTimeout, onHidden, onInactiveTimeout, onVisibilityChange, onVisible, }?: PageVisibilityObserverConstructorProps);
|
|
34
|
+
clear(): void;
|
|
35
|
+
private set hidden(value);
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class ScaledDecimal {
|
|
2
|
+
protected readonly value: string;
|
|
3
|
+
private readonly decimals;
|
|
4
|
+
constructor(value: string, decimals?: number);
|
|
5
|
+
get asLong(): string;
|
|
6
|
+
toJSON(): string;
|
|
7
|
+
toString(): string;
|
|
8
|
+
}
|
|
9
|
+
export declare const ScaledDecimal2: (value: string) => ScaledDecimal;
|
|
10
|
+
export declare const ScaledDecimal4: (value: string) => ScaledDecimal;
|
|
11
|
+
export declare const ScaledDecimal6: (value: string) => ScaledDecimal;
|
|
12
|
+
export declare const ScaledDecimal8: (value: string) => ScaledDecimal;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RpcResponseHandler } from "@vuu-ui/vuu-data-types";
|
|
2
|
+
import type { DefaultColumnConfiguration, DefaultTableConfiguration } from "@vuu-ui/vuu-table-types";
|
|
3
|
+
import { LookupTableProvider } from "./feature-utils";
|
|
4
|
+
export interface ShellContextProps {
|
|
5
|
+
getDefaultColumnConfig?: DefaultColumnConfiguration;
|
|
6
|
+
getDefaultTableConfig?: DefaultTableConfiguration;
|
|
7
|
+
getLookupValues?: LookupTableProvider;
|
|
8
|
+
handleRpcResponse?: RpcResponseHandler;
|
|
9
|
+
}
|
|
10
|
+
export declare const ShellContext: import("react").Context<ShellContextProps>;
|
|
11
|
+
export declare const useShellContext: () => ShellContextProps;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export declare const DEFAULT_DENSITY: Density;
|
|
3
|
+
export declare const DEFAULT_THEME = "salt-theme";
|
|
4
|
+
export declare const DEFAULT_THEME_MODE: ThemeMode;
|
|
5
|
+
export type Density = "high" | "medium" | "low" | "touch";
|
|
6
|
+
export type ThemeMode = "light" | "dark";
|
|
7
|
+
export type TargetElement = "root" | "scope" | "child";
|
|
8
|
+
export interface ThemeContextProps {
|
|
9
|
+
density: Density;
|
|
10
|
+
theme: string;
|
|
11
|
+
themeMode: ThemeMode;
|
|
12
|
+
}
|
|
13
|
+
export declare const ThemeContext: import("react").Context<ThemeContextProps>;
|
|
14
|
+
export type ThemeClasses = [string, string, ThemeMode];
|
|
15
|
+
export type ThemeAttributes = {
|
|
16
|
+
themeClass: string;
|
|
17
|
+
densityClass: string;
|
|
18
|
+
dataMode: ThemeMode;
|
|
19
|
+
};
|
|
20
|
+
export declare const useThemeAttributes: (themeAttributes?: ThemeAttributes) => [string, string, ThemeMode];
|
|
21
|
+
interface ThemeProviderProps {
|
|
22
|
+
applyThemeClasses?: boolean;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
density?: Density;
|
|
25
|
+
theme?: string;
|
|
26
|
+
themeMode?: ThemeMode;
|
|
27
|
+
}
|
|
28
|
+
export declare function ThemeProvider({ applyThemeClasses, children, theme: themeProp, themeMode: themeModeProp, density: densityProp, }: ThemeProviderProps): import("react").JSX.Element;
|
|
29
|
+
export declare namespace ThemeProvider {
|
|
30
|
+
var displayName: string;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type PartitionTest<T> = (value: T, index: number) => boolean;
|
|
2
|
+
export declare function partition<T>(array: T[], test: PartitionTest<T>, pass?: T[], fail?: T[]): [T[], T[]];
|
|
3
|
+
export declare function itemsChanged<T = unknown>(currentItems: T[], newItems: T[], identityProperty?: string): boolean;
|
|
4
|
+
export declare function itemsOrOrderChanged<T = unknown>(currentItems: T[], newItems: T[], identityProperty?: string): boolean;
|
|
5
|
+
export declare const moveItemDeprecated: <T = unknown>(items: T[], item: T, moveTo: number) => T[];
|
|
6
|
+
export declare const moveItem: <T = unknown>(items: T[], fromIndex: number, toIndex: number) => T[];
|
|
7
|
+
export declare const getAddedItems: <T>(values: undefined | T[], newValues: T[]) => T[];
|
|
8
|
+
export declare const getMissingItems: <T, I>(sourceItems: T[], items: I[], identity: (s: T) => I) => I[];
|
|
9
|
+
/**
|
|
10
|
+
* Given an ordered list of item names, return items in same order
|
|
11
|
+
*/
|
|
12
|
+
export declare const reorderItems: <T extends {
|
|
13
|
+
name: string;
|
|
14
|
+
} = {
|
|
15
|
+
name: string;
|
|
16
|
+
}>(originalItems: readonly T[], orderedNames: string[]) => T[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface rect {
|
|
2
|
+
bottom: number;
|
|
3
|
+
left: number;
|
|
4
|
+
right: number;
|
|
5
|
+
top: number;
|
|
6
|
+
}
|
|
7
|
+
export type rectTuple = [number, number, number, number];
|
|
8
|
+
export type dimension = "width" | "height";
|
|
9
|
+
export declare function boxContainsPoint(rect: rect, x: number, y: number): boolean | undefined;
|
|
10
|
+
export type GridLayoutSplitDirection = "north" | "south" | "east" | "west";
|
|
11
|
+
export type GridLayoutDropPosition = GridLayoutSplitDirection | "header" | "centre";
|
|
12
|
+
export declare function getCenteredBox({ right, left, top, bottom }: rect, pctSize: number): {
|
|
13
|
+
left: number;
|
|
14
|
+
top: number;
|
|
15
|
+
right: number;
|
|
16
|
+
bottom: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function pointPositionWithinRect(x: number, y: number, rect: rect, borderZone?: number): {
|
|
19
|
+
pctX: number;
|
|
20
|
+
pctY: number;
|
|
21
|
+
closeToTheEdge: number;
|
|
22
|
+
};
|
|
23
|
+
export declare const isGridLayoutSplitDirection: (position: GridLayoutDropPosition) => position is GridLayoutSplitDirection;
|
|
24
|
+
export declare function getPositionWithinBox(x: number, y: number, rect: rect, pctX: number, pctY: number): GridLayoutDropPosition;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface VuuBroadcastChannelEventMap<T> {
|
|
2
|
+
message: MessageEvent<T>;
|
|
3
|
+
messageerror: MessageEvent<T>;
|
|
4
|
+
}
|
|
5
|
+
export interface VuuBroadcastChannel<T> extends EventTarget {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
onmessage: ((this: BroadcastChannel, evt: MessageEvent<T>) => void) | null;
|
|
8
|
+
onmessageerror: ((this: BroadcastChannel, evt: MessageEvent<T>) => void) | null;
|
|
9
|
+
close(): void;
|
|
10
|
+
postMessage<M extends T = T>(message: M): void;
|
|
11
|
+
addEventListener<K extends keyof VuuBroadcastChannelEventMap<T>>(type: K, listener: (this: BroadcastChannel, evt: VuuBroadcastChannelEventMap<T>[K]) => void, options?: boolean | AddEventListenerOptions): void;
|
|
12
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
13
|
+
removeEventListener<K extends keyof VuuBroadcastChannelEventMap<T>>(type: K, listener: (this: BroadcastChannel, evt: VuuBroadcastChannelEventMap<T>[K]) => void, options?: boolean | EventListenerOptions): void;
|
|
14
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
15
|
+
}
|
|
16
|
+
export {};
|