stevdza-san / KotlinBootstrap

Use the official Bootstrap UI components with Kotlin and Compose HTML, to build a frontend on the web.
203 stars 12 forks source link
bootstrap bootstrap5 compose-html frontend jetpack-compose kobweb kotlin web

Kotlin-Bootstrap logo

Kotlin Bootstrap

⚡Highly experimental library built on top of the Kobweb (Compose HTML framework). It allows you to use the official Bootstrap UI components with Kotlin and Jetpack Compose, to build a frontend on the web. You are required to use the kobweb framework, otherwise it won't work. At the moment, components are not yet fully customizable, but I'll work on it. ⚽ The goal is to release all bootstrap components, and only then work on it's customization furthermore. Silk UI layer which is included with Kobweb is not required for this library to work.

Available Components

Bootstrap Icons usage

Install

Update a Project level build.gradle.kts file:

repositories {
    ..
    maven(url = "https://jitpack.io")
}

Update a site module build.gradle.kts file:

kotlin { 
    sourceSets {
        ..
        val jsMain by getting {
            dependencies {
                ..
                implementation("com.github.stevdza-san:KotlinBootstrap:0.1.5")
            }
        }
    }
}

Button

Buttons Preview

A simple button usage:

BSButton(
    text = "Sign in",
    onClick = {}
)

You can update your button state to loading, as well as specify the exact Loading Text (optional):

var buttonLoading by remember { mutableStateOf(false) }
BSButton(
    text = "Sign in",
    loading = buttonLoading,
    loadingText = "Please wait...",
    onClick = { buttonLoading = true }
)

Add a Badge to your button:

BSButton(
    text = "Shopping Cart",
    badge = ButtonBadge(
        text = "10"
    ),
    onClick = {}
)

Buttons with custom properties.

CustomButtons Preview

Column(modifier = Modifier.gap(20.px).fillMaxSize()) {
    Row(modifier = Modifier.gap(12.px)) {
        BSButton(
            text = "Apply Now",
            customization = ButtonCustomization(
                color = Colors.White,
                hoverColor = Colors.White,
                backgroundColor = Colors.Black,
                hoverBackgroundColor = rgba(0, 0, 0, 0.8),
                fontFamily = "Space Grotesk"
            ),
            onClick = {}
        )
        BSButton(
            text = "Get Started",
            customization = ButtonCustomization(
                color = Colors.White,
                hoverColor = Colors.White,
                activeColor = Colors.WhiteSmoke,
                borderColor = Colors.White,
                hoverBorderColor = Colors.White,
                activeBorderColor = rgb(168, 192, 255),
                gradient = linearGradient(
                    from = rgb(168, 192, 255),
                    to = rgb(63, 43, 150),
                    dir = LinearGradient.Direction.ToTopRight
                ),
                borderRadius = BSBorderRadius(all = 50.px),
                horizontalPadding = 1.25.cssRem
            ),
            onClick = {}
        )
    }
    Row(modifier = Modifier.gap(12.px)) {
        BSButton(
            text = "Submit",
            customization = ButtonCustomization(
                color = Colors.White,
                hoverColor = Colors.Wheat,
                activeColor = Colors.White,
                borderColor = Colors.White,
                hoverBorderColor = Colors.Wheat,
                activeBorderColor = Colors.White,
                gradient = linearGradient(
                    from = rgb(188, 78, 156),
                    to = rgb(248, 7, 89),
                    dir = LinearGradient.Direction.ToTopRight
                ),
                borderRadius = BSBorderRadius(topLeft = 20.px, bottomRight = 20.px),
                fontFamily = "Rubik"
            ),
            onClick = {}
        )
    }
}

IconButton

IconButtons Preview

BSIconButton is a component used to display a Bootstrap Icon (BSIcons) inside a button. You can customize similar properties like with a regular BSButton as well. A basic usage:

Column(modifier = Modifier.gap(20.px).fillMaxSize()) {
    Row(modifier = Modifier.gap(12.px)) {
        BSIconButton(
            icon = BSIcons.UPLOAD,
            onClick = {}
        )
        BSIconButton(
            icon = BSIcons.UPLOAD,
            variant = ButtonVariant.PrimaryOutline,
            onClick = {}
        )
    }
    Row(modifier = Modifier.gap(12.px)) {
        BSIconButton(
            icon = BSIcons.ANDROID,
            variant = ButtonVariant.Success,
            onClick = {}
        )
        BSIconButton(
            icon = BSIcons.ANDROID,
            variant = ButtonVariant.SuccessOutline,
            onClick = {}
        )
    }
}

Input

Inputs Preview

A simple usage with a placeholder:

var inputValue by remember { mutableStateOf("") }
BSInput(
    value = inputValue,
    placeholder = "Type here",
    onValueChange = {
        inputValue = it
    }
)

Floating style input field, where a label is animated:

BSInput(
    value = inputValue,
    label = "Email Address",
    floating = true,
    onValueChange = {}
)

Positive validation style input field:

BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    validation = InputValidation(
      isValid = true
    ),
    onValueChange = {}
)

Negative validation style input field:

BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    validation = InputValidation(
      isInvalid = true
    ),
    onValueChange = {}
)

Disabled input field:

BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    disabled = true,
    onValueChange = {}
)

Plain text input field:

BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    plainText = true,
    onValueChange = {}
)

Dropdown

Dropdown Preview

Dropdown with a placeholder:

BSDropdown(
    placeholder = "Select a Platform",
    items = listOf("Android", "iOS", "Web"),
    onItemSelect = { index, value -> }
)

Dropdown with a dark background:

BSDropdown(
    items = listOf("Android", "iOS", "Web"),
    darkBackground = true,
    onItemSelect = { index, value -> }
)

Disabled Dropdown item:

BSDropdown(
    items = listOf("Android", "iOS", "Web"),
    disabledItems = listOf("iOS"),
    onItemSelect = { index, value -> }
)

TextArea

TextArea Preview

Basic TextArea example with a label:

var value by remember { mutableStateOf("") }
BSTextArea(
    value = value,
    label = "Email Address",
    placeholder = "Type here...",
    onValueChange = { value = it }
)

Floating TextArea:

var value by remember { mutableStateOf("") }
BSTextArea(
    value = value,
    label = "Email Address",
    floating = true,
    onValueChange = { value = it }
)

Checkbox

Checkboxes Preview

Basic Checkbox usage:

BSCheckbox(
    label = "Kotlin",
    onClick = {}
)

Reversed order checkbox:

BSCheckbox(
    label = "C++",
    reverse = true,
    onClick = {}
)

Toggle button style checkbox:

BSCheckbox(
    label = "Python",
    toggleButton = true,
    onClick = {}
)

RadioButton

RadioButtons Preview

Basic RadioButtonGroup usage:

BSRadioButtonGroup {
    BSRadioButton(label = "Android", onClick = {})
    BSRadioButton(label = "iOS", onClick = {})
    BSRadioButton(label = "Web", onClick = {})
}

RadioButtonGroup in a horizontal orientation:

BSRadioButtonGroup(inline = true) {
    BSRadioButton(label = "Android", onClick = {})
    BSRadioButton(label = "iOS", onClick = {})
    BSRadioButton(label = "Web", onClick = {})
}

ToggleButton style of a RadioButtonGroup:

BSRadioButtonGroup(toggleButton = true) {
    BSRadioButton(label = "Android", onClick = {})
    BSRadioButton(label = "iOS", onClick = {})
    BSRadioButton(label = "Web", onClick = {})
}

Switch

Switches Preview

Switch with a default checked state:

BSSwitch(
    label = "Android",
    defaultChecked = true,
    onClick = {}
)

Disabled Switch with a default unchecked state:

BSSwitch(
    label = "Android",
    disabled = true,
    onClick = {}
)

Alert

Alerts Preview

Primary Style Alert with an Info icon and a bold text:

BSAlert(
    message = "Visit my YouTube Channel: Stevdza-San",
    icon = AlertIcon.Info,
    bold = "Stevdza-San"
)

Success Style Alert with a Checkmark icon and a link text:

BSAlert(
    message = "You have successfully purchased a book!",
    alertLink = Pair("book", "https://google.com"),
    icon = AlertIcon.Checkmark,
    style = AlertStyle.Success
)

Dismissable Alert:

