Skip to content

Version 0.12

Compare
Choose a tag to compare
@haukesomm haukesomm released this 31 Aug 13:43

Breaking Changes

PR #494: Allow sub() on all Stores

Removes the RootStore dependency in the SubStore, so that SubStores can be created by calling sub() function implemented in Store interface. Therefore it is now possible to create a SubStore from every Store object.

Because one SubStore generic type gets obsolete, it will break existing code, but it is easy to fix that. Just remove the first not type parameter.

// before
class SubStore<R, P, T> {}
// now
class SubStore<P, T> {}
// before
val addressSub: SubStore<Person, Person, Address> = store.sub(addressLens)
// now
val addressSub: SubStore<Person, Address> = store.sub(addressLens)

PR #492: Remove deprecated direction-api from RadioGroupComponent and CheckboxGroupComponent

This PR removes the deprecated direction -api from RadioGroupComponent and CheckboxGroupComponent.
Use orientation instead.

PR #489: Some Improvements and Bugfixing around Forms

Improve FormControl code

  • make FormSizeSpecifier values uppercase, so more Kotlin alike
  • improve render strategies initialization and customization: Instead of setting all up within the init block, there is now a new private method initRenderStrategies that produces the mapping and is directly called at beginning of the rendering process. This way no this pointer is leaked as before. For custom implementations there is now a protected hook function finalizeRenderStrategies that can be used to extend or change the renderer strategies!
Migration Guide:

If a custom renderer should be applied or a new factory should be added, custom implementation of FormControlComponent must replace the old registerRenderStrategy within the init block by a new mechanism:

// old way, no more possible!
class MyFormControlComponent : FormControlComponent {
    init {
        registerRenderStrategy("radioGroupWithInput", ControlGroupRenderer(this))
    }
}

// new way, override `finalizeRenderStrategies` method:
class MyFormControlComponent : FormControlComponent {

    // some new factory: `creditCardInput` with key of same name; used with `SingleControlRenderer`

    // another new factory: `colorInput` with key of same name and new renderer

    override fun finalizeRenderStrategies(
        strategies: MutableMap<String, ControlRenderer>,
        single: ControlRenderer,
        group: ControlRenderer
     ) {
         // override setup for a built-in factory:
         strategies.put(ControlNames.textArea, MySpecialRendererForTextAreas(this))

         // register new factory
         strategies.put("creditCardInput", single)

         // register new factory with new renderer
         strategies.put("colorInput", ColorInputRenderer(this))
     }
}

Improve sizes aspect for components in theme

  • in respect to EfC 502 a new mixin alike interface FormSizesAware has been introduced
  • the FormSizes interface is renamed to FormSizesStyles
Migration Guide:

