<commit_before>package com.github.vok.framework.vaadin

import com.vaadin.data.provider.DataProvider
import com.vaadin.server.ExternalResource
import com.vaadin.server.Resource
import com.vaadin.ui.*
import kotlin.reflect.KClass

/**
 * A specialized version of [ComponentContainer], for certain special containers. The DSL's
 * [init] method will invoke [addComponent] method with the components being registered.
 *
 * For example, there may be a special container (say, a ticker) which does not attach the components as its Vaadin
 * children immediately - rather it only remembers the components added via [addComponent] in a special list and
 * Vaadin-attaches them once every 10 seconds, one at a time. This way, you can use the DSL to define all children (or
 * pages) of this special component, without having them attached immediately as Vaadin children.
 */
interface SpecialContainer : HasComponents {
    /**
     * Adds the component into this container. Called by [init] when DSL-adding children to this container.
     *
     * Note that there is no `removeComponent()` method nor any sort of support for listing of the components added via this
     * method. This is because the DSL only needs to add the components; the component list itself highly depends on the implementation
     * of the component and might cause confusion with the [HasComponents] list of attached components.
     * Therefore, I'm keeping this interface as dumb as possible.
     * @param component the component to be added.
     */
    fun addComponent(component: Component)
}

/**
 * When introducing extensions for your custom components, just call this in your method. For example:
 *
 * `fun HasComponents.shinyComponent(caption: String? = null, block: ShinyComponent.()->Unit = {}) = init(ShinyComponent(caption), block)`
 *
 * Input [component] is automatically added to the children of this [ComponentContainer], or replaces content in [SingleComponentContainer] or [PopupView].
 * For custom containers just implement the [SpecialContainer] interface.
 *
 * @param component the component to attach
 * @param block optional block to run over the component, allowing you to add children to the [component]
 */
fun <T : Component> HasComponents.init(component: T, block: T.()->Unit = {}): T {
    when (this) {
        is ComponentContainer -> addComponent(component)
        is SpecialContainer -> addComponent(component)
        is SingleComponentContainer -> content = component
        is PopupView -> popupComponent = component
        else -> throw RuntimeException("Unsupported component container $this")
    }
    component.block()
    return component
}

fun HasComponents.verticalLayout(block: VerticalLayout.()->Unit = {}) = init(VerticalLayout(), block)

fun HasComponents.horizontalLayout(block: HorizontalLayout.()->Unit = {}) = init(HorizontalLayout(), block)

fun HasComponents.formLayout(block: FormLayout.()->Unit = {}) = init(FormLayout(), block)

fun HasComponents.absoluteLayout(block: AbsoluteLayout.()->Unit = {}) = init(AbsoluteLayout(), block)

fun HasComponents.button(caption: String? = null, leftClickListener: ((Button.ClickEvent)->Unit)? = null, block: Button.() -> Unit = {})
        = init(Button(caption), block).apply {
    if (leftClickListener != null) onLeftClick(leftClickListener)
}

fun <T: Any> HasComponents.grid(clazz: KClass<T>, caption: String? = null, dataProvider: DataProvider<T, *>? = null, block: Grid<T>.() -> Unit = {}) =
    init(Grid<T>(clazz.java)) {
        this.caption = caption
        if (dataProvider != null) this.dataProvider = dataProvider
        block()
    }

fun HasComponents.image(caption: String? = null, resource: Resource? = null, block: Image.()->Unit = {}) = init(Image(caption, resource), block)

/**
 * Creates a [TextField] and attaches it to this component.
 * @param caption optional caption
 * @param value the optional value<commit_msg>Vaadin DSL: added support for adding children to SplitPanel<commit_after>package com.github.vok.framework.vaadin

import com.vaadin.data.provider.DataProvider
import com.vaadin.server.ExternalResource
import com.vaadin.server.Resource
import com.vaadin.ui.*
import kotlin.reflect.KClass

/**
 * A specialized version of [ComponentContainer], for certain special containers. The DSL's
 * [init] method will invoke [addComponent] method with the components being registered.
 *
 * For example, there may be a special container (say, a ticker) which does not attach the components as its Vaadin
 * children immediately - rather it only remembers the components added via [addComponent] in a special list and
 * Vaadin-attaches them once every 10 seconds, one at a time. This way, you can use the DSL to define all children (or
 * pages) of this special component, without having them attached immediately as Vaadin children.
 */
