import { hierarchy } from 'd3-hierarchy' const dateFormatter = new Intl.DateTimeFormat('fr-FR', { month: 'long', year: 'numeric' }) export const RELATIONS = [ 'parents', 'siblings', 'creation_siblings', 'children' ] export const DATA_LEVELS = { initial: 0, partial: 1, full: 2 } export const ID_VARIANTS = { 9: 'depart', 22: 'critique', 63: 'echo', 6: 'reflexion', 7: 'lecture', 8: 'sensible', 23: 'kit', 0: 'creation' } export const VARIANT_IDS = Object.fromEntries( Object.entries(ID_VARIANTS).map(([key, value]) => [value, parseInt(key)]) ) export const SEARCH_KEYS = ['content', 'title', 'preTitle', 'authors'] export function formatDate (date) { return dateFormatter.format(new Date(date)) } export function parseNodesQueryParams ({ mode, nodes = [] }) { let nodebook = typeof nodes === 'string' ? [nodes] : nodes nodebook = nodebook.map(ids => { return typeof ids === 'string' ? ids.split(',').map(id => parseInt(id)) : [...ids] }) return { mode, nodebook } } export function reduceQueryLevels (lvl, lowestLvl = -1) { return Array(lvl - lowestLvl).fill(lowestLvl + 1).map((v, i) => v + i) } export function reduceQueryIds (ids, dataLevel, data) { const lvl = DATA_LEVELS[dataLevel] const idsToQuery = [] let lowestLvl = lvl for (const id of ids) { const nodeLvl = id in data ? data[id].dataLevel : -1 if (nodeLvl < lvl) { idsToQuery.push(id) } if (nodeLvl < lowestLvl) { lowestLvl = nodeLvl } } return idsToQuery.length === 0 ? {} : { idsToQuery, levelsToQuery: reduceQueryLevels(lvl, lowestLvl) } } export function getUniqueNodesIds (tree) { function extractId (ids, node) { ids.add(node.id) for (const relation of RELATIONS) { if (relation in node && node[relation]) { for (const subNode of node[relation]) { extractId(ids, subNode) } } } return ids } return Array.from(extractId(new Set(), tree)) } export function getRelatedNodesIds (nodes, ignored) { const ids = new Set() for (const node of nodes) { for (const relation of RELATIONS) { if (relation in node && node[relation]) { node[relation].forEach(({ id }) => { if (ignored.includes(id)) return ids.add(id) }) } } } return Array.from(ids) } export function getRandomIds (ids, count = 3) { const randomIds = [] while (randomIds.length < count) { const id = ids[Math.floor(Math.random() * ids.length)] if (!randomIds.includes(id)) { randomIds.push(id) } } return randomIds } export function parseTree (treeData) { if ('creations' in treeData) { if (treeData.creations) { if (!Array.isArray(treeData.children)) { treeData.children = [] } treeData.creations.forEach(item => { item.variant = [{ id: 0 }] treeData.children.push(item) }) } delete treeData.creations } for (const relation of RELATIONS) { if (relation in treeData && treeData[relation]) { treeData[relation] = treeData[relation].map(node => parseTree(node)) } } return treeData } export function buildTree (treeData, nodesData) { const uniqueIds = [] const nodes = [] const links = [] const h = hierarchy(treeData, (node) => { return RELATIONS.reduce((acc, relation) => { if (node[relation]) { node[relation].forEach((item, i) => { item.linkType = relation }) return [...acc, ...node[relation]] } return acc }, []) }) h.each(node => { node.id = node.data.id if (!uniqueIds.includes(node.id)) { uniqueIds.push(node.id) node.data = nodesData.find(n => n.id === node.id) // Add `x` and `y` keys so Vue can update on these values updates Object.assign(node, { x: undefined, y: undefined }) nodes.push(node) if (node.children) { node.children.forEach(child => { const asSource = links.find(link => link.source === node.id && link.target === child.data.id) const asTarget = links.find(link => link.source === child.data.id && link.target === node.id) if (!asSource && !asTarget) { links.push({ source: node.id, target: child.data.id, linkType: child.data.linkType }) } }) } } }) return { nodes, links } } export function searchInNode (search, node) { if (!search) return true for (const key of SEARCH_KEYS) { if (!node[key]) continue let match = false if (key === 'authors') { match = node[key].some(author => author.name.toLowerCase().includes(search)) } else { match = node[key].toLowerCase().includes(search) } if (match) return true } return false } export function tagsInNode (tags, nodeTags) { if (tags.length === 0) return true if (!nodeTags || nodeTags.length === 0) return false return tags.every(tag => nodeTags.some(nodeTag => nodeTag.name === tag)) } export function getRelation (node) { if (node.type === 'prod' && node.parents && node.parents.length) { return { parentId: node.parents[0].id, childId: node.id } } return { parentId: node.id } } export function getStrangenessOpacity (currentStrangeness, node) { if (currentStrangeness === undefined) return 1 const strangeness = ( node.type === 'prod' && node.parents && node.parents.length ? node.parents[0] : node ).strangeness return 1 / (Math.abs(strangeness - currentStrangeness) + 1) }