Balise pre avec scroll horizontal, largeur adaptative

Pour afficher du JSON "doublement encodé" et donc de potentiellement très longues chaînes

Le problème exposé dans ce sujet a été résolu.

Bonjour / bonsoir !

Je viens vers vous ce soir parce que je n’ai jamais caché mes maigres compétences en CSS, et là, j’ai besoin d’aide.

Afin de pouvoir corriger à la volée des valeurs dans du JSON qui lui-même contient du JSON dans une propriété, je me suis bricolé une page avec un peu de mise en forme. L’idée est d’avoir des outils de calcul de prix et de quoi afficher le JSON source sous une forme plus digeste. Mon souci actuellement est que je n’ai pas le défilement ou je le souhaiterais.

Mon code en l’état
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8" />
        <title>Ledger Restream Help Tool</title>
        <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
        <style type="text/css">
        body {
            overflow: hidden;
        }
        label {
            display: inline-block;
            min-width: 8rem;
        }
        form#Computings {
            display: flex;
            flex-direction: row;
            width: 100%;
            flex-wrap: wrap;
        }
        textarea {
            width: calc(100% - 11px);
            resize: vertical;
        }
        pre {
            background-color: #2b2c2d;
            width: calc(100% - 14px);
            color: white;
            min-height: 2em;
            padding: 5px;
            overflow-x: scroll;
        }
        .key {
            color: grey;
        }
        .string {
            color: #a00;
        }
        .number {
            color: #008080;
        }
        .boolean, .null, .undefined {
            color: #99cf50;
        }}
        </style>
    </head>
    <body>
        <form action="#" id="Computings">
            <fieldset>
                <legend>Produit</legend>
                <label for="ProductHT">Prix hors taxes</label><input type="number" name="product[HT]" id="ProductHT" step="0.01" data-group-name="product" data-group-id="Product" /><br />
                <label for="ProductRate">Taux TVA <strong style="color: red;">*</strong></label><input type="number" name="product[rate]" id="ProductRate" step="0.01" data-group-name="product" data-group-id="Product" /> %<br />
                <label for="ProductTVA">Montant TVA</label><input type="number" name="product[TVA]" id="ProductTVA" step="0.01" data-group-name="product" data-group-id="Product" /><br />
                <label for="ProductTotal">Prix TTC</label><input type="number" name="product[total]" id="ProductTotal" step="0.01" data-group-name="product" data-group-id="Product" />
            </fieldset>
            <fieldset>
                <legend>Frais d'envoi</legend>
                <label for="PostageHT">Prix hors taxes</label><input type="number" name="postage[HT]" id="PostageHT" step="0.01" data-group-name="postage" data-group-id="Postage" /><br />
                <label for="PostageRate">Taux TVA <strong style="color: red;">*</strong></label><input type="number" name="postage[rate]" id="PostageRate" step="0.01" data-group-name="postage" data-group-id="Postage" /> %<br />
                <label for="PostageTVA">Montant TVA</label><input type="number" name="postage[TVA]" id="PostageTVA" step="0.01" data-group-name="postage" data-group-id="Postage" /><br />
                <label for="PostageTotal">Prix TTC</label><input type="number" name="postage[total]" id="PostageTotal" step="0.01" data-group-name="postage" data-group-id="Postage" />
            </fieldset>
            <fieldset>
                <legend>Grand total</legend>
                <label for="GrandHT">Total hors taxes</label><input type="number" name="grand[HT]" id="GrandHT" step="0.01" value="0.00" readonly="readonly" /><br />
                <br />
                <label for="GrandTVA">Total TVA</label><input type="number" name="grand[TVA]" id="GrandTVA" step="0.01" value="0.00" readonly="readonly" /><br />
                <label for="GrandTotal">Total TTC</label><input type="number" name="grand[total]" id="GrandTotal" step="0.01" value="0.00" readonly="readonly" />
            </fieldset>
        </form>
        <form action="#">
            <fieldset id="LedgerData">
                <legend>Source</legend>
                <textarea id="LedgerSource"></textarea>
            </fieldset>
            <hr />
        </form>
        <fieldset>
            <legend>Prévisualisation</legend>
            <pre id="Parse"></pre>
            <pre id="ParseSquare"></pre>
        </fieldset>
    </body>
    <script>//<![CDATA[
        const parse = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*(?=:))?|\b(true|false|null|undefined)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g;
        const jsonSyntaxColoring = function(match) {
            var cls = 'number';
            if (/^"/.test(match)) {
                if (/:$/.test(match)) {
                    cls = 'key';
                } else {
                    cls = 'string';
                }
            } else if (/true|false/.test(match)) {
                cls = 'boolean';
            } else if (/null/.test(match)) {
                cls = 'null';
            } else if (/undefined/.test(match)) {
                cls = 'undefined';
            }
            return '<span class="' + cls + '">' + match + '</span>';
        };
        
        $(document).ready( function() {
            $('#Computings').on('change', 'input', function (e) {
                const meta = $(e.target).data();
                meta.property = $(this).attr('name').substr(meta.groupName.length + 1, $(this).attr('name').length - 2 - meta.groupName.length);
                const values = {};
                $('[name^="' + meta.groupName + '"]').each( function() {
                    if (this.value === '') {
                        return true;
                    }
                    values[$(this).attr('name').substr(meta.groupName.length + 1, $(this).attr('name').length - 2 - meta.groupName.length)] = this.value;
                });
                if (typeof values.rate === 'undefined') {
                    return;
                } else if (meta.property === 'rate') {
                    meta.property = Object.keys(values).concat([meta.property]).filter( function(e, i, array) {
                        return array.indexOf(e) === array.lastIndexOf(e);
                    })[0];
                }
                switch (meta.property) {
                    case 'HT':
                        values.TVA = values.HT * values.rate / 100;
                        values.total = parseFloat(values.HT) + parseFloat(values.TVA);
                        $('#' + meta.groupId + 'TVA').val(values.TVA.toFixed(2));
                        $('#' + meta.groupId + 'Total').val(values.total.toFixed(2));
                        break;

                    case 'TVA':
                        values.HT = values.TVA / (1 + values.rate / 100);
                        values.total = values.HT + values.TVA;
                        $('#' + meta.groupId + 'HT').val(values.HT.toFixed(2));
                        $('#' + meta.groupId + 'Total').val(values.total.toFixed(2));
                        break;
                
                    case 'total':
                        values.HT = values.total / (1 + values.rate / 100);
                        values.TVA = values.total * (values.rate / 100) / (1 + values.rate / 100);
                        $('#' + meta.groupId + 'HT').val(values.HT.toFixed(2));
                        $('#' + meta.groupId + 'TVA').val(values.TVA.toFixed(2));
                        break;
                        
                    default:
                        break;
                }
                ['HT', 'TVA', 'Total'].forEach( function(property) {
                    let value = 0;
                    ['Product', 'Postage'].forEach( function(section) {
                        value += parseFloat($('#' + section + property).val() || 0);
                    });
                    $('#Grand' + property).val(value.toFixed(2));
                });
            });
            $('#LedgerData').on('change', 'textarea', function(e) {
                const source = $(e.target).val();
                try {
                    const json = JSON.parse(source);
                    $('#Parse').html(JSON.stringify(json, null, 4).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(parse, jsonSyntaxColoring));

                    $('#ParseSquare').html(JSON.stringify(JSON.parse(source.replace(/\\"/g, '"').replace(/\\\\/g, '\\').replace(/"\{/g, '{').replace(/\}"/g, '}')), null, 4).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(parse, jsonSyntaxColoring));
                } catch (e) {
                    
                }
            });
        });
    //]]></script>
</html>

Ce que je souhaite, c’est que je puisse faire défiler l’intérieur de la partie Prévisualisation uniquement, et de manière verticale seulement. Pour le défilement horizontal, je le souhaiterais dans les deux <pre>. Actuellement, si j’ai de trop longues chaînes dans mon JSON, les <pre> n’ont aucun défilement, malgré mes tentatives (ajout de wrappers notamment). Le souci est notamment que je souhaite que la largeur puisse être maximale sans pour autant perdre les marges et que ce ne soit pas simplement adapté à la résolution de mon écran.

Est-ce qu’une bonne âme saurait comment réaliser ce que je souhaite ?

Merci d’avance  :)

+0 -0

Comment on obtient du texte dans la balise <pre> ?

A-312

Pardon, tu copies-colles une chaîne JSON dans le <textarea> "Source" et il l’affiche ensuite dans le premier <pre> sous forme "pretty print", et dans le second il réaffiche la même chose, mais en ayant "converti" un élément contenant une "sous-chaîne" JSON.
Exemple avec {"meuh":"{\"tché\":\"don\",\"vin\":100,\"vraiment\":null}"}.

Sinon cette solution marche : https://stackoverflow.com/a/12420043/2226755

A-312

En fait, le plus gros souci est que j’ai un gros objet "doublement sérialisé", et du coup, la chaîne en valeur dans le premier <pre> fait qu’il s’élargit pour l’afficher en entier, ce qui le sort de l’écran, et du coup je n’ai pas le défilement horizontal sur le <pre> mais ailleurs, et j’aimerais justement avoir le défilement horizontal pour ces balises (ou un éventuel conteneur), pas ailleurs.

Tu peux voir en utilisant mon exemple et redimensionnant la fenêtre du fiddle à 990 pixels de large au total, par exemple (je fais bêtement un Windows + sur mon PC avec un écran 1920x1080 pour ma part).

Note que je cherche une solution CSS uniquement, mais ne connaissant vraiment pas assez, peut-être que ce n’est pas possible et qu’il me faudra utiliser du JS pour fixer des largeurs.

+0 -0

Rajoute une balise div dans ou à la place du dernier fieldset, comme ceci :

<div>
  <legend>Prévisualisation</legend>
  <pre id="Parse"></pre>
  <pre id="ParseSquare"></pre>
</div>

ou :

fieldset {
    min-inline-size: auto;
}

Je ne connais pas le support de cette solution mais il est récent. En lisant le mdn, on voit qu’on doit définir la taille minimum, donc avec du CSS moins récent on a une 3ème solution.

sinon :

fieldset {
  min-width: 100%;
  /* dans ton cas : `min-width: calc(100% - 90px);` ou moins */
}

Merci, min-inline-size fonctionne pour moi.

Quant à n’avoir le défilement vertical que dans le <fieldset> de prévisualisation tout en conservant un affichage qui ne dépasse jamais la hauteur disponible, je me rends compte que ça va probablement se passer en JavaScript pour le moment.

+0 -0

Je vais juste répondre ainsi au sujet plutôt qu’éditer "discrètement".

J’ai donc ajouté du JavaScript pour recalculer la hauteur du <fieldset> de prévisualisation, et recalcule cela quand il y a redimensionnement de la fenêtre ou du textarea#LedgerSource. Je ne sais pas exactement d’où sort le 45 que j’ai mis en dur pour avoir le rendu que je voulais (probablement des histoires de margin ou padding), mais j’ai ce que je souhaitais.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte