@dative-gpi/foundation-shared-services 1.0.193-test-unit-formatter → 1.0.193-value-data-composable
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/composables/index.ts +1 -2
- package/config/index.ts +0 -1
- package/package.json +3 -3
- package/composables/useUnitFormatter.ts +0 -230
- package/config/units/index.ts +0 -1
- package/config/units/unitPrefixes.ts +0 -13
package/composables/index.ts
CHANGED
package/config/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"url": "https://github.com/Dative-GPI/foundation-shared-ui.git"
|
|
5
5
|
},
|
|
6
6
|
"sideEffects": false,
|
|
7
|
-
"version": "1.0.193-
|
|
7
|
+
"version": "1.0.193-value-data-composable",
|
|
8
8
|
"description": "",
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"author": "",
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@dative-gpi/foundation-shared-domain": "1.0.193-
|
|
16
|
+
"@dative-gpi/foundation-shared-domain": "1.0.193-value-data-composable",
|
|
17
17
|
"@vueuse/core": "^14.0.0"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
"vue": "^3.4.38",
|
|
23
23
|
"vue-router": "^4.3.0"
|
|
24
24
|
},
|
|
25
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "e6d7d1fd7a3bc060cebb335596f4d95a6ce91356"
|
|
26
26
|
}
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import { useAppLanguageCode } from "@dative-gpi/foundation-shared-services/composables";
|
|
2
|
-
import { SI_PREFIXES } from "@dative-gpi/foundation-shared-services/config/units";
|
|
3
|
-
import { unitRegistry, unitFamilies, type UnitDefinition } from "@dative-gpi/foundation-shared-domain/models";
|
|
4
|
-
|
|
5
|
-
export function useUnitFormatter() {
|
|
6
|
-
const { languageCode } = useAppLanguageCode();
|
|
7
|
-
|
|
8
|
-
function findBestSIPrefix(value: number): { prefix: string; factor: number } {
|
|
9
|
-
if (value === 0) {
|
|
10
|
-
return SI_PREFIXES[3];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const absValue = Math.abs(value);
|
|
14
|
-
const magnitude = Math.floor(Math.log10(absValue) / 3) * 3;
|
|
15
|
-
|
|
16
|
-
const prefixIndex = Math.floor((magnitude + 9) / 3);
|
|
17
|
-
|
|
18
|
-
return SI_PREFIXES[Math.max(0, Math.min(prefixIndex, SI_PREFIXES.length - 1))];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function parseUnitWithPrefix(unitString: string): { prefix: string; baseUnit: string } {
|
|
22
|
-
const s = unitString.trim();
|
|
23
|
-
|
|
24
|
-
// 1) Priorité au match exact (évite "m" => prefix "m" + base "")
|
|
25
|
-
// et évite "Pa" => prefix "P" + base "a"
|
|
26
|
-
if (unitRegistry[s]) {
|
|
27
|
-
return { prefix: "", baseUnit: s };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 2) Préfixes SI (sans ""), du plus long au plus court (robuste si un jour tu ajoutes "da")
|
|
31
|
-
const prefixes = SI_PREFIXES
|
|
32
|
-
.map(p => p.prefix)
|
|
33
|
-
.filter(p => p !== "")
|
|
34
|
-
.sort((a, b) => b.length - a.length);
|
|
35
|
-
|
|
36
|
-
// 3) On n'accepte un préfixe que si le reste est une unité connue du registry
|
|
37
|
-
for (const prefix of prefixes) {
|
|
38
|
-
if (!s.startsWith(prefix)) {continue;}
|
|
39
|
-
|
|
40
|
-
const baseUnit = s.slice(prefix.length);
|
|
41
|
-
if (!baseUnit) {continue;}
|
|
42
|
-
|
|
43
|
-
if (unitRegistry[baseUnit]) {
|
|
44
|
-
return { prefix, baseUnit };
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 4) Aucun préfixe valide détecté => on garde l'unité telle quelle
|
|
49
|
-
return { prefix: "", baseUnit: s };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function findPrefixByName(prefixName: string): { prefix: string; factor: number } | null {
|
|
54
|
-
const found = SI_PREFIXES.find(p => p.prefix === prefixName);
|
|
55
|
-
return found || null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function convertWithinFamily(value: number, fromUnit: string, toUnit: string): number {
|
|
59
|
-
const from = unitRegistry[fromUnit];
|
|
60
|
-
const to = unitRegistry[toUnit];
|
|
61
|
-
|
|
62
|
-
if (!from || !to) {
|
|
63
|
-
throw new Error(`Unknown units: ${fromUnit} or ${toUnit}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (from.family !== to.family) {
|
|
67
|
-
throw new Error(`Different families: ${from.family} vs ${to.family}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return (value * from.toPivot) / to.toPivot;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
function applySpecialConversions(value: number, unit: string, unitDefinition: UnitDefinition ): { value: number; unit: string } {
|
|
75
|
-
if (!unitDefinition.specialConversions?.length) {
|
|
76
|
-
return { value, unit };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (const conversion of unitDefinition.specialConversions) {
|
|
80
|
-
if (Math.abs(value) >= conversion.threshold) {
|
|
81
|
-
const convertedValue = convertWithinFamily(value, unit, conversion.toUnit);
|
|
82
|
-
|
|
83
|
-
return { value: convertedValue, unit: conversion.toUnit };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return { value, unit };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function selectBestUnit(value: number, unit: string, unitPrecision?: string): { value: number; unit: string; symbol: string; } {
|
|
91
|
-
const unitSourceDefinition = unitRegistry[unit];
|
|
92
|
-
|
|
93
|
-
// Unknown unit : apply SI prefixes only
|
|
94
|
-
if (!unitSourceDefinition) {
|
|
95
|
-
const prefix = findBestSIPrefix(value);
|
|
96
|
-
const scaledValue = value / prefix.factor;
|
|
97
|
-
return {
|
|
98
|
-
value: scaledValue,
|
|
99
|
-
unit: `${prefix.prefix}${unit}`,
|
|
100
|
-
symbol: `${prefix.prefix}${unit}`
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// if unitPrecision is specified and valid
|
|
105
|
-
if (unitPrecision) {
|
|
106
|
-
const parsed = parseUnitWithPrefix(unitPrecision);
|
|
107
|
-
const precisionUnitDefinition = unitRegistry[parsed.baseUnit];
|
|
108
|
-
|
|
109
|
-
if (precisionUnitDefinition && precisionUnitDefinition.family === unitSourceDefinition.family) {
|
|
110
|
-
// Convert value to the precision unit
|
|
111
|
-
const valueInPrecision = convertWithinFamily(value, unit, parsed.baseUnit);
|
|
112
|
-
|
|
113
|
-
// If a prefix is specified in unitPrecision, apply it EXACTLY
|
|
114
|
-
if (parsed.prefix) {
|
|
115
|
-
const forcedPrefix = findPrefixByName(parsed.prefix);
|
|
116
|
-
if (forcedPrefix) {
|
|
117
|
-
const scaledValue = valueInPrecision / forcedPrefix.factor;
|
|
118
|
-
return {
|
|
119
|
-
value: scaledValue,
|
|
120
|
-
unit: parsed.baseUnit,
|
|
121
|
-
symbol: `${forcedPrefix.prefix}${precisionUnitDefinition.symbol}`,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// No prefix specified in unitPrecision: return WITHOUT any SI prefix
|
|
127
|
-
return {
|
|
128
|
-
value: valueInPrecision,
|
|
129
|
-
unit: parsed.baseUnit,
|
|
130
|
-
symbol: precisionUnitDefinition.symbol,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Apply special conversions
|
|
136
|
-
const afterSpecial = applySpecialConversions(value, unit, unitSourceDefinition);
|
|
137
|
-
if (afterSpecial.unit !== unit) {
|
|
138
|
-
return selectBestUnit(afterSpecial.value, afterSpecial.unit, unitPrecision);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// if the unit support SI prefix
|
|
142
|
-
if (unitSourceDefinition.usesSIPrefixes) {
|
|
143
|
-
const prefix = findBestSIPrefix(value);
|
|
144
|
-
const scaledValue = value / prefix.factor;
|
|
145
|
-
return {
|
|
146
|
-
value: scaledValue,
|
|
147
|
-
unit,
|
|
148
|
-
symbol: `${prefix.prefix}${unitSourceDefinition.symbol}`,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
value,
|
|
154
|
-
unit,
|
|
155
|
-
symbol: unitSourceDefinition.symbol,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function formatNumber(value: number, precision: number, locale?: string): string {
|
|
160
|
-
return new Intl.NumberFormat(locale, {
|
|
161
|
-
minimumFractionDigits: 0,
|
|
162
|
-
maximumFractionDigits: precision,
|
|
163
|
-
}).format(value);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* take a value with its source unit, convert it to targetUnit (if specified),
|
|
168
|
-
* find the best SI prefix (unless unitPrecision is specified),
|
|
169
|
-
* and format it as a string.
|
|
170
|
-
*/
|
|
171
|
-
function formatQuantity(valueToConvert: number, sourceUnit: string, options?: { targetUnit?: string; unitPrecision?: string; decimalPrecision?: number; }):
|
|
172
|
-
{ formatted: string;
|
|
173
|
-
value: string;
|
|
174
|
-
unit: string;
|
|
175
|
-
} {
|
|
176
|
-
if (!isFinite(valueToConvert)) {
|
|
177
|
-
return {
|
|
178
|
-
formatted: "—",
|
|
179
|
-
value: "—",
|
|
180
|
-
unit: ""
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const sourceUnitDef = unitRegistry[sourceUnit];
|
|
185
|
-
let finalValue = valueToConvert;
|
|
186
|
-
let finalUnit = sourceUnit;
|
|
187
|
-
|
|
188
|
-
if (options?.targetUnit && options.targetUnit !== sourceUnit) {
|
|
189
|
-
const targetUnitDef = unitRegistry[options.targetUnit];
|
|
190
|
-
|
|
191
|
-
if (!sourceUnitDef || !targetUnitDef) {
|
|
192
|
-
throw new Error(`Unknown unit: ${sourceUnit} or ${options.targetUnit}`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Conversion with custom function (temperatures)
|
|
196
|
-
const family = unitFamilies[sourceUnitDef.family];
|
|
197
|
-
if (family.customConverter) {
|
|
198
|
-
finalValue = family.customConverter(valueToConvert, sourceUnit, options.targetUnit);
|
|
199
|
-
finalUnit = options.targetUnit;
|
|
200
|
-
}
|
|
201
|
-
// Conversion between units of the same family
|
|
202
|
-
else if (sourceUnitDef.family === targetUnitDef.family) {
|
|
203
|
-
finalValue = convertWithinFamily(valueToConvert, sourceUnit, options.targetUnit);
|
|
204
|
-
finalUnit = options.targetUnit;
|
|
205
|
-
}
|
|
206
|
-
// Different families: error
|
|
207
|
-
else {
|
|
208
|
-
throw new Error(`Cannot convert between ${sourceUnitDef.family} and ${targetUnitDef.family}`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Find the best prefix
|
|
213
|
-
const result = selectBestUnit(finalValue, finalUnit, options?.unitPrecision);
|
|
214
|
-
|
|
215
|
-
const decimalPrecision = options?.decimalPrecision ?? 2;
|
|
216
|
-
|
|
217
|
-
const formattedValue = formatNumber(result.value, decimalPrecision, languageCode.value);
|
|
218
|
-
const formatted = `${formattedValue} ${result.symbol}`;
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
formatted,
|
|
222
|
-
value: formattedValue,
|
|
223
|
-
unit: result.symbol
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
formatQuantity
|
|
229
|
-
};
|
|
230
|
-
}
|
package/config/units/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./unitPrefixes";
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { UnitPrefix } from "@dative-gpi/foundation-shared-domain/enums/units";
|
|
2
|
-
|
|
3
|
-
export const SI_PREFIXES = [
|
|
4
|
-
{ prefix: UnitPrefix.Nano, factor: 1e-9 },
|
|
5
|
-
{ prefix: UnitPrefix.Micro, factor: 1e-6 },
|
|
6
|
-
{ prefix: UnitPrefix.Milli, factor: 1e-3 },
|
|
7
|
-
{ prefix: UnitPrefix.None, factor: 1 },
|
|
8
|
-
{ prefix: UnitPrefix.Kilo, factor: 1e3 },
|
|
9
|
-
{ prefix: UnitPrefix.Mega, factor: 1e6 },
|
|
10
|
-
{ prefix: UnitPrefix.Giga, factor: 1e9 },
|
|
11
|
-
{ prefix: UnitPrefix.Tera, factor: 1e12 },
|
|
12
|
-
{ prefix: UnitPrefix.Peta, factor: 1e15 },
|
|
13
|
-
];
|