If a component supports a size property and relies on the old FormSizes interface, just rename the receiver type appropriate to `FormSizesStyles``:

// old
val size = ComponentProperty<FormSizes.() -> Style<BasicParams>> { Theme().someComponent.sizes.normal }

// new
val size = ComponentProperty<FormSizesStyles.() -> Style<BasicParams>> { Theme().someComponent.sizes.normal }
//                           ^^^^^^^^^^^^^^^
//                           choose new name

Make FormControl Labels dynamic

  • the label property of a FormControl now accepts also Flow<String>. Thus the label can dynamically react to some other state changing.
Migration Guide

The code for a ControlRenderer implementation needs to be adapted. Use the following recipe:

class ControlGroupRenderer(private val component: FormControlComponent) : ControlRenderer {
    override fun render(/*...*/) {
        // somewhere the label gets rendered (label / legend or alike)
        label {
            // old:
            +component.label.value
            // change to:
            component.label.values.asText()
        }
    }
}

Adapt FormControl's textArea function parameters

  • the optional store parameter now also is named value like as within the main textArea factory.

Repairs label behaviour for SelectField

  • a click onto a label within a FormControl now directs to the SelectField.
  • the id of a SelectField is now correctly set, so that a FormControl can now fill the for attribute of the label correctly.

PR #490: Move Modal’s close handler into content property

Until now the close handler was injected directly into the main configuration scope of a modal. With repsect to EfC 416 this is now changed. The handler only gets injected into the content property, where it is considered to be used.

Migration

Just move the handler parameter from the build expression of modal into the content property:

// old
clickButton {
    text("Custom Close Button")
} handledBy modal { close -> // injected at top level
    content { 
        clickButton {icon { logOut } } handledBy close
    }
}

// new
clickButton {
    text("Custom Close Button")
} handledBy modal {
    content { close -> // injected only within content
        clickButton {icon { logOut } } handledBy close
    }
}

PR #487: Rework ToastComponent

Changes

This PR cleans up the ToastComponent code. This includes:

  • Adding styling-options to the theme
  • Use correct z-indizes
  • General code-cleanup

What's API breaking?

  • Theme().toast.base has been renamed to Theme().toast.body and might break themes that have custom toast-styles

New Features

PR #505: Add TooltipComponent

This PR deals with the new TooltipComponent

A tooltip should be used to display fast information for the user.
The individual text will be shown on hover the RenderContext in which be called.

This class offers the following configuration features:

  • text can be a vararg, a flow, a list, a flow of list of String or a simple string, optional can be use the @Property textFromParam.
  • placement of the text around the RenderContextin which be called. Available placements are top, topStart, topEnd, bottom, bottomStart, bottomEnd, left, leftStart, leftEnd, right, rightStart, rightEnd.

Example usage:

span {
   +"hover me"
   tooltip("my Tooltip on right side") {
      placement { right }
    }
}
 
span {
   +"hover me to see a multiline tooltip"
   tooltip("first line", "second line"){}
}

span {
   +"hover me for custom colored tooltip"
   tooltip({
        color { danger.mainContrast }
        background {
            color { danger.main }
        }
   }) {
        text(listOf("first line", "second line"))
        placement { TooltipComponent.PlacementContext.bottomEnd }
    }
}

Migration

  • The old tooltip component remains as deprecated for the next releases, so clients have time to migrate.

Motivation

The old Tooltip component is based upon pure CSS. This is fine for basic usage, but it fails when it comes to advanced features like automatic positioning. Also the API does not fit into the "fritz2 component style". That's why there is the need to rework the tooltip.

PR #493: Add PopupComponent

The PopupComponent should be used for to positioning content like tooltip or popover automatically in the right place near a trigger.

A popup mainly consists of a trigger (the Element(s)) which calls the content .
It can cen configured by

  • offset the space (in px) between trigger and content
  • flipping if no space on chosen available it will be find a right placement automatically
  • placement of the content around the trigger

The trigger provides two handler which can be used, the first is important to open/toggle the content the second close it.
content provides one handler which can be used to close it.

Example:

popup {
    offset(10.0)
    flipping(false)
    placement { topStart }
    trigger { toggle, close ->
        span {
            +"hover me"
            mouseenters.map { it.currentTarget } handledBy toggle
            mouseleaves.map { } handledBy close
        }
    }
    content { close ->
        div {
            +"my content"
            clicks.map{ } handledBy close
        }
    }
}

PR #496: Add PaperComponent and CardComponent

This PR adds a new CardComponent that behaves similar to the old PopoverComponents content.

Paper Component

Component displaying content in a card-like box that can either appear elevated or outlined and scales with the specified size of the component.

Example
paper {
    size { /* small | normal | large */ }
    type { /* normal | outline | ghost */ }

    content {
        // ...
    }
}

Bildschirmfoto 2021-08-17 um 19 09 11

CardComponent

A component displaying the typical sections of a card inside a PaperComponent.
The available sections are a header, a footer and the actual content.

Example
card {
    size { /* small | normal | large */ }
    type { /* normal | outline | ghost */ }

    header {
        // ...
    }
    content {
        // ...
    }
    footer {
        // ...
    }
}

Bildschirmfoto 2021-08-17 um 19 17 18

PR #481: Add possibility to pass arbitrary payload data in fritz2's DSL

In fritz2 you can now put some arbitrary payload data to every fritz2 html element (for styled elements too) and receive this payload data later (or deeper) in your html tree. This possibility can be use to know in with kind of context your own component get rendered or you can provide some additional information to your context for making decision on styling or rendering.

Example:

enum class Sizes {
    SMALL, NORMAL, LARGE;

    companion object {
        val key = keyOf<Sizes>()
    }
}

fun main() {
    render {
        div {
            div(scope = {
                set(Sizes.key, Sizes.SMALL)
            }) {
                section {
                    scope.asDataAttr()
                    when (scope[Sizes.key]) {
                        Sizes.SMALL -> div({ fontSize { small } }) { +"small text" }
                        Sizes.NORMAL -> div({ fontSize { normal } }) { +"normal text" }
                        Sizes.LARGE -> div({ fontSize { large } }) { +"large text" }
                        else -> div { +"no size in scope available" }
                    }
                }
            }
            p {
                scope.asDataAttr()
                // scope is context-based and therefore scope is here empty
                +"no scope entries here (context-based)"
            }
        }
    }
}

Results in:
Screenshot_20210707_165401

PR #470: Add TypeAheadComponent

Adds a TypeAhead component to fritz2's component portfolio.

A TypeAhead offers the possibility to input some string and get some list of proposals to choose from. This is reasonable for large static lists, that can't be managed by SelectFields or RadioGroups or where the proposals rely on a remote resource.

Example usage:

 val proposals = listOf("Kotlin", "Scala", "Java", "OCaml", "Haskell").asProposals()
 val choice = storeOf("")
 typeAhead(value = choice, items = proposals) { }

For further details have a look at our KitchenSink


Improvements

PR #514: Upgrade Kotlin to Version 1.5.30

PR #507: Add TooltipMixin and TooltipProperties to use TooltipComponent in Components more easily

With this PR it beeing easier to integrate a tooltip in a component and the user get a same usage of a tooltip.

PR #486: Use colors in appFrame and added submenu in menu component

The appFrame component uses now different ColorSchemes to coloring itself.

The menu component can now contain submenus:

menu {
    header("Entries")
    entry {
        text("Basic entry")
    }
    divider()
    custom {
        pushButton {
            text("I'm a custom entry")
        }
    }
    submenu {
        icon { menu }
        text("Sub Menu")
        entry {
            icon { sun }
            text("Entry with icon")
        }
        entry {
            icon { ban }
            text("Disabled entry")
            disabled(true)
        }
    }
}

PR #483: Deprecate box in favor of div

This PR deprecates the box factory method in favor of div because, in combination with fritz2.styling, it offers the exact same functionality. All calls of box will have to be replaced eventually.

PR #477: Add convenience functions to create toasts with alert-content

This PR adds two convenience functions alertToast and showAlertToast which can be used to easily create toasts with alert's as their content.

New functions:

fun showAlertToast(
    styling: BasicParams.() -> Unit = {},
    baseClass: StyleClass = StyleClass.None,
    id: String? = null,
    prefix: String = "toast-alert",
    build: AlertComponent.() -> Unit
)

fun alertToast(
    styling: BasicParams.() -> Unit = {},
    baseClass: StyleClass = StyleClass.None,
    id: String? = null,
    prefix: String = "toast-alert",
    build: AlertComponent.() -> Unit
): SimpleHandler<Unit>

Example usage:

showAlertToast(buildToast = {
   
}) { 
    // toast-properties:
    duration(6000)

    // setup of the alert
    alert {
        title("Alert-Toast")
        content("Alert in a toast")
        severity { /* some severity */ }
        variant { leftAccent }
    }
}

// 'alertToast' is used similarly and bound to a flow  like 'toast'
Previous usage
val alertComponent = AlertComponent()
    .apply {
        content("...")
        stacking { toast }
    }

showToast {
    // adjust the close-button to match the alert's color-scheme:
    closeButtonStyle(Theme().toast.closeButton.close + {
        color {
            val colorScheme = alertComponent.severity.value(Theme().alert.severities).colorScheme
            when(alertComponent.variant.value(AlertComponent.VariantContext)) {
                AlertComponent.AlertVariant.SUBTLE -> colorScheme.main
                AlertComponent.AlertVariant.TOP_ACCENT -> colorScheme.main
                AlertComponent.AlertVariant.LEFT_ACCENT -> colorScheme.main
                else -> colorScheme.mainContrast
            }
        }
    })
    content {
        alertComponent.render(this, styling, baseClass, id, prefix)
    }
}

Result:
Bildschirmfoto 2021-07-06 um 10 35 19

PR #462: Rename alert variant discreet to ghost

This PR renames the discreet alert-variant to ghost in order to match the common naming scheme of fritz2.

All references of discreet have to be changed:

alert {
    variant { discreet } // <-- previously
    variant { ghost } // <-- now
}

Fixed Bugs

  • PR #515: Handle possible CastException
  • PR #511: fix appearance of inputs, textareas and select on iOS
  • PR #502: fix type of InputEvent
  • PR #500: Fix layout issues in radio, checkbox and switch components
  • PR #471: Fix close-button margin in ToastComponent