Skip to content

Commit

Permalink
Improves PopUpPanel (#835)
Browse files Browse the repository at this point in the history
- extends `arrow`-factory to take some `offset`-parameter alongside to the size parameter that is meant for changing width and height
- improves default styling, so that the arrow is located behind the panels content
- adds, corrects or improves API- and prose-documentation regarding the `PopUpPanel` usage, `arrow`-usage or `middleware`-usage.

Co-authored-by: christian.hausknecht <christian.hausknecht@oeffentliche.de>
  • Loading branch information
Lysander and christian.hausknecht committed Jan 3, 2024
1 parent 66564c1 commit 3c42185
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ package dev.fritz2.headlessdemo.components
import dev.fritz2.core.RenderContext
import dev.fritz2.core.transition
import dev.fritz2.headless.components.popOver
import dev.fritz2.headless.foundation.PopUpPanelSize
import dev.fritz2.headless.foundation.utils.floatingui.core.Middleware
import dev.fritz2.headless.foundation.utils.floatingui.core.MiddlewareReturn
import dev.fritz2.headless.foundation.utils.floatingui.core.MiddlewareState
import dev.fritz2.headless.foundation.utils.floatingui.core.middleware.offset
import dev.fritz2.headless.foundation.utils.floatingui.utils.PlacementValues
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlin.js.Promise


fun RenderContext.popOverDemo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,104 +71,103 @@ abstract class PopUpPanel<C : HTMLElement>(
addGlobalStyles(
listOf(
""".$POPUP_RELATIVE {
position: relative;
position: relative;
}""".trimIndent(),
""".popup[data-popup-reference-hidden] {
visibility: hidden;
pointer-events: none;
}""".trimIndent(),
visibility: hidden;
pointer-events: none;
}""".trimIndent(),
""".popup-arrow-default {
width: 8px;
height: 8px;
background: inherit;
}""".trimIndent(),
width: 8px;
height: 8px;
}""".trimIndent(),
""".popup-arrow::before {
content: '';
transform: rotate(45deg);
background: inherit;
width: 100%;
height: 100%;
}""".trimIndent(),
}""".trimIndent(),
""".popup-arrow, .popup-arrow::before {
position: absolute;
}""".trimIndent(),
position: absolute;
z-index: -1;
}""".trimIndent(),
""".popup-arrow {
visibility: hidden;
}""".trimIndent(),
""".popup-arrow::before {
content: '';
transform: rotate(45deg);
background: inherit;
}""".trimIndent(),
visibility: hidden;
background: inherit;
}""".trimIndent(),
""".popup-arrow::before, .popup.$FRITZ2_POPUP_VISIBLE .popup-arrow::before {
visibility: visible;
}""".trimIndent(),
visibility: visible;
}""".trimIndent(),
""".popup-arrow::before, .popup.$FRITZ2_POPUP_HIDDEN .popup-arrow::before {
visibility: hidden;
}""".trimIndent(),
visibility: hidden;
}""".trimIndent(),
""".popup[data-popup-placement^='bottom'] .popup-arrow::before {
top: -50%;
}""".trimIndent(),
top: -50%;
}""".trimIndent(),
""".popup[data-popup-placement^='top'] .popup-arrow::before {
bottom: -50%;
}""".trimIndent(),
bottom: -50%;
}""".trimIndent(),
""".popup[data-popup-placement^='left'] .popup-arrow::before {
right: -50%;
}""".trimIndent(),
right: -50%;
}""".trimIndent(),
""".popup[data-popup-placement^='right'] .popup-arrow::before {
left: -50%;
}""".trimIndent(),
left: -50%;
}""".trimIndent(),
""".popup[data-popup-placement^='bottom'] .popup-arrow {
top: 0;
}""".trimIndent(),
top: 0;
}""".trimIndent(),
""".popup[data-popup-placement^='top'] .popup-arrow {
bottom: 0;
}""".trimIndent(),
bottom: 0;
}""".trimIndent(),
""".popup[data-popup-placement^='left'] .popup-arrow {
right: 0;
}""".trimIndent(),
right: 0;
}""".trimIndent(),
""".popup[data-popup-placement^='right'] .popup-arrow {
left: 0;
}""".trimIndent(),
left: 0;
}""".trimIndent(),
""".popup[data-popup-placement='bottom'] > .transform {
transform-origin: top;
}""".trimIndent(),
transform-origin: top;
}""".trimIndent(),
""".popup[data-popup-placement='bottom-start'] > .transform {
transform-origin: top left;
}""".trimIndent(),
transform-origin: top left;
}""".trimIndent(),
""".popup[data-popup-placement='bottom-right'] > .transform {
transform-origin: top right;
}""".trimIndent(),
transform-origin: top right;
}""".trimIndent(),
""".popup[data-popup-placement='top'] > .transform {
transform-origin: bottom;
}""".trimIndent(),
transform-origin: bottom;
}""".trimIndent(),
""".popup[data-popup-placement='top-start'] > .transform {
transform-origin: bottom left;
}""".trimIndent(),
transform-origin: bottom left;
}""".trimIndent(),
""".popup[data-popup-placement='top-right'] > .transform {
transform-origin: bottom right;
}""".trimIndent(),
transform-origin: bottom right;
}""".trimIndent(),
""".popup[data-popup-placement='left'] > .transform {
transform-origin: right;
}""".trimIndent(),
transform-origin: right;
}""".trimIndent(),
""".popup[data-popup-placement='left-start'] > .transform {
transform-origin: top right;
}""".trimIndent(),
transform-origin: top right;
}""".trimIndent(),
""".popup[data-popup-placement='left-end'] > .transform {
transform-origin: bottom right;
}""".trimIndent(),
transform-origin: bottom right;
}""".trimIndent(),
""".popup[data-popup-placement='right'] > .transform {
transform-origin: left;
}""".trimIndent(),
transform-origin: left;
}""".trimIndent(),
""".popup[data-popup-placement='right-start'] > .transform {
transform-origin: top left;
}""".trimIndent(),
transform-origin: top left;
}""".trimIndent(),
""".popup[data-popup-placement='right-end'] > .transform {
transform-origin: bottom left;
}""".trimIndent(),
transform-origin: bottom left;
}""".trimIndent(),
""".$FRITZ2_POPUP_VISIBLE {
visibility: visible;
}""".trimIndent(),
visibility: visible;
}""".trimIndent(),
""".$FRITZ2_POPUP_HIDDEN {
visibility: hidden;
}""".trimIndent(),
visibility: hidden;
}""".trimIndent(),
)
)
}
Expand Down Expand Up @@ -201,10 +200,17 @@ abstract class PopUpPanel<C : HTMLElement>(
addMiddleware(flip())
}


/**
* Adds a new Middleware to the array of middlewares.
*
* For example
* ```kotlin
* addMiddleware(offset(10))
* ```
*
* Be aware to follow the recommended precedences by floating-ui's middlewares:
* https://floating-ui.com/docs/middleware#ordering
*
* Check https://floating-ui.com/docs/middleware for available middlewares.
*
* @see ComputePositionConfig.middleware
Expand All @@ -218,14 +224,29 @@ abstract class PopUpPanel<C : HTMLElement>(
/**
* Adds an arrow to the PopupPanel. The exact position will be calculated by the FloatingUI component and can be
* collected from [computedPosition]. The arrow points to the reference element.
*
* By default, a width and height of `8px` each is set by the default class.
* If you want to change this, you must provide both properties somehow, depending on your CSS handling / framework.
*
* The [offset] from the reference element has a default of `5px` and can be also adapted as needed.
*
* Remember that fritz2 is completely CSS framework-agnostic!
*
* @param size the size of the arrow using any valid CSS `width` or `height` expression. Defaults to `8` each
* @param offset the distance between the reference element and the panel in pixels. Defaults to `5`
*/
fun arrow(c: String = "popup-arrow-default") {
div(classes(c, "popup-arrow")) {
fun arrow(size: String = "popup-arrow-default", offset: Int = 5) {
div(classes(size, "popup-arrow")) {
arrow = this
addMiddleware(offset(offset))
addMiddleware(arrow { element = domNode })
addMiddleware(offset(5))
inlineStyle(computedPosition.mapNotNull { it.middlewareData?.arrow }
.map { "left: ${it.x}px; top: ${it.y}px;" })
.map {
buildString {
it.x?.let { x -> append("left: ${x}px;") }
it.y?.let { y -> append(" top: ${y}px;") }
}
})
}
}

Expand All @@ -251,7 +272,9 @@ abstract class PopUpPanel<C : HTMLElement>(
attr("data-popup-placement", computedPosition.map { it.placement ?: "" })
inlineStyle(computedPosition.map {
listOfNotNull(
"position: ${it.strategy}", "left: ${it.x}px", "top: ${it.y}px",
"position: ${it.strategy}",
it.x?.let { x -> "left: ${x}px" },
it.y?.let { y -> "top: ${y}px" },
when (size) {
PopUpPanelSize.Min -> "min-width: ${reference.domNode.offsetWidth}px"
PopUpPanelSize.Max -> "max-width: ${reference.domNode.offsetWidth}px"
Expand Down
11 changes: 6 additions & 5 deletions www/src/pages/headless/headlessComponents.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,18 +507,19 @@ in order to influence the positioning of the content:
| Scope property | Typ | Description |
|----------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `size` | `PopUpPanelSize` | Defines the width restrictions of the building block, e.g. `PopUpPanelSize.Min`, `PopUpPanelSize.Max`, etc. |
| `placement` | `Placement` | Defines the position of the building block, e.g. `Placement.top`, `Placement.bottomRight`, etc. |
| `placement` | `PlacementValues` | Defines the position of the building block, e.g. `PlacementValues.top`, `PlacementValues.bottom`, etc. |
| `strategy` | `Strategy` | Determines whether the block should be positioned `absolute` (default) or `fixed`. |
| `middleware` | `Array<Middleware>` | Middleware are plain objects that modify the positioning coordinates in some fashion, or provide useful data for rendering, as calculated by the positioning cycle. |

In addition, an arrow can be added pointing to the reference element. By default, the arrow is 8 pixels wide and
inherits the background color of the panel. It can be styled as usual:
inherits the background color of the panel. You are recommended to only change its `width` or `height` by providing
any valid CSS expression for that for its `size`parameter. Alongside of changing the size, you usually also have to
adapt the `offset` too, so there is also a parameter to provide the value in pixels:

```kotlin
popOverPanel {
popOverPanel("bg-gray-200") {
//...

arrow("h-3 w-3 bg-white")
arrow("h-3 w-3", 8) // w-3 -> 12px in tailwindcss, use at least the half of the arrow size for the offset
}
```

Expand Down
4 changes: 2 additions & 2 deletions www/src/pages/headless/listbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ as a reference element:

```kotlin
listboxItems {
placement = Placement.Top
distance = 20
placement = PlacementValues.top
addMiddleware(offset(20))

characters.forEach { (entry, disabledState) ->
listboxItem(entry) {
Expand Down
4 changes: 2 additions & 2 deletions www/src/pages/headless/menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ as a reference element:

```kotlin
menuItems {
placement = Placement.Top
distance = 20
placement = PlacementValues.top
addMiddleware(offset(20))

menuItem {
//...
Expand Down
9 changes: 5 additions & 4 deletions www/src/pages/headless/popOver.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ as a reference element:

```kotlin
popOverPanel {
placement = Placement.Bottom
distance = 20
placement = PlacementValues.bottom
addMiddleware(offset(20))

//...
}
Expand All @@ -119,10 +119,11 @@ popOverPanel {
}
```

By default, the arrow is 8 pixels wide and inherits the background-color from the `popOverPanel` but it can easily be styled by adding classes:
By default, the arrow is 8 pixels wide and inherits the background-color from the `popOverPanel` its size and offset can
be adapted:

```kotlin
arrow("h-3 w-3 bg-white")
arrow("h-3 w-3", 8)
```


Expand Down
10 changes: 5 additions & 5 deletions www/src/pages/headless/tooltip.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ button {
}.tooltip {
+ "some description"

placement = Placement.Top
distance = 20
placement = PlacementValues.top
addMiddleware(offset(20))
}
```

Expand All @@ -92,11 +92,11 @@ button {
}
```

By default, the arrow is 8 pixels wide and inherits the background-color from the `tooltip` but it can easily be styled
by added classes:
By default, the arrow is 8 pixels wide and inherits the background-color from the `tooltip` but its size and offset can
be adapted:

```kotlin
arrow("h-3 w-3 bg-white")
arrow("h-3 w-3", 8)
```


Expand Down

0 comments on commit 3c42185

Please sign in to comment.