Визуализация Sunburst в Nivo опирается на строго иерархическую структуру данных. Основной принцип заключается в том, что каждый узел дерева может содержать дочерние элементы, а также числовое значение, которое используется для расчёта площади сегмента. Такая модель позволяет представлять сложные многослойные структуры — от организационных иерархий до распределения бюджета или аналитики пользовательских сегментов.
Базовая форма данных строится вокруг рекурсивного объекта, где каждый
уровень описывается через children, а листовые узлы
содержат value.
const data = {
name: "root",
children: [
{
name: "A",
children: [
{ name: "A1", value: 10 },
{ name: "A2", value: 20 }
]
},
{
name: "B",
children: [
{ name: "B1", value: 15 },
{ name: "B2", value: 5 }
]
}
]
};
Ключевой момент заключается в том, что value может
присутствовать только у конечных узлов. Если значение указано у
промежуточного узла, оно либо игнорируется, либо используется в
зависимости от режима агрегации.
Sunburst-компонент Nivo использует внутреннюю обработку через
иерархические алгоритмы наподобие d3-hierarchy. Это
накладывает несколько требований к структуре:
name или
idchildren обязательно для
узлов-родителейvalueПример корректной структуры с глубокой вложенностью:
const data = {
name: "Market",
children: [
{
name: "Electronics",
children: [
{
name: "Phones",
children: [
{ name: "iOS", value: 120 },
{ name: "Android", value: 300 }
]
},
{
name: "Laptops",
children: [
{ name: "Ultrabooks", value: 80 },
{ name: "Gaming", value: 60 }
]
}
]
}
]
};
Чем глубже структура, тем больше уровней отображается в Sunburst-диаграмме, где каждый слой соответствует одному уровню вложенности.
На практике данные часто поступают в плоском виде, например из API или аналитических систем. В таком случае требуется преобразование в древовидную структуру.
Пример плоского набора:
const flatData = [
{ category: "Electronics", sub: "Phones", type: "iOS", value: 120 },
{ category: "Electronics", sub: "Phones", type: "Android", value: 300 },
{ category: "Electronics", sub: "Laptops", type: "Gaming", value: 60 }
];
Для Sunburst необходимо преобразовать это в дерево:
function buildTree(data) {
const root = { name: "root", children: [] };
data.forEach(item => {
let category = root.children.find(c => c.name === item.category);
if (!category) {
category = { name: item.category, children: [] };
root.children.push(category);
}
let sub = category.children.find(s => s.name === item.sub);
if (!sub) {
sub = { name: item.sub, children: [] };
category.children.push(sub);
}
sub.children.push({
name: item.type,
value: item.value
});
});
return root;
}
Такая трансформация является обязательной, поскольку Sunburst не работает напрямую с табличными структурами.
Важной особенностью является автоматическая агрегация значений на
промежуточных уровнях. Если у родительского узла отсутствует
value, он вычисляется как сумма всех дочерних
элементов.
{
name: "A",
children: [
{ name: "A1", value: 10 },
{ name: "A2", value: 20 }
]
}
В этом случае узел A получит суммарное значение 30.
Если же value задан вручную, поведение зависит от
конфигурации компонента и может приводить к несоответствию
визуализации.
Иногда требуется фиксировать значения на всех уровнях, включая промежуточные. Это используется при работе с заранее агрегированными данными.
const data = {
name: "Total",
value: 1000,
children: [
{
name: "Segment A",
value: 600,
children: [
{ name: "A1", value: 400 },
{ name: "A2", value: 200 }
]
},
{
name: "Segment B",
value: 400,
children: [
{ name: "B1", value: 250 },
{ name: "B2", value: 150 }
]
}
]
};
Такой подход применяется в случаях, когда данные уже прошли этап агрегации на стороне backend.
В сложных системах поле name не всегда является
уникальным. Поэтому используется дополнительный идентификатор:
const data = {
id: "root",
children: [
{
id: "electronics",
children: [
{ id: "phones_ios", value: 120, name: "iOS" },
{ id: "phones_android", value: 300, name: "Android" }
]
}
]
};
Использование id повышает стабильность данных при
динамических обновлениях, особенно при анимациях и перерендеринге.
В некоторых случаях данные приходят в виде путей:
const pathData = [
{ path: ["Electronics", "Phones", "iOS"], value: 120 },
{ path: ["Electronics", "Phones", "Android"], value: 300 }
];
Преобразование в дерево выполняется через поэтапное построение:
function buildFromPath(data) {
const root = { name: "root", children: [] };
const findOrCreate = (children, name) => {
let node = children.find(n => n.name === name);
if (!node) {
node = { name, children: [] };
children.push(node);
}
return node;
};
data.forEach(item => {
let current = root;
item.path.forEach((segment, index) => {
if (index === item.path.length - 1) {
const parent = findOrCreate(current.children, segment);
parent.children = parent.children || [];
parent.children.push({
name: segment,
value: item.value
});
} else {
current = findOrCreate(current.children, segment);
}
});
});
return root;
}
Такой формат часто используется в логировании, файловых структурах и аналитике событий.
Перед использованием данных в Sunburst необходимо соблюдать несколько правил нормализации:
children и
valuenull и undefined значенийПример фильтрации:
function clean(node) {
if (node.children) {
node.children = node.children
.map(clean)
.filter(Boolean);
}
if (!node.children || node.children.length === 0) {
return node.value ? node : null;
}
return node;
}
Форма данных напрямую определяет визуальное поведение диаграммы:
value определяет угловую ширину
сегментовОсобенно важно учитывать баланс значений: если один узел значительно превышает остальные, визуальная структура становится доминированной одним сегментом, что снижает информативность.
При работе с Sunburst часто возникают типовые ошибки структуры:
value и children без чёткой
иерархииПример некорректной структуры:
const broken = [
{ name: "A", value: 10 },
{ name: "B", children: [{ name: "B1", value: 5 }] }
];
Такая структура не будет интерпретирована как дерево и приведёт к ошибке рендеринга.
В интерактивных приложениях структура данных Sunburst часто изменяется в реальном времени. В таких случаях важны:
Пример обновления значения без перестроения дерева:
function updateValue(node, id, newValue) {
if (node.id === id) {
return { ...node, value: newValue };
}
if (node.children) {
return {
...node,
children: node.children.map(child => updateValue(child, id, newValue))
};
}
return node;
}
При работе с большими наборами данных важна оптимизация структуры:
Глубокие структуры увеличивают нагрузку на вычисление layout и могут замедлять отрисовку Sunburst-компонента, особенно при анимациях переходов.