interface SpecialContainer : HasComponents {
    /**
     * Adds the component into this container. Called by [init] when DSL-adding children to this container.
     *
     * Note that there is no `removeComponent()` method nor any sort of support for listing of the components added via this
     * method. This is because the DSL only needs to add the components; the component list itself highly depends on the implementation
     * of the component and might cause confusion with the [HasComponents] list of attached components.
     * Therefore, I'm keeping this interface as dumb as possible.
     * @param component the component to be added.
     */
    fun addComponent(component: Component)
}

/**
 * When introducing extensions for your custom components, just call this in your method. For example:
 *
 * `fun HasComponents.shinyComponent(caption: String? = null, block: ShinyComponent.()->Unit = {}) = init(ShinyComponent(caption), block)`
 *
 * Input [component] is automatically added to the children of this [ComponentContainer], or replaces content in [SingleComponentContainer] or [PopupView].
 * For custom containers just implement the [SpecialContainer] interface.
 *
 * @param component the component to attach
 * @param block optional block to run over the component, allowing you to add children to the [component]
 */
fun <T : Component> HasComponents.init(component: T, block: T.()->Unit = {}): T {
    addChild(component)
    component.block()
    return component
}

/**
 * Adds a [child] to this component. Only concrete subclasses are supported. The function will fail if the component
 * is already full (e.g. it is a split panel and it already contains two components).
 */
fun HasComponents.addChild(child: Component) {
    when (this) {
        is ComponentContainer -> addComponent(child)
        is SpecialContainer -> addComponent(child)
        is SingleComponentContainer -> {
            if (componentCount >= 1) throw IllegalArgumentException("$this can only have one child")
            content = child
        }
        is PopupView -> popupComponent = child
        is AbstractSplitPanel -> when (componentCount) {
            0 -> firstComponent = child
            1 -> secondComponent = child
            else -> throw IllegalArgumentException("$this can only have 2 children")
        }
        else -> throw IllegalArgumentException("Unsupported component container $this")
    }
}

fun HasComponents.verticalLayout(block: VerticalLayout.()->Unit = {}) = init(VerticalLayout(), block)

fun HasComponents.horizontalLayout(block: HorizontalLayout.()->Unit = {}) = init(HorizontalLayout(), block)

fun HasComponents.formLayout(block: FormLayout.()->Unit = {}) = init(FormLayout(), block)

fun HasComponents.absoluteLayout(block: AbsoluteLayout.()->Unit = {}) = init(AbsoluteLayout(), block)

fun HasComponents.button(caption: String? = null, leftClickListener: ((Button.ClickEvent)->Unit)? = null, block: Button.() -> Unit = {})
        = init(Button(caption), block).apply {
    if (leftClickListener != null) onLeftClick(leftClickListener)
}

fun <T: Any> HasComponents.grid(clazz: KClass<T>, caption: String? = null, dataProvider: DataProvider<T, *>? = null, block: Grid<T>.() -> Unit = {}) =
    init(Grid<T>(clazz.java)) {
        this.caption = caption
        if (dataProvider != null) this.dataProvider = dataProvider
        block()
    }

fun HasComponents.image(caption: String? = null, resource: Resource? = null, block: Image.()->Unit = {}) = init(Image(caption, resource), block)

/**
 * Creates a [TextField] and attaches it to this component.
 * @param caption optional caption
 * @param value the optional value
{'fraction_non_alphanumeric': 0.10247681652776147, 'fraction_numerical': 0.0009390773564972414, 'mean_word_length': 4.056379821958457, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6274036', 'n_tokens_mistral': 2170, 'n_tokens_neox': 2129, 'n_words': 854}