New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@evidence-dev/component-utilities

Package Overview
Dependencies
Maintainers
5
Versions
387
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@evidence-dev/component-utilities - npm Package Compare versions

Comparing version 0.0.0-eca5c79a to 0.0.0-f00ada2d

src/helpers/getCompletedData.helpers.js

19

CHANGELOG.md
# @evidence-dev/component-utilities
## 1.1.2
### Patch Changes
- 4944f21c: getCompletedData() fills all x values for categorical series
- 287126fe: Ensure that numeric and date x-axis series are sorted
## 1.1.1
### Patch Changes
- 16112191: Fixes to series completion; duplicate series labels in timestamps should no longer appear
## 1.1.0
### Minor Changes
- 121c7868: Adds formatting control to components
## 1.0.0

@@ -4,0 +23,0 @@

4

LICENSE.md

@@ -1,6 +0,4 @@

# License
MIT License
Copyright \(c\) 2021 Evidence
Copyright \(c\) 2023 Evidence

@@ -7,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files \(the "Software"\), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

{
"name": "@evidence-dev/component-utilities",
"version": "0.0.0-eca5c79a",
"version": "0.0.0-f00ada2d",
"description": "",

@@ -13,2 +13,4 @@ "main": "index.js",

"devDependencies": {
"@faker-js/faker": "^8.0.2",
"@vitest/ui": "^0.34.2",
"vitest": "^0.31.1"

@@ -15,0 +17,0 @@ },

@@ -5,2 +5,11 @@ import { registerTheme, init } from 'echarts';

/**
* @typedef {import("echarts").EChartsOption & {
* dispatch?: ReturnType<typeof import("svelte").createEventDispatcher>;
* showAllXAxisLabels?: boolean;
* }
* } ActionParams
*/
/** @type {import("svelte/action").Action<HTMLElement, ActionParams>} */
export default (node, option) => {

@@ -437,3 +446,3 @@ registerTheme('evidence-light', {

const containerElement = document.querySelector('div.content > article');
const resizeChart = debounce(() => {
const onWindowResize = debounce(() => {
chart.resize({

@@ -444,16 +453,46 @@ animation: {

});
updateLabelWidths();
}, 100);
const updateLabelWidths = () => {
// Make sure we operate on an up-to-date options object
/** @type {import("echarts").EChartsOption} */
const prevOption = chart.getOption();
if (!prevOption) return;
// If the options object includes showing all x axis labels
// Note: this isn't a standard option, but right now this is the easiest way to pass something to the action.
// We don't want to have multiple resize observers if we can avoid it, and this is all due for a cleanup anyways
if (prevOption.showAllXAxisLabels) {
// Get all the possible x values
const distinctXValues = new Set(prevOption.series.flatMap((s) => s.data?.map((d) => d[0])));
const modConst = 4 / 5;
const clientWidth = node?.clientWidth ?? 0;
/** @type {import("echarts").EChartsOption} */
const newOption = {
xAxis: {
axisLabel: {
interval: 0,
overflow: 'truncate',
width: (clientWidth * modConst) / distinctXValues.size
}
}
};
chart.setOption(newOption);
}
};
if (window.ResizeObserver && containerElement) {
// TODO: This was originally added to combat a bug here: https://github.com/evidence-dev/evidence/pull/450
// Another solution is required. Something like lodash debounce might be an easy win to solve this.
resizeObserver = new ResizeObserver(resizeChart);
resizeObserver = new ResizeObserver(onWindowResize);
resizeObserver.observe(containerElement);
} else {
window.addEventListener('resize', resizeChart);
window.addEventListener('resize', onWindowResize);
}
onWindowResize();
return {
update(option) {
chart.setOption(option, true, true);
updateLabelWidths();
},

@@ -464,3 +503,3 @@ destroy() {

} else {
window.removeEventListener('resize', resizeChart);
window.removeEventListener('resize', onWindowResize);
}

@@ -467,0 +506,0 @@ chart.dispose();

@@ -7,2 +7,3 @@ import ssf from 'ssf';

import { standardizeDateString } from './dateParsing';
import { inferValueType } from './inferColumnTypes';

@@ -13,3 +14,3 @@ const AXIS_FORMATTING_CONTEXT = 'axis';

export const getCustomFormats = () => {
return getContext(CUSTOM_FORMATTING_SETTINGS_CONTEXT_KEY).getCustomFormats() || [];
return getContext(CUSTOM_FORMATTING_SETTINGS_CONTEXT_KEY)?.getCustomFormats() || [];
};

@@ -50,2 +51,29 @@

/**
* Returns an Evidence format object to be used in the applyFormatting function
* @param {string} formatString string containing an Excel-style format code, or a format name matching a built-in or custom format
* @param {string} valueType optional - a string representing the data type within the column that will be formatted ('number', 'date', 'boolean', or 'string)
* @returns a format object based on the formatString matching a built-in or custom format name, or a new custom format object containing an Excel-style format code
*/
export function getFormatObjectFromString(formatString, valueType = undefined) {
let potentialFormatTag = formatString;
let customFormats = getCustomFormats();
let matchingFormat = [...BUILT_IN_FORMATS, ...customFormats].find(
(format) => format.formatTag?.toLowerCase() === potentialFormatTag?.toLowerCase()
);
let newFormat = {};
if (matchingFormat) {
return matchingFormat;
} else {
newFormat = {
formatTag: 'custom',
formatCode: potentialFormatTag
};
if (valueType) {
newFormat.valueType = valueType;
}
return newFormat;
}
}
export const formatValue = (value, columnFormat = undefined, columnUnitSummary = undefined) => {

@@ -141,2 +169,3 @@ try {

}
let result = undefined;

@@ -206,1 +235,14 @@ if (columnFormat) {

}
/**
* Formats a value to whichever format is passed in
* @param {*} value the value to be formatted
* @param {string} format string containing an Excel-style format code, or a format name matching a built-in or custom format
* @returns a formatted value
*/
export function fmt(value, format) {
let formatObj = getFormatObjectFromString(format);
let valueType = inferValueType(value);
formatObj.valueType = valueType;
return formatValue(value, formatObj);
}

@@ -1,132 +0,100 @@

import { tidy, complete } from '@tidyjs/tidy';
import { tidy, complete, mutate } from '@tidyjs/tidy';
import getDistinctValues from './getDistinctValues';
import { findInterval, vectorSeq } from './helpers/getCompletedData.helpers.js';
function getDiffs(arr) {
var diffs = [];
for (var i = 1; i < arr.length; i++) diffs.push(arr[i] - arr[i - 1]);
return diffs;
}
/**
* This function fills missing data points in the given data array for a specific series.
*
* @param {Record<string, unknown>[]} data - The data as an array of objects.
* @param {string} x - The property used as x-axis.
* @param {string} y - The property used as y-axis.
* @param {string} series - The specific series in the data to be filled.
* @param {boolean} [nullsZero=false] - A flag indicating whether nulls should be replaced with zero.
* @param {boolean} [fillX=false] - A flag indicating whether the x-axis values should be filled (based on the found interval distance).
* @return {Record<string, unknown>[]} An array containing the filled data objects.
*/
export default function getCompletedData(data, x, y, series, nullsZero = false, fillX = false) {
const groups = Array.from(data).reduce((a, v) => {
if (series) {
if (!a[v[series]]) a[v[series]] = [];
a[v[series]].push(v);
} else {
if (!a.default) a.default = [];
a.default.push(v);
}
return a;
}, {});
function gcd(a, b) {
// Get greatest common divisor of the differences between values
if (a < b) return gcd(b, a);
// Ensures that all permutations of this map exist in the output
// e.g. can include series and x values to ensure that all series have all x values
const expandKeys = {};
// base case
if (Math.abs(b) < 0.001) return a;
else return gcd(b, a - Math.floor(a / b) * b);
}
const xIsDate = data[0]?.[x] instanceof Date;
function extent(values, valueof) {
let min;
let max;
if (valueof === undefined) {
for (const value of values) {
if (value != null) {
if (min === undefined) {
if (value >= value) min = max = value;
} else {
if (min > value) min = value;
if (max < value) max = value;
}
/** @type {Array<number | string>} */
let xDistinct;
const exampleX = data[0]?.[x];
switch (typeof exampleX) {
case 'object':
// If x is not a date; this shouldn't be hit, abort!
if (!(exampleX instanceof Date)) {
throw new Error('Unexpected object property, expected string, date, or number');
}
}
} else {
let index = -1;
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null) {
if (min === undefined) {
if (value >= value) min = max = value;
} else {
if (min > value) min = value;
if (max < value) max = value;
}
// Map dates to numeric values
xDistinct = getDistinctValues(
data.map((d) => ({ [x]: d[x].getTime() })),
x
);
// We don't fillX here because date numbers are very large, so a small interval would create a _massive_ array
break;
case 'number':
// Numbers are the most straightforward
xDistinct = getDistinctValues(data, x);
if (fillX) {
// Attempt to derive the interval between X values and interpolate missing values in that set (within the bounds of min/max)
const interval = findInterval(xDistinct);
expandKeys[x] = vectorSeq(xDistinct, interval);
}
}
break;
case 'string':
xDistinct = getDistinctValues(data, x);
expandKeys[x] = xDistinct;
break;
}
return [min, max];
}
function vectorSeq(values, period) {
let min = extent(values)[0];
let max = extent(values)[1];
const output = [];
const sequence = [];
let value = min;
while (value <= max) {
sequence.push(Math.round((value + Number.EPSILON) * 100000000) / 100000000);
value += period;
}
for (const value of Object.values(groups)) {
const nullySpec = series ? { [series]: null } : {};
if (nullsZero) {
nullySpec[y] = 0;
} else {
// Ensure null for consistency
nullySpec[y] = null;
}
return sequence;
}
if (series) {
expandKeys[series] = series;
}
function findInterval(arr) {
if (arr.length === 1) {
return;
}
// Sort array ascending
arr.sort(function (a, b) {
return a - b;
});
// 1. Multiply array by 100
arr = arr.map(function (x) {
return x * 100000000;
});
// 2. Get diffs
arr = getDiffs(arr);
// 3. Calculate greatest common divisor of diffs and divide by 100
let interval = arr.reduce(gcd) / 100000000;
interval = Math.round((interval + Number.EPSILON) * 100000000) / 100000000;
return interval;
}
export default function getCompletedData(data, x, y, series, nullsZero = false, fillX = false) {
let xDistinct = getDistinctValues(data, x);
let interval;
let filledData;
if (series) {
if (fillX) {
interval = findInterval(xDistinct);
if (nullsZero) {
filledData = tidy(
data,
complete({ [x]: vectorSeq(xDistinct, interval), [series]: series }, { [y]: 0 })
);
} else {
filledData = tidy(
data,
complete({ [x]: vectorSeq(xDistinct, interval), [series]: series })
);
}
const tidyFuncs = [];
if (Object.keys(expandKeys).length === 0) {
// empty object, no special configuration
tidyFuncs.push(complete([x], nullySpec));
} else {
filledData = tidy(
data,
complete(
[x, series],
// Nully values in the x and series columns to be treated as nulls
{ [series]: null, [x]: null }
)
tidyFuncs.push(complete(expandKeys, nullySpec));
}
if (xIsDate) {
// Ensure that x is actually a date
tidyFuncs.push(
mutate({
[x]: (val) => new Date(val[x])
})
);
}
} else {
if (fillX) {
interval = findInterval(xDistinct);
if (nullsZero) {
filledData = tidy(data, complete({ [x]: vectorSeq(xDistinct, interval) }, { [y]: 0 }));
} else {
filledData = tidy(data, complete({ [x]: vectorSeq(xDistinct, interval) }));
}
} else {
filledData = tidy(data, complete([x]));
}
output.push(tidy(value, ...tidyFuncs));
}
return filledData;
return output.flat();
}
export default function getDistinctValues(data, column) {
let distinctValues = [];
const distinctValueSet = new Set();
data.forEach((d) => {
distinctValueSet.add(d[column]);
});
distinctValues = [...distinctValueSet];
return distinctValues;
const set = new Set(data.map((val) => val[column]));
return Array.from(set);
}

@@ -17,3 +17,3 @@ // To-do, replace with import from db-commons

const inferValueType = function (columnValue) {
export const inferValueType = function (columnValue) {
if (typeof columnValue === 'number') {

@@ -20,0 +20,0 @@ return EvidenceType.NUMBER;

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc