@smallwebco/tinypivot-core 1.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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Detect column data type from values
3
+ */
4
+ export function detectColumnType(values) {
5
+ const nonNullValues = values.filter(v => v !== null && v !== undefined && v !== '');
6
+ if (nonNullValues.length === 0)
7
+ return 'string';
8
+ const sample = nonNullValues.slice(0, 100);
9
+ let numberCount = 0;
10
+ let dateCount = 0;
11
+ let booleanCount = 0;
12
+ for (const val of sample) {
13
+ if (typeof val === 'boolean') {
14
+ booleanCount++;
15
+ }
16
+ else if (typeof val === 'number' || (!Number.isNaN(Number(val)) && val !== '')) {
17
+ numberCount++;
18
+ }
19
+ else if (val instanceof Date || !Number.isNaN(Date.parse(String(val)))) {
20
+ dateCount++;
21
+ }
22
+ }
23
+ const threshold = sample.length * 0.8;
24
+ if (booleanCount >= threshold)
25
+ return 'boolean';
26
+ if (numberCount >= threshold)
27
+ return 'number';
28
+ if (dateCount >= threshold)
29
+ return 'date';
30
+ return 'string';
31
+ }
32
+ /**
33
+ * Detect field type from sample data (for pivot)
34
+ */
35
+ export function detectFieldType(data, field) {
36
+ const values = data.map(row => row[field]).filter(v => v !== null && v !== undefined && v !== '');
37
+ const sample = values.slice(0, 100);
38
+ let numberCount = 0;
39
+ const uniqueSet = new Set();
40
+ for (const val of sample) {
41
+ uniqueSet.add(String(val));
42
+ if (typeof val === 'number' || (!Number.isNaN(Number(val)) && val !== '')) {
43
+ numberCount++;
44
+ }
45
+ }
46
+ const isNumeric = numberCount >= sample.length * 0.8;
47
+ return {
48
+ field,
49
+ type: isNumeric ? 'number' : 'string',
50
+ uniqueCount: uniqueSet.size,
51
+ isNumeric,
52
+ };
53
+ }
54
+ /**
55
+ * Get unique values for a column (for Excel-style filter dropdown)
56
+ */
57
+ export function getColumnUniqueValues(data, columnKey, maxValues = 500) {
58
+ const values = [];
59
+ let nullCount = 0;
60
+ for (const row of data) {
61
+ const value = row[columnKey];
62
+ if (value === null || value === undefined || value === '') {
63
+ nullCount++;
64
+ }
65
+ else {
66
+ values.push(value);
67
+ }
68
+ }
69
+ // Get unique values
70
+ const uniqueSet = new Set();
71
+ for (const val of values) {
72
+ uniqueSet.add(String(val));
73
+ if (uniqueSet.size >= maxValues)
74
+ break;
75
+ }
76
+ const uniqueValues = Array.from(uniqueSet).sort((a, b) => {
77
+ // Natural sort for numbers
78
+ const numA = Number.parseFloat(a);
79
+ const numB = Number.parseFloat(b);
80
+ if (!Number.isNaN(numA) && !Number.isNaN(numB)) {
81
+ return numA - numB;
82
+ }
83
+ return a.localeCompare(b);
84
+ });
85
+ return {
86
+ uniqueValues,
87
+ totalCount: data.length,
88
+ nullCount,
89
+ type: detectColumnType(values),
90
+ };
91
+ }
92
+ /**
93
+ * Format cell value for display
94
+ */
95
+ export function formatCellValue(value, type) {
96
+ if (value === null || value === undefined)
97
+ return '';
98
+ if (value === '')
99
+ return '';
100
+ switch (type) {
101
+ case 'number': {
102
+ const num = typeof value === 'number' ? value : Number.parseFloat(String(value));
103
+ if (Number.isNaN(num))
104
+ return String(value);
105
+ // Format with commas for large numbers
106
+ if (Math.abs(num) >= 1000) {
107
+ return num.toLocaleString('en-US', { maximumFractionDigits: 2 });
108
+ }
109
+ return num.toLocaleString('en-US', { maximumFractionDigits: 4 });
110
+ }
111
+ case 'date': {
112
+ const date = value instanceof Date ? value : new Date(String(value));
113
+ if (Number.isNaN(date.getTime()))
114
+ return String(value);
115
+ return date.toLocaleDateString('en-US', {
116
+ year: 'numeric',
117
+ month: 'short',
118
+ day: 'numeric',
119
+ });
120
+ }
121
+ case 'boolean':
122
+ return value ? 'Yes' : 'No';
123
+ default:
124
+ return String(value);
125
+ }
126
+ }
127
+ /**
128
+ * Format number for display with appropriate precision
129
+ */
130
+ export function formatNumber(value, options) {
131
+ if (value === null)
132
+ return '-';
133
+ const maxDigits = options?.maximumFractionDigits ?? (Math.abs(value) >= 1000 ? 2 : 4);
134
+ return value.toLocaleString('en-US', { maximumFractionDigits: maxDigits });
135
+ }
136
+ /**
137
+ * Create a composite key from field values (for pivot grouping)
138
+ */
139
+ export function makeKey(row, fields) {
140
+ return fields.map(f => String(row[f] ?? '(blank)')).join('|||');
141
+ }
142
+ /**
143
+ * Parse composite key back to values
144
+ */
145
+ export function parseKey(key) {
146
+ return key.split('|||');
147
+ }
148
+ /**
149
+ * Natural sort comparator
150
+ */
151
+ export function naturalSort(a, b) {
152
+ const numA = Number.parseFloat(a);
153
+ const numB = Number.parseFloat(b);
154
+ if (!Number.isNaN(numA) && !Number.isNaN(numB)) {
155
+ return numA - numB;
156
+ }
157
+ return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
158
+ }
159
+ /**
160
+ * Debounce function
161
+ */
162
+ export function debounce(fn, delay) {
163
+ let timeoutId = null;
164
+ return (...args) => {
165
+ if (timeoutId)
166
+ clearTimeout(timeoutId);
167
+ timeoutId = setTimeout(() => fn(...args), delay);
168
+ };
169
+ }
170
+ /**
171
+ * Clamp a number between min and max
172
+ */
173
+ export function clamp(value, min, max) {
174
+ return Math.max(min, Math.min(max, value));
175
+ }
176
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@smallwebco/tinypivot-core",
3
+ "version": "1.0.0",
4
+ "description": "Core logic for TinyPivot - framework agnostic pivot table and data grid utilities",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "devDependencies": {
19
+ "typescript": "~5.3.3"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "tsc --watch"
24
+ }
25
+ }