@vuu-ui/vuu-utils 4.0.0-beta.7 → 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.
Files changed (89) hide show
  1. package/package.json +7 -7
  2. package/src/ScaledDecimal.js +0 -1
  3. package/src/filters/filterAsQuery.js +12 -3
  4. package/src/filters/index.js +1 -0
  5. package/src/filters/scaled-decimal-filter.js +130 -0
  6. package/src/form-utils.js +10 -4
  7. package/src/formatting-utils.js +2 -2
  8. package/src/round-decimal.js +35 -6
  9. package/types/component-registry.d.ts +6 -1
  10. package/types/filters/filterAsQuery.d.ts +3 -2
  11. package/types/filters/index.d.ts +1 -0
  12. package/types/filters/scaled-decimal-filter.d.ts +58 -0
  13. package/types/src/Clock.d.ts +20 -0
  14. package/types/src/PageVisibilityObserver.d.ts +36 -0
  15. package/types/src/ScaledDecimal.d.ts +12 -0
  16. package/types/src/ShellContext.d.ts +11 -0
  17. package/types/src/ThemeProvider.d.ts +32 -0
  18. package/types/src/array-utils.d.ts +16 -0
  19. package/types/src/box-utils.d.ts +24 -0
  20. package/types/src/broadcast-channel.d.ts +16 -0
  21. package/types/src/column-utils.d.ts +243 -0
  22. package/types/src/common-types.d.ts +6 -0
  23. package/types/src/component-registry.d.ts +65 -0
  24. package/types/src/context-definitions/DataContext.d.ts +21 -0
  25. package/types/src/context-definitions/DataProvider.d.ts +7 -0
  26. package/types/src/context-definitions/DataSourceProvider.d.ts +12 -0
  27. package/types/src/context-definitions/WorkspaceContext.d.ts +17 -0
  28. package/types/src/cookie-utils.d.ts +9 -0
  29. package/types/src/css-utils.d.ts +1 -0
  30. package/types/src/data-utils.d.ts +33 -0
  31. package/types/src/datasource/BaseDataSource.d.ts +63 -0
  32. package/types/src/datasource/datasource-action-utils.d.ts +7 -0
  33. package/types/src/datasource/datasource-filter-utils.d.ts +2 -0
  34. package/types/src/datasource/datasource-utils.d.ts +61 -0
  35. package/types/src/date/date-utils.d.ts +39 -0
  36. package/types/src/date/dateTimePattern.d.ts +8 -0
  37. package/types/src/date/formatter.d.ts +9 -0
  38. package/types/src/date/index.d.ts +4 -0
  39. package/types/src/date/types.d.ts +29 -0
  40. package/types/src/debug-utils.d.ts +9 -0
  41. package/types/src/edit-utils.d.ts +2 -0
  42. package/types/src/errors.d.ts +2 -0
  43. package/types/src/event-emitter.d.ts +21 -0
  44. package/types/src/feature-utils.d.ts +110 -0
  45. package/types/src/filters/filter-utils.d.ts +75 -0
  46. package/types/src/filters/filterAsQuery.d.ts +8 -0
  47. package/types/src/filters/index.d.ts +2 -0
  48. package/types/src/form-utils.d.ts +37 -0
  49. package/types/src/formatting-utils.d.ts +14 -0
  50. package/types/src/getUniqueId.d.ts +1 -0
  51. package/types/src/group-utils.d.ts +10 -0
  52. package/types/src/html-utils.d.ts +21 -0
  53. package/types/src/index.d.ts +76 -0
  54. package/types/src/input-utils.d.ts +2 -0
  55. package/types/src/invariant.d.ts +1 -0
  56. package/types/src/itemToString.d.ts +2 -0
  57. package/types/src/json-types.d.ts +52 -0
  58. package/types/src/json-utils.d.ts +6 -0
  59. package/types/src/keyboard-utils.d.ts +15 -0
  60. package/types/src/keyset.d.ts +16 -0
  61. package/types/src/layout-types.d.ts +22 -0
  62. package/types/src/list-utils.d.ts +2 -0
  63. package/types/src/local-storage-utils.d.ts +4 -0
  64. package/types/src/logging-utils.d.ts +45 -0
  65. package/types/src/module-utils.d.ts +8 -0
  66. package/types/src/nanoid/index.d.ts +1 -0
  67. package/types/src/perf-utils.d.ts +5 -0
  68. package/types/src/promise-utils.d.ts +8 -0
  69. package/types/src/protocol-message-utils.d.ts +43 -0
  70. package/types/src/range-utils.d.ts +29 -0
  71. package/types/src/react-utils.d.ts +15 -0
  72. package/types/src/round-decimal.d.ts +4 -0
  73. package/types/src/row-utils.d.ts +28 -0
  74. package/types/src/selection-utils.d.ts +9 -0
  75. package/types/src/shell-layout-types.d.ts +15 -0
  76. package/types/src/sort-utils.d.ts +11 -0
  77. package/types/src/table-schema-utils.d.ts +5 -0
  78. package/types/src/text-utils.d.ts +10 -0
  79. package/types/src/theme-utils.d.ts +14 -0
  80. package/types/src/tree-types.d.ts +9 -0
  81. package/types/src/tree-utils.d.ts +9 -0
  82. package/types/src/ts-utils.d.ts +20 -0
  83. package/types/src/typeahead-utils.d.ts +1 -0
  84. package/types/src/url-utils.d.ts +3 -0
  85. package/types/src/useId.d.ts +1 -0
  86. package/types/src/useLayoutEffectSkipFirst.d.ts +2 -0
  87. package/types/src/useResizeObserver.d.ts +15 -0
  88. package/types/src/useStateRef.d.ts +2 -0
  89. package/types/src/user-types.d.ts +3 -0
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.0.0-beta.7",
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.72.0"
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-beta.7",
19
- "@vuu-ui/vuu-table-types": "4.0.0-beta.7",
20
- "@vuu-ui/vuu-filter-types": "4.0.0-beta.7",
21
- "@vuu-ui/vuu-protocol-types": "4.0.0-beta.7"
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-beta.7",
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"
@@ -9,7 +9,6 @@ class ScaledDecimal {
9
9
  constructor(value, decimals = 6){
10
10
  this.value = value;
11
11
  this.decimals = decimals;
12
- console.log(`create a scaled decimal ${value} (${decimals} decimals)`);
13
12
  }
14
13
  get asLong() {
15
14
  if ("" === this.value) return "";
@@ -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
- if (isSerializableFilter(filter)) return true;
147
- return void 0 !== getTemporalInfo(columnsByName?.[filter.column]);
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 };
@@ -1,3 +1,4 @@
1
1
  export * from "./filter-utils.js";
2
2
  export * from "./filterAsQuery.js";
3
3
  export * from "./temporal-cell-filter.js";
4
+ export * from "./scaled-decimal-filter.js";
@@ -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 ScaledDecimal8(value);
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);
@@ -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;
@@ -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)=>value.length > MAX_INTEGER_DIGITS || value.length === MAX_INTEGER_DIGITS && value > MAX_SAFE_INTEGER;
7
- const carriesIntoIntegral = (fraction, decimals, roundingRule)=>"round" === roundingRule && decimals !== DECIMALS_AUTO && fraction.length > decimals && parseFloat(`0.${fraction}`).toFixed(decimals).startsWith("1");
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).toLocaleString() : parseFloat(part1).toString();
96
+ integral = "-0" === part1 ? "-0" : useLocaleString ? integerFormat.format(parseFloat(part1)) : parseFloat(part1).toString();
74
97
  if (align === Align.Left && alignOnDecimals) integral = padLeft(integral);