BSAlert(
    message = "Dismissable Alert.",
    dismissible = true,
    style = AlertStyle.Dark
)

Toast

Taosts Preview

Even though a Toast component is not yet fully customizable, from this preview above you can see that there are different variations and styles that you can apply to it. For triggering a Toast component, you do need to call a special function showToast(toastId) and pass your toast id, in order to properly display it on the screen. Every Toast components needs to be wrapped inside the BSToastGroup composable. Also there's a ToastPlacement parameter available on BSToastGroup that you can use to modify a toast placement.

BSToast gets visible once you trigger a showToast() function:

BSToastGroup {
    BSToast(
        id = "toast",
        title = "Welcome",
        body = "Browse our website for more interesting products!",
        onCloseClick = {}
    )
}

BSButton(
    text = "Show Toast",
    onClick = {
        showToast("toast")
    }
)

BSToastBasic which is not automatically dismissable, because it has autoHide parameter equal to false:

BSToastGroup {
    BSToastBasic(
        id = "toastBasic",
        text = "Thank you for your feedback!",
        style = ToastStyle.Dark,
        autoHide = false,
        closeButtonDark = false,
        onCloseClick = {}
    )
}

BSToastAction which contains additional positive/negative buttons:

BSToastGroup {
    BSToastAction(
        id = "toastAction2",
        text = "Are you sure you want to delete 24 items?",
        positiveButtonText = "Yes",
        positiveButtonVariant = ButtonVariant.Primary,
        negativeButtonVariant = ButtonVariant.Danger,
        negativeButtonText = "Cancel",
        style = ToastStyle.Dark,
        onPositiveButtonClick = {},
        onNegativeButtonClick = {}
    )
}

Modal

Modal Preview

BSModal component is not visible by default. If you want to show it on your page, then you need to call a modifier showModalOnClick() on a BSButton or any other clickable composable, and pass the ID of the modal itself. After you do that, just click the component that has that modifier, and your BSModal will appear.

A basic Modal usage:

BSModal(
    id = "contactModal",
    title = "Contact us",
    body = {
        Column {
            BSInput(
                modifier = Modifier
                  .fillMaxWidth()
                  .margin(bottom = 14.px),
                value = "",
                label = "Email Address",
                placeholder = "Type here...",
                onValueChange = {}
            )
            BSTextArea(
                modifier = Modifier.fillMaxWidth(),
                value = "",
                label = "Message",
                placeholder = "Type here...",
                onValueChange = {}
            )
        }
    },
    positiveButtonText = "Send Message",
    negativeButtonText = "Close",
    onPositiveButtonClick = {},
    onNegativeButtonClick = {}
)

BSButton(
    modifier = Modifier.showModalOnClick(id = "contactModal"),
    text = "Trigger",
    onClick = {}
)

Select

Select Preview

Select component's basic usage:

BSSelect(
    items = listOf("Android", "iOS", "Web", "Desktop"),
    placeholder = "Choose a Platform",
    onItemSelected = { index, value -> }
)

Floating style of a Select component:

BSSelect(
    items = listOf("Android", "iOS", "Web", "Desktop"),
    placeholder = "Choose a Platform",
    floating = true,
    onItemSelected = { index, value -> }
)

Range

Range Preview

Range's basic usage:

BSRange(
    modifier = Modifier.width(300.px),
    label = "Range (0-10)",
    min = 0,
    max = 10,
    onSelect = {}
)

Progress

Progress Preview

Basic Progress component usage:

BSProgress(percentage = 85.percent)

Stripped style:

BSProgress(
  percentage = 85.percent,
  striped = true
)

Animated Stripped style:

BSProgress(
  percentage = 85.percent,
  stripedAnimated = true
)

Spinner

Spinner Preview

Default Spinner style:

BSSpinner(variant = SpinnerVariant.Default)

Grow Spinner style:

BSSpinner(variant = SpinnerVariant.DefaultGrow)

Tooltip

Tooltip Preview

Before you can use and display a Tooltip, you need to initialize them by calling initializeTooltips() function:

LaunchedEffect(Unit) {
    initializeTooltips()
}

Usually, the content on top of which you want to add a tooltip, is specified as a content lambda of the BSTooltip composable:

