@lobehub/charts
Version:
React modern charts components built on recharts
199 lines (194 loc) • 7.99 kB
JavaScript
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
import { differenceInCalendarDays, eachDayOfInterval, endOfYear, formatISO, getDay, getMonth, nextDay, parseISO, startOfYear, subWeeks } from 'date-fns';
import { isOnSeverSide } from "./index";
export var DEFAULT_MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
export var DEFAULT_LABELS = {
legend: {
less: 'Less',
more: 'More'
},
months: DEFAULT_MONTH_LABELS,
tooltip: '{{count}} activities on {{date}}',
totalCount: '{{count}} activities in {{year}}',
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
};
var fillHoles = function fillHoles(activities) {
if (activities.length === 0) {
return [];
}
var calendar = new Map(activities.map(function (a) {
return [a.date, a];
}));
var firstActivity = activities[0];
var lastActivity = activities.at(-1);
return eachDayOfInterval({
end: parseISO(lastActivity.date),
start: parseISO(firstActivity.date)
}).map(function (day) {
var date = formatISO(day, {
representation: 'date'
});
if (calendar.has(date)) {
return calendar.get(date);
}
return {
count: 0,
date: date,
level: 0
};
});
};
var calcTextDimensions = function calcTextDimensions(text, fontSize) {
if (isOnSeverSide) return;
if (fontSize < 1) {
throw new RangeError('fontSize must be positive');
}
if (text.length === 0) {
return {
height: 0,
width: 0
};
}
var namespace = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(namespace, 'svg');
svg.style.position = 'absolute';
svg.style.visibility = 'hidden';
svg.style.fontFamily = window.getComputedStyle(document.body).fontFamily;
svg.style.fontSize = "".concat(fontSize, "px");
var textNode = document.createElementNS(namespace, 'text');
textNode.textContent = text;
svg.append(textNode);
document.body.append(svg);
var boundingBox = textNode.getBBox();
svg.remove();
return {
height: boundingBox.height,
width: boundingBox.width
};
};
export var groupByWeeks = function groupByWeeks(activities) {
var weekStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
if (activities.length === 0) {
return [];
}
var normalizedActivities = fillHoles(activities);
// Determine the first date of the calendar. If the first date is not the
// set start weekday, the selected weekday one week earlier is used.
var firstActivity = normalizedActivities[0];
var firstDate = parseISO(firstActivity.date);
var firstCalendarDate = getDay(firstDate) === weekStart ? firstDate : subWeeks(nextDay(firstDate, weekStart), 1);
// To correctly group activities by week, it is necessary to left-pad the list
// because the first date might not be set start weekday.
var paddedActivities = [].concat(_toConsumableArray(Array.from({
length: differenceInCalendarDays(firstDate, firstCalendarDate)
}).fill(null)), _toConsumableArray(normalizedActivities));
var numberOfWeeks = Math.ceil(paddedActivities.length / 7);
// Finally, group activities by week
return Array.from({
length: numberOfWeeks
}).fill(null).map(function (_, weekIndex) {
return paddedActivities.slice(weekIndex * 7, weekIndex * 7 + 7);
});
};
export var getMonthLabels = function getMonthLabels(weeks) {
var monthNames = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_MONTH_LABELS;
return weeks.reduce(function (labels, week, weekIndex) {
var firstActivity = week.filter(Boolean).find(function (activity) {
return activity !== undefined;
});
if (!firstActivity) {
throw new Error("Unexpected error: Week ".concat(weekIndex + 1, " is empty: [").concat(week, "]."));
}
var month = monthNames[getMonth(parseISO(firstActivity.date))];
if (!month) {
var monthName = new Date(firstActivity.date).toLocaleString('en-US', {
month: 'short'
});
throw new Error("Unexpected error: undefined month label for ".concat(monthName, "."));
}
var prevLabel = labels.at(-1);
if (weekIndex === 0 || !prevLabel || prevLabel.label !== month) {
return [].concat(_toConsumableArray(labels), [{
label: month,
weekIndex: weekIndex
}]);
}
return labels;
}, []).filter(function (_ref, index, labels) {
var weekIndex = _ref.weekIndex;
// Labels should only be shown if there is "enough" space (data).
// This is a naive implementation that does not take the block size,
// font size, etc. into account.
var minWeeks = 3;
// Skip the first month label if there is not enough space to the next one.
if (index === 0) {
return labels[1] && labels[1].weekIndex - weekIndex >= minWeeks;
}
// Skip the last month label if there is not enough data in that month
// to avoid overflowing the calendar on the right.
if (index === labels.length - 1) {
return weeks.slice(weekIndex).length >= minWeeks;
}
return true;
});
};
export var generateEmptyData = function generateEmptyData() {
var year = new Date().getFullYear();
var days = eachDayOfInterval({
end: new Date(year, 11, 31),
start: new Date(year, 0, 1)
});
return days.map(function (date) {
return {
count: 0,
date: formatISO(date, {
representation: 'date'
}),
level: 0
};
});
};
export var generateTestData = function generateTestData(args) {
var _args$interval;
var maxCount = 20;
var maxLevel = args.maxLevel ? Math.max(1, args.maxLevel) : 4;
var now = new Date();
var days = eachDayOfInterval((_args$interval = args.interval) !== null && _args$interval !== void 0 ? _args$interval : {
end: endOfYear(now),
start: startOfYear(now)
});
return days.map(function (date) {
// The random activity count is shifted by up to 80% towards zero.
var c = Math.round(Math.random() * maxCount - Math.random() * (0.8 * maxCount));
var count = Math.max(0, c);
var level = Math.ceil(count / maxCount * maxLevel);
return {
count: count,
date: formatISO(date, {
representation: 'date'
}),
level: level
};
});
};
export var maxWeekdayLabelLength = function maxWeekdayLabelLength(firstWeek, weekStart, labels, fontSize) {
if (isOnSeverSide) return 0;
if (labels.length !== 7) {
throw new Error('Exactly 7 labels, one for each weekday must be passed.');
}
return firstWeek.reduce(function (maxLength, _, index) {
if (index % 2 !== 0) {
var dayIndex = (index + weekStart) % 7;
var label = labels[dayIndex];
// @ts-ignore
var curLength = Math.ceil(calcTextDimensions(label, fontSize).width);
return Math.max(maxLength, curLength);
}
return maxLength;
}, 0);
};