75
- fraction = decimals === DECIMALS_AUTO || actualDecimals === decimals ? part2 : actualDecimals > decimals ? "round" === roundingRule ? parseFloat("0." + part2).toFixed(decimals).slice(2) : part2.slice(0, decimals) : (Pad = zeroPad ? Zero : alignOnDecimals && align !== Align.Left ? Space : null) ? 0 === actualDecimals ? Pad.FULL_PADDING[decimals] ?? "" : pad(part2, decimals, Pad) : part2;
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).toLocaleString() : parseFloat(part1).toLocaleString() : 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
- fraction = decimals === DECIMALS_AUTO || actualDecimals === decimals ? part2 : actualDecimals > decimals ? "round" === roundingRule ? parseFloat("0." + part2).toFixed(decimals).slice(2) : part2.slice(0, decimals) : (Pad = zeroPad ? Zero : alignOnDecimals && align !== Align.Left ? Space : null) ? 0 === actualDecimals ? Pad.FULL_PADDING[decimals] ?? "" : pad(part2, decimals, Pad) : part2;
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
- export type RowClassNameGenerator = (dataRow: DataRow) => string | undefined;
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 themselves. Such filters
47
- * must be resolved (see resolveFilterQuery) before they can be evaluated locally.
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;
@@ -1,3 +1,4 @@
1
1
  export * from "./filter-utils";
2
2
  export * from "./filterAsQuery";
3
3
  export * from "./temporal-cell-filter";
4
+ export * from "./scaled-decimal-filter";
@@ -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 {};