Расположите все дочерние карты D3 в одной строке / строке - PullRequest
0 голосов
/ 22 апреля 2020

Мы используем древовидную карту d3 для построения в виде диаграммы с одной строкой / диаграммы Брауна ie.

, когда у нас есть данные 5, это нормально, но когда мы передали больше данных в него. Это была автоматическая настройка на несколько строк Это то, что мы получаем здесь

ожидаемое поведение для любых данных no.of. максимальная длина составляет 16

Имеем ли мы какой-либо контроль над вычислениями высоты и рендерингом логи c?

/* eslint-disable max-lines */
/* eslint-disable babel/no-invalid-this */

import React, { useEffect } from 'react';
import * as d3 from 'd3';

// https://bl.ocks.org/JacquesJahnichen/42afd0cde7cbf72ecb81
// https://bl.ocks.org/ganeshv/6a8e9ada3ab7f2d88022
// https://gist.github.com/tkafka/6d00c44d5ae52182f548a18e8db44811
Tree.defaultProps = {
    height: 200,
    brownieChart: true,
    data: []
};

export default function Tree( props ) {

    const { height, brownieChart, data } = props;
    const margin = { top: 24, right: 0, bottom: 0, left: 0 };
    const formatNumber = d3.format( ',d' );
    let node = '';
    let width = 100;
    let transitioning = false;
    let x = d3.scaleLinear()
    .domain( [ 0, width ] )
    .range( [ 0, width ] );
    const y = d3.scaleLinear()
    .domain( [ 0, height - margin.top - margin.bottom ] )
    .range( [ 0, height - margin.top - margin.bottom ] );
    var color = d3.scaleOrdinal()
    .range( d3.schemeCategory10
    .map( function ( c ) {

        c = d3.rgb( c ); c.opacity = 0.6;

        return c;

    } ) );
    let treemap;
    let svg, grandparent;

    const tooltip = d3.select( 'body' )
    .append( 'div' )
    .style( 'position', 'absolute' )
    .style( 'z-index', '10' )
    .style( 'visibility', 'hidden' )
    .style( 'color', '#3D4042' )
    .style( 'padding', '8px' )
    .style( 'background-color', 'white' )
    .style( 'font-weight', '500' )
    .style( 'width', '290px' )
    .style( 'height', '100px' )
    .style( 'border', '1px solid lightgrey' )
    // .style( 'box-shadow', '1px 1px lightgrey' )
    .text( 'tooltip' );


    useEffect( () => {

        const chartDiv = document.getElementById( 'chart' );
        const parentWidth = chartDiv.clientWidth;
        width = parentWidth;

        x = d3.scaleLinear()
        .domain( [ 0, width ] )
        .range( [ 0, width ] );

        updateDrillDown();

    }, [] );

    function updateDrillDown() {

        if ( svg ) {

            svg.selectAll( '*' ).remove();

        }
        else {

            const chartDiv = document.getElementById( 'chart' );
            const parentWidth = chartDiv.clientWidth;

            svg = d3.select( node ).append( 'svg' )
            .attr( 'width', parentWidth - margin.left - margin.right )
            .attr( 'height', height - margin.bottom - margin.top )
            .style( 'margin-top', `${ -margin.top - 5 }px` )
            .style( 'margin-left', `${ -margin.left }px` )
            .style( 'margin.right', `${ -margin.right }px` )
            .append( 'g' )
            .attr( 'transform', `translate(${ margin.left },${ margin.top })` )
            .style( 'shape-rendering', 'crispEdges' );

            grandparent = svg.append( 'g' )
            .attr( 'class', 'grandparent' )
            .style( 'visibility', brownieChart ? 'hidden' : 'visible' );

            grandparent.append( 'rect' )
            .attr( 'y', -margin.top )
            .attr( 'width', parentWidth )
            .attr( 'height', margin.top );

            grandparent.append( 'text' )
            .attr( 'x', 6 )
            .attr( 'y', 6 - margin.top )
            .attr( 'dy', '.75em' );

            treemap = d3.treemap()
            // .tile( d3.treemapResquarify.ratio( height / parentWidth * 0.5 * ( 1 + Math.sqrt( 5 ) ) ) )
            .size( [ parentWidth, height ] )
            .round( false )
            .paddingInner( 1 );

        }

        var root = d3.hierarchy( data )
        .eachBefore( function ( d ) {

            d.id = ( d.parent ? `${ d.parent.id }.` : '' ) + d.data.name;

        } )
        .sum( ( d ) => d.size )
        .sort( function ( a, b ) {

            return b.height - a.height || b.value - a.value;

        } );

        initialize( root );
        accumulate( root );
        layout( root );
        treemap( root );
        display( root );
        // d3.selectAll( '.dotme' ).call( dotme );

    }

    function initialize( root ) {

        root.x = root.y = 0;
        root.x1 = width;
        root.y1 = height;
        root.depth = 0;

    }

    // Aggregate the values for internal nodes. This is normally done by the
    // treemap layout, but not here because of our custom implementation.
    // We also take a snapshot of the original children (_children) to avoid
    // the children being overwritten when when layout is computed.
    function accumulate( d ) {


        // eslint-disable-next-line no-cond-assign
        return ( d._children = d.children )
            ? d.value = d.children.reduce( function ( p, v ) {

                return p + accumulate( v );

            }, 0 ) : d.value;

    }

    // Compute the treemap layout recursively such that each group of siblings
    // uses the same size (1×1) rather than the dimensions of the parent cell.
    // This optimizes the layout for the current zoom state. Note that a wrapper
    // object is created for the parent node for each group of siblings so that
    // the parent’s dimensions are not discarded as we recurse. Since each group
    // of sibling was laid out in 1×1, we must rescale to fit using absolute
    // coordinates. This lets us use a viewport to zoom.
    function layout( d ) {

        if ( d._children ) {


            d._children.forEach( function ( c ) {

                c.x0 = d.x0 + c.x0 * d.x1;
                c.y0 = d.y0 + c.y0 * d.y1;
                c.x1 *= ( d.x1 - d.x0 );
                c.y1 *= ( d.y1 - d.y0 );
                c.parent = d;
                layout( c );

            } );

        }

    }

    function display( d ) {

        grandparent
        .datum( d.parent )
        .on( 'click', transition )
        .select( 'text' )
        .text( name( d ) );

        var g1 = svg.insert( 'g', '.grandparent' )
        .datum( d )
        .attr( 'class', 'depth' );

        var g = g1.selectAll( 'g' )
        .data( d._children )
        .enter()
        .append( 'g' );

        g.filter( function ( d ) {

            return d._children;

        } )
        .classed( 'children', true )
        .on( 'click', transition );

        var children = g.selectAll( '.child' )
        .data( function ( d ) {

            return d._children || [ d ];

        } )
        .enter()
        .append( 'g' );

        children.append( 'rect' )
        .attr( 'class', 'child' )
        .call( rect )
        .append( 'title' )
        .text( function ( d ) {

            return `${ d.data.name } (${ formatNumber( d.value ) })`;

        } );

        children.append( 'text' )
        .attr( 'class', 'ctext' )
        .text( function ( d ) {

            return d.data.name;

        } )
        .call( text2 );

        g.append( 'rect' )
        .attr( 'class', 'parent' )
        .call( rect )
        .on( 'mouseover', ( d ) => {

            tooltip.html( nameTooltip( d ) );
            tooltip.transition();

            return tooltip.style( 'visibility', 'visible' ).style( 'opacity', 1 ); // transition().duration(5000).style('opacity', 0);

        } )
        .on( 'mousemove', () => {

            tooltip.style( 'opacity', 1 );
            const left = d3.event.pageX > ( window.innerWidth - 300 ) ? `${ d3.event.pageX - 300 }px` : `${ d3.event.pageX + 10 }px`;

            return tooltip.style( 'top', `${ d3.event.pageY - 10 }px` ).style( 'left', left );

        } )
        .on( 'mouseout', ()=>{

            return tooltip.style( 'visibility', 'hidden' );

        } );

        // label
        g.append( 'rect' )
        .attr( 'class', 'ptext_rect' )
        .attr( 'height', 50 )
        // .attr( 'width', 100 )
        .style( 'fill', 'white' )
        .style( 'opacity', '0.95' )
        .attr( 'rx', '2px' )
        .attr( 'ry', '2px' )
        .attr( 'x', ( d ) => {

            return x( d.x0 ) + 15;

        } )
        .attr( 'y', ( d ) => {

            return y( d.y0 ) + 15;

        } )
        .attr( 'width', function ( d ) {

            var w = x( d.x1 ) - x( d.x0 );
            console.warn( 'width---w', w );

            return w > 150 ? 150 : w - 25;

        } );


        var t = g.append( 'text' )
        .attr( 'class', 'ptext' )
        .attr( 'dy', '1em' )
        .attr( 'class', 'dotme' );

        t.append( 'tspan' )
        .style( 'font-weight', '600' )
        .text( function ( d ) {

            return d.data.name;

        } )
        .attr( 'width', function ( d ) {

            var w = x( d.x1 ) - x( d.x0 );

            return w > 150 ? 150 : w - 25;

        } )
        .call( dotme );


        t.append( 'tspan' )
        .attr( 'dy', '1.2em' )
        .text( function ( d ) {

            return formatNumber( d.value );

        } )
        .call( text3 )
        .call( dotme );

        t.call( text );


        g.selectAll( 'rect' )
        .style( 'fill', function ( d ) {

            return d.data.color || color( d.data.name );


        } );

        function transition( d ) {

            if ( transitioning || !d ) return;
            transitioning = true;
            var g2 = display( d ),
                t1 = g1.transition().duration( 750 ),
                t2 = g2.transition().duration( 750 );


            x.domain( [ d.x0, d.x0 + ( d.x1 - d.x0 ) ] );
            y.domain( [ d.y0, d.y0 + ( d.y1 - d.y0 ) ] );

            // Enable anti-aliasing during the transition.
            svg.style( 'shape-rendering', null );

            // Draw child nodes on top of parent nodes.
            svg.selectAll( '.depth' ).sort( function ( a, b ) {

                return a.depth - b.depth;

            } );

            // Fade-in entering text.
            g2.selectAll( 'text' ).style( 'fill-opacity', 0 );

            // Transition to the new view.
            t1.selectAll( '.ptext' ).call( text )
            .style( 'fill-opacity', 0 );
            t2.selectAll( '.ptext' ).call( text )
            .style( 'fill-opacity', 1 );
            t1.selectAll( '.ctext' ).call( text2 )
            .style( 'fill-opacity', 0 );
            t2.selectAll( '.ctext' ).call( text2 )
            .style( 'fill-opacity', 1 );
            t1.selectAll( 'rect' ).call( rect );
            t2.selectAll( 'rect' ).call( rect );

            // Remove the old node when the transition is finished.
            t1.remove().on( 'end', function () {

                svg.style( 'shape-rendering', 'crispEdges' );
                transitioning = false;

            } );

        }

        return g;

    }

    function dotme( text ) {

        text.each( function () {

            var text = d3.select( this );
            var words = text.text().split( '' );
            // var words = text.text().split( /\s+/ );


            var ellipsis = text.text( '' )
            .append( 'tspan' )
            .text( '...' );


            var width = parseFloat( text.attr( 'width' ) ) - 2 * ellipsis.node().getComputedTextLength();
            var numWords = words.length;


            var tspan = text.insert( 'tspan', ':first-child' ).text( words.join( '' ) );

            // Try the whole line
            // While it's too long, and we have words left, keep removing words

            while ( tspan.node().getComputedTextLength() > width && words.length ) {

                words.pop();
                tspan.text( words.join( '' ) );

            }

            if ( words.length === numWords ) {

                ellipsis.remove();

            }

        } );


    }

    function text3( text ) {

        text.attr( 'x', ( d ) => {

            return x( d.x0 ) + 25;

        } )
        .attr( 'y', ( d ) => {

            return y( d.y0 ) + 35;

        } );

    }

    function text( text ) {

        // text.selectAll( 'tspan' )
        // .attr( 'x', ( d ) => {

        //     return x( d.x0 ) + 25;

        // } );
        text.attr( 'x', ( d ) => {

            return x( d.x0 ) + 25;

        } )
        .attr( 'y', ( d ) => {

            return y( d.y0 ) + 22;

        } )
        .style( 'opacity', 1 );

    }

    function text2( text ) {

        text.attr( 'x', function ( d ) {

            return x( d.x1 ) - this.getComputedTextLength() - 6;

        } )
        .attr( 'y', function ( d ) {

            return y( d.y1 ) - 6;

        } )
        .style( 'opacity', function ( d ) {

            var w = x( d.x1 ) - x( d.x0 );

            return this.getComputedTextLength() < w - 6 ? 1 : 0;

        } );

    }

    function rect( rect ) {

        rect.attr( 'x', function ( d ) {

            return x( d.x0 );

        } )
        .attr( 'y', function ( d ) {

            return y( d.y0 );

        } )
        .attr( 'width', function ( d ) {

            var w = x( d.x1 ) - x( d.x0 );

            return w;

        } )
        .attr( 'height', function ( d ) {

            var h = y( d.y1 ) - y( d.y0 );

            return h;

        } );

    }

    function name( d ) {

        return d.parent ? `${ name( d.parent ) } / ${ d.data.name } (${ formatNumber( d.value ) })` : `${ d.data.name } (${ formatNumber( d.value ) })`;

    }

    function nameTooltip( d ) {

        return d.data.name ? `<div class='tool-tip'><div class='name'>${ d.data.name }</div><hr class='hr-tag' /><div class='second-div'>Market Value<div class="value">${ formatNumber( d.value ) } </div></div> </div>` : '';

    }

    return (
        <div ref={ ( el )=>node = el } id='chart'></div>
    );

}
...