BSTooltip(
    text = "https://stevdza-san.com",
    content = {
        A(href = "https://stevdza-san.com") {
            SpanText(text = "Online Courses")
        }
    }
)

You can also change a direction of the tooltip, by using TooltipDirection parameter:

BSTooltip(
    text = "https://stevdza-san.com",
    direction = TooltipDirection.Right,
    content = {
        A(href = "https://stevdza-san.com") {
            SpanText(text = "Online Courses")
        }
    }
)

Collapse

Collapse Preview

To make your button or any other clickable component as the one that triggers the BSCollapse, you need to add a .showCollapse(id) modifier and pass the BSCollapse id.

Column(
    modifier = Modifier.width(400.px),
    horizontalAlignment = Alignment.CenterHorizontally
) {
    BSButton(
        modifier = Modifier
            .alignContent(AlignContent.Center)
            .showCollapse(id = "collapse1"),
        text = "FAQ",
        onClick = {}
    )
    BSCollapse(id = "collapse1") {
        Column(modifier = Modifier.margin(top = 14.px)) {
            SpanText(
                modifier = Modifier
                    .fontSize(18.px)
                    .fontWeight(FontWeight.Bold),
                text = "1. How long does the course take to complete?"
            )
            SpanText(
                text = """
                      The course is self-paced, so you can complete it at your own speed.
                      On average, most students finish the course in about 3-6 weeks, 
                      depending on the time they can dedicate to learning.
                """.trimIndent()
            )
        }
    }
}

Carousel

Carousel Preview

A basic usage of Carousel component:

BSCarousel(
    items = listOf(
        CarouselItem(
            image = "https://images.pexels.com/photos/2662116/pexels-photo-2662116.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
            title = "Moraine Lake"
        ),
        CarouselItem(
            image = "https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
            title = "Italy"
        ),
        CarouselItem(
            image = "https://images.pexels.com/photos/1166209/pexels-photo-1166209.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
            title = "Lavender"
        ),
    ),
    width = 900.px,
    height = 500.px
)

Breadcrumb

Breadcrumb Preview

You can specify and replace a default divider parameter to change a separator string, and also you can set a currently selected BreadcrumbItem as well.

BSBreadcrumb(
    items = listOf(
        BreadcrumbItem(
            text = "Home",
            href = "#"
        ),
        BreadcrumbItem(
            text = "Pricing",
            href = "#"
        ),
        BreadcrumbItem(
            text = "Services",
            href = "#"
        ),
        BreadcrumbItem(
            text = "About",
            href = "#"
        ),
        BreadcrumbItem(
            text = "Contact us",
            href = "#"
        )
    ),
    divider = ">",
    currentItem = "About"
)

Accordion

Accordion Preview

You can customize it's flush parameter which will remove some borders and rounded corners to render accordions edge-to-edge with their parent container. alwaysOpen parameter will make accordion items stay open when another item is opened.

Basic Accordion example:

BSAccordion(
    modifier = Modifier.width(300.px),
    items = listOf(
        AccordionItem(
            title = "Step 01: Identify your goals",
            content = { SpanText(text = "Body text here...") },
            defaultOpened = true
        ),
        AccordionItem(
            title = "Step 02: Write your goals",
            content = {  SpanText(text = "Body text here...")}
        ),
        AccordionItem(
            title = "Step 03: Analysis",
            content = {  SpanText(text = "Body text here...")}
        ),
        AccordionItem(
            title = "Step 04: Objectives",
            content = { SpanText(text = "Body text here...")}
        )
    )
)

NavBar

NavBar Preview

The NavBar typically appears at the top of the web page and contains various navigation elements such as links, buttons, dropdown menus, and branding elements like logos or site names. It adapts to different screen sizes and devices, making it ideal for responsive web design.

