diff options
Diffstat (limited to 'src/main/kotlin/pl')
9 files changed, 419 insertions, 10 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt index 258e870d..1e93d95a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt +++ b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt @@ -8,7 +8,12 @@ import pl.treksoft.kvision.dropdown.DD.* import pl.treksoft.kvision.dropdown.DropDown import pl.treksoft.kvision.html.* import pl.treksoft.kvision.html.TAG.H1 +import pl.treksoft.kvision.modal.Alert +import pl.treksoft.kvision.modal.Confirm +import pl.treksoft.kvision.modal.MODALSIZE +import pl.treksoft.kvision.modal.Modal import pl.treksoft.kvision.routing.routing +import kotlin.browser.window class Showcase : ApplicationBase() { @@ -73,6 +78,11 @@ class Showcase : ApplicationBase() { val img = Image(Img("kotlin.png"), "Image", true, IMAGESHAPE.ROUNDED) root.add(img) + val modal = Modal("Test okienka") + modal.add(Tag(TAG.H4, "ABC")) + modal.add(Image(Img("kotlin.png"))) + modal.addButton(Button("To jest button")) + val button = Button("To jest przycisk FA", "fa-flag", BUTTONSTYLE.DANGER) button.setEventListener<Button> { click = { _ -> @@ -80,9 +90,24 @@ class Showcase : ApplicationBase() { dd3.text = "Zmiana" dd3.style = BUTTONSTYLE.WARNING dd3.disabled = true + modal.show() + window.setTimeout({ + modal.size = MODALSIZE.SMALL + modal.animation = false + }, 2000) } } root.add(button) + + fun alerts() { + Alert.show("This is alert", "A text of the <b>alert</b>", true) { + println("abc") + Alert.show("This is alert 2", "A text of the <b>alert</b> 2", true) { + println("def") + } + } + } + val button2 = Button("To jest przycisk", "flag", BUTTONSTYLE.DANGER) button2.setEventListener { click = { e -> @@ -91,17 +116,20 @@ class Showcase : ApplicationBase() { button.setEventListener { click = null } + alerts() } } root.add(button2) val button3 = Button("To jest przycisk IMG", image = Img("kotlin.png")) button3.setEventListener { click = { e -> - dd.show() - println("3" + e) - button.setEventListener<Button> { - click = { _ -> println(self.text) } - dblclick = { e -> println("111" + e) } + Confirm.show("Pytanie", "Czy na pewno chcesz kliknąć przycisk?", noCallback = { println("no") }) { + dd.show() + println("3" + e) + button.setEventListener<Button> { + click = { _ -> println(self.text) } + dblclick = { e -> println("111" + e) } + } } } } @@ -112,7 +140,6 @@ class Showcase : ApplicationBase() { .on("/abc", { -> println("abc") }) .on("/test", { -> println("test") }) .resolve() - // jQuery(document).off(".data-api") // routing.on(RegExp("/abc/def/(.*)/(.*)/(.*)"), { x,y,z,u,v -> println(x) }) diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Root.kt b/src/main/kotlin/pl/treksoft/kvision/core/Root.kt index ef2e7616..abb5d3cf 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Root.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Root.kt @@ -1,18 +1,31 @@ package pl.treksoft.kvision.core import com.github.snabbdom.VNode +import pl.treksoft.kvision.modal.Modal import pl.treksoft.kvision.snabbdom.StringBoolPair class Root(id: String, private val fluid: Boolean = false) : Container() { + private val modals: MutableList<Modal> = mutableListOf() private var rootVnode: VNode = render() init { rootVnode = KVManager.patch(id, this.render()) this.id = id + roots.add(this) } override fun render(): VNode { - return kvh("div#" + id, childrenVNodes()) + return kvh("div#" + id, childrenVNodes() + modalsVNodes()) + } + + private fun modalsVNodes(): Array<VNode> { + return modals.filter { it.visible }.map { it.render() }.toTypedArray() + } + + internal fun addModal(modal: Modal) { + modals.add(modal) + modal.parent = this + refresh() } override fun getSnClass(): List<StringBoolPair> { @@ -28,4 +41,14 @@ class Root(id: String, private val fluid: Boolean = false) : Container() { return this } + companion object { + private val roots: MutableList<Root> = mutableListOf() + + internal fun getLastRoot(): Root? { + if (roots.size > 0) + return roots[roots.size - 1] + else + return null + } + } } diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index 7ee9a28f..e2f88e72 100644 --- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -73,10 +73,10 @@ open class DropDown(text: String, elements: List<StringPair>? = null, icon: Stri refresh() } - val idc = "kv_dropdown_" + counter - val button: DropDownButton = DropDownButton(idc, text, icon, style, size, block, + private val idc = "kv_dropdown_" + counter + protected val button: DropDownButton = DropDownButton(idc, text, icon, style, size, block, disabled, image, setOf("dropdown")) - val list: DropDownListTag = DropDownListTag(idc, setOf("dropdown-menu")) + protected val list: DropDownListTag = DropDownListTag(idc, setOf("dropdown-menu")) init { button.setEventListener { diff --git a/src/main/kotlin/pl/treksoft/kvision/helpers/CloseIcon.kt b/src/main/kotlin/pl/treksoft/kvision/helpers/CloseIcon.kt new file mode 100644 index 00000000..b5f1a642 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/helpers/CloseIcon.kt @@ -0,0 +1,25 @@ +package pl.treksoft.kvision.helpers + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.core.KVManager +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.snabbdom.StringBoolPair +import pl.treksoft.kvision.snabbdom.StringPair + +open class CloseIcon(classes: Set<String> = setOf()) : Widget(classes) { + + override fun render(): VNode { + return kvh("button", arrayOf(KVManager.virtualize("<span aria-hidden='true'>×</span>"))) + } + + override fun getSnClass(): List<StringBoolPair> { + val cl = super.getSnClass().toMutableList() + cl.add("close" to true) + return cl + } + + override fun getSnAttrs(): List<StringPair> { + return super.getSnAttrs() + listOf("type" to "button", "aria-label" to "Close") + } + +} diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt index 37a7bf98..febee81b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Tag.kt @@ -19,6 +19,7 @@ enum class TAG(val tagName: String) { BLOCKQUOTE("blockquote"), FOOTER("footer"), PRE("pre"), + DIV("div"), MARK("mark"), DEL("del"), diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt new file mode 100644 index 00000000..5e0e910a --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt @@ -0,0 +1,54 @@ +package pl.treksoft.kvision.modal + +import pl.treksoft.kvision.html.ALIGN +import pl.treksoft.kvision.html.BUTTONSTYLE +import pl.treksoft.kvision.html.Button +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag + +open class Alert(caption: String? = null, text: String? = null, rich: Boolean = false, + align: ALIGN = ALIGN.NONE, size: MODALSIZE? = null, animation: Boolean = true, + private val callback: (() -> Unit)? = null) : Modal(caption, true, size, animation) { + var text + get() = content.text + set(value) { + content.text = value + } + var rich + get() = content.rich + set(value) { + content.rich = value + } + var align + get() = content.align + set(value) { + content.align = value + } + + val content = Tag(TAG.SPAN, text, rich, align) + + init { + body.add(content) + val okButton = Button("OK", "ok", BUTTONSTYLE.PRIMARY) + okButton.setEventListener { + click = { + hide() + } + } + this.addButton(okButton) + } + + override fun hide() { + super.hide() + this.callback?.invoke() + } + + companion object { + @Suppress("LongParameterList") + fun show(caption: String? = null, text: String? = null, rich: Boolean = false, + align: ALIGN = ALIGN.NONE, size: MODALSIZE? = null, animation: Boolean = true, + callback: (() -> Unit)? = null) { + Alert(caption, text, rich, align, size, animation, callback).show() + } + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt new file mode 100644 index 00000000..366eec52 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt @@ -0,0 +1,84 @@ +package pl.treksoft.kvision.modal + +import pl.treksoft.kvision.html.ALIGN +import pl.treksoft.kvision.html.BUTTONSTYLE +import pl.treksoft.kvision.html.Button +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag + +open class Confirm(caption: String? = null, text: String? = null, rich: Boolean = false, + align: ALIGN = ALIGN.NONE, size: MODALSIZE? = null, animation: Boolean = true, + cancelVisible: Boolean = false, + private val noCallback: (() -> Unit)? = null, + private val yesCallback: (() -> Unit)? = null) : Modal(caption, false, size, animation, false) { + var text + get() = content.text + set(value) { + content.text = value + } + var rich + get() = content.rich + set(value) { + content.rich = value + } + var align + get() = content.align + set(value) { + content.align = value + } + var cancelVisible = cancelVisible + set(value) { + field = value + refreshCancelButton() + } + + val content = Tag(TAG.SPAN, text, rich, align) + val cancelButton = Button("Cancel", "remove") + + init { + body.add(content) + cancelButton.setEventListener { + click = { + hide() + } + } + this.addButton(cancelButton) + val noButton = Button("No", "ban-circle") + noButton.setEventListener { + click = { + hide() + noCallback?.invoke() + } + } + this.addButton(noButton) + val yesButton = Button("Yes", "ok", BUTTONSTYLE.PRIMARY) + yesButton.setEventListener { + click = { + hide() + yesCallback?.invoke() + } + } + this.addButton(yesButton) + refreshCancelButton() + } + + private fun refreshCancelButton() { + if (cancelVisible) { + cancelButton.show() + closeIcon.show() + } else { + cancelButton.hide() + closeIcon.hide() + } + } + + companion object { + @Suppress("LongParameterList") + fun show(caption: String? = null, text: String? = null, rich: Boolean = false, + align: ALIGN = ALIGN.NONE, size: MODALSIZE? = null, animation: Boolean = true, + cancelVisible: Boolean = false, + noCallback: (() -> Unit)? = null, yesCallback: (() -> Unit)? = null) { + Confirm(caption, text, rich, align, size, animation, cancelVisible, noCallback, yesCallback).show() + } + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt new file mode 100644 index 00000000..0b11d45f --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt @@ -0,0 +1,191 @@ +package pl.treksoft.kvision.modal + +import com.github.snabbdom.VNode +import org.w3c.dom.CustomEvent +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.Root +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.helpers.CloseIcon +import pl.treksoft.kvision.html.Button +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag +import pl.treksoft.kvision.snabbdom.StringBoolPair +import pl.treksoft.kvision.snabbdom.StringPair +import pl.treksoft.kvision.snabbdom.obj + +enum class MODALSIZE(val className: String) { + LARGE("modal-lg"), + SMALL("modal-sm") +} + +@Suppress("TooManyFunctions") +open class Modal(caption: String? = null, closeButton: Boolean = true, + size: MODALSIZE? = null, animation: Boolean = true, val escape: Boolean = true, + classes: Set<String> = setOf()) : Container(classes) { + var caption + get() = captionTag.text + set(value) { + captionTag.text = value + checkHeaderVisibility() + } + var closeButton + get() = closeIcon.visible + set(value) { + closeIcon.visible = value + checkHeaderVisibility() + } + var size + get() = dialog.size + set(value) { + dialog.size = value + } + var animation = animation + set(value) { + field = value + refresh() + } + + protected val dialog = ModalDialog(size) + protected val header = Container(setOf("modal-header")) + protected val closeIcon = CloseIcon() + protected val captionTag = Tag(TAG.H4, caption, classes = setOf("modal-title")) + protected val body = Container(setOf("modal-body")) + protected val footer = Container(setOf("modal-footer")) + + init { + this.hide() + this.role = "dialog" + this.addInternal(dialog) + val content = Container(setOf("modal-content")) + dialog.role = "document" + dialog.add(content) + closeIcon.visible = closeButton + closeIcon.setEventListener { + click = { + hide() + } + } + header.add(closeIcon) + header.add(captionTag) + checkHeaderVisibility() + content.add(header) + content.add(body) + content.add(footer) + val root = Root.getLastRoot() + if (root != null) { + root.addModal(this) + } else { + println("At least one Root object is required to create a modal!") + } + } + + private fun checkHeaderVisibility() { + if (!closeButton && caption == null) { + header.hide() + } else { + header.show() + } + } + + override fun add(child: Widget) { + body.add(child) + } + + override fun addAll(children: List<Widget>) { + body.addAll(children) + } + + open fun addButton(button: Button) { + footer.add(button) + } + + open fun removeButton(button: Button) { + footer.remove(button) + } + + open fun removeButtonAt(index: Int) { + footer.removeAt(index) + } + + open fun removeAllButtons() { + footer.removeAll() + } + + override fun getSnAttrs(): List<StringPair> { + val pr = super.getSnAttrs().toMutableList() + pr.add("tabindex" to "-1") + return pr + } + + override fun getSnClass(): List<StringBoolPair> { + val cl = super.getSnClass().toMutableList() + cl.add("modal" to true) + if (animation) { + cl.add("fade" to true) + } + return cl + } + + @Suppress("UnsafeCastFromDynamic") + override fun afterInsert(node: VNode) { + getElementJQueryD()?.modal(obj { + keyboard = escape + backdrop = if (escape) "true" else "static" + }) + this.getElementJQuery()?.on("show.bs.modal", { _, _ -> + val event = CustomEvent("showBsModal") + this.getElement()?.dispatchEvent(event) + }) + this.getElementJQuery()?.on("shown.bs.modal", { _, _ -> + val event = CustomEvent("shownBsModal") + this.getElement()?.dispatchEvent(event) + }) + this.getElementJQuery()?.on("hide.bs.modal", { _, _ -> + val event = CustomEvent("hideBsModal") + this.getElement()?.dispatchEvent(event) + }) + this.getElementJQuery()?.on("hidden.bs.modal", { _, _ -> + this.visible = false + val event = CustomEvent("hiddenBsModal") + this.getElement()?.dispatchEvent(event) + }) + } + + override fun hide() { + if (visible) hideInternal() + super.hide() + } + + open fun toggle() { + if (visible) + hide() + else + show() + } + + @Suppress("UnsafeCastFromDynamic") + private fun showInternal() { + getElementJQueryD()?.modal("show") + } + + @Suppress("UnsafeCastFromDynamic") + private fun hideInternal() { + getElementJQueryD()?.modal("hide") + } +} + +open class ModalDialog(size: MODALSIZE?) : Container(setOf("modal-dialog")) { + var size = size + set(value) { + field = value + refresh() + } + + override fun getSnClass(): List<StringBoolPair> { + val cl = super.getSnClass().toMutableList() + if (size != null) { + cl.add(size?.className.orEmpty() to true) + } + return cl + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt index 8a292910..a9ee9442 100644 --- a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt +++ b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt @@ -30,6 +30,10 @@ interface BtOn : On { var shownBsDropdown: ((DropDownEvent) -> kotlin.Unit)? var hideBsDropdown: ((DropDownEvent) -> kotlin.Unit)? var hiddenBsDropdown: ((DropDownEvent) -> kotlin.Unit)? + var showBsModal: ((CustomEvent) -> kotlin.Unit)? + var shownBsModal: ((CustomEvent) -> kotlin.Unit)? + var hideBsModal: ((CustomEvent) -> kotlin.Unit)? + var hiddenBsModal: ((CustomEvent) -> kotlin.Unit)? } interface SnOn<T> : BtOn { |