diff options
author | Robert Jaros <rjaros@finn.pl> | 2019-10-03 19:03:21 +0200 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2019-10-03 19:03:21 +0200 |
commit | 6b53324c97bfc80ed14dfca6a5dbc879950715b9 (patch) | |
tree | 55c7bb7d06e37470795a93e1f542e51ef3f1ace6 /kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal | |
parent | 22a8d5c35db97d65a90b21d97e6835380191845d (diff) | |
download | kvision-6b53324c97bfc80ed14dfca6a5dbc879950715b9.tar.gz kvision-6b53324c97bfc80ed14dfca6a5dbc879950715b9.tar.bz2 kvision-6b53324c97bfc80ed14dfca6a5dbc879950715b9.zip |
Upgrade to Bootstrap 4.
Upgrade to Font Awesome 5.
Restructure modules.
Diffstat (limited to 'kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal')
4 files changed, 637 insertions, 0 deletions
diff --git a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt new file mode 100644 index 00000000..5f5a1a80 --- /dev/null +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Alert.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.modal + +import pl.treksoft.kvision.core.Widget +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 +import pl.treksoft.kvision.utils.ENTER_KEY + +/** + * Alert window based on Bootstrap modal. + * + * @constructor + * @param caption window title + * @param text window content text. + * @param rich determines if [text] can contain HTML code + * @param align text align + * @param size modal window size + * @param animation determines if animations are used + * @param callback a function called after closing window with OK button + */ +open class Alert( + caption: String? = null, text: String? = null, rich: Boolean = false, + align: Align? = null, size: ModalSize? = null, animation: Boolean = true, + private val callback: (() -> Unit)? = null +) : Modal(caption, true, size, animation) { + + /** + * Window content text. + */ + var text + get() = contentTag.content + set(value) { + contentTag.content = value + } + /** + * Determines if [text] can contain HTML code. + */ + var rich + get() = contentTag.rich + set(value) { + contentTag.rich = value + } + /** + * Text align. + */ + var align + get() = contentTag.align + set(value) { + contentTag.align = value + } + + private val contentTag = Tag(TAG.DIV, text, rich, align) + + init { + body.add(contentTag) + val okButton = Button("OK", "ok", ButtonStyle.PRIMARY) + okButton.setEventListener { + click = { + hide() + } + } + this.addButton(okButton) + this.setEventListener { + keydown = { e -> + if (e.keyCode == ENTER_KEY) { + hide() + } + } + } + } + + override fun hide(): Widget { + super.hide() + this.callback?.invoke() + return this + } + + companion object { + /** + * Helper function for opening Alert window. + * @param caption window title + * @param text window content text. + * @param rich determines if [text] can contain HTML code + * @param align text align + * @param size modal window size + * @param animation determines if animations are used + * @param callback a function called after closing window with OK button + */ + @Suppress("LongParameterList") + fun show( + caption: String? = null, text: String? = null, rich: Boolean = false, + align: Align? = null, size: ModalSize? = null, animation: Boolean = true, + callback: (() -> Unit)? = null + ) { + Alert(caption, text, rich, align, size, animation, callback).show() + } + } +} diff --git a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/CloseIcon.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/CloseIcon.kt new file mode 100644 index 00000000..5f0440a6 --- /dev/null +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/CloseIcon.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.modal + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.KVManager +import pl.treksoft.kvision.core.StringBoolPair +import pl.treksoft.kvision.core.StringPair +import pl.treksoft.kvision.core.Widget + +/** + * Helper class for close icon component. + */ +open class CloseIcon : Widget(setOf()) { + + override fun render(): VNode { + return render("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/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt new file mode 100644 index 00000000..e16ca87e --- /dev/null +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Confirm.kt @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.modal + +import pl.treksoft.kvision.html.Align +import pl.treksoft.kvision.html.Button +import pl.treksoft.kvision.html.ButtonStyle +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag + +/** + * Confirm window based on Bootstrap modal. + * + * @constructor + * @param caption window title + * @param text window content text. + * @param rich determines if [text] can contain HTML code + * @param align text align + * @param size modal window size + * @param animation determines if animations are used + * @param cancelVisible determines if Cancel button is visible + * @param yesTitle yes button text + * @param noTitle no button text + * @param cancelTitle cancel button text + * @param noCallback a function called after closing window with No button + * @param yesCallback a function called after closing window with Yes button + */ +open class Confirm( + caption: String? = null, text: String? = null, rich: Boolean = false, + align: Align? = null, size: ModalSize? = null, animation: Boolean = true, + cancelVisible: Boolean = false, yesTitle: String = "Yes", noTitle: String = "No", cancelTitle: String = "Cancel", + private val noCallback: (() -> Unit)? = null, + private val yesCallback: (() -> Unit)? = null +) : Modal(caption, false, size, animation, false) { + /** + * Window content text. + */ + var text + get() = contentTag.content + set(value) { + contentTag.content = value + } + /** + * Determines if [text] can contain HTML code. + */ + var rich + get() = contentTag.rich + set(value) { + contentTag.rich = value + } + /** + * Text align. + */ + var align + get() = contentTag.align + set(value) { + contentTag.align = value + } + /** + * Determines if Cancel button is visible. + */ + var cancelVisible by refreshOnUpdate(cancelVisible) { refreshCancelButton() } + + /** + * Yes button text. + */ + var yesTitle + get() = yesButton.text + set(value) { + yesButton.text = value + } + + /** + * No button text. + */ + var noTitle + get() = noButton.text + set(value) { + noButton.text = value + } + + /** + * Cancel button text. + */ + var cancelTitle + get() = cancelButton.text + set(value) { + cancelButton.text = value + } + + private val contentTag = Tag(TAG.DIV, text, rich, align) + private val cancelButton = Button(cancelTitle, "remove") + private val noButton = Button(noTitle, "ban-circle") + private val yesButton = Button(yesTitle, "ok", ButtonStyle.PRIMARY) + + init { + body.add(contentTag) + cancelButton.setEventListener { + click = { + hide() + } + } + this.addButton(cancelButton) + noButton.setEventListener { + click = { + hide() + noCallback?.invoke() + } + } + this.addButton(noButton) + 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 { + /** + * Helper function for opening Confirm window. + * @param caption window title + * @param text window content text. + * @param rich determines if [text] can contain HTML code + * @param align text align + * @param size modal window size + * @param animation determines if animations are used + * @param cancelVisible determines if Cancel button is visible + * @param noCallback a function called after closing window with No button + * @param yesCallback a function called after closing window with Yes button + */ + @Suppress("LongParameterList") + fun show( + caption: String? = null, text: String? = null, rich: Boolean = false, + align: Align? = null, size: ModalSize? = null, animation: Boolean = true, + cancelVisible: Boolean = false, yesTitle: String = "Yes", noTitle: String = "No", + cancelTitle: String = "Cancel", noCallback: (() -> Unit)? = null, yesCallback: (() -> Unit)? = null + ) { + Confirm( + caption, text, rich, align, size, animation, cancelVisible, yesTitle, noTitle, cancelTitle, + noCallback, yesCallback + ).show() + } + } +} diff --git a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt new file mode 100644 index 00000000..08fc7aa8 --- /dev/null +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.modal + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.core.Component +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.StringBoolPair +import pl.treksoft.kvision.core.StringPair +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.html.Button +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag +import pl.treksoft.kvision.panel.Root +import pl.treksoft.kvision.panel.Root.Companion.addModal +import pl.treksoft.kvision.panel.Root.Companion.removeModal +import pl.treksoft.kvision.panel.SimplePanel +import pl.treksoft.kvision.utils.obj + +/** + * Modal window sizes. + */ +enum class ModalSize(val className: String) { + XLARGE("modal-xl"), + LARGE("modal-lg"), + SMALL("modal-sm") +} + +/** + * Configurable modal window based on Bootstrap modal. + * + * @constructor + * @param caption window title + * @param closeButton determines if Close button is visible + * @param size modal window size + * @param animation determines if animations are used + * @param escape determines if dialog can be closed with Esc key + * @param classes a set of CSS class names + * @param init an initializer extension function + */ +@Suppress("TooManyFunctions") +open class Modal( + caption: String? = null, closeButton: Boolean = true, + size: ModalSize? = null, animation: Boolean = true, private val escape: Boolean = true, + classes: Set<String> = setOf(), init: (Modal.() -> Unit)? = null +) : SimplePanel(classes) { + + override var parent: Container? = Root.getFirstRoot() + + /** + * Window caption text. + */ + var caption + get() = captionTag.content + set(value) { + captionTag.content = value + checkHeaderVisibility() + } + /** + * Determines if Close button is visible. + */ + var closeButton + get() = closeIcon.visible + set(value) { + closeIcon.visible = value + checkHeaderVisibility() + } + /** + * Window size. + */ + var size + get() = dialog.size + set(value) { + dialog.size = value + } + /** + * Determines if animations are used. + */ + var animation by refreshOnUpdate(animation) + + private val dialog = ModalDialog(size) + private val header = SimplePanel(setOf("modal-header")) + /** + * @suppress + * Internal property. + */ + protected val closeIcon = CloseIcon() + private val captionTag = Tag(TAG.H5, caption, classes = setOf("modal-title")) + /** + * @suppress + * Internal property. + */ + protected val body = SimplePanel(setOf("modal-body")) + private val footer = SimplePanel(setOf("modal-footer")) + + init { + this.hide() + this.role = "dialog" + this.tabindex = -1 + this.addInternal(dialog) + val content = SimplePanel(setOf("modal-content")) + dialog.role = "document" + dialog.add(content) + closeIcon.visible = closeButton + closeIcon.setEventListener { + click = { + hide() + } + } + header.add(captionTag) + header.add(closeIcon) + checkHeaderVisibility() + content.add(header) + content.add(body) + content.add(footer) + @Suppress("LeakingThis") + addModal(this) + @Suppress("LeakingThis") + init?.invoke(this) + } + + private fun checkHeaderVisibility() { + if (!closeButton && caption == null) { + header.hide() + } else { + header.show() + } + } + + override fun add(child: Component): SimplePanel { + body.add(child) + return this + } + + override fun addAll(children: List<Component>): SimplePanel { + body.addAll(children) + return this + } + + override fun remove(child: Component): SimplePanel { + body.remove(child) + return this + } + + override fun removeAll(): SimplePanel { + body.removeAll() + return this + } + + override fun getChildren(): List<Component> { + return body.getChildren() + } + + /** + * Adds given button to the bottom section of dialog window. + * @param button a [Button] component + * @return this modal + */ + open fun addButton(button: Button): Modal { + footer.add(button) + return this + } + + /** + * Removes given button from the bottom section of dialog window. + * @param button a [Button] component + * @return this modal + */ + open fun removeButton(button: Button): Modal { + footer.remove(button) + return this + } + + /** + * Removes all buttons from the bottom section of dialog window. + * @return this modal + */ + open fun removeAllButtons(): Modal { + footer.removeAll() + return this + } + + 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") { e, _ -> + this.dispatchEvent("showBsModal", obj { detail = e }) + } + this.getElementJQuery()?.on("shown.bs.modal") { e, _ -> + this.dispatchEvent("shownBsModal", obj { detail = e }) + } + this.getElementJQuery()?.on("hide.bs.modal") { e, _ -> + this.dispatchEvent("hideBsModal", obj { detail = e }) + } + this.getElementJQuery()?.on("hidden.bs.modal") { e, _ -> + this.visible = false + hide() + this.dispatchEvent("hiddenBsModal", obj { detail = e }) + } + } + + override fun hide(): Widget { + if (visible) hideInternal() + return super.hide() + } + + /** + * Toggle modal window visibility. + */ + open fun toggle() { + if (visible) + hide() + else + show() + } + + @Suppress("UnsafeCastFromDynamic") + private fun showInternal() { + getElementJQueryD()?.modal("show") + } + + @Suppress("UnsafeCastFromDynamic") + private fun hideInternal() { + getElementJQueryD()?.modal("hide") + } + + override fun clearParent(): Widget { + this.parent = null + return this + } + + override fun getRoot(): Root? { + return this.parent?.getRoot() + } + + override fun dispose() { + removeModal(this) + } +} + +/** + * Internal helper class for modal content. + * + * @constructor + * @param size modal window size + */ +internal class ModalDialog(size: ModalSize?) : SimplePanel(setOf("modal-dialog")) { + + /** + * Modal window size. + */ + var size by refreshOnUpdate(size) + + override fun getSnClass(): List<StringBoolPair> { + val cl = super.getSnClass().toMutableList() + size?.let { + cl.add(it.className to true) + } + return cl + } +} |