Extra options D3 in Block Plus

The manual of the Block Plus manual refers for the D3 block to CoopArchives · CoopArchives - Fondation Maison de Salins as an example. In this example there are some extra options like the zoom/unzoom/full screen icons.

How can I add these options to the D3 block?

Here are the options used for that block:

{
    "items": {
        "limit": 1000
    } ,
    "item_sets": null,
    "relations": [
        "objects",
        "subjects",
        "item_sets"
    ],
    "config": {
        "height": 800,
        "forceCharge": -100,
        "forceLinkDistance": 100,
        "baseCirclePow": 0.6,
        "baseCircleMin": 5,
        "fontSizeTop": 35,
        "fontSizeMin": ".1px",
        "fontSizeMax": "16px"
    }
}

But there is a custom template too:

<?php
/**
 * @var \Laminas\View\Renderer\PhpRenderer $this
 * @var \Omeka\Api\Representation\SitePageBlockRepresentation $block
 * @var string $heading
 * @var array $params
 */

$plugins = $this->getHelperPluginManager();
$escape = $plugins->get('escapeHtml');
$references = $plugins->get('references');
$themeSetting = $plugins->get('themeSetting');

// The links sources and targets should be the keys of the nodes.
$data = [
    'nodes' => [],
    'links' => [],
];
$refs = $references->list('dcterms:subject', null, ['list_by_max' => 1024]);
// $targets = array_keys(array_replace(...array_column($refs['o:references'], 'resources')));
$orderTargets = [];
foreach ($refs['o:references'] as $ref) {
    $data['nodes'][] = [
        'id' => null,
        'title' => $ref['val'],
        'type' => 'value',
        'total' => $ref['total'],
    ];
    $orderSource = count($data['nodes']) - 1;
    foreach ($ref['resources'] as $resId => $resTitle) {
        if (!isset($orderTargets[$resId])) {
            $data['nodes'][] = [
                'id' => $resId,
                'title' => $resTitle,
                'type' => 'item',
                // TODO Total of relations (requires a first loop or a array sum on sub array column).
                'total' => 1,
            ];
            $orderTarget = count($data['nodes']) - 1;
            $orderTargets[$resId] = $orderTarget;
        }
        $data['links'][] = [
            'source' => $orderSource,
            'target' => $orderTargets[$resId],
            // TODO Compute value here?
            'value' => 1,
        ];
    }
}

$jsonUrlSite = json_encode($this->url('site', [], true), 320);
$jsonData = json_encode($data, 448);
$jsonConfig = json_encode($params['config'] ?? [], 448);
$script = <<<JS
var d3GraphbaseUrl = $jsonUrlSite;
var d3GraphData = $jsonData;
var d3GraphConfig = $jsonConfig;
JS;
$this->headScript()->appendScript($script);
?>

<div class="block d3-graph">
    <?php if (!empty($heading)): ?>
    <h2><?= $escape($heading) ?></h2>
    <?php endif; ?>
    <div class="d3-graph-warning">
        <a href="#"  class="close-button"></a>
        <p>Cette partie offre une expérience optimale consultée sur un ordinateur ou une tablette.</p>
    </div>
    <div id="d3-graph" class="graph bg-loading"></div>
    <div class="d3-graph-tools top-tools">
        <a href="#" class="d3-graph-info-tool"></a>
    </div>
    <div class="d3-graph-tools bottom-tools">
        <div class="d3-graph-zoom-in-out">
            <a href="#" class="d3-graph-zoom-in-tool"></a>
            <a href="#" class="d3-graph-zoom-out-tool"></a>
        </div>
        <div class="d3-graph-display">
            <a href="#" class="d3-graph-reset-tool"></a>
            <a href="#" class="d3-graph-fullscreen-tool"></a>
        </div>
    </div>
    <a href="#" class="d3-graph-close-fullscreen"></a>
    <?php if ($info = $themeSetting('d3graph_info')): ?>
    <div class="block d3-graph-popup">
        <div class="block d3-graph-popup-content">
            <a href="#" class="d3-graph-close-popup"></a>
            <?= $info ?>
        </div>
    </div>
    <?php endif; ?>
</div>

