All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.patternfly.component.codeblock.CodeBlock Maven / Gradle / Ivy

There is a newer version: 0.2.11
Show newest version
/*
 *  Copyright 2023 Red Hat
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.patternfly.component.codeblock;

import org.jboss.elemento.Attachable;
import org.jboss.elemento.Elements;
import org.jboss.elemento.Id;
import org.patternfly.component.BaseComponent;
import org.patternfly.component.ComponentType;
import org.patternfly.component.expandable.ExpandableSection;
import org.patternfly.style.Classes;

import elemental2.dom.HTMLDivElement;
import elemental2.dom.HTMLElement;
import elemental2.dom.MutationRecord;

import static java.util.Arrays.copyOfRange;
import static org.jboss.elemento.Elements.div;
import static org.jboss.elemento.Elements.failSafeRemoveFromParent;
import static org.jboss.elemento.Elements.insertAfter;
import static org.jboss.elemento.Elements.insertFirst;
import static org.jboss.elemento.Elements.pre;
import static org.jboss.elemento.Elements.removeChildrenFrom;
import static org.jboss.elemento.Elements.wrapHtmlContainer;
import static org.patternfly.component.codeblock.CodeBlockActions.codeBlockActions;
import static org.patternfly.component.codeblock.CodeBlockHeader.codeBlockHeader;
import static org.patternfly.component.expandable.ExpandableSection.expandableSection;
import static org.patternfly.component.expandable.ExpandableSectionContent.expandableSectionContent;
import static org.patternfly.component.expandable.ExpandableSectionToggle.expandableSectionToggle;
import static org.patternfly.style.Classes.codeBlock;
import static org.patternfly.style.Classes.component;
import static org.patternfly.style.Classes.content;
import static org.patternfly.style.Classes.pre;

/**
 * A code block is a component that contains 2 or more lines of read-only code. The code in a code block can be copied to the
 * clipboard.
 *
 * @see https://www.patternfly.org/components/code-block
 */
public class CodeBlock extends BaseComponent implements Attachable {

    // ------------------------------------------------------ factory

    public static CodeBlock codeBlock() {
        return new CodeBlock(null);
    }

    public static CodeBlock codeBlock(String code) {
        return new CodeBlock(code);
    }

    // ------------------------------------------------------ instance

    public static final int DEFAULT_TRUNCATE = 3;

    private final HTMLElement preElement;
    private final HTMLElement codeElement;
    private String code;
    private int truncate;
    private CodeBlockHeader header;
    private ExpandableSection esCode;
    private ExpandableSection esTrigger;

    CodeBlock(String code) {
        super(ComponentType.CodeBlock, div().css(component(codeBlock)).element());

        add(div().css(component(codeBlock, content))
                .add(preElement = pre().css(component(codeBlock, pre))
                        .add(codeElement = Elements.code().css(component(codeBlock, Classes.code))
                                .element())
                        .element()));

        if (code != null) {
            codeElement.textContent = code;
        }

        storeComponent();
        Attachable.register(this, this);
    }

    @Override
    public void attach(MutationRecord mutationRecord) {
        if (mustSplitCode()) {
            splitCode();
        }
    }

    // ------------------------------------------------------ add

    public CodeBlock addHeader(CodeBlockHeader header) {
        return add(header);
    }

    // override to ensure internal wiring
    public CodeBlock add(CodeBlockHeader header) {
        this.header = header;
        insertFirst(element(), header.element());
        return this;
    }

    public CodeBlock addAction(CodeBlockAction action) {
        if (header == null) {
            addHeader(codeBlockHeader());
        }
        if (header.actions == null) {
            header.addActions(codeBlockActions());
        }
        header.actions.addAction(action);
        return this;
    }

    // ------------------------------------------------------ builder

    /** Same as {@linkplain #truncate(int) truncate(3)} */
    public CodeBlock truncate() {
        return truncate(DEFAULT_TRUNCATE);
    }

    public CodeBlock truncate(int truncate) {
        this.truncate = truncate;
        return this;
    }

    public CodeBlock code(String code) {
        this.code = code;
        if (element().isConnected) { // already attached?
            if (mustSplitCode()) {
                splitCode();
            } else {
                unsplitCode();
                codeElement.textContent = code;
            }
        } else {
            codeElement.textContent = code;
        }
        return this;
    }

    @Override
    public CodeBlock that() {
        return this;
    }

    // ------------------------------------------------------ api

    public String code() {
        return code;
    }

    // ------------------------------------------------------ internal

    private String[] lines() {
        return code != null && !code.trim().isEmpty() ? code.split("\n") : new String[0];
    }

    private boolean mustSplitCode() {
        return truncate > 0 && truncate < lines().length;
    }

    private void splitCode() {
        String visibleCode = String.join("\n", copyOfRange(lines(), 0, truncate));
        String moreCode = String.join("\n", copyOfRange(lines(), truncate, lines().length));

        if (esCode == null && esTrigger == null) {
            String codeId = Id.unique(componentType().id, "es-code");
            String triggerId = Id.unique(componentType().id, "es-trigger");
            esCode = expandableSection(codeId)
                    .detachedFrom(triggerId)
                    .addContent(expandableSectionContent().textContent(moreCode));
            esTrigger = expandableSection(triggerId)
                    .detachedFrom(codeId)
                    .addToggle(expandableSectionToggle("Show more", "Show less"));

            removeChildrenFrom(codeElement);
            wrapHtmlContainer(codeElement)
                    .add(visibleCode)
                    .add(esCode);
            insertAfter(esTrigger.element(), preElement);

        } else if (esCode != null) {
            codeElement.textContent = visibleCode;
            esCode.content().textContent(moreCode);
        }
    }

    private void unsplitCode() {
        failSafeRemoveFromParent(esCode);
        failSafeRemoveFromParent(esTrigger);
        esCode = null;
        esTrigger = null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy