/patternfly-fritz2

Pure Kotlin implementation of PatternFly 4 based on fritz2

Primary LanguageKotlinApache License 2.0Apache-2.0

PatternFly Fritz2

GitHub Super-Linter Detekt Build Passing API Docs Slack IR Download

PatternFly Fritz2 is a 💯 Kotlin implementation of PatternFly based on fritz2 targeting Kotlin/JS.

The goal of this project is to provide all PatternFly components in Kotlin. This is done in a way that matches the reactive nature of fritz2. In particular, the components use stores, handlers, and other elements from the fritz2 API.

To get a quick overview what this is all about head over to the PatternFly Fritz2 showcase. It demonstrates the usage of all supported components and also includes more complex demos of data driven components such as card view, data list and data tables.

To get all details about how to use PatternFly Fritz2 take a look at the API documentation.

Get Started

Dependencies

To use PatternFly Fritz2 add its dependency to your gradle.build.kts file. PatternFly Fritz2 is available on Maven Central. All components are implemented in Kotlin only. You won't need any additional external JS libraries.

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.patternfly:patternfly-fritz2:0.2.0")
}

If you want to use the latest snapshot build of PatternFly Fritz2, add the following lines to your gradle.build.kts file:

repositories {
    mavenCentral()
    maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}

dependencies {
    implementation("org.patternfly:patternfly-fritz2:0.3.0-SNAPSHOT")
}

PatternFly Assets

PatternFly Fritz2 does not come with stylesheets, fonts or other static PatternFly assets. We don't want to dictate how to embed these assets. One way is to add a npm dependency to PatternFly:

dependencies {
    implementation("org.jetbrains:kotlin-extensions:<version>")
    implementation(npm("@patternfly/patternfly", "4"))
}

and make a call to require() (provided by Kotlin JS wrappers):

import kotlinext.js.require

fun main() {
    require("@patternfly/patternfly/patternfly.css")
    require("@patternfly/patternfly/patternfly-addons.css")
}

Another option is to download or get PatternFly using a CDN provider like jsDelivr and include the stylesheets in your HTML page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My App</title>
    <link rel="stylesheet" href="patternfly.css">
    <link rel="stylesheet" href="patternfly-addons.css">
</head>
<body>
<script src="your-app.js"></script>
</body>
</html>

See also the getting started section on the PatternFly website for more details.

Page

The typical setup in PatternFly starts with adding a Page component to the document body. The page contains the main components such as the header, an optional sidebar and the main content container.

A typical setup might look something like this:

fun main() {
    val router = router("home")
    render {
        page {
            pageHeader {
                brand {
                    home("#home")
                    img("/assets/logo.svg")
                }
                headerTools {
                    notificationBadge()
                }
            }
            pageSidebar {
                sidebarBody {
                    verticalNavigation(router) {
                        items {
                            item("item1", "Item 1")
                            item("item2", "Item 2")
                        }
                    }
                }
            }
            pageMain {
                pageSection {
                    h1 { +"Welcome" }
                    p { +"Lorem ipsum" }
                }
                pageSection {
                    +"Another section"
                }
            }
        }
    }
}

API

All components in PatternFly Fritz2 are completely implemented in Kotlin and are created by factory functions. These functions integrate in the fritz2 DSL and follow a common pattern:

  1. Parameter(s) specific to the component
  2. id: String? = null ID attribute assigned to the component
  3. baseClass: String? = null CSS class(es) added to the list of classes of the component
  4. content code block to customize the component

Most of the parameters are optional and have reasonable defaults.

Let's take the card component as an example. The following code snippet creates a card component with an image and a dropdown in the header, a title, body and footer.

See also the card section in the showcase.

fun main() {
    render {
        card {
            cardHeader {
                img { src("./logo.svg") }
                cardAction {
                    dropdown<String>(align = RIGHT) {
                        kebabToggle()
                        items {
                            item("Item 1")
                            item("Disabled Item") { disabled = true }
                            separator()
                            item("Separated Item")
                        }
                    }
                }
            }
            cardTitle { +"Title" }
            cardBody { +"Body" }
            cardFooter { +"Footer" }
        }
    }    
}

Whenever possible, the components make use of reactive concepts and classes such as stores, handlers and flows. The following example creates a chip group component whose chips are backed by a store and rendered by a display function. The function uses the number of letters to add a badge component to the chip. When a chip is removed from the group an info notification is added to the notification store which in turn is fetched by the toast alert group.

See also the chip group section in the showcase.

fun main() {
    data class Word(val text: String, val letters: Int = text.length)

    val store = ChipGroupStore<Word> { Id.build(it.text) }.apply {
        addAll(
            listOf(
                Word("Chip one"),
                Word("Really long chip that goes on and on"),
                Word("Chip three"),
                Word("Chip four"),
                Word("Chip five")
            )
        )
        removes handledBy Notification.add { word ->
            info("You removed ${word.text}.")
        }
    }

    AlertGroup.addToastAlertGroup()
    render {
        chipGroup(store) {
            +"Letters"
            display { word ->
                chip {
                    +word.text
                    badge {
                        value(word.letters)
                    }
                }
            }
        }
    }
}

To see more components in action, take a look at the showcase. To learn how to use the components, read the API documentation.

Get Involved

PatternFly Fritz2 is still under development. The API might change and things might not work as expected. Please give it a try and share your feedback. Join the chat at https://slack.patternfly.org/ or use the GitHub issues to report bugs or request new features.

Of course, you're very welcome to contribute to PatternFly Fritz2. If you like what you're seeing, leave us a star!