<script>
    $(document).ready(function() {

        const $body = $('body');

        let svgScale = 1;
        let svgTranslateX = 0;
        let svgTranslateY = 0;
        let svgScaleInterval;

        const updateTransform = function() {
            const $svg = $('.d3-graph svg > g');
            $svg.css('transform', 'scale('+ svgScale +') translate('+ svgTranslateX + 'px,' + svgTranslateY +'px)')
            $svg.css('transform-origin', '50% 50%')
        };

        const scaleSvg = function() {
            // On limite le zoom :
            svgScale = Math.max(0.2, Math.min(5, svgScale));
            updateTransform();
        };

        const scaleUp = function() {
            svgScale *= 1.01;
            scaleSvg();
        };

        const scaleDown = function() {
            svgScale /= 1.01;
            scaleSvg();
        };

        const translate = function(x, y) {
            svgTranslateX = x;
            svgTranslateY = y;
            updateTransform();
        };

        const toggleFullscreen = function() {

            if (!document.fullscreenElement &&    // alternative standard method
                !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {  // current working methods
                if (document.documentElement.requestFullscreen) {
                    document.documentElement.requestFullscreen();
                } else if (document.documentElement.msRequestFullscreen) {
                    document.documentElement.msRequestFullscreen();
                } else if (document.documentElement.mozRequestFullScreen) {
                    document.documentElement.mozRequestFullScreen();
                } else if (document.documentElement.webkitRequestFullscreen) {
                    document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
                }
                $(".contents.publication").addClass("fullscreen");

            } else {

                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                } else if (document.mozCancelFullScreen) {
                    document.mozCancelFullScreen();
                } else if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                }
                $(".contents.publication").removeClass("fullscreen");
            }
        };

        const stopScaleInterval = function(e) {
            svgParent.off('mousemove');
            if (svgScaleInterval) clearInterval(svgScaleInterval);
        };

        let mouseDownX, mouseDownY;
        const svgParent = $('#d3-graph');

        svgParent.on('mousedown', function(e) {
            stopScaleInterval();
            mouseDownX = e.originalEvent.clientX - svgTranslateX;
            mouseDownY = e.originalEvent.clientY - svgTranslateY;
            svgParent.on('mousemove', function(e) {
                translate( e.originalEvent.clientX - mouseDownX,  e.originalEvent.clientY - mouseDownY );
            });
        });

        svgParent.on('mouseup', stopScaleInterval);
        $body.on('mouseup', stopScaleInterval);

        $('.d3-graph-info-tool').on('mousedown', function(e) {
            e.stopPropagation();
            $('.d3-graph').removeClass('with-graph-popup').addClass('with-graph-popup');
        });

        $('.d3-graph-close-popup').on('mousedown', function(e) {
            e.stopPropagation();
            $('.d3-graph').removeClass('with-graph-popup');
        });

        $('.d3-graph-zoom-in-tool').on('mousedown', function(e) {
            e.stopPropagation();
            scaleUp();
            if (svgScaleInterval) clearInterval(svgScaleInterval);
            svgScaleInterval = setInterval(scaleUp, 10);
        });

        $('.d3-graph-zoom-out-tool').on('mousedown', function(e) {
            e.stopPropagation();
            scaleDown();
            if (svgScaleInterval) clearInterval(svgScaleInterval);
            svgScaleInterval = setInterval(scaleDown, 10);
        });

        $('.d3-graph-reset-tool').on('mousedown', function() {
            svgScale = 1;
            svgTranslateX = 0;
            svgTranslateY = 0;
            scaleSvg();
        });

        $('.d3-graph-fullscreen-tool').on('mousedown', function() {
            toggleFullscreen();
        });

        $('.d3-graph-close-fullscreen').on('mousedown', function() {
            toggleFullscreen();
        });

        svgParent.on('click', function(e) {

            const $target = $(e.target);

            const $nodeItem = $target.closest('.node.item');
            if ($nodeItem.length) {
                $nodeItemHyperlink = $nodeItem.find('a')
                if ($nodeItemHyperlink.length) {
                    e.preventDefault();

                    const targetURL = $nodeItemHyperlink.attr('href');

                    let htmlContent = '';
                    htmlContent += '<div class="popup-carte">';
                    htmlContent += '<div class="popup-header"><a href="#" class="popup-close-btn popup-close"></a></div>';
                    htmlContent += '<div class="popup-content popup-close"><iframe  src="' + targetURL  + '" height="100%" width="100%" style="none;" class="thin-scroll" /></div>';
                    htmlContent += '</div>'

                    $body.append(htmlContent);
                    $body.addClass("with-advanced-search-on-top");

                    const $popup = $('.popup-carte');
                    const $popupIframe = $('.popup-content');
                    const $popupCloseContent = $('.popup-close');

                    $popup.addClass('is-opening');

                    $popupIframe.on('click', function(e) {
                        e.stopPropagation();
                    });

                    $popupCloseContent.on('click', function(e) {
                        e.preventDefault();
                        $popupIframe.off('click');
                        $popupCloseContent.off('click');
                        $popup.remove();
                        $body.removeClass("with-advanced-search-on-top");
                    });

                }
            } else {
                const $nodeCluster = $target.closest('.node.value');
                if ($nodeCluster.length) {
                    const nodeTitle = $nodeCluster.find('text').text();
                    $(location).attr("href", "/s/fr/guide?q=&facet[dcterms_subject_ss][0]=" + encodeURIComponent (nodeTitle));
                }
            }

        })

    })
</script>