BSNavBar(
    modifier = Modifier.fillMaxWidth(),
    stickyTop = true,
    itemsAlignment = Alignment.CenterHorizontally,
    brand = NavBarBrand(
        title = "KotlinBootstrap",
        image = "https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-logo.svg",
        href = "#"
    ),
    expand = NavBarExpand.LG,
    backgroundStyle = BackgroundStyle.Dark,
    items = listOf(
        NavLink(
            id = "homeLink",
            title = "Home",
            onClick = {
                println("Index: $it Title: Home")
            }   
        ),
        NavLink(
            id = "servicesLink",
            title = "Services",
            onClick = {}
        ),
        NavLink(
            id = "pricingLink",
            title = "Pricing",
            onClick = {}
        ),
        NavLink(
            id = "aboutLink",
            title = "About us",
            onClick = {}
        ),
        NavDropdown(
            placeholder = "Language",
            items = listOf(
                NavDropdownItem(
                    id = "kotlinLanguage",
                    title = "Kotlin",
                    onClick = {
                        println("Index: $it Title: Kotlin")
                    }
                ),
                NavDropdownItem(
                    id = "javaLanguage",
                    title = "Java",
                    onClick = {}
                )
            )
        )
    ),
    inputField = NavBarInputField(
        placeholder = "Search",
        value = "",
        onValueChange = {}
    ),
    button = NavBarButton(
        text = "Search",
        onClick = {}
    )
)

NavBar Preview

You can add an extra parameter, to replace a default expandable menu with an Offcanvas side bar:

BSNavBar(
    ..
    offcanvas = NavBarOffcanvas(
        id = "myOffcanvas",
        title = "KotlinBootstrap",
        dark = true
    )
    ..
)

Offcanvas

Offcanvas Preview

Offcanvas is used to create sidebar or panel that can slide in and out of the viewport. This component is often used to display additional content, navigation menus, or options without taking up the entire screen space.

val links = listOf("Home", "Pricing", "Services", "Contact us")
BSOffcanvas(
    id = "myOffCanvas",
    title = "Welcome!",
    body = {
        Column {
            links.forEach { name ->
                A(
                    attrs = Modifier
                        .margin(bottom = 16.px)
                        .textDecorationLine(TextDecorationLine.None)
                        .cursor(Cursor.Pointer)
                        .toAttrs()
                ) {
                    SpanText(name)
                }
            }
            BSButton(
            text = "Sign in",
            onClick = {}
            )
        }
    },
    placement = OffcanvasPlacement.END
)

Column(
    modifier = Modifier.fillMaxSize(),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally
) {
    BSButton(
        modifier = Modifier.showOffcanvasOnClick(id = "myOffCanvas"),
        text = "Show",
        onClick = {}
    )
}

Badge

Badge Preview

There are four different BadgeVariant's: Straight, Regular, Rounded, Empty. You can customize the BackgroundStyle of the badge, a fontFamily, fontSize, and fontWeight as well.

Row(verticalAlignment = Alignment.CenterVertically) {
    SpanText(
        modifier = Modifier.margin(right = 8.px),
        text = "Fitness Tracker"
    )
    BSBadge(
        modifier = Modifier.margin(bottom = 8.px),
        text = "New",
        variant = BadgeVariant.Straight
    )
}

CloseButton

CloseButton Preview

A basic usage:

BSCloseButton()

ColorPicker

ColorPicker Preview

A basic usage:

BSColorPicker(onColorSelected = {})

FilePicker

FilePicker Preview

FilePicker component provides a required lambda onFileSelected, that returns two strings. The first one represents a fileName, while the second one the actual file encoded in BASE_64 string.

A basic usage:

BSFileInput(
    label = "Choose a file",
    onFileSelected = { fileName, file -> }
)

Pagination

Pagination Preview

BSPagination component is used to divide long lists or tables into multiple pages, making it easier for users to navigate through the content.

A basic usage:

var currentPage by remember { mutableStateOf(1) }

BSPagination(
    pages = 15,
    maxVisiblePages = 3,
    currentPage = currentPage,
    previousButton = PreviousButton(
        onClick = { currentPage = it }
    ),
    nextButton = NextButton(
        onClick = { currentPage = it }
    ),
    onPageClick = { currentPage = it }
)

Icons

There are over 2.000 icons available in a Bootstrap library. You can use a BSIcons object to access all icons. There's a BSIcon composable function that allows you to display those same icons as well. There's also a BSIconButton composable that displays an icon inside the button.

Example (Zoomed in) icon, that represents one of the many vector icons in the library:

BSIcon(
  icon = BSIcons.ANDROID,
  size = 1.cssRem,
  color = Colors.LightGreen
)

Android Icon Preview