diff --git a/demo/index.html b/demo/index.html
index 568a5f8..93dc802 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -9,6 +9,10 @@
/>
+
=12.0.0"
+ }
+ },
"node_modules/html-minifier-terser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
@@ -1601,6 +1610,11 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
+ "highlight.js": {
+ "version": "11.6.0",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz",
+ "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw=="
+ },
"html-minifier-terser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
diff --git a/demo/package.json b/demo/package.json
index fce3843..96d3f97 100644
--- a/demo/package.json
+++ b/demo/package.json
@@ -8,13 +8,14 @@
"preview": "vite preview"
},
"devDependencies": {
+ "http-proxy": "1.18.1",
"vite": "2.9.0",
- "vite-plugin-html": "2.0.7",
- "http-proxy": "1.18.1"
+ "vite-plugin-html": "2.0.7"
},
"dependencies": {
"@ui5/webcomponents": "1.3.0",
"@ui5/webcomponents-fiori": "1.3.0",
- "@ui5/webcomponents-icons": "1.3.0"
+ "@ui5/webcomponents-icons": "1.3.0",
+ "highlight.js": "11.6.0"
}
}
diff --git a/demo/src/main/scala/demo/AvatarExample.scala b/demo/src/main/scala/demo/AvatarExample.scala
index 8ddaaef..2f003ef 100644
--- a/demo/src/main/scala/demo/AvatarExample.scala
+++ b/demo/src/main/scala/demo/AvatarExample.scala
@@ -3,7 +3,7 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import demo.helpers.MTG
object AvatarExample extends Example("Avatar") {
@@ -11,25 +11,30 @@ object AvatarExample extends Example("Avatar") {
private def sherpal = img(src := "/images/avatars/sherpal.png", alt := "sherpal")
private def manaSymbolImage(name: String) = img(src := MTG.manaSymbolsRefs(name), alt := name)
- def component: HtmlElement = div(
- DemoPanel(
- "Basic examples",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic examples") {
+ //-- Begin: Basic examples
div(Avatar(_ => sherpal), Avatar(_.shape := AvatarShape.Square, _ => sherpal))
- ),
- DemoPanel(
- "Avatar sizes",
+ //-- End
+ },
+ DemoPanel("Avatar sizes") {
+ //-- Begin: Avatar sizes
div(
AvatarSize.allValues
.zip(MTG.manaSymbolsShortNames)
.map((size, mana) => Avatar(_.size := size, _ => manaSymbolImage(mana)))
)
- ),
- DemoPanel(
- "Avatar with icons",
+ //-- End
+ },
+ DemoPanel("Avatar with icons") {
+ //-- Begin: Avatar with icons
div(AvatarSize.allValues.zip(IconName.allValues).map((size, icon) => Avatar(_.size := size, _.icon := icon)))
- ),
- DemoPanel(
- "Avatar with initials",
+ //-- End
+ },
+ DemoPanel("Avatar with initials") {
+ //-- Begin: Avatar with initials
div(AvatarSize.allValues.map { size =>
val initials: AvatarInitials = size.value.toList.take(2) match {
case c :: Nil => c
@@ -38,7 +43,8 @@ object AvatarExample extends Example("Avatar") {
}
Avatar(_.size := size, _.initials := initials)
})
- )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/AvatarGroupExample.scala b/demo/src/main/scala/demo/AvatarGroupExample.scala
new file mode 100644
index 0000000..990e96b
--- /dev/null
+++ b/demo/src/main/scala/demo/AvatarGroupExample.scala
@@ -0,0 +1,52 @@
+package demo
+
+import be.doeraene.webcomponents.ui5.*
+import be.doeraene.webcomponents.ui5.configkeys.*
+import com.raquo.laminar.api.L.*
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import org.scalajs.dom
+
+object AvatarGroupExample extends Example("AvatarGroup") {
+
+ def component(using demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo): HtmlElement = div(
+ DemoPanel("Avatar Group - type 'Individual'") {
+ //-- Begin: Avatar Group - type 'Individual'
+ AvatarGroup(
+ _.tpe := AvatarGroupType.Individual,
+ _ => IconName.allValues.take(5).map(icon => Avatar(_.icon := icon, _.size := AvatarSize.M))
+ )
+ //-- End
+ },
+ DemoPanel("Avatar Group - type 'Group'") {
+ //-- Begin: Avatar Group - type 'Group'
+ AvatarGroup(
+ _.tpe := AvatarGroupType.Group,
+ _ => IconName.allValues.take(5).map(icon => Avatar(_.icon := icon, _.size := AvatarSize.M))
+ )
+ //-- End
+ },
+ DemoPanel("Avatar Group with overflow") {
+ //-- Begin: Avatar Group with overflow
+ def allAvatars = IconName.allValues.take(10).map(icon => Avatar(_.icon := icon, _.size := AvatarSize.M))
+
+ val avatarsForPopoverBus: EventBus[List[HtmlElement]] = new EventBus
+ val popoverOpenerBus: EventBus[dom.HTMLElement] = new EventBus
+
+ div(
+ AvatarGroup(
+ _.tpe := AvatarGroupType.Individual,
+ _ => allAvatars,
+ _ => width := "400px",
+ _.events.onClick.map(_.target.hiddenItems.length).map(allAvatars.takeRight) --> avatarsForPopoverBus.writer,
+ _.events.onClick.filter(_.detail.overflowButtonClicked).map(_.detail.targetRef) --> popoverOpenerBus.writer
+ ),
+ Popover(
+ _.showAtFromEvents(popoverOpenerBus.events),
+ _ => children <-- avatarsForPopoverBus
+ )
+ )
+ //-- End
+ }
+ )
+
+}
diff --git a/demo/src/main/scala/demo/BadgeExample.scala b/demo/src/main/scala/demo/BadgeExample.scala
index 514e3cf..cdd6a2c 100644
--- a/demo/src/main/scala/demo/BadgeExample.scala
+++ b/demo/src/main/scala/demo/BadgeExample.scala
@@ -3,7 +3,7 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object BadgeExample extends Example("Badge") {
@@ -20,22 +20,26 @@ object BadgeExample extends Example("Badge") {
"the last"
)
- def component: HtmlElement = div(
- DemoPanel(
- "Basic badge",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic badge")(
+ //-- Begin: Basic badge
div(
ColourScheme.allValues
.zip(someTexts)
.map((colourScheme, text) => Badge(_.colourScheme := colourScheme, _ => text)),
Badge(_ => width := "200px", _ => "This text is very long and it will be truncated with ellipsis")
)
+ //-- End
),
- DemoPanel(
- "Badge with icon",
+ DemoPanel("Badge with icon")(
+ //-- Begin: Badge with icon
div(
Badge(_.colourScheme := ColourScheme._1, _.slots.icon := Icon(_.name := IconName.add), _ => "Add"),
Badge(_.colourScheme := ColourScheme._2, _.slots.icon := Icon(_.name := IconName.customer))
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/BarExample.scala b/demo/src/main/scala/demo/BarExample.scala
index d3fe7dc..c6f9b77 100644
--- a/demo/src/main/scala/demo/BarExample.scala
+++ b/demo/src/main/scala/demo/BarExample.scala
@@ -3,7 +3,7 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object BarExample extends Example("Bar") {
@@ -23,16 +23,16 @@ object BarExample extends Example("Bar") {
_.slots.endContent := Button(_.design := ButtonDesign.Transparent, _ => "Cancel")
)
- def component: HtmlElement = div(
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
List(BarDesign.Header, BarDesign.Subheader).map(design =>
- DemoPanel(
- s"${design.value} Bar",
+ DemoPanel(s"${design.value} Bar")(
Bar((headerBarContent(s"${design.value} Title") :+ (_.design := design)): _*)
)
),
List(BarDesign.Footer, BarDesign.FloatingFooter).map(design =>
- DemoPanel(
- s"${design.value} Bar",
+ DemoPanel(s"${design.value} Bar")(
Bar(footerBarContent: _*)
)
)
diff --git a/demo/src/main/scala/demo/BarcodeScannerDialogExample.scala b/demo/src/main/scala/demo/BarcodeScannerDialogExample.scala
new file mode 100644
index 0000000..e71052e
--- /dev/null
+++ b/demo/src/main/scala/demo/BarcodeScannerDialogExample.scala
@@ -0,0 +1,49 @@
+package demo
+
+import be.doeraene.webcomponents.ui5.*
+import be.doeraene.webcomponents.ui5.configkeys.*
+import com.raquo.laminar.api.L.*
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+
+object BarcodeScannerDialogExample extends Example("BarcodeScannerDialog") {
+
+ def component(using demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo): HtmlElement = div(
+ DemoPanel("Usage") {
+ //-- Begin: Usage
+ // Feed this bus in order to open the barcode scanner
+ val openScannerBus: EventBus[Unit] = new EventBus
+
+ // Feed this bus to pass the information of a failed scan
+ val barcodeScanFailedBus: EventBus[BarcodeScannerDialog.events.ErrorInfo] = new EventBus
+ // Feed this bus to pass the information of a successful scan
+ val barcodeScanSuccessBus: EventBus[BarcodeScannerDialog.events.SuccessInfo] = new EventBus
+
+ val barcodeScanEvents = EventStream
+ .merge(
+ barcodeScanSuccessBus.events,
+ barcodeScanFailedBus.events
+ )
+ .mapTo(())
+
+ div(
+ BarcodeScannerDialog(
+ _.showOnEvents(openScannerBus.events),
+ _.closeOnEvents(barcodeScanEvents),
+ _.events.onScanSuccess.map(_.detail) --> barcodeScanSuccessBus.writer,
+ _.events.onScanError.map(_.detail) --> barcodeScanFailedBus.writer
+ ),
+ Title.h3(_ => "Click on the button below and show a QR code to the camera:"),
+ Button(
+ _.icon := IconName.camera,
+ _.tooltip := "Start Camera",
+ _ => "Scan",
+ _.events.onClick.mapTo(()) --> openScannerBus.writer
+ ),
+ div(Label(_ => child.text <-- barcodeScanSuccessBus.events.map(_.text))),
+ div(Label(_ => child.text <-- barcodeScanFailedBus.events.map(_.message)))
+ )
+ //-- End
+ }
+ )
+
+}
diff --git a/demo/src/main/scala/demo/BreadcrumbsExample.scala b/demo/src/main/scala/demo/BreadcrumbsExample.scala
index bf0c71b..8d26eb0 100644
--- a/demo/src/main/scala/demo/BreadcrumbsExample.scala
+++ b/demo/src/main/scala/demo/BreadcrumbsExample.scala
@@ -3,12 +3,14 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object BreadcrumbsExample extends Example("Breadcrumbs") {
- def component: HtmlElement = div(
- DemoPanel(
- "Standard Breadcrumbs",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Standard Breadcrumbs")(
+ //-- Begin: Standard Breadcrumbs
Breadcrumbs(
_.Item(
_.href := "https://github.com/sherpal/LaminarSAPUI5Bindings",
@@ -19,9 +21,10 @@ object BreadcrumbsExample extends Example("Breadcrumbs") {
_.Item(_ => "Current page"),
_.events.onItemClick.map(_.detail.item) --> Observer(x => org.scalajs.dom.console.log(x))
)
+ //-- End
),
- DemoPanel(
- "Breadcrumbs with no current page",
+ DemoPanel("Breadcrumbs with no current page")(
+ //-- Begin: Breadcrumbs with no current page
Breadcrumbs(
_.design := BreadcrumbsDesign.NoCurrentPage,
_.Item(
@@ -31,9 +34,10 @@ object BreadcrumbsExample extends Example("Breadcrumbs") {
),
_.Item(_.href := "https://github.com/sherpal/LaminarSAPUI5Bindings", _ => "Parent page")
)
+ //-- End
),
- DemoPanel(
- "Breadcrumbs with specific separator",
+ DemoPanel("Breadcrumbs with specific separator")(
+ //-- Begin: Breadcrumbs with specific separator
div(
BreadcrumbsSeparatorStyle.allValues.map(separatorStyle =>
Breadcrumbs(
@@ -48,6 +52,7 @@ object BreadcrumbsExample extends Example("Breadcrumbs") {
)
)
)
+ //-- End
)
)
}
diff --git a/demo/src/main/scala/demo/BusyIndicatorExample.scala b/demo/src/main/scala/demo/BusyIndicatorExample.scala
index d74795e..6a56704 100644
--- a/demo/src/main/scala/demo/BusyIndicatorExample.scala
+++ b/demo/src/main/scala/demo/BusyIndicatorExample.scala
@@ -3,13 +3,15 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object BusyIndicatorExample extends Example("BusyIndicator") {
- def component: HtmlElement = div(
- DemoPanel(
- "Busy indicator with difference size",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Busy indicator with difference size")(
+ //-- Begin: Busy indicator with difference size
div(
display := "flex",
flexDirection := "column",
@@ -21,36 +23,37 @@ object BusyIndicatorExample extends Example("BusyIndicator") {
)
)
)
+ //-- End
),
- DemoPanel(
- "Busy indicator wrapping other elements", {
- val fetchListDataBus: EventBus[Unit] = new EventBus
- val allData = (1 to 10).map(j => s"Data $j").toList
- val numberOfDataToFetch = fetchListDataBus.events.delay(3000).foldLeft(0)((acc, _) => acc + 3)
- val fetchedData = numberOfDataToFetch.map(allData.take)
- val busyStates = EventStream
- .merge(
- fetchListDataBus.events.map(_ => 1),
- numberOfDataToFetch.changes.map(_ => -1)
- )
- .foldLeft(0)(_ + _)
- .map(_ > 0)
+ DemoPanel("Busy indicator wrapping other elements") {
+ //-- Begin: Busy indicator wrapping other elements
+ val fetchListDataBus: EventBus[Unit] = new EventBus
+ val allData = (1 to 10).map(j => s"Data $j").toList
+ val numberOfDataToFetch = fetchListDataBus.events.delay(3000).foldLeft(0)((acc, _) => acc + 3)
+ val fetchedData = numberOfDataToFetch.map(allData.take)
+ val busyStates = EventStream
+ .merge(
+ fetchListDataBus.events.map(_ => 1),
+ numberOfDataToFetch.changes.map(_ => -1)
+ )
+ .foldLeft(0)(_ + _)
+ .map(_ > 0)
+ div(
+ div(Button(_ => "Fetch list data", _.events.onClick.mapTo(()) --> fetchListDataBus.writer)),
div(
- div(Button(_ => "Fetch list data", _.onClick.mapTo(()) --> fetchListDataBus.writer)),
- div(
- BusyIndicator(
- _.active <-- busyStates,
- _ =>
- UList(
- _ => width := "400px",
- _ => children <-- fetchedData.map(data => data.map(pieceOfData => UList.Li(_ => pieceOfData))),
- _.noDataText := "No Data"
- )
- )
+ BusyIndicator(
+ _.active <-- busyStates,
+ _ =>
+ UList(
+ _ => width := "400px",
+ _ => children <-- fetchedData.map(data => data.map(pieceOfData => UList.item(_ => pieceOfData))),
+ _.noDataText := "No Data"
+ )
)
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/ButtonExample.scala b/demo/src/main/scala/demo/ButtonExample.scala
index 12f7cc9..045a9bb 100644
--- a/demo/src/main/scala/demo/ButtonExample.scala
+++ b/demo/src/main/scala/demo/ButtonExample.scala
@@ -3,11 +3,13 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object ButtonExample extends Example("Button") {
- def component: HtmlElement = {
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = {
val exampleButtonContainerClass = "exampleButtonContainerClass"
div(
styleTag(s"""
@@ -16,8 +18,8 @@ object ButtonExample extends Example("Button") {
|}
|""".stripMargin),
h1("Button"),
- DemoPanel(
- "Basic Button",
+ DemoPanel("Basic Button")(
+ //-- Begin: Basic Button
div(
className := exampleButtonContainerClass,
Button(_.design := ButtonDesign.Default, _ => "Default"),
@@ -28,9 +30,10 @@ object ButtonExample extends Example("Button") {
Button(_.design := ButtonDesign.Attention, _ => "Warning"),
Button(_.design := ButtonDesign.Emphasized, _ => "Subscribe")
)
+ //-- End
),
- DemoPanel(
- "Button with Icon",
+ DemoPanel("Button with Icon")(
+ //-- Begin: Button with Icon
div(
className := exampleButtonContainerClass,
Button(_.icon := IconName.employee, _ => "Add"),
@@ -40,9 +43,10 @@ object ButtonExample extends Example("Button") {
Button(_.design := ButtonDesign.Attention, _.icon := IconName.`message-warning`, _ => "Warning"),
Button(_.design := ButtonDesign.Transparent, _.icon := IconName.accept, _ => "Transparent")
)
+ //-- End
),
- DemoPanel(
- "Icon Only Button",
+ DemoPanel("Icon Only Button")(
+ //-- Begin: Icon Only Button
div(
className := exampleButtonContainerClass,
Button(_.icon := IconName.away),
@@ -53,6 +57,7 @@ object ButtonExample extends Example("Button") {
Button(_.icon := IconName.bookmark, _.design := ButtonDesign.Positive),
Button(_.icon := IconName.cart, _.design := ButtonDesign.Transparent)
)
+ //-- End
)
)
}
diff --git a/demo/src/main/scala/demo/CalendarExample.scala b/demo/src/main/scala/demo/CalendarExample.scala
new file mode 100644
index 0000000..5d743ba
--- /dev/null
+++ b/demo/src/main/scala/demo/CalendarExample.scala
@@ -0,0 +1,62 @@
+package demo
+
+import be.doeraene.webcomponents.ui5.*
+import be.doeraene.webcomponents.ui5.configkeys.*
+import com.raquo.laminar.api.L.*
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+
+object CalendarExample extends Example("Calendar") {
+
+ def component(using demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo): HtmlElement = div(
+ DemoPanel("Basic Calendar") {
+ //-- Begin: Basic Calendar
+ val maybeSelectedDateVar: Var[Option[Calendar.events.SelectedDatesChangeInfo]] = Var(None)
+ div(
+ Label(_ =>
+ child.text <-- maybeSelectedDateVar.signal.map {
+ case None => "No selected date yet."
+ case Some(info) => s"Selected dates: ${info.values.zip(info.dates).mkString(", ")}."
+ }
+ ),
+ br(),
+ Calendar(_.events.onSelectedDatesChange.map(_.detail) --> maybeSelectedDateVar.writer.contramapSome)
+ )
+ //-- End
+ },
+ DemoPanel("Calendar with Minimum and Maximum Date & Format Pattern") {
+ //-- Begin: Calendar with Minimum and Maximum Date & Format Pattern
+ Calendar(_.minDateRaw := "7/7/2020", _.maxDateRaw := "20/10/2020", _.formatPattern := "dd/MM/yyyy")
+ //-- End
+ },
+ DemoPanel("Calendar with Hidden Week Numbers") {
+ //-- Begin: Calendar with Hidden Week Numbers
+ Calendar(_.hideWeekNumbers := true)
+ //-- End
+ },
+ DemoPanel("Calendar with Selection Mode Multiple") {
+ //-- Begin: Calendar with Selection Mode Multiple
+ Calendar(_.selectionMode := CalendarSelectionMode.Multiple)
+ //-- End
+ },
+ DemoPanel("Calendar with Selection Mode Range") {
+ //-- Begin: Calendar with Selection Mode Range
+ Calendar(_.selectionMode := CalendarSelectionMode.Range)
+ //-- End
+ },
+ DemoPanel("Other types of Calendar") {
+ //-- Begin: Other types of Calendar
+
+ div(CalendarType.allValues.filterNot(_ == CalendarType.Gregorian).map { calendarType =>
+ // Need to import the support for your calendar type. This has to be done at least once in your application
+ // (for example where your application starts, or the first place you use this calendar type)
+ calendarType.importObject
+ div(
+ Title.h3(_ => s"Type of calendar: ${calendarType.value}"),
+ Calendar(_.primaryCalendarType := calendarType)
+ )
+ })
+ //-- End
+ }
+ )
+
+}
diff --git a/demo/src/main/scala/demo/CardExample.scala b/demo/src/main/scala/demo/CardExample.scala
index 7f0e1e8..3033d40 100644
--- a/demo/src/main/scala/demo/CardExample.scala
+++ b/demo/src/main/scala/demo/CardExample.scala
@@ -3,7 +3,7 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
object CardExample extends Example("Card") {
@@ -16,9 +16,11 @@ object CardExample extends Example("Card") {
private val contentPadding = "content-padding"
- def component: HtmlElement = div(
- DemoPanel(
- "Card with List",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Card with List")(
+ //-- Begin: Card with List
div(
styleTag(s"""
|.$cardContentClassName {
@@ -57,7 +59,7 @@ object CardExample extends Example("Card") {
.zip(MTG.manaSymbolsShortNames)
.take(3)
.map((name, shortName) =>
- UList.Li(_.image := MTG.manaSymbolsRefs(shortName), _.description := name, _ => name)
+ UList.item(_.image := MTG.manaSymbolsRefs(shortName), _.description := name, _ => name)
)
)
)
@@ -83,15 +85,16 @@ object CardExample extends Example("Card") {
.zip(MTG.manaSymbolsShortNames)
.drop(3)
.map((name, shortName) =>
- UList.Li(_.image := MTG.manaSymbolsRefs(shortName), _.description := name, _ => name)
+ UList.item(_.image := MTG.manaSymbolsRefs(shortName), _.description := name, _ => name)
)
)
)
)
)
+ //-- End
),
- DemoPanel(
- "Card with Table",
+ DemoPanel("Card with Table")(
+ //-- Begin: Card with Table
div(
styleTag(s"""
|.$statusError {color: #b00;}
@@ -137,7 +140,9 @@ object CardExample extends Example("Card") {
)
)
)
- )
+ //-- End
+ ),
+ mtgImageWarning
)
}
diff --git a/demo/src/main/scala/demo/CarouselExample.scala b/demo/src/main/scala/demo/CarouselExample.scala
index 0f9dd42..07570cb 100644
--- a/demo/src/main/scala/demo/CarouselExample.scala
+++ b/demo/src/main/scala/demo/CarouselExample.scala
@@ -3,10 +3,11 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
object CarouselExample extends Example("Carousel") {
+ //-- Begin Common
private def threeMagicWallpapers: List[Carousel.ModFunction] = List(
_ =>
img(
@@ -18,32 +19,39 @@ object CarouselExample extends Example("Carousel") {
src := "https://media.magic.wizards.com/images/wallpaper/sparas_headquarters_kieran_yanner_1280x960_poozxbqpcw.jpg"
)
)
+ //-- End Common
- def component: HtmlElement = div(
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
styleTag("""
|ui5-carousel > img {
| max-width: 500px
|}
|""".stripMargin),
- DemoPanel(
- "Carousel With Single Item per Page",
+ DemoPanel("Carousel With Single Item per Page")(
+ //-- Begin: Carousel With Single Item per Page
Carousel(threeMagicWallpapers: _*)
+ //-- End
),
- DemoPanel(
- "Carousel with Multiple items per Page",
+ DemoPanel("Carousel with Multiple items per Page")(
+ //-- Begin: Carousel with Multiple items per Page
Carousel(
_.itemsPerPageS := 1,
_.itemsPerPageM := 2,
_.itemsPerPageL := 2,
_ => MTG.manaSymbolsShortNames.map(name => img(src := MTG.manaSymbolsRefs(name), alt := name))
)
+ //-- End
),
- DemoPanel(
- "Carousel With Arrow Placement and Cyclic",
+ DemoPanel("Carousel With Arrow Placement and Cyclic")(
+ //-- Begin: Carousel With Arrow Placement and Cyclic
Carousel(
threeMagicWallpapers ++ List(_.arrowsPlacement := CarouselArrowsPlacement.Navigation, _.cyclic := true): _*
)
- )
+ //-- End
+ ),
+ mtgImageWarning
)
}
diff --git a/demo/src/main/scala/demo/CheckBoxExample.scala b/demo/src/main/scala/demo/CheckBoxExample.scala
index 5a180e7..7e982f0 100644
--- a/demo/src/main/scala/demo/CheckBoxExample.scala
+++ b/demo/src/main/scala/demo/CheckBoxExample.scala
@@ -3,22 +3,26 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
object CheckBoxExample extends Example("CheckBox") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic CheckBox",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ maxWidth := "calc(100% - 300px)",
+ DemoPanel("Basic CheckBox")(
+ //-- Begin: Basic CheckBox
div(
CheckBox(_.text := "Chocolate", _.checked := true),
CheckBox(_.text := "Strawberry", _.checked := true),
CheckBox(_.text := "Waffles", _.checked := true, _.valueState := ValueState.Error),
CheckBox(_.text := "Cake", _.checked := true, _.valueState := ValueState.Warning)
)
+ //-- End
),
- DemoPanel(
- "CheckBox states",
+ DemoPanel("CheckBox states")(
+ //-- Begin: CheckBox states
div(
for {
valueState <- ValueState.allValues
@@ -38,9 +42,10 @@ object CheckBoxExample extends Example("CheckBox") {
_.checked := true
)
)
+ //-- End
),
- DemoPanel(
- "CheckBox with Text Wrapping",
+ DemoPanel("CheckBox with Text Wrapping")(
+ //-- Begin: CheckBox with Text Wrapping
div(
CheckBox(
_.text := "ui5-checkbox with 'wrapping-type=Normal' set and some long text.",
@@ -53,39 +58,40 @@ object CheckBoxExample extends Example("CheckBox") {
_ => width := "200px"
)
)
+ //-- End
),
- DemoPanel(
- "CheckBox with indeterminate", {
- val texts = List("English", "German", "French")
+ DemoPanel("CheckBox with indeterminate") {
+ //-- Begin: CheckBox with indeterminate
+ val texts = List("English", "German", "French")
- /** [[Var]] containing the state of all checkboxes. The few following lines help to keep that in sync. */
- val textsStatusesVar = Var(Map("English" -> true, "German" -> false, "French" -> false))
+ /** [[Var]] containing the state of all checkboxes. The few following lines help to keep that in sync. */
+ val textsStatusesVar = Var(Map("English" -> true, "German" -> false, "French" -> false))
- val textsStatusesUpdater = textsStatusesVar.updater[(String, Boolean)](_ + _)
- val textsStatusesAllUpdater = textsStatusesVar.updater[Boolean]((map, b) => map.map((key, _) => key -> b))
+ val textsStatusesUpdater = textsStatusesVar.updater[(String, Boolean)](_ + _)
+ val textsStatusesAllUpdater = textsStatusesVar.updater[Boolean]((map, b) => map.map((key, _) => key -> b))
- val numberOfCheckedSignal = textsStatusesVar.signal.map(_.values.count(identity))
+ val numberOfCheckedSignal = textsStatusesVar.signal.map(_.values.count(identity))
- div(
+ div(
+ CheckBox(
+ _.text := "Select / deselect all",
+ _.indeterminate <-- numberOfCheckedSignal.map(count => count != 0 && count != 3),
+ _.checked <-- numberOfCheckedSignal.map(_ > 0),
+ // mapToChecked is possible but a bit hacky
+ _.events.onChange.mapToChecked --> textsStatusesAllUpdater
+ ),
+ hr(),
+ texts.map(text =>
CheckBox(
- _.text := "Select / deselect all",
- _.indeterminate <-- numberOfCheckedSignal.map(count => count != 0 && count != 3),
- _.checked <-- numberOfCheckedSignal.map(_ > 0),
- // mapToChecked is possible but a bit hacky
- _.events.onChange.mapToChecked --> textsStatusesAllUpdater
- ),
- hr(),
- texts.map(text =>
- CheckBox(
- _.text := text,
- _.checked <-- textsStatusesVar.signal.map(_(text)),
- // map(_.target.checked) is completely typesafe
- _.events.onChange.map(_.target.checked).map(text -> _) --> textsStatusesUpdater
- )
+ _.text := text,
+ _.checked <-- textsStatusesVar.signal.map(_(text)),
+ // map(_.target.checked) is completely typesafe
+ _.events.onChange.map(_.target.checked).map(text -> _) --> textsStatusesUpdater
)
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/ColourPaletteExample.scala b/demo/src/main/scala/demo/ColourPaletteExample.scala
index 86b07a1..4a982cb 100644
--- a/demo/src/main/scala/demo/ColourPaletteExample.scala
+++ b/demo/src/main/scala/demo/ColourPaletteExample.scala
@@ -3,11 +3,12 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
import be.doeraene.webcomponents.ui5.scaladsl.colour.Colour
object ColourPaletteExample extends Example("ColourPalette") {
+ //-- Begin Common
def someColourPaletteItems = List(
Colour.fromString("darkblue"),
Colour.fromString("pink"),
@@ -22,11 +23,15 @@ object ColourPaletteExample extends Example("ColourPalette") {
Colour.fromIntColour(0x5480e7),
Colour.fromIntColour(0xff6699)
).map(colour => ColourPalette.item(_.value := colour))
+ //-- End Common
- def component: HtmlElement = div(
- DemoPanel(
- "Colour Palette",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Colour Palette")(
+ //-- Begin: Colour Palette
ColourPalette(_ => someColourPaletteItems)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/ColourPalettePopoverExample.scala b/demo/src/main/scala/demo/ColourPalettePopoverExample.scala
index aa79707..e1b2a02 100644
--- a/demo/src/main/scala/demo/ColourPalettePopoverExample.scala
+++ b/demo/src/main/scala/demo/ColourPalettePopoverExample.scala
@@ -3,43 +3,51 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
import be.doeraene.webcomponents.ui5.scaladsl.colour.Colour
import org.scalajs.dom.HTMLElement
import com.raquo.airstream.eventbus.EventBus
object ColourPalettePopoverExample extends Example("ColourPalettePopover") {
- private def colourPalettePopoverExample(withExtraFeatures: Boolean) = {
- val openPopoverBus: EventBus[HTMLElement] = new EventBus
- div(
- Button(
- _ => "Open ColourPalettePopover",
- _.events.onClick.mapToEvent.map(_.target) --> openPopoverBus.writer
- ),
- ColourPalettePopover(
- _ =>
- inContext(el =>
- openPopoverBus.events.map(el.ref -> _) --> Observer[(ColourPalettePopover.Ref, HTMLElement)](_ showAt _)
- ),
- _ => ColourPaletteExample.someColourPaletteItems,
- _.showRecentColours := withExtraFeatures,
- _.showMoreColours := withExtraFeatures,
- _.showDefaultColour := withExtraFeatures,
- _.defaultColour := Colour.green
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Color Palette Popover with recent colors, default color and more colors features") {
+ //-- Begin: Color Palette Popover with recent colors, default color and more colors features
+ val openPopoverBus: EventBus[HTMLElement] = new EventBus
+ div(
+ Button(
+ _ => "Open ColourPalettePopover",
+ _.events.onClick.mapToEvent.map(_.target) --> openPopoverBus.writer
+ ),
+ ColourPalettePopover(
+ _.showAtFromEvents(openPopoverBus.events),
+ _ => ColourPaletteExample.someColourPaletteItems,
+ _.showRecentColours := true,
+ _.showMoreColours := true,
+ _.showDefaultColour := true,
+ _.defaultColour := Colour.green
+ )
)
- )
- }
-
- def component: HtmlElement = div(
- DemoPanel(
- "Color Palette Popover with recent colors, default color and more colors features",
- colourPalettePopoverExample(true)
- ),
- DemoPanel(
- "Color Palette Popover without any additional features",
- colourPalettePopoverExample(false)
- )
+ //-- End
+ },
+ DemoPanel("Color Palette Popover without any additional features") {
+ //-- Begin: Color Palette Popover without any additional features
+ val openPopoverBus: EventBus[HTMLElement] = new EventBus
+ div(
+ Button(
+ _ => "Open ColourPalettePopover",
+ _.events.onClick.mapToEvent.map(_.target) --> openPopoverBus.writer
+ ),
+ ColourPalettePopover(
+ _.showAtFromEvents(openPopoverBus.events),
+ _ => ColourPaletteExample.someColourPaletteItems,
+ _.defaultColour := Colour.green
+ )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/ColourPickerExample.scala b/demo/src/main/scala/demo/ColourPickerExample.scala
index 678c49f..f3c8a84 100644
--- a/demo/src/main/scala/demo/ColourPickerExample.scala
+++ b/demo/src/main/scala/demo/ColourPickerExample.scala
@@ -3,26 +3,28 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
import be.doeraene.webcomponents.ui5.scaladsl.colour.Colour
object ColourPickerExample extends Example("ColourPicker") {
- def component: HtmlElement = div(
- DemoPanel(
- "Pick colour", {
- val maybeChosenColourVar: Var[Option[Colour]] = Var(Option.empty)
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Pick colour") {
+ //-- Begin: Pick Colour
+ val maybeChosenColourVar: Var[Option[Colour]] = Var(Option.empty)
+ div(
+ ColourPicker(_.events.onChange.map(_.target.colour).map(Some(_)) --> maybeChosenColourVar.writer),
div(
- ColourPicker(_.events.onChange.map(_.target.colour).map(Some(_)) --> maybeChosenColourVar.writer),
- div(
- child.text <-- maybeChosenColourVar.signal.map {
- case Some(colour) => s"You have chosen colour ${colour.rgba}."
- case None => "Chose a colour."
- }
- )
+ child.text <-- maybeChosenColourVar.signal.map {
+ case Some(colour) => s"You have chosen colour ${colour.rgba}."
+ case None => "Chose a colour."
+ }
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/ComboBoxExample.scala b/demo/src/main/scala/demo/ComboBoxExample.scala
index 65c93a9..3fbab56 100644
--- a/demo/src/main/scala/demo/ComboBoxExample.scala
+++ b/demo/src/main/scala/demo/ComboBoxExample.scala
@@ -3,7 +3,7 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
object ComboBoxExample extends Example("ComboBox") {
@@ -19,24 +19,28 @@ object ComboBoxExample extends Example("ComboBox") {
private val someOtherCountries =
List("Argentina", "Australia", "Austria", "Barhain", "Belgium", "Brazil", "Canada", "Chile")
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Example",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Example")(
+ //-- Begin: Basic Example
div(
ValueState.allValues.map(valueState =>
ComboBox(_.placeholder := "Enter value", _.valueState := valueState, someItems)
)
)
+ //-- End
),
- DemoPanel(
- "Disabled and Readonly properties",
+ DemoPanel("Disabled and Readonly properties")(
+ //-- Begin: Disabled and Readonly properties
div(
ComboBox(_.value := "Disabled", _.disabled := true, someItems),
ComboBox(_.value := "Readonly", _.readonly := true, someItems)
)
+ //-- End
),
- DemoPanel(
- "Filters (StartsWithPerTerm(default), StartsWith, Contains)",
+ DemoPanel("Filters (StartsWithPerTerm(default), StartsWith, Contains)")(
+ //-- Begin: Filters (StartsWithPerTerm(default), StartsWith, Contains)
div(
ComboBoxFilter.allValues.map(filter =>
ComboBox(
@@ -46,9 +50,10 @@ object ComboBoxExample extends Example("ComboBox") {
)
)
)
+ //-- End
),
- DemoPanel(
- "ComboBox with Two-Column Layout Items",
+ DemoPanel("ComboBox with Two-Column Layout Items")(
+ //-- Begin: ComboBox with Two-Column Layout Items
ComboBox(
_.placeholder := "Two-column layout",
_ =>
@@ -61,9 +66,10 @@ object ComboBoxExample extends Example("ComboBox") {
)
)
)
+ //-- End
),
- DemoPanel(
- "ComboBox with Grouping of Items",
+ DemoPanel("ComboBox with Grouping of Items")(
+ //-- Begin: ComboBox with Grouping of Items
ComboBox(
_.placeholder := "ComboBox with grouping of suggestions",
_ =>
@@ -76,6 +82,7 @@ object ComboBoxExample extends Example("ComboBox") {
)
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/DatePickerExample.scala b/demo/src/main/scala/demo/DatePickerExample.scala
index c4851d1..5e949a0 100644
--- a/demo/src/main/scala/demo/DatePickerExample.scala
+++ b/demo/src/main/scala/demo/DatePickerExample.scala
@@ -3,44 +3,49 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
import java.time.LocalDate
object DatePickerExample extends Example("DatePicker") {
- def component: HtmlElement = div(
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
MessageStrip(
_.design := MessageStripDesign.Warning,
_ =>
"Note: you must provide an implementation for java.time classes in order to use the DatePicker. " +
"For example using the scala-java-time library."
),
- DemoPanel(
- "Basic DatePicker",
+ DemoPanel("Basic DatePicker")(
+ //-- Begin: Basic DatePicker
DatePicker()
+ //-- End
),
- DemoPanel(
- "DatePicker with Placeholder, Tooltip, Events, ValueState and valueStateMessage", {
- val validityStateBus: EventBus[Boolean] = new EventBus
- DatePicker(
- _.placeholder := "Delivery date...",
- _.slots.valueStateMessage := div("The value is not valid. Please provide a valid value."),
- _.valueState <-- validityStateBus.events.map(if _ then ValueState.None else ValueState.Error),
- _.events.onChange.map(_.detail.valid) --> validityStateBus.writer
- )
- }
- ),
- DemoPanel(
- "DatePicker with Minimum and Maximum Date - 1/1/2020 - 4/5/2020 format-pattern='yyyy-MM-dd'",
+ DemoPanel("DatePicker with Placeholder, Tooltip, Events, ValueState and valueStateMessage") {
+ //-- Begin: DatePicker with Placeholder, Tooltip, Events, ValueState and valueStateMessage
+ val validityStateBus: EventBus[Boolean] = new EventBus
+ DatePicker(
+ _.placeholder := "Delivery date...",
+ _.slots.valueStateMessage := div("The value is not valid. Please provide a valid value."),
+ _.valueState <-- validityStateBus.events.map(if _ then ValueState.None else ValueState.Error),
+ _.events.onChange.map(_.detail.valid) --> validityStateBus.writer
+ )
+ //-- End
+ },
+ DemoPanel("DatePicker with Minimum and Maximum Date - 1/1/2020 - 4/5/2020 format-pattern='yyyy-MM-dd'")(
+ //-- Begin: DatePicker with Minimum and Maximum Date - 1/1/2020 - 4/5/2020 format-pattern='yyyy-MM-dd'
DatePicker(
_.formatPattern := "yyyy-MM-dd",
_.minDate := LocalDate.of(2020, 1, 1),
_.maxDate := LocalDate.of(2020, 5, 4)
)
+ //-- End
),
- DemoPanel(
- "DatePicker with Japanese Calendar Type",
+ DemoPanel("DatePicker with Japanese Calendar Type")(
+ //-- Begin: DatePicker with Japanese Calendar Type
DatePicker(_.primaryCalendarType := CalendarType.Japanese)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/DateRangePickerExample.scala b/demo/src/main/scala/demo/DateRangePickerExample.scala
index f60d586..bcf39df 100644
--- a/demo/src/main/scala/demo/DateRangePickerExample.scala
+++ b/demo/src/main/scala/demo/DateRangePickerExample.scala
@@ -3,10 +3,38 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
object DateRangePickerExample extends Example("DateRangePicker") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic DateRangePicker") {
+ //-- Begin: Basic DateRangePicker
+ DateRangePicker()
+ //-- End
+ },
+ DemoPanel("DateRangePicker with Minimum and Maximum Date - 1/1/2020 - 4/5/2020 format-pattern='dd/MM/yyyy'") {
+ //-- Begin: DateRangePicker with Minimum and Maximum Date - 1/1/2020 - 4/5/2020 format-pattern='dd/MM/yyyy'
+ DateRangePicker(_.minDateRaw := "1/1/2020", _.maxDateRaw := "4/5/2020", _.formatPattern := "dd/MM/yyyy")
+ //-- End
+ },
+ DemoPanel("DateRangePicker with format-pattern='long'") {
+ //-- Begin: DateRangePicker with format-pattern='long'
+ DateRangePicker(_.formatPattern := "long")
+ //-- End
+ },
+ DemoPanel("Disabled DateRangePicker") {
+ //-- Begin: Disabled DateRangePicker
+ DateRangePicker(_.disabled := true, _.value := "Mar 31, 2021 - Apr 9, 2021")
+ //-- End
+ },
+ DemoPanel("Readonly DateRangePicker") {
+ //-- Begin: Readonly DateRangePicker
+ DateRangePicker(_.readonly := true, _.value := "Mar 31, 2021 - Apr 9, 2021")
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/DateTimePickerExample.scala b/demo/src/main/scala/demo/DateTimePickerExample.scala
index d9a2e46..2648eae 100644
--- a/demo/src/main/scala/demo/DateTimePickerExample.scala
+++ b/demo/src/main/scala/demo/DateTimePickerExample.scala
@@ -3,10 +3,55 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import java.time.format.DateTimeFormatter
+import java.time.LocalDateTime
+import scala.util.Try
object DateTimePickerExample extends Example("DateTimePicker") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("DateTimePicker") {
+ //-- Begin: DateTimePicker
+ val localDateTimeFormatter = DateTimeFormatter.ofPattern("d MMM y, HH:mm:ss")
+ val valueUpdateBus: EventBus[LocalDateTime] = new EventBus
+ div(
+ Label(_ =>
+ child.text <-- valueUpdateBus.events.startWithNone.map {
+ case None => "No value selected yet"
+ case Some(dateTime) => s"Selected value: $dateTime"
+ }
+ ),
+ br(),
+ DateTimePicker(
+ _.events.onInput
+ .map(_.detail.value)
+ // Wrap in Try because input triggers even if element is not valid
+ .map(value => Try(LocalDateTime.parse(value, localDateTimeFormatter)).toOption)
+ .collect { case Some(dateTime) => dateTime } --> valueUpdateBus.writer,
+ _.events.onChange
+ .map(_.detail.value) // no need for Try shenanigans, change does not trigger for invalid values
+ .map(LocalDateTime.parse(_, localDateTimeFormatter)) --> valueUpdateBus.writer
+ )
+ )
+ //-- End
+ },
+ DemoPanel("DateTimePicker with format-pattern") {
+ //-- Begin: DateTimePicker with format-pattern
+ div(
+ DateTimePicker(_.formatPattern := "dd/MM/yyyy, hh:mm aa", _.value := "13/04/2020, 09:16 AM"),
+ DateTimePicker(_.formatPattern := "yyyy-MM-dd-hh:mm:ss aa", _.value := "2020-04-13-04:16:16 AM"),
+ DateTimePicker(_.formatPattern := "dd/MM/yyyy, hh:mm:ss aa", _.value := "13/04/2020, 03:16:16 AM")
+ )
+ //-- End
+ },
+ DemoPanel("DateTimePicker in states") {
+ //-- Begin: DateTimePicker in states
+ div(ValueState.allValues.map(state => DateTimePicker(_.valueState := state)))
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/DialogExample.scala b/demo/src/main/scala/demo/DialogExample.scala
index 088d5f4..3b53268 100644
--- a/demo/src/main/scala/demo/DialogExample.scala
+++ b/demo/src/main/scala/demo/DialogExample.scala
@@ -3,106 +3,108 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object DialogExample extends Example("Dialog") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Dialog", {
- val openDialogBus: EventBus[Boolean] = new EventBus
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Dialog") {
+ //-- Begin: Basic Dialog
+ val openDialogBus: EventBus[Boolean] = new EventBus
+ div(
+ styleTagForLoginFormClass,
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Open Dialog",
+ _.events.onClick.mapTo(true) --> openDialogBus.writer
+ ),
div(
- styleTagForLoginFormClass,
- Button(
- _.design := ButtonDesign.Emphasized,
- _ => "Open Dialog",
- _.events.onClick.mapTo(true) --> openDialogBus.writer
- ),
- div(
- MessageStrip(
- _.design := MessageStripDesign.Information,
- _ =>
- "The opening of this dialog works using an `EventBus`. " +
- "Clicking on the 'Open Dialog' button writes to the bus, and the Dialog listens to it."
- )
- ),
- Dialog(
- _ =>
- inContext(el => openDialogBus.events --> Observer[Boolean](if _ then el.ref.show() else el.ref.close())),
- _.headerText := "Register Form",
- _ =>
- section(
- className := loginFormClass,
- div(
- Label(_.forId := "username", _.required := true, _ => "Username:"),
- Input(_.id := "username")
- ),
- div(
- Label(_.forId := "password", _.required := true, _ => "Password:"),
- Input(_.id := "password", _.tpe := InputType.Password, _.valueState := ValueState.Error)
- ),
- div(
- Label(_.forId := "email", _.required := true, _ => "Email:"),
- Input(_.id := "email", _.tpe := InputType.Email)
- )
- ),
- _.slots.footer := div(
- div(flex := "1"),
- Button(
- _.design := ButtonDesign.Emphasized,
- _ => "Register",
- _.onClick.mapTo(false) --> openDialogBus.writer
- )
- )
- )
- )
- }
- ),
- DemoPanel(
- "Draggable and Resizable Dialog", {
- val dialogId = "the-dialog-id"
- div(
- Button(
- _ => "Open Draggable/Resizable dialog",
- _.events.onClick.mapTo(Dialog.getDialogById(dialogId)) --> Observer[Option[Dialog.Ref]] {
- case Some(dialog) => dialog.show()
- case None => throw new IllegalStateException(s"The Dialog with id $dialogId does not exist.")
- }
- ),
MessageStrip(
_.design := MessageStripDesign.Information,
- _ => "The opening of this dialog works by finding the dialog by id (with `Dialog.getDialogById`)."
- ),
- Dialog(
- _.id := dialogId,
- _.headerText := "Draggable/Resizable dialog",
_ =>
- section(
- "Resize this dialog by dragging it by its resize handle.",
- br(),
- "This feature is available only on Desktop",
- br(),
- "Move this dialog around the screen by dragging it by its header",
- br(),
- "This feature is available only on Desktop"
+ "The opening of this dialog works using an `EventBus`. " +
+ "Clicking on the 'Open Dialog' button writes to the bus, and the Dialog listens to it."
+ )
+ ),
+ Dialog(
+ _.showFromEvents(openDialogBus.events.filter(identity).mapTo(())),
+ _.closeFromEvents(openDialogBus.events.map(!_).filter(identity).mapTo(())),
+ _.headerText := "Register Form",
+ _ =>
+ section(
+ className := loginFormClass,
+ div(
+ Label(_.forId := "username", _.required := true, _ => "Username:"),
+ Input(_.id := "username")
+ ),
+ div(
+ Label(_.forId := "password", _.required := true, _ => "Password:"),
+ Input(_.id := "password", _.tpe := InputType.Password, _.valueState := ValueState.Error)
),
- _.slots.footer := div(
- div(flex := "1"),
- Button(
- _.design := ButtonDesign.Emphasized,
- _ => "Close",
- _.onClick.mapTo(Dialog.getDialogById(dialogId)) --> Observer[Option[Dialog.Ref]] {
- case Some(dialog) => dialog.close()
- case None => throw new IllegalStateException(s"Dialog with id $dialogId does not exist.")
- }
+ div(
+ Label(_.forId := "email", _.required := true, _ => "Email:"),
+ Input(_.id := "email", _.tpe := InputType.Email)
)
),
- _.draggable := true,
- _.resizable := true
+ _.slots.footer := div(
+ div(flex := "1"),
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Register",
+ _.events.onClick.mapTo(false) --> openDialogBus.writer
+ )
)
)
- }
- )
+ )
+ //-- End
+ },
+ DemoPanel("Draggable and Resizable Dialog") {
+ //-- Begin: Draggable and Resizable Dialog
+ val dialogId = "the-dialog-id"
+ div(
+ Button(
+ _ => "Open Draggable/Resizable dialog",
+ _.events.onClick.mapTo(Dialog.getDialogById(dialogId)) --> Observer[Option[Dialog.Ref]] {
+ case Some(dialog) => dialog.show()
+ case None => throw new IllegalStateException(s"The Dialog with id $dialogId does not exist.")
+ }
+ ),
+ MessageStrip(
+ _.design := MessageStripDesign.Information,
+ _ => "The opening of this dialog works by finding the dialog by id (with `Dialog.getDialogById`)."
+ ),
+ Dialog(
+ _.id := dialogId,
+ _.headerText := "Draggable/Resizable dialog",
+ _ =>
+ section(
+ "Resize this dialog by dragging it by its resize handle.",
+ br(),
+ "This feature is available only on Desktop",
+ br(),
+ "Move this dialog around the screen by dragging it by its header",
+ br(),
+ "This feature is available only on Desktop"
+ ),
+ _.slots.footer := div(
+ div(flex := "1"),
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Close",
+ _.events.onClick.mapTo(Dialog.getDialogById(dialogId)) --> Observer[Option[Dialog.Ref]] {
+ case Some(dialog) => dialog.close()
+ case None => throw new IllegalStateException(s"Dialog with id $dialogId does not exist.")
+ }
+ )
+ ),
+ _.draggable := true,
+ _.resizable := true
+ )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/DynamicSideContentExample.scala b/demo/src/main/scala/demo/DynamicSideContentExample.scala
index 72678ff..162fcc9 100644
--- a/demo/src/main/scala/demo/DynamicSideContentExample.scala
+++ b/demo/src/main/scala/demo/DynamicSideContentExample.scala
@@ -3,10 +3,123 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object DynamicSideContentExample extends Example("DynamicSideContent") {
- def component: HtmlElement = missing
+ val mainContent =
+ "103.1. At the start of a game, the players determine which one of them will choose who takes the first turn. In " +
+ "the first game of a match (including a single-game match), the players may use any mutually agreeable method " +
+ "(flipping a coin, rolling dice, etc.) to do so. In a match of several games, the loser of the previous game " +
+ "chooses who takes the first turn. If the previous game was a draw, the player who made the choice in that game" +
+ " makes the choice in this game. The player chosen to take the first turn is the starting player. The game’s " +
+ "default turn order begins with the starting player and proceeds clockwise."
+
+ val sideContent =
+ "103.4. Each player draws a number of cards equal to their starting hand size, which is normally seven. (Some " +
+ "effects can modify a player’s starting hand size.) A player who is dissatisfied with their initial hand may " +
+ "take a mulligan. First, the starting player declares whether they will take a mulligan. Then each other player " +
+ "in turn order does the same. Once each player has made a declaration, all players who decided to take " +
+ "mulligans do so at the same time. To take a mulligan, a player shuffles the cards in their hand back into their " +
+ "library, draws a new hand of cards equal to their starting hand size, then puts a number of those cards equal " +
+ "to the number of times that player has taken a mulligan on the bottom of their library in any order. Once a " +
+ "player chooses not to take a mulligan, the remaining cards become that player’s opening hand, and that player " +
+ "may not take any further mulligans. This process is then repeated until no player takes a mulligan. A player" +
+ " can take mulligans until their opening hand would be zero cards, after which they may not take further" +
+ " mulligans."
+
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Dynamic Side Content with default properties") {
+ //-- Begin: Dynamic Side Content with default properties
+ DynamicSideContent(
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content with hideMainContent set") {
+ //-- Begin: Dynamic Side Content with hideMainContent set
+ DynamicSideContent(
+ _.hideMainContent := true,
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content with hideSideContent set") {
+ //-- Begin: Dynamic Side Content with hideSideContent set
+ DynamicSideContent(
+ _.hideSideContent := true,
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content with equalSplit set") {
+ //-- Begin: Dynamic Side Content with equalSplit set
+ DynamicSideContent(
+ _.equalSplit := true,
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content with sideContentPosition='Start'") {
+ //-- Begin: Dynamic Side Content with sideContentPosition='Start'
+ DynamicSideContent(
+ _.sideContentPosition := SideContentPosition.Start,
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content with sideContentFallDown='BelowXL'") {
+ //-- Begin: Dynamic Side Content with sideContentFallDown='BelowXL'
+ DynamicSideContent(
+ _.sideContentFallDown := SideContentFallDown.BelowXL,
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content with sideContentVisibility='ShowAboveM'") {
+ //-- Begin: Dynamic Side Content with sideContentVisibility='ShowAboveM'
+ DynamicSideContent(
+ _.sideContentVisibility := SideContentVisibility.ShowAboveM,
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ )
+ //-- End
+ },
+ DemoPanel("Dynamic Side Content - toggle contents on mobile device (S size)") {
+ //-- Begin: Dynamic Side Content - toggle contents on mobile device (S size)
+ val toggleContentsBus: EventBus[Unit] = new EventBus
+
+ Page(
+ _ => height := "500px",
+ _ => maxWidth := "360px",
+ _.floatingFooter := true,
+ _.hideFooter := false,
+ //_ => maxWidth := "360px",
+ _ =>
+ DynamicSideContent(
+ _ => inContext(el => toggleContentsBus.events --> Observer[Any](_ => el.ref.toggleContents())),
+ _ => div(h1("Main Content"), p(mainContent)),
+ _.slots.sideContent := div(h1("Side Content"), p(sideContent))
+ ),
+ _.slots.footer := Bar(
+ _.design := BarDesign.FloatingFooter,
+ _.slots.endContent := Button(
+ _.design := ButtonDesign.Positive,
+ _ => "Toggle Contents",
+ _.events.onClick.mapTo(()) --> toggleContentsBus.writer
+ )
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/EntryPoint.scala b/demo/src/main/scala/demo/EntryPoint.scala
index d05b230..f00cb6b 100644
--- a/demo/src/main/scala/demo/EntryPoint.scala
+++ b/demo/src/main/scala/demo/EntryPoint.scala
@@ -8,13 +8,17 @@ import demo.helpers.Example
object EntryPoint {
def main(args: Array[String]): Unit = {
+ ResponsivePopover
val componentsDemo: List[Example] = List(
AvatarExample,
+ AvatarGroupExample,
BadgeExample,
BarExample,
+ BarcodeScannerDialogExample,
BreadcrumbsExample,
BusyIndicatorExample,
ButtonExample,
+ CalendarExample,
CardExample,
CarouselExample,
CheckBoxExample,
@@ -87,25 +91,29 @@ object EntryPoint {
else
div(
display := "flex",
- SideNavigation(
- _.events.onSelectionChange.map(_.detail.item.dataset.get("componentName")) --> Observer[Option[String]] {
- case Some(componentName) => dom.document.location.href = s"/$componentName"
- case None => throw new IllegalArgumentException(s"This item did not have data 'componentName'.")
- },
- _ =>
- componentsDemo.map(example =>
- SideNavigation.item(
- _.text := example.name,
- _ => width := "200px",
- _ => dataAttr("component-name") := example.name,
- _.selected := (example.name == componentName)
- )
- ),
- _ => height := "100vh",
- _ => overflowY := "auto"
+ div(
+ width := "300px",
+ SideNavigation(
+ _.events.onSelectionChange.map(_.detail.item.dataset.get("componentName")) --> Observer[Option[String]] {
+ case Some(componentName) => dom.document.location.href = s"/$componentName"
+ case None => throw new IllegalArgumentException(s"This item did not have data 'componentName'.")
+ },
+ _ =>
+ componentsDemo.map(example =>
+ SideNavigation.item(
+ _.text := example.name,
+ _ => width := "200px",
+ _ => dataAttr("component-name") := example.name,
+ _.selected := (example.name == componentName)
+ )
+ ),
+ _ => height := "100vh",
+ _ => overflowY := "auto"
+ )
),
div(
padding := "10px",
+ maxWidth := "calc(100% - 320px)",
componentsDemo.find(_.name == componentName).map(_.completeComponent).getOrElse(div("Not Found"))
)
)
diff --git a/demo/src/main/scala/demo/FileUploaderExample.scala b/demo/src/main/scala/demo/FileUploaderExample.scala
index 5108dec..8f9ed82 100644
--- a/demo/src/main/scala/demo/FileUploaderExample.scala
+++ b/demo/src/main/scala/demo/FileUploaderExample.scala
@@ -3,10 +3,73 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import org.scalajs.dom
object FileUploaderExample extends Example("FileUploader") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Upload multiple images") {
+ //-- Begin: Upload multiple images
+ // contains the selected images. Starts with None before the user chose files
+ val selectedImagesVar: Var[Option[List[dom.File]]] = Var(None)
+
+ div(
+ div(
+ FileUploader(
+ _.accept := List("image/*"),
+ _.multiple := true,
+ _.events.onChange.map(_.target.files) --> selectedImagesVar.writer.contramapSome,
+ _ => Button(_ => "Upload Images", _.icon := IconName.upload)
+ )
+ ),
+ div(
+ className := "results-container",
+ styleTag("""
+ |.results-container > img {
+ | margin: 0.5rem;
+ |}
+ |""".stripMargin),
+ children <-- selectedImagesVar.signal.changes.collect { case Some(files) => files }.map {
+ case Nil => List(span("No files selected."))
+ case files =>
+ files.map { file =>
+ img(
+ widthAttr := 100,
+ heightAttr := 100,
+ src := dom.URL.createObjectURL(file),
+ inContext(el => onLoad --> Observer[Any](_ => dom.URL.revokeObjectURL(el.ref.src)))
+ )
+ }
+ }
+ )
+ )
+ //-- End
+ },
+ DemoPanel("File Uploader With No Input") {
+ //-- Begin: File Uploader With No Input
+ FileUploader(_.hideInput := true, _ => Button(_ => "Upload File"))
+ //-- End
+ },
+ DemoPanel("Custom File Uploaders") {
+ //-- Begin: Custom File Uploaders
+ div(
+ FileUploader(_.hideInput := true, _ => Avatar(_.icon := IconName.upload)),
+ FileUploader(_.hideInput := true, _ => Badge(_ => "Upload File"))
+ )
+ //-- End
+ },
+ DemoPanel("Button With Icon File Uploader") {
+ //-- Begin: Button With Icon File Uploader
+ div(
+ FileUploader(_ => Button(_.icon := IconName.upload, _ => "Upload")),
+ FileUploader(_ => Button(_.icon := IconName.upload, _.iconEnd := true, _ => "Upload")),
+ FileUploader(_ => Button(_.icon := IconName.upload, _.iconOnly := true))
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/FlexibleColumnLayoutExample.scala b/demo/src/main/scala/demo/FlexibleColumnLayoutExample.scala
index 69cd7c8..6a114ff 100644
--- a/demo/src/main/scala/demo/FlexibleColumnLayoutExample.scala
+++ b/demo/src/main/scala/demo/FlexibleColumnLayoutExample.scala
@@ -3,34 +3,36 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
object FlexibleColumnLayoutExample extends Example("FlexibleColumnLayout") {
import MTG.{cards, Card}
- def component: HtmlElement = div(
- DemoPanel(
- "FlexibleColumnLayout - One Initial Column", {
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("FlexibleColumnLayout - One Initial Column") {
+ //-- Begin: FlexibleColumnLayout - One Initial Column
- /** Feed in here whatever layout you want to give to the FlexibleColumnLayout */
- val layoutBus: EventBus[FCLLayout] = new EventBus
+ /** Feed in here whatever layout you want to give to the FlexibleColumnLayout */
+ val layoutBus: EventBus[FCLLayout] = new EventBus
- val maybeSelectedCardVar = Var(Option.empty[Card])
+ val maybeSelectedCardVar = Var(Option.empty[Card])
- def startColumnListItem(card: Card): HtmlElement = UList.Li(
- _ => card.name,
- _.description := card.tpe,
- _.additionalText := s"Cost: ${card.cost}",
- _.iconEnd := true,
- _.icon := IconName.`slim-arrow-right`,
- _ => dataAttr("card-name") := card.name
- )
+ def startColumnListItem(card: Card): HtmlElement = UList.item(
+ _ => card.name,
+ _.description := card.tpe,
+ _.additionalText := s"Cost: ${card.cost}",
+ _.iconEnd := true,
+ _.icon := IconName.`slim-arrow-right`,
+ _ => dataAttr("card-name") := card.name
+ )
- def cardFromName(name: String): Option[Card] = cards.find(_.name == name)
+ def cardFromName(name: String): Option[Card] = cards.find(_.name == name)
- div(
- styleTag("""
+ div(
+ styleTag("""
|:host([description]) .ui5-li-root {
| padding: 1rem;
|}
@@ -45,47 +47,47 @@ object FlexibleColumnLayoutExample extends Example("FlexibleColumnLayout") {
| box-sizing: border-box;
|}
|""".stripMargin),
- FlexibleColumnLayout(
- _.layout <-- layoutBus.events.startWith(FCLLayout.OneColumn),
- _.slots.startColumn := div(
- ShellBar(_.primaryTitle := "Magic"),
- UList(
- _ => height := "500px",
- _.headerText := "Power Nine",
- _ => cards.filter(_.comment == "Power Nine").map(startColumnListItem),
- _.events.onItemClick
- .map(event =>
- for {
- cardName <- event.detail.item.dataset.get("cardName")
- card <- cardFromName(cardName)
- } yield card
- ) --> maybeSelectedCardVar.writer
- )
- ),
- _.slots.midColumn <-- maybeSelectedCardVar.signal.changes.collect { case Some(card) => card }.map { card =>
+ FlexibleColumnLayout(
+ _.layout <-- layoutBus.events.startWith(FCLLayout.OneColumn),
+ _.slots.startColumn := div(
+ ShellBar(_.primaryTitle := "Magic"),
+ UList(
+ _ => height := "500px",
+ _.headerText := "Power Nine",
+ _ => cards.filter(_.comment == "Power Nine").map(startColumnListItem),
+ _.events.onItemClick
+ .map(event =>
+ for {
+ cardName <- event.detail.item.dataset.get("cardName")
+ card <- cardFromName(cardName)
+ } yield card
+ ) --> maybeSelectedCardVar.writer
+ )
+ ),
+ _.slots.midColumn <-- maybeSelectedCardVar.signal.changes.collect { case Some(card) => card }.map { card =>
+ div(
div(
- div(
- display := "flex",
- alignItems := "center",
- Button(
- _.icon := IconName.`slim-arrow-left`,
- _.events.onClick.mapTo(Option.empty[Card]) --> maybeSelectedCardVar.writer,
- _ => marginRight := "1em",
- _.design := ButtonDesign.Transparent
- ),
- h1(card.name)
+ display := "flex",
+ alignItems := "center",
+ Button(
+ _.icon := IconName.`slim-arrow-left`,
+ _.events.onClick.mapTo(Option.empty[Card]) --> maybeSelectedCardVar.writer,
+ _ => marginRight := "1em",
+ _.design := ButtonDesign.Transparent
),
- img(src := MTG.cardImages(card.name))
- )
- },
- _ =>
- maybeSelectedCardVar.signal.changes.map(maybeCard =>
- if maybeCard.isDefined then FCLLayout.TwoColumnsMidExpanded else FCLLayout.OneColumn
- ) --> layoutBus.writer
- )
+ h1(card.name)
+ ),
+ img(src := MTG.cardImages(card.name))
+ )
+ },
+ _ =>
+ maybeSelectedCardVar.signal.changes.map(maybeCard =>
+ if maybeCard.isDefined then FCLLayout.TwoColumnsMidExpanded else FCLLayout.OneColumn
+ ) --> layoutBus.writer
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/IconExample.scala b/demo/src/main/scala/demo/IconExample.scala
index 799ca36..e00f8ab 100644
--- a/demo/src/main/scala/demo/IconExample.scala
+++ b/demo/src/main/scala/demo/IconExample.scala
@@ -3,19 +3,20 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object IconExample extends Example("Icon") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Icons",
- div(
- IconName.allValues.take(10).map(name => Icon(_.name := name, _ => marginRight := "5px"))
- )
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Icons")(
+ //-- Begin: Basic Icons
+ div(IconName.allValues.take(10).map(name => Icon(_.name := name, _ => marginRight := "5px")))
+ //-- End
),
- DemoPanel(
- "Customized Icons",
+ DemoPanel("Customized Icons")(
+ //-- Begin: Customized Icons
div(
IconName.allValues.reverse
.take(3)
@@ -34,6 +35,7 @@ object IconExample extends Example("Icon") {
)
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/IllustratedMessageExample.scala b/demo/src/main/scala/demo/IllustratedMessageExample.scala
index 4dece9f..2cb1942 100644
--- a/demo/src/main/scala/demo/IllustratedMessageExample.scala
+++ b/demo/src/main/scala/demo/IllustratedMessageExample.scala
@@ -3,13 +3,15 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object IllustratedMessageExample extends Example("IllustratedMessage") {
- def component: HtmlElement = div(
- DemoPanel(
- "Illustrated Message",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Illustrated Message")(
+ //-- Begin: Illustrated Message
IllustratedMessage(
_.name := IllustrationMessageType.PageNotFound,
_ =>
@@ -18,31 +20,32 @@ object IllustratedMessageExample extends Example("IllustratedMessage") {
Button(_ => "Action 2")
)
)
+ //-- End
),
- DemoPanel(
- "Illustrated Message in dialog", {
- val dialogShowActionBus: EventBus[Boolean] = new EventBus
- div(
- Button(_ => "Open Dialog", _.events.onClick.mapTo(true) --> dialogShowActionBus.writer),
- Dialog(
- _.headerText := "Error",
- _ =>
- inContext(el =>
- dialogShowActionBus.events --> Observer[Boolean](if _ then el.ref.show() else el.ref.close())
- ),
- _ => IllustratedMessage(_.name := IllustrationMessageType.ErrorScreen),
- _.slots.footer := Bar(
- _.design := BarDesign.Footer,
- _.slots.endContent := Button(
- _.design := ButtonDesign.Emphasized,
- _ => "Close",
- _.events.onClick.mapTo(false) --> dialogShowActionBus.writer
- )
+ DemoPanel("Illustrated Message in dialog") {
+ //-- Begin: Illustrated Message in dialog
+ val dialogShowActionBus: EventBus[Boolean] = new EventBus
+ div(
+ Button(_ => "Open Dialog", _.events.onClick.mapTo(true) --> dialogShowActionBus.writer),
+ Dialog(
+ _.headerText := "Error",
+ _ =>
+ inContext(el =>
+ dialogShowActionBus.events --> Observer[Boolean](if _ then el.ref.show() else el.ref.close())
+ ),
+ _ => IllustratedMessage(_.name := IllustrationMessageType.ErrorScreen),
+ _.slots.footer := Bar(
+ _.design := BarDesign.Footer,
+ _.slots.endContent := Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Close",
+ _.events.onClick.mapTo(false) --> dialogShowActionBus.writer
)
)
)
- }
- )
+ )
+ //-- End
+ }
// todo: uncomment this example when we have a high enough sap ui5 version.
// DemoPanel(
// "Illustrated Message with link in subtitle",
diff --git a/demo/src/main/scala/demo/InputExample.scala b/demo/src/main/scala/demo/InputExample.scala
index c828f91..1e99e80 100644
--- a/demo/src/main/scala/demo/InputExample.scala
+++ b/demo/src/main/scala/demo/InputExample.scala
@@ -3,108 +3,113 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object InputExample extends Example("Input") {
private val countries = List("Argentina", "Belgium", "Bulgaria", "Canada", "Columbia", "Croatia", "Denmark")
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Input",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Input")(
+ //-- Begin: Basic Input
div(
Input(_.showClearIcon := true, _.value := "Input"),
Input(_.readonly := true, _.value := "Readonly Input"),
Input(_.disabled := true, _.value := "Disabled Input")
)
+ //-- End
),
- DemoPanel(
- "Input With Suggestions (note: the usage depends on the framework you are using)", {
+ DemoPanel("Input With Suggestions (note: the usage depends on the framework you are using)") {
+ //-- Begin: Input With Suggestions (note: the usage depends on the framework you are using)
- val filterValueBus: EventBus[String] = new EventBus
+ val filterValueBus: EventBus[String] = new EventBus
- val suggestions =
- filterValueBus.events
- .map(input =>
- if input.trim.isEmpty then Nil
- else countries.filter(_.toLowerCase.contains(input.toLowerCase))
- )
- .startWith(Nil)
- .map(
- _.map(country =>
- Input.suggestion(
- _.icon := IconName.world,
- _.additionalText := "explore",
- _.additionalTextState := ValueState.Success,
- _.description := "travel the world",
- _.text := country
- )
+ val suggestions =
+ filterValueBus.events
+ .map(input =>
+ if input.trim.isEmpty then Nil
+ else countries.filter(_.toLowerCase.contains(input.toLowerCase))
+ )
+ .startWith(Nil)
+ .map(
+ _.map(country =>
+ Input.suggestion(
+ _.icon := IconName.world,
+ _.additionalText := "explore",
+ _.additionalTextState := ValueState.Success,
+ _.description := "travel the world",
+ _.text := country
)
)
+ )
- Input(
- _.showSuggestions := true,
- _.showClearIcon := true,
- _.placeholder := "Start typing country name",
- _ => children <-- suggestions,
- _.events.onInput.mapToValue --> filterValueBus.writer
- )
- }
- ),
- DemoPanel(
- "Input with Value State",
+ Input(
+ _.showSuggestions := true,
+ _.showClearIcon := true,
+ _.placeholder := "Start typing country name",
+ _ => children <-- suggestions,
+ _.events.onInput.mapToValue --> filterValueBus.writer
+ )
+ //-- End
+ },
+ DemoPanel("Input with Value State")(
+ //-- Begin: Input with Value State
div(ValueState.allValues.map(state => Input(_.value := state.value, _.valueState := state)))
+ //-- End
),
- DemoPanel(
- "Input with Suggestions and Value State message",
+ DemoPanel("Input with Suggestions and Value State message")(
+ //-- Begin: Input with Suggestions and Value State message
div(
Input(
_.placeholder := "Choose content density",
_.showSuggestions := true,
_.slots.valueStateMessage := div("This is an error message. Extra long text used as an error message."),
- _ => List("Cozy", "Compact", "Condensed").map(item => UList.Li(_ => item))
+ _ => List("Cozy", "Compact", "Condensed").map(item => UList.item(_ => item))
)
)
+ //-- End
),
- DemoPanel(
- "Input as Search Field", {
- val searchCriteriaVar: Var[String] = Var("")
+ DemoPanel("Input as Search Field") {
+ //-- Begin: Input as Search Field
+ val searchCriteriaVar: Var[String] = Var("")
- val showSearchResultBus: EventBus[Unit] = new EventBus
- val closeSearchResultBus: EventBus[Unit] = new EventBus
+ val showSearchResultBus: EventBus[Unit] = new EventBus
+ val closeSearchResultBus: EventBus[Unit] = new EventBus
- div(
- Input(
- _.placeholder := "Enter search criteria",
- _ => width := "100%",
- _.events.onInput.mapToValue --> searchCriteriaVar.writer,
- _.slots.icon := Icon(_.name := IconName.search, _ => onClick.mapTo(()) --> showSearchResultBus.writer)
+ div(
+ Input(
+ _.placeholder := "Enter search criteria",
+ _ => width := "100%",
+ _.events.onInput.mapToValue --> searchCriteriaVar.writer,
+ _.slots.icon := Icon(_.name := IconName.search, _ => onClick.mapTo(()) --> showSearchResultBus.writer)
+ ),
+ Dialog(
+ _.headerText := "Search result",
+ _ =>
+ child <-- showSearchResultBus.events
+ .sample(searchCriteriaVar.signal)
+ .map(searchCriteria => div(s"Here would go the results of search for '$searchCriteria'.")),
+ _.slots.footer := div(
+ Bar(
+ _.slots.endContent := Button(_ => "Close", _.events.onClick.mapTo(()) --> closeSearchResultBus.writer),
+ _.design := BarDesign.Footer
+ )
),
- Dialog(
- _.headerText := "Search result",
- _ =>
- child <-- showSearchResultBus.events
- .sample(searchCriteriaVar.signal)
- .map(searchCriteria => div(s"Here would go the results of search for '$searchCriteria'.")),
- _.slots.footer := div(
- Bar(
- _.slots.endContent := Button(_ => "Close", _.events.onClick.mapTo(()) --> closeSearchResultBus.writer),
- _.design := BarDesign.Footer
+ _ =>
+ inContext(el =>
+ showSearchResultBus.events.sample(searchCriteriaVar.signal).filter(_.nonEmpty).mapTo(()) --> Observer(_ =>
+ el.ref.show()
)
),
- _ =>
- inContext(el =>
- showSearchResultBus.events.sample(searchCriteriaVar.signal).filter(_.nonEmpty).mapTo(()) --> Observer(
- _ => el.ref.show()
- )
- ),
- _ => inContext(el => closeSearchResultBus.events --> Observer(_ => el.ref.close()))
- )
+ _ => inContext(el => closeSearchResultBus.events --> Observer(_ => el.ref.close()))
)
- }
- ),
- DemoPanel(
- "Input with Label",
+ )
+ //-- End
+ },
+ DemoPanel("Input with Label")(
+ //-- Begin: Input with Label
div(
className := loginFormClass,
styleTagForLoginFormClass,
@@ -123,6 +128,7 @@ object InputExample extends Example("Input") {
)
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/LabelExample.scala b/demo/src/main/scala/demo/LabelExample.scala
index 48b7db6..bb72db6 100644
--- a/demo/src/main/scala/demo/LabelExample.scala
+++ b/demo/src/main/scala/demo/LabelExample.scala
@@ -3,17 +3,35 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object LabelExample extends Example("Label") {
- def component: HtmlElement = div(
- DemoPanel("Basic Label", Label(_ => "Simple Label")),
- DemoPanel("Required Label", Label(_ => "Required Label", _.required := true)),
- DemoPanel("Required Label With Colon", Label(_ => "Required Label", _.required := true, _.showColon := true)),
- DemoPanel("Truncated Label", Label(_ => width := "200px", _ => "Long labels are truncated by default.")),
- DemoPanel(
- "Label 'for'",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Label")(
+ //-- Begin: Basic label
+ Label(_ => "Simple Label")
+ //-- End
+ ),
+ DemoPanel("Required Label")(
+ //-- Begin: Required Label
+ Label(_ => "Required Label", _.required := true)
+ //-- End
+ ),
+ DemoPanel("Required Label With Colon")(
+ //-- Begin: Required Label With Colon
+ Label(_ => "Required Label", _.required := true, _.showColon := true)
+ //-- End
+ ),
+ DemoPanel("Truncated Label")(
+ //-- Begin: Truncated Label
+ Label(_ => width := "200px", _ => "Long labels are truncated by default.")
+ //-- End
+ ),
+ DemoPanel("Label 'for'")(
+ //-- Begin: Label 'for'
div(
className := loginFormClass,
styleTagForLoginFormClass,
@@ -66,6 +84,7 @@ object LabelExample extends Example("Label") {
CheckBox(_.id := "myCB")
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/LinkExample.scala b/demo/src/main/scala/demo/LinkExample.scala
index 87e8336..1193894 100644
--- a/demo/src/main/scala/demo/LinkExample.scala
+++ b/demo/src/main/scala/demo/LinkExample.scala
@@ -3,13 +3,15 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object LinkExample extends Example("Link") {
- def component: HtmlElement = div(
- DemoPanel(
- "Different Link Designs",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Different Link Designs")(
+ //-- Begin: Different Link Designs
div(
LinkDesign.allValues.map(design =>
Link(
@@ -22,6 +24,7 @@ object LinkExample extends Example("Link") {
),
Link(_.href := "https://www.scala-js.org/", _.target := LinkTarget._blank, _.disabled := true, _ => "Disabled")
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/ListExample.scala b/demo/src/main/scala/demo/ListExample.scala
index 63eb3a0..e489458 100644
--- a/demo/src/main/scala/demo/ListExample.scala
+++ b/demo/src/main/scala/demo/ListExample.scala
@@ -3,39 +3,41 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object ListExample extends Example("List") {
private val countries = List("Argentina", "Belgium", "Bulgaria", "Canada", "Columbia", "Croatia", "Denmark")
- def component: HtmlElement = div(
- DemoPanel(
- "Basic List",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic List")(
+ //-- Begin: Basic List
UList(
_ => width := "100%",
- _.Li(
+ _.item(
_.icon := IconName.`nutrition-activity`,
_.description := "Tropical plant with an edible fruit",
_.additionalText := "In-stock",
_.additionalTextState := ValueState.Success,
_ => "Pineapple"
),
- _.Li(
+ _.item(
_.icon := IconName.`nutrition-activity`,
_.description := "Occurs between red and yellow",
_.additionalText := "Expires",
_.additionalTextState := ValueState.Warning,
_ => "Orange"
),
- _.Li(
+ _.item(
_.icon := IconName.`nutrition-activity`,
_.description := "The yellow lengthy fruit",
_.additionalText := "Re-stock",
_.additionalTextState := ValueState.Information,
_ => "Blueberry"
),
- _.Li(
+ _.item(
_.icon := IconName.`nutrition-activity`,
_.description := "The tropical stone fruit",
_.additionalText := "Re-stock",
@@ -43,132 +45,139 @@ object ListExample extends Example("List") {
_ => "Mango"
)
)
+ //-- End
),
- DemoPanel(
- "List with growing='Scroll'", {
- val fruits = LazyList.from(0).map { fruitIndex =>
- val additionalTextState = ValueState.allValues(fruitIndex % ValueState.allValues.size)
- UList.Li(
- _.icon := IconName.`nutrition-activity`,
- _.description := s"This is the description of fruit $fruitIndex",
- _.additionalText := additionalTextState.value,
- _.additionalTextState := additionalTextState,
- _ => s"Fruit $fruitIndex"
- )
- }
-
- val listGrowingBus: EventBus[Unit] = new EventBus
- val numberOfFruitsToDisplaySignal = listGrowingBus.events.delay(200).mapTo(5).foldLeft(5)(_ + _)
-
- val fruitsToDisplaySignal = numberOfFruitsToDisplaySignal.map(fruits.take)
-
- UList(
- _.events.onLoadMore.mapTo(()) --> listGrowingBus.writer,
- _ => children <-- fruitsToDisplaySignal,
- _ => height := "300px",
- _.growing := ListGrowingMode.Scroll,
- _.headerText <-- numberOfFruitsToDisplaySignal.map(numberOfFruits =>
- s"List of fruits (currently $numberOfFruits displayed)."
- )
+ DemoPanel("List with growing='Scroll'") {
+ //-- Begin: List with growing='Scroll'
+ val fruits = LazyList.from(0).map { fruitIndex =>
+ val additionalTextState = ValueState.allValues(fruitIndex % ValueState.allValues.size)
+ UList.item(
+ _.icon := IconName.`nutrition-activity`,
+ _.description := s"This is the description of fruit $fruitIndex",
+ _.additionalText := additionalTextState.value,
+ _.additionalTextState := additionalTextState,
+ _ => s"Fruit $fruitIndex"
)
}
- ),
- DemoPanel(
- "List in Single-selection Mode", {
- val maybeSelectedCountryVar: Var[Option[String]] = Var(Option.empty)
- UList(
- _.mode := ListMode.SingleSelect,
- _.headerText <-- maybeSelectedCountryVar.signal.map(maybeCountry =>
- s"Select a country:${maybeCountry.fold("")(country => s" (selected: $country)")}"
- ),
- _.events.onSelectionChange
- .map(_.detail.maybeSelectedItem.flatMap(_.dataset.get("countryName"))) --> maybeSelectedCountryVar.writer,
- _ =>
- countries.map { country =>
- val isInactive = country == countries.last
- UList.Li(
- _ => country ++ (if isInactive then " (Item with 'type' set to 'Inactive')" else ""),
- _ => dataAttr("country-name") := country,
- _.tpe.maybe(Option.when(isInactive)(ListItemType.Inactive))
- )
- }
- )
- }
- ),
- DemoPanel(
- "List in Multi-selection Mode", {
- val selectedItemsVar: Var[List[String]] = Var(List.empty)
- val selectedItemsInfoSignal = selectedItemsVar.signal.map {
- case Nil => "No selected items"
- case items => s"Selected items: ${items.mkString(", ")}"
- }
+ val listGrowingBus: EventBus[Unit] = new EventBus
+ val numberOfFruitsToDisplaySignal = listGrowingBus.events.delay(200).mapTo(5).foldLeft(5)(_ + _)
- UList(
- _.mode := ListMode.MultiSelect,
- _.headerText <-- selectedItemsInfoSignal.map(selectedItems =>
- s"Multiple selection is possible: ($selectedItems)"
- ),
- _ => countries.map(country => UList.Li(_ => country, _ => dataAttr("country-name") := country)),
- _.events.onSelectionChange.map(
- _.detail.selectedItems.flatMap(_.dataset.get("countryName"))
- ) --> selectedItemsVar.writer
- )
+ val fruitsToDisplaySignal = numberOfFruitsToDisplaySignal.map(fruits.take)
- }
- ),
- DemoPanel("Buzy List", UList(_.busy := true, _.headerText := "Fetching data...")),
- DemoPanel(
- "List with GroupHeaders", {
- def expansionListItem(expansion: String) = (_: UList.type).Li(
- _.iconEnd := true,
- _.icon := IconName.`slim-arrow-right`,
- _ => expansion
- )
- UList(
- _.mode := ListMode.MultiSelect,
- _.headerText := "Expansion list",
- _.group(_ => "Mirrodin Block"),
- expansionListItem("Mirrodin"),
- expansionListItem("Darksteel"),
- expansionListItem("Fifth Dawn"),
- _.group(_ => "Kamigawa Block"),
- expansionListItem("Champions of Kamigawa"),
- expansionListItem("Betrayers of Kamigawa"),
- expansionListItem("Saviors of Kamigawa"),
- _.group(_ => "Ravnica Block"),
- expansionListItem("Ravnica: City of Guilds"),
- expansionListItem("Guildpact"),
- expansionListItem("Dissension")
+ UList(
+ _.events.onLoadMore.mapTo(()) --> listGrowingBus.writer,
+ _ => children <-- fruitsToDisplaySignal,
+ _ => height := "300px",
+ _.growing := ListGrowingMode.Scroll,
+ _.headerText <-- numberOfFruitsToDisplaySignal.map(numberOfFruits =>
+ s"List of fruits (currently $numberOfFruits displayed)."
)
+ )
+ //-- End
+ },
+ DemoPanel("List in Single-selection Mode") {
+ //-- Begin: List in Single-selection Mode
+ val maybeSelectedCountryVar: Var[Option[String]] = Var(Option.empty)
+
+ UList(
+ _.mode := ListMode.SingleSelect,
+ _.headerText <-- maybeSelectedCountryVar.signal.map(maybeCountry =>
+ s"Select a country:${maybeCountry.fold("")(country => s" (selected: $country)")}"
+ ),
+ _.events.onSelectionChange
+ .map(_.detail.maybeSelectedItem.flatMap(_.dataset.get("countryName"))) --> maybeSelectedCountryVar.writer,
+ _ =>
+ countries.map { country =>
+ val isInactive = country == countries.last
+ UList.item(
+ _ => country ++ (if isInactive then " (Item with 'type' set to 'Inactive')" else ""),
+ _ => dataAttr("country-name") := country,
+ _.tpe.maybe(Option.when(isInactive)(ListItemType.Inactive))
+ )
+ }
+ )
+ //-- End
+ },
+ DemoPanel("List in Multi-selection Mode") {
+ //-- Begin: List in Multi-selection Mode
+ val selectedItemsVar: Var[List[String]] = Var(List.empty)
+ val selectedItemsInfoSignal = selectedItemsVar.signal.map {
+ case Nil => "No selected items"
+ case items => s"Selected items: ${items.mkString(", ")}"
}
+
+ UList(
+ _.mode := ListMode.MultiSelect,
+ _.headerText <-- selectedItemsInfoSignal.map(selectedItems =>
+ s"Multiple selection is possible: ($selectedItems)"
+ ),
+ _ => countries.map(country => UList.item(_ => country, _ => dataAttr("country-name") := country)),
+ _.events.onSelectionChange.map(
+ _.detail.selectedItems.flatMap(_.dataset.get("countryName"))
+ ) --> selectedItemsVar.writer
+ )
+ //-- End
+ },
+ DemoPanel("Buzy List")(
+ //-- Begin: Buzy List
+ UList(_.busy := true, _.headerText := "Fetching data...")
+ //-- End
),
- DemoPanel(
- "List in Delete Mode",
+ DemoPanel("List with GroupHeaders") {
+ //-- Begin: List with GroupHeaders
+ def expansionListItem(expansion: String) = (_: UList.type).item(
+ _.iconEnd := true,
+ _.icon := IconName.`slim-arrow-right`,
+ _ => expansion
+ )
+ UList(
+ _.mode := ListMode.MultiSelect,
+ _.headerText := "Expansion list",
+ _.group(_ => "Mirrodin Block"),
+ expansionListItem("Mirrodin"),
+ expansionListItem("Darksteel"),
+ expansionListItem("Fifth Dawn"),
+ _.group(_ => "Kamigawa Block"),
+ expansionListItem("Champions of Kamigawa"),
+ expansionListItem("Betrayers of Kamigawa"),
+ expansionListItem("Saviors of Kamigawa"),
+ _.group(_ => "Ravnica Block"),
+ expansionListItem("Ravnica: City of Guilds"),
+ expansionListItem("Guildpact"),
+ expansionListItem("Dissension")
+ )
+ //-- End
+ },
+ DemoPanel("List in Delete Mode")(
+ //-- Begin: List in Delete Mode
UList(
_.mode := ListMode.Delete,
_.headerText := "Note: The list items removal is up to application developers",
- _ => countries.map(country => UList.Li(_ => country))
+ _ => countries.map(country => UList.item(_ => country))
)
+ //-- End
),
- DemoPanel(
- "List with No Data",
+ DemoPanel("List with No Data")(
+ //-- Begin: List with No Data
UList(_.headerText := "Products", _.noDataText := "No Data Available", _.separators := ListSeparator.None)
+ //-- End
),
- DemoPanel(
- "List Item Speration Types",
+ DemoPanel("List Item Speration Types")(
+ //-- Begin: List Item Speration Types
div(
UList(
_.headerText := "No separators",
_.separators := ListSeparator.None,
- _ => countries.take(3).map(country => UList.Li(_ => country, _.icon := IconName.world))
+ _ => countries.take(3).map(country => UList.item(_ => country, _.icon := IconName.world))
),
UList(
_.headerText := "Inner separators",
_.separators := ListSeparator.Inner,
- _ => countries.drop(3).take(3).map(country => UList.Li(_ => country, _.icon := IconName.`hello-world`))
+ _ => countries.drop(3).take(3).map(country => UList.item(_ => country, _.icon := IconName.`hello-world`))
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/MediaGalleryExample.scala b/demo/src/main/scala/demo/MediaGalleryExample.scala
index 849ea10..0b20315 100644
--- a/demo/src/main/scala/demo/MediaGalleryExample.scala
+++ b/demo/src/main/scala/demo/MediaGalleryExample.scala
@@ -3,10 +3,50 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object MediaGalleryExample extends Example("MediaGallery") {
- def component: HtmlElement = missing
+ //-- Begin Common
+ def fiveMagicWallpapers = List(
+ "https://media.magic.wizards.com/images/wallpaper/senseis-divining-top-2x2-background-1280x960.jpg",
+ "https://media.magic.wizards.com/images/wallpaper/mana-vault-2x2-background-1280x960.jpg",
+ "https://media.magic.wizards.com/images/wallpaper/sparas_headquarters_kieran_yanner_1280x960_poozxbqpcw.jpg",
+ "https://media.magic.wizards.com/images/wallpaper/baldurs-gate-clb-background-1280x960.jpg",
+ "https://media.magic.wizards.com/images/wallpaper/1280x960-neo-ukiyo-e-plains.jpg"
+ ).map(link => MediaGallery.item(_ => img(src := link)))
+ //-- End Common
+
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Usage") {
+ //-- Begin: Usage
+ div(width := "800px", MediaGallery(_.showAllThumbnails := true, _ => fiveMagicWallpapers))
+ //-- End
+ },
+ DemoPanel("MediaGallery with vertical layout") {
+ //-- Begin: MediaGallery with vertical layout
+ div(
+ width := "800px",
+ MediaGallery(_.layout := MediaGalleryLayout.Vertical, _.showAllThumbnails := true, _ => fiveMagicWallpapers)
+ )
+ //-- End
+ },
+ DemoPanel("MediaGallery with thumbnails on the right") {
+ //-- Begin: MediaGallery with thumbnails on the right
+ div(
+ width := "800px",
+ MediaGallery(
+ _.layout := MediaGalleryLayout.Horizontal,
+ _.menuHorizontalAlign := MediaGalleryMenuHorizontalAlign.Right,
+ _.showAllThumbnails := true,
+ _ => fiveMagicWallpapers
+ )
+ )
+ //-- End
+ },
+ mtgImageWarning
+ )
}
diff --git a/demo/src/main/scala/demo/MenuExample.scala b/demo/src/main/scala/demo/MenuExample.scala
index 20fe4fa..fd2d257 100644
--- a/demo/src/main/scala/demo/MenuExample.scala
+++ b/demo/src/main/scala/demo/MenuExample.scala
@@ -3,73 +3,75 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom.HTMLElement
object MenuExample extends Example("Menu") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Menu", {
- // feed the bus to open the menu at the fed element
- val openMenuBus: EventBus[HTMLElement] = new EventBus
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Menu") {
+ //-- Begin: Basic Menu
+ // feed the bus to open the menu at the fed element
+ val openMenuBus: EventBus[HTMLElement] = new EventBus
- div(
- Button(_ => "Open Menu", _.events.onClick.map(_.target) --> openMenuBus.writer),
- Menu(
- _ => inContext(el => openMenuBus.events.map(el.ref -> _) --> Observer[(Menu.Ref, HTMLElement)](_ showAt _)),
- _.item(_.text := "New File", _.icon := IconName.`add-document`),
- _.item(_.text := "New Folder", _.icon := IconName.`add-folder`, _.disabled := true),
- _.item(_.text := "Open", _.icon := IconName.`open-folder`, _.startsSection := true),
- _.item(_.text := "Close"),
- _.item(_.text := "Preferences", _.icon := IconName.`action-settings`, _.startsSection := true),
- _.item(_.text := "Exit", _.icon := IconName.`journey-arrive`)
- )
+ div(
+ Button(_ => "Open Menu", _.events.onClick.map(_.target) --> openMenuBus.writer),
+ Menu(
+ _ => inContext(el => openMenuBus.events.map(el.ref -> _) --> Observer[(Menu.Ref, HTMLElement)](_ showAt _)),
+ _.item(_.text := "New File", _.icon := IconName.`add-document`),
+ _.item(_.text := "New Folder", _.icon := IconName.`add-folder`, _.disabled := true),
+ _.item(_.text := "Open", _.icon := IconName.`open-folder`, _.startsSection := true),
+ _.item(_.text := "Close"),
+ _.item(_.text := "Preferences", _.icon := IconName.`action-settings`, _.startsSection := true),
+ _.item(_.text := "Exit", _.icon := IconName.`journey-arrive`)
)
- }
- ),
- DemoPanel(
- "Menu with Sub-menu items", {
- // feed the bus to open the menu at the fed element
- val openMenuBus: EventBus[HTMLElement] = new EventBus
+ )
+ //-- End
+ },
+ DemoPanel("Menu with Sub-menu items") {
+ //-- Begin: Menu with Sub-menu items
+ // feed the bus to open the menu at the fed element
+ val openMenuBus: EventBus[HTMLElement] = new EventBus
- div(
- Button(_ => "Open Menu", _.events.onClick.map(_.target) --> openMenuBus.writer),
- Menu(
- _ => inContext(el => openMenuBus.events.map(el.ref -> _) --> Observer[(Menu.Ref, HTMLElement)](_ showAt _)),
- _.item(_.text := "New File", _.icon := IconName.`add-document`),
- _.item(_.text := "New Folder", _.icon := IconName.`add-folder`, _.disabled := true),
+ div(
+ Button(_ => "Open Menu", _.events.onClick.map(_.target) --> openMenuBus.writer),
+ Menu(
+ _ => inContext(el => openMenuBus.events.map(el.ref -> _) --> Observer[(Menu.Ref, HTMLElement)](_ showAt _)),
+ _.item(_.text := "New File", _.icon := IconName.`add-document`),
+ _.item(_.text := "New Folder", _.icon := IconName.`add-folder`, _.disabled := true),
+ _.item(
+ _.text := "Open",
+ _.icon := IconName.`open-folder`,
+ _.startsSection := true,
_.item(
- _.text := "Open",
+ _.text := "Open Locally",
_.icon := IconName.`open-folder`,
- _.startsSection := true,
- _.item(
- _.text := "Open Locally",
- _.icon := IconName.`open-folder`,
- _.item(_.text := "Open from C"),
- _.item(_.text := "Open from D"),
- _.item(_.text := "Open from E")
- ),
- _.item(_.text := "Open from Cloud")
+ _.item(_.text := "Open from C"),
+ _.item(_.text := "Open from D"),
+ _.item(_.text := "Open from E")
),
+ _.item(_.text := "Open from Cloud")
+ ),
+ _.item(
+ _.text := "Save",
+ _.icon := IconName.save,
_.item(
- _.text := "Save",
+ _.text := "Save Locally",
_.icon := IconName.save,
- _.item(
- _.text := "Save Locally",
- _.icon := IconName.save,
- _.item(_.text := "Save from C", _.icon := IconName.save),
- _.item(_.text := "Save from D", _.icon := IconName.save),
- _.item(_.text := "Save from E", _.icon := IconName.save)
- )
- ),
- _.item(_.text := "Close"),
- _.item(_.text := "Preferences", _.icon := IconName.`action-settings`, _.startsSection := true),
- _.item(_.text := "Exit", _.icon := IconName.`journey-arrive`)
- )
+ _.item(_.text := "Save from C", _.icon := IconName.save),
+ _.item(_.text := "Save from D", _.icon := IconName.save),
+ _.item(_.text := "Save from E", _.icon := IconName.save)
+ )
+ ),
+ _.item(_.text := "Close"),
+ _.item(_.text := "Preferences", _.icon := IconName.`action-settings`, _.startsSection := true),
+ _.item(_.text := "Exit", _.icon := IconName.`journey-arrive`)
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/MessageStripExample.scala b/demo/src/main/scala/demo/MessageStripExample.scala
index 37d93b2..60f4d57 100644
--- a/demo/src/main/scala/demo/MessageStripExample.scala
+++ b/demo/src/main/scala/demo/MessageStripExample.scala
@@ -3,14 +3,16 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom.HTMLElement
object MessageStripExample extends Example("MessageStrip") {
- def component: HtmlElement = div(
- DemoPanel(
- "MessageStrip",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("MessageStrip")(
+ //-- Begin: MessageStrip
div(
MessageStripDesign.allValues.map { design =>
val closeBus: EventBus[Unit] = new EventBus
@@ -27,9 +29,10 @@ object MessageStripExample extends Example("MessageStrip") {
)
}
)
+ //-- End
),
- DemoPanel(
- "MessageStrip With No Close Button",
+ DemoPanel("MessageStrip With No Close Button")(
+ //-- Begin: MessageStrip With No Close Button
div(
MessageStripDesign.allValues.map(design =>
MessageStrip(
@@ -39,9 +42,10 @@ object MessageStripExample extends Example("MessageStrip") {
)
)
)
+ //-- End
),
- DemoPanel(
- "MessageStrip With No Icon",
+ DemoPanel("MessageStrip With No Icon")(
+ //-- Begin: MessageStrip With No Icon
div(
MessageStripDesign.allValues.map { design =>
val closeBus: EventBus[Unit] = new EventBus
@@ -59,25 +63,26 @@ object MessageStripExample extends Example("MessageStrip") {
)
}
)
+ //-- End
),
- DemoPanel(
- "Dynamic Message Strip Generator", {
- val clickedBus: EventBus[Unit] = new EventBus
- val numberOfClicks = clickedBus.events.mapTo(1).foldLeft(0)(_ + _).changes
- div(
- Button(_ => "Generate MessageStrip", _.events.onClick.mapTo(()) --> clickedBus.writer),
- child <-- numberOfClicks.map(count =>
- MessageStrip(
- _.design := MessageStripDesign.allValues(count % MessageStripDesign.allValues.size),
- _ => s"You clicked $count times.",
- _.hideCloseButton := true
- )
+ DemoPanel("Dynamic Message Strip Generator") {
+ //-- Begin: Dynamic Message Strip Generator
+ val clickedBus: EventBus[Unit] = new EventBus
+ val numberOfClicks = clickedBus.events.mapTo(1).foldLeft(0)(_ + _).changes
+ div(
+ Button(_ => "Generate MessageStrip", _.events.onClick.mapTo(()) --> clickedBus.writer),
+ child <-- numberOfClicks.map(count =>
+ MessageStrip(
+ _.design := MessageStripDesign.allValues(count % MessageStripDesign.allValues.size),
+ _ => s"You clicked $count times.",
+ _.hideCloseButton := true
)
)
- }
- ),
- DemoPanel(
- "Custom MessageStrip",
+ )
+ //-- End
+ },
+ DemoPanel("Custom MessageStrip")(
+ //-- Begin: Custom MessageStrip
div(
MessageStrip(
_.design := MessageStripDesign.Information,
@@ -116,6 +121,7 @@ object MessageStripExample extends Example("MessageStrip") {
)
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/MultiComboBoxExample.scala b/demo/src/main/scala/demo/MultiComboBoxExample.scala
index 75497b7..1d41b79 100644
--- a/demo/src/main/scala/demo/MultiComboBoxExample.scala
+++ b/demo/src/main/scala/demo/MultiComboBoxExample.scala
@@ -3,23 +3,26 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object MultiComboBoxExample extends Example("MultiComboBox") {
private val countries = List("Argentina", "Belgium", "Bulgaria", "Canada", "Columbia", "Croatia", "Denmark")
- def component: HtmlElement = div(
- DemoPanel(
- "Basic MultiComboBox",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic MultiComboBox")(
+ //-- Begin: Basic MultiComboBox"
div(
MultiComboBox(_.placeholder := "Type your value", _.item(_.selected := true, _.text := "UI5")),
MultiComboBox(_.readonly := true, _.value := "Readonly combo", _.item(_.selected := true, _.text := "UI5")),
MultiComboBox(_.disabled := true, _.value := "Disabled combo", _.item(_.selected := true, _.text := "UI5"))
)
+ //-- End
),
- DemoPanel(
- "MultiComboBox with items",
+ DemoPanel("MultiComboBox with items")(
+ //-- Begin: MultiComboBox with items
MultiComboBox(
_.placeholder := "Choose your countries",
_ => width := "500px",
@@ -28,9 +31,10 @@ object MultiComboBoxExample extends Example("MultiComboBox") {
MultiComboBox.item(_.text := country, _.selected := (index == 0))
)
)
+ //-- End
),
- DemoPanel(
- "MultiComboBox with free text input",
+ DemoPanel("MultiComboBox with free text input")(
+ //-- Begin: MultiComboBox with free text input
MultiComboBox(
_.placeholder := "Choose your countries",
_ => width := "500px",
@@ -40,9 +44,10 @@ object MultiComboBoxExample extends Example("MultiComboBox") {
MultiComboBox.item(_.text := country, _.selected := (index % 3 == 0))
)
)
+ //-- End
),
- DemoPanel(
- "MultiComboBox with Value State",
+ DemoPanel("MultiComboBox with Value State")(
+ //-- Begin: MultiComboBox with Value State
div(
ValueState.allValues.filterNot(_ == ValueState.Information).filterNot(_ == ValueState.None).zipWithIndex.map {
(valueState, index) =>
@@ -52,7 +57,28 @@ object MultiComboBoxExample extends Example("MultiComboBox") {
)
}
)
- )
+ //-- End
+ ),
+ DemoPanel("MultiComboBox with Grouping of Items") {
+ //-- Begin: MultiComboBox with Grouping of Items
+ MultiComboBox(
+ _.placeholder := "Select a country",
+ _.group(_.text := "Asia"),
+ _.item(_.text := "Afghanistan"),
+ _.item(_.text := "China"),
+ _.item(_.text := "India"),
+ _.item(_.text := "Indonesia"),
+ _.group(_.text := "Europe"),
+ _.item(_.text := "Austria"),
+ _.item(_.text := "Belgium"),
+ _.item(_.text := "Germany"),
+ _.item(_.text := "Italy"),
+ _.group(_.text := "North America"),
+ _.item(_.text := "Canada"),
+ _.item(_.text := "United States")
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/MultiInputExample.scala b/demo/src/main/scala/demo/MultiInputExample.scala
index 87021a7..d5b836e 100644
--- a/demo/src/main/scala/demo/MultiInputExample.scala
+++ b/demo/src/main/scala/demo/MultiInputExample.scala
@@ -3,10 +3,106 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object MultiInputExample extends Example("MultiInput") {
- def component: HtmlElement = missing
+ //-- Begin Common
+ val countries = List("Argentina", "Belgium", "Bulgaria", "Canada", "Columbia", "Croatia", "Denmark")
+ //-- End Common
+
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ styleTagForLoginFormClass,
+ DemoPanel("Basic Multi Input") {
+ //-- Begin: Basic Multi Input
+ val firstValueVar = Var("basic input")
+ val secondValueVar = Var("value help icon")
+ div(
+ display := "flex",
+ className := loginFormClass,
+ div(
+ Label(
+ _.wrappingType := WrappingType.Normal,
+ _ => width := "200px",
+ _ => "MultiInput",
+ _ => child.text <-- firstValueVar.signal.map(value => s" (current value is $value)")
+ ),
+ MultiInput(
+ _.value <-- firstValueVar,
+ _.events.onInput.mapToValue --> firstValueVar.writer,
+ _.events.onChange.mapToValue --> firstValueVar.writer
+ )
+ ),
+ div(
+ Label(
+ _.wrappingType := WrappingType.Normal,
+ _ => width := "200px",
+ _ => "MultiInput",
+ _ => child.text <-- secondValueVar.signal.map(value => s" (current value is $value)")
+ ),
+ MultiInput(
+ _.showValueHelpIcon := true,
+ _.value <-- secondValueVar,
+ _.events.onInput.mapToValue --> secondValueVar.writer,
+ _.events.onChange.mapToValue --> secondValueVar.writer
+ )
+ )
+ )
+ //-- End
+ },
+ DemoPanel("Multi Input with tokens") {
+ //-- Begin: Multi Input with tokens
+ div(
+ div(
+ MultiInput(_.slots.tokens := MultiInput.token(_.text := "Bulgaria")),
+ MultiInput(_.slots.tokens := countries.map(country => MultiInput.token(_.text := country)))
+ ),
+ MessageStrip(
+ _.design := MessageStripDesign.Information,
+ _ => "These input are only there for display. They won't add tokens dynamically. See below for such example."
+ )
+ )
+ //-- End
+ },
+ DemoPanel("Multi Input and token creation onChange") {
+ //-- Begin: Multi Input and token creation onChange
+ val tokenValuesVar = Var(List("Argentina"))
+
+ val changeBus: EventBus[String] = new EventBus
+
+ // Emits the new values list, with whether or not they should be actually patched to the values
+ // This check is required because we want tokens to be unique
+ val newValuesWithShouldWeUpdate = changeBus.events
+ .withCurrentValueOf(tokenValuesVar.signal)
+ .map((newValue, previousValues) => (previousValues :+ newValue, !previousValues.contains(newValue)))
+
+ // emits when token must be changed
+ val newValuesChanges = newValuesWithShouldWeUpdate.collect { case (values, true) => values }
+
+ // When the new value was already present, we issue the error message ...
+ val valueStateBecomesErrorEvents = newValuesWithShouldWeUpdate.filter(!_._2).mapTo(ValueState.Error)
+ // ... and we clear it 2 seconds later
+ val valueStateBecomesNormalEvents = valueStateBecomesErrorEvents.delay(2000).mapTo(ValueState.None)
+
+ val valueStateChanges = EventStream.merge(valueStateBecomesErrorEvents, valueStateBecomesNormalEvents)
+
+ MultiInput(
+ _.showSuggestions := true,
+ _.valueState <-- valueStateChanges,
+ _ => width := "50%",
+ _.slots.valueStateMessage := div("Token is already in the list"),
+ _ => countries.map(country => MultiInput.suggestion(_.text := country)),
+ _.slots.tokens <-- tokenValuesVar.signal.map(_.map(tokenValue => MultiInput.token(_.text := tokenValue))),
+ _.events.onChange.map(_.target.value) --> changeBus.writer,
+ _ => newValuesChanges --> tokenValuesVar.writer,
+ _.events.onTokenDelete.map(_.detail.token.text) --> tokenValuesVar.updater((values, toRemove) =>
+ values.filterNot(_ == toRemove)
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/NotificationListGroupItemExample.scala b/demo/src/main/scala/demo/NotificationListGroupItemExample.scala
index 9af6f34..b37f7af 100644
--- a/demo/src/main/scala/demo/NotificationListGroupItemExample.scala
+++ b/demo/src/main/scala/demo/NotificationListGroupItemExample.scala
@@ -3,10 +3,52 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object NotificationListGroupItemExample extends Example("NotificationListGroupItem") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("NotificationListGroupItem") {
+ //-- Begin: NotificationListGroupItem
+ UList(
+ _.headerText := "Notifications grouped",
+ _.notificationGroup(
+ _.showClose := true,
+ _.showCounter := true,
+ _.priority := Priority.High,
+ _.titleText := "Some high priority notifications",
+ _.item(
+ _.showClose := true,
+ _.titleText := "Some notification was triggered!",
+ _.priority := Priority.High
+ ),
+ _.item(
+ _.showClose := true,
+ _.titleText := "Some other notification was triggered!",
+ _.priority := Priority.High
+ )
+ ),
+ _.notificationGroup(
+ _.showClose := true,
+ _.showCounter := true,
+ _.priority := Priority.Medium,
+ _.titleText := "Some medium priority notifications",
+ _.item(
+ _.showClose := true,
+ _.titleText := "Some medium notification was triggered!",
+ _.priority := Priority.Medium
+ ),
+ _.item(
+ _.showClose := true,
+ _.titleText := "Some other medium notification was triggered!",
+ _.priority := Priority.Medium
+ )
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/NotificationListItemExample.scala b/demo/src/main/scala/demo/NotificationListItemExample.scala
index 0dc297a..2355abe 100644
--- a/demo/src/main/scala/demo/NotificationListItemExample.scala
+++ b/demo/src/main/scala/demo/NotificationListItemExample.scala
@@ -3,10 +3,116 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
+import org.scalajs.dom
object NotificationListItemExample extends Example("NotificationListItem") {
- def component: HtmlElement = missing
+ // I know it's a bit sketchy to put the below thing common, but in this case it's ok, just need to make sure that
+ // the ids do not clash accross examples...
+ //-- Begin Common
+ // Each notification receives an id in its dataset, that is used to be removed when the delete icon is clicked
+ val closeItemsBus: EventBus[dom.HTMLElement] = new EventBus
+
+ val notifIdName = "notif-id"
+ // Modifiers to be given to the notification with specified id. It will hide the item on delete
+ def notifWithId(id: String): NotificationListItem.ModFunction = _ =>
+ List(
+ hidden <-- closeItemsBus.events.map(_.dataset("notifId")).filter(_ == id).mapTo(true).startWith(false),
+ dataAttr(notifIdName) := id
+ )
+ //-- End Common
+
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("NotificationListItem") {
+ //-- Begin: NotificationListItem
+ // Each notification receives an id in its dataset, that is used to be removed when the delete icon is clicked
+ val closeItemsBus: EventBus[dom.HTMLElement] = new EventBus
+
+ val notifIdName = "notif-id"
+ // Modifiers to be given to the notification with specified id. It will hide the item on delete
+ def notifWithId(id: String): NotificationListItem.ModFunction = _ =>
+ List(
+ hidden <-- closeItemsBus.events.map(_.dataset("notifId")).filter(_ == id).mapTo(true).startWith(false),
+ dataAttr(notifIdName) := id
+ )
+
+ UList(
+ _.headerText := "Notifications",
+ _.events.onItemClose.map(_.detail.item) --> closeItemsBus.writer,
+ _.notificationItem(
+ notifWithId("notif-1"),
+ _.showClose := true,
+ _.titleText := "New order (#2525) With a very long title - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.priority := Priority.High,
+ _ =>
+ "And with a very long description and long labels of the action buttons - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.slots.avatar := Avatar(_.size := AvatarSize.XS, _ => img(src := MTG.manaSymbolsRefs("W"))),
+ _.slots.footnotes := span("Monique Legrand"),
+ _.slots.footnotes := span("2 Days")
+ ),
+ _.notificationItem(
+ notifWithId("notif-2"),
+ _.showClose := true,
+ _.titleText := "New order (#2526) With a very long title - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.priority := Priority.High,
+ _ =>
+ "And with a very long description and long labels of the action buttons - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.slots.avatar := Avatar(_.size := AvatarSize.XS, _ => img(src := MTG.manaSymbolsRefs("B"))),
+ _.slots.footnotes := span("Alain Chevalier"),
+ _.slots.footnotes := span("2 Days")
+ )
+ )
+ //-- End
+ },
+ DemoPanel("NotificationListItem In ShellBar") {
+ //-- Begin: NotificationListItem In ShellBar
+ val openNotifPopoverBus: EventBus[dom.HTMLElement] = new EventBus
+ div(
+ ShellBar(
+ _.primaryTitle := "Corporate Portal",
+ _.slots.logo := img(src := "/images/avatars/scala-logo.png"),
+ _.showNotifications := true,
+ _.notificationsCount := "4",
+ _.events.onNotificationsClick.map(_.detail.targetRef) --> openNotifPopoverBus.writer
+ ),
+ Popover(
+ _ => inContext(el => openNotifPopoverBus.events.map(el.ref -> _) --> Popover.showAtObserver),
+ _ => maxWidth := "600px",
+ _.placementType := PopoverPlacementType.Bottom,
+ _.horizontalAlign := PopoverHorizontalAlign.Right,
+ _ =>
+ UList(
+ _.headerText := "Notifications",
+ _.events.onItemClose.map(_.detail.item) --> closeItemsBus.writer,
+ _.notificationItem(
+ notifWithId("notif-shellbar-1"),
+ _.showClose := true,
+ _.titleText := "New order (#2525) With a very long title - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.priority := Priority.High,
+ _ => "And with a very long description and long labels of the action buttons - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.slots.avatar := Avatar(_.size := AvatarSize.XS, _ => img(src := MTG.manaSymbolsRefs("W"))),
+ _.slots.footnotes := span("Monique Legrand"),
+ _.slots.footnotes := span("2 Days"),
+ _.slots.actions := NotificationListItem.action(_.icon := IconName.accept, _.text := "Accept")
+ ),
+ _.notificationItem(
+ notifWithId("notif-shellbar-2"),
+ _.showClose := true,
+ _.titleText := "New order (#2526) With a very long title - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.priority := Priority.High,
+ _ => "And with a very long description and long labels of the action buttons - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat, turpis vel scelerisque pharetra, tellus odio vehicula dolor, nec elementum lectus turpis at nunc.",
+ _.slots.avatar := Avatar(_.size := AvatarSize.XS, _ => img(src := MTG.manaSymbolsRefs("B"))),
+ _.slots.footnotes := span("Alain Chevalier"),
+ _.slots.footnotes := span("2 Days")
+ )
+ )
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/PageExample.scala b/demo/src/main/scala/demo/PageExample.scala
index 83b23cc..f259305 100644
--- a/demo/src/main/scala/demo/PageExample.scala
+++ b/demo/src/main/scala/demo/PageExample.scala
@@ -3,10 +3,51 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object PageExample extends Example("Page") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Page with floating footer") {
+ //-- Begin: Page with floating footer
+ Page(
+ _.floatingFooter := true,
+ _ => height := "500px",
+ _ => width := "500px",
+ _.slots.header := Bar(
+ _.design := BarDesign.Header,
+ _.slots.startContent := Button(_.tooltip := "Go Home", _.icon := IconName.home),
+ _.slots.endContent := Button(_.tooltip := "Settings", _.icon := IconName.`action-settings`),
+ _ => "Page Title"
+ ),
+ _ =>
+ div(
+ overflowY := "auto",
+ p(
+ "103.3. Each player begins the game with a starting life total of 20. Some variant games have different starting life totals."
+ ),
+ p("103.3a In a Two-Headed Giant game, each team’s starting life total is 30."),
+ p(
+ "103.3b In a Vanguard game, each player’s starting life total is 20 plus or minus the life modifier of their vanguard card."
+ ),
+ p("103.3c In a Commander game, each player’s starting life total is 40."),
+ p(
+ "103.3d In a two-player Brawl game, each player’s starting life total is 25. In a multiplayer Brawl game, each player’s starting life total is 30."
+ ),
+ p("103.3e In an Archenemy game, the archenemy’s starting life total is 40.")
+ ),
+ _.slots.footer := Bar(
+ _.design := BarDesign.FloatingFooter,
+ _.slots.startContent := Button(_.design := ButtonDesign.Transparent, _.icon := IconName.home),
+ _.slots.endContent := Button(_.design := ButtonDesign.Positive, _ => "Accept"),
+ _.slots.endContent := Button(_.design := ButtonDesign.Negative, _ => "Reject"),
+ _.slots.endContent := Button(_.design := ButtonDesign.Transparent, _ => "Cancel")
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/PanelExample.scala b/demo/src/main/scala/demo/PanelExample.scala
index c016584..b4a4a5f 100644
--- a/demo/src/main/scala/demo/PanelExample.scala
+++ b/demo/src/main/scala/demo/PanelExample.scala
@@ -3,15 +3,17 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object PanelExample extends Example("Panel") {
private val countries = List("Argentina", "Belgium", "Bulgaria", "Canada", "Columbia", "Croatia", "Denmark")
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Panel",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Panel")(
+ //-- Begin: Basic Panel
Panel(
_ => width := "100%",
_.headerText := "Both expandable and expanded",
@@ -25,33 +27,36 @@ object PanelExample extends Example("Panel") {
"do eu duis elit. Sunt ea pariatur nulla est laborum proident sunt labore commodo Lorem laboris nisi Lorem."
)
)
+ //-- End
),
- DemoPanel(
- "Panel with List",
+ DemoPanel("Panel with List")(
+ //-- Begin: Panel with List
Panel(
_.headerText := "Select your country",
_ => width := "100%",
_ =>
UList(
_.mode := ListMode.MultiSelect,
- _ => countries.map(country => UList.Li(_ => country))
+ _ => countries.map(country => UList.item(_ => country))
)
)
+ //-- End
),
- DemoPanel(
- "Fixed Panel (Can't be Collapsed/Expanded)",
+ DemoPanel("Fixed Panel (Can't be Collapsed/Expanded)")(
+ //-- Begin: Fixed Panel (Can't be Collapsed/Expanded)
Panel(
_.fixed := true,
_.headerText := "Country Of Birth",
_ =>
UList(
_.mode := ListMode.SingleSelectBegin,
- _ => countries.map(country => UList.Li(_ => country))
+ _ => countries.map(country => UList.item(_ => country))
)
)
+ //-- End
),
- DemoPanel(
- "Panel with Custom Header",
+ DemoPanel("Panel with Custom Header")(
+ //-- Begin: Panel with Custom Header
div(
styleTag("""
|.header {
@@ -75,10 +80,11 @@ object PanelExample extends Example("Panel") {
_ =>
UList(
_.mode := ListMode.MultiSelect,
- _ => countries.map(country => UList.Li(_ => country))
+ _ => countries.map(country => UList.item(_ => country))
)
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/PopoverExample.scala b/demo/src/main/scala/demo/PopoverExample.scala
index f1d0880..38b65b8 100644
--- a/demo/src/main/scala/demo/PopoverExample.scala
+++ b/demo/src/main/scala/demo/PopoverExample.scala
@@ -3,52 +3,49 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom.HTMLElement
object PopoverExample extends Example("Popover") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Popover", {
- val openPopoverBus: EventBus[Option[HTMLElement]] = new EventBus
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Popover") {
+ //-- Begin: Basic Popover
+ val openPopoverBus: EventBus[Option[HTMLElement]] = new EventBus
- div(
- Button(
- _ => "Open Popover",
- _.design := ButtonDesign.Emphasized,
- _.events.onClick.map(_.target).map(Some(_)) --> openPopoverBus.writer
- ),
- Popover(
- _ =>
- inContext(el =>
- openPopoverBus.events --> Observer[Option[HTMLElement]] {
- case Some(element) => el.ref.showAt(element)
- case None => el.ref.close()
- }
- ),
- _.headerText := "Newsletter subscription",
- _ =>
+ div(
+ Button(
+ _ => "Open Popover",
+ _.design := ButtonDesign.Emphasized,
+ _.events.onClick.map(_.target).map(Some(_)) --> openPopoverBus.writer
+ ),
+ Popover(
+ _.showAtFromEvents(openPopoverBus.events.collect { case Some(opener) => opener }),
+ _.closeFromEvents(openPopoverBus.events.collect { case None => () }),
+ _.headerText := "Newsletter subscription",
+ _ =>
+ div(
+ className := loginFormClass,
+ styleTagForLoginFormClass,
div(
- className := loginFormClass,
- styleTagForLoginFormClass,
- div(
- Label(_.forId := "emailInput", _.required := true, _ => "Email"),
- Input(_.id := "emailInput", _.placeholder := "Enter Email", _.tpe := InputType.Email)
- )
- ),
- _.slots.footer := div(
- div(flex := "1"),
- Button(
- _.design := ButtonDesign.Emphasized,
- _ => "Subscribe",
- _.events.onClick.mapTo(None) --> openPopoverBus.writer
+ Label(_.forId := "emailInput", _.required := true, _ => "Email"),
+ Input(_.id := "emailInput", _.placeholder := "Enter Email", _.tpe := InputType.Email)
)
+ ),
+ _.slots.footer := div(
+ div(flex := "1"),
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Subscribe",
+ _.events.onClick.mapTo(None) --> openPopoverBus.writer
)
)
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/ProductSwitchExample.scala b/demo/src/main/scala/demo/ProductSwitchExample.scala
index 615e995..0987044 100644
--- a/demo/src/main/scala/demo/ProductSwitchExample.scala
+++ b/demo/src/main/scala/demo/ProductSwitchExample.scala
@@ -3,14 +3,16 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom.HTMLElement
object ProductSwitchExample extends Example("ProductSwitch") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic sample",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic sample")(
+ //-- Begin: Basic sample
ProductSwitch(
_.item(_.titleText := "Home", _.subtitleText := "Central Home", _.icon := IconName.home),
_.item(
@@ -21,51 +23,52 @@ object ProductSwitchExample extends Example("ProductSwitch") {
_.item(_.titleText := "Catalog", _.subtitleText := "Ariba", _.icon := IconName.contacts),
_.item(_.titleText := "Travel & Expense", _.subtitleText := "Concur", _.icon := IconName.flight)
)
+ //-- End
),
- DemoPanel(
- "ProductSwitch within ShellBar", {
- val togglePopoverOpeningEventBus: EventBus[HTMLElement] = new EventBus
+ DemoPanel("ProductSwitch within ShellBar") {
+ //-- Begin: ProductSwitch within ShellBar
+ val togglePopoverOpeningEventBus: EventBus[HTMLElement] = new EventBus
- val togglePopoverOpeningEvents = togglePopoverOpeningEventBus.events
- .foldLeft(Option.empty[HTMLElement]) {
- case (Some(_), _) => None
- case (None, element) => Some(element)
- }
- .changes
+ val togglePopoverOpeningEvents = togglePopoverOpeningEventBus.events
+ .foldLeft(Option.empty[HTMLElement]) {
+ case (Some(_), _) => None
+ case (None, element) => Some(element)
+ }
+ .changes
- div(
- ShellBar(
- _.primaryTitle := "Corporate Portal",
- _.secondaryTitle := "secondary title",
- _.slots.logo := img(src := "/images/avatars/scala-logo.png"),
- _.showProductSwitch := true,
- _.showCoPilot := true,
- _.events.onProductSwitchClick.map(_.detail.targetRef) --> togglePopoverOpeningEventBus.writer
- ),
- Popover(
- _ =>
- inContext(el =>
- togglePopoverOpeningEvents --> Observer[Option[HTMLElement]] {
- case Some(element) => el.ref.showAt(element)
- case None => el.ref.close()
- }
+ div(
+ ShellBar(
+ _.primaryTitle := "Corporate Portal",
+ _.secondaryTitle := "secondary title",
+ _.slots.logo := img(src := "/images/avatars/scala-logo.png"),
+ _.showProductSwitch := true,
+ _.showCoPilot := true,
+ _.events.onProductSwitchClick.map(_.detail.targetRef) --> togglePopoverOpeningEventBus.writer
+ ),
+ Popover(
+ _ =>
+ inContext(el =>
+ togglePopoverOpeningEvents --> Observer[Option[HTMLElement]] {
+ case Some(element) => el.ref.showAt(element)
+ case None => el.ref.close()
+ }
+ ),
+ _.placementType := PopoverPlacementType.Bottom,
+ _ =>
+ ProductSwitch(
+ _.item(_.titleText := "Home", _.subtitleText := "Central Home", _.icon := IconName.home),
+ _.item(
+ _.titleText := "Analytics Cloud",
+ _.subtitleText := "Analystics Could",
+ _.icon := IconName.`business-objects-experience`
),
- _.placementType := PopoverPlacementType.Bottom,
- _ =>
- ProductSwitch(
- _.item(_.titleText := "Home", _.subtitleText := "Central Home", _.icon := IconName.home),
- _.item(
- _.titleText := "Analytics Cloud",
- _.subtitleText := "Analystics Could",
- _.icon := IconName.`business-objects-experience`
- ),
- _.item(_.titleText := "Catalog", _.subtitleText := "Ariba", _.icon := IconName.contacts),
- _.item(_.titleText := "Travel & Expense", _.subtitleText := "Concur", _.icon := IconName.flight)
- )
- )
+ _.item(_.titleText := "Catalog", _.subtitleText := "Ariba", _.icon := IconName.contacts),
+ _.item(_.titleText := "Travel & Expense", _.subtitleText := "Concur", _.icon := IconName.flight)
+ )
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/ProgressIndicatorExample.scala b/demo/src/main/scala/demo/ProgressIndicatorExample.scala
index e449703..0ec0a96 100644
--- a/demo/src/main/scala/demo/ProgressIndicatorExample.scala
+++ b/demo/src/main/scala/demo/ProgressIndicatorExample.scala
@@ -3,41 +3,47 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object ProgressIndicatorExample extends Example("ProgressIndicator") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Progress Indicator",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Progress Indicator")(
+ //-- Begin: Basic Progress Indicator
div(
ProgressIndicator(_.value := 0),
ProgressIndicator(_.value := 25),
ProgressIndicator(_.value := 75),
ProgressIndicator(_.value := 100)
)
+ //-- End
),
- DemoPanel(
- "Progress Indicator With Custom Display Value",
+ DemoPanel("Progress Indicator With Custom Display Value")(
+ //-- Begin: Progress Indicator With Custom Display Value
ProgressIndicator(
_.value := 25,
_.displayValue := "Custom Display Value"
)
+ //-- End
),
- DemoPanel(
- "Progress Indicator With Value State",
+ DemoPanel("Progress Indicator With Value State")(
+ //-- Begin: Progress Indicator With Value State
div(
ValueState.allValues
.zip(10 to 100 by 20)
.map((valueState, value) => ProgressIndicator(_.value := value, _.valueState := valueState))
)
+ //-- End
),
- DemoPanel(
- "Progress Indicator With Custom Sizes",
+ DemoPanel("Progress Indicator With Custom Sizes")(
+ //-- Begin: Progress Indicator With Custom Sizes
div(
ProgressIndicator(_.value := 25, _ => height := "50px", _ => width := "200px"),
ProgressIndicator(_.value := 75, _ => height := "50px", _ => width := "200px")
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/RadioButtonExample.scala b/demo/src/main/scala/demo/RadioButtonExample.scala
index a086678..bb4d79d 100644
--- a/demo/src/main/scala/demo/RadioButtonExample.scala
+++ b/demo/src/main/scala/demo/RadioButtonExample.scala
@@ -3,50 +3,52 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object RadioButtonExample extends Example("RadioButton") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic RadioButton Types", {
- val texts = LazyList.from(0).map('A' + _).map(_.toChar).map("Option " ++ _.toString)
- val name = "GroupA"
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic RadioButton Types") {
+ //-- Begin: Basic RadioButton Types
+ val texts = LazyList.from(0).map('A' + _).map(_.toChar).map("Option " ++ _.toString)
+ val name = "GroupA"
- div(
- RadioButton(_.text := texts(0), _.checked := true, _.name := name),
- RadioButton(_.text := texts(1), _.valueState := ValueState.None, _.name := name),
- RadioButton(_.text := texts(2), _.valueState := ValueState.Warning, _.name := name),
- RadioButton(_.text := texts(6), _.disabled := true, _.name := name),
- RadioButton(_.text := texts(7), _.readonly := true, _.name := name)
- )
- }
- ),
- DemoPanel(
- "RadioButton in group - navigate via [UP/Right] and [DOWN/Left] arrow keys", {
- val selectedValueVar: Var[String] = Var("None")
+ div(
+ RadioButton(_.text := texts(0), _.checked := true, _.name := name),
+ RadioButton(_.text := texts(1), _.valueState := ValueState.None, _.name := name),
+ RadioButton(_.text := texts(2), _.valueState := ValueState.Warning, _.name := name),
+ RadioButton(_.text := texts(6), _.disabled := true, _.name := name),
+ RadioButton(_.text := texts(7), _.readonly := true, _.name := name)
+ )
+ //-- End
+ },
+ DemoPanel("RadioButton in group - navigate via [UP/Right] and [DOWN/Left] arrow keys") {
+ //-- Begin: RadioButton in group - navigate via [UP/Right] and [DOWN/Left] arrow keys
+ val selectedValueVar: Var[String] = Var("None")
+ div(
+ h1("Group of states"),
+ Label(_ => child.text <-- selectedValueVar.signal.map(state => s"Selected radio: $state")),
div(
- h1("Group of states"),
- Label(_ => child.text <-- selectedValueVar.signal.map(state => s"Selected radio: $state")),
- div(
- display := "flex",
- flexDirection := "column",
- ValueState.allValues.map(state =>
- RadioButton(
- _.name := "GroupB",
- _.text := state.value,
- _.events.onChange.mapToChecked.filter(identity).mapTo(state.value) --> selectedValueVar.writer,
- _.checked <-- selectedValueVar.signal.map(_ == state.value),
- _.valueState := state
- )
+ display := "flex",
+ flexDirection := "column",
+ ValueState.allValues.map(state =>
+ RadioButton(
+ _.name := "GroupB",
+ _.text := state.value,
+ _.events.onChange.mapToChecked.filter(identity).mapTo(state.value) --> selectedValueVar.writer,
+ _.checked <-- selectedValueVar.signal.map(_ == state.value),
+ _.valueState := state
)
)
)
- }
- ),
- DemoPanel(
- "RadioButton with Text Wrapping",
+ )
+ //-- End
+ },
+ DemoPanel("RadioButton with Text Wrapping")(
+ //-- Begin: RadioButton with Text Wrapping
div(
RadioButton(
_ => width := "300px",
@@ -61,6 +63,7 @@ object RadioButtonExample extends Example("RadioButton") {
_.name := "GroupD"
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/RangeSliderExample.scala b/demo/src/main/scala/demo/RangeSliderExample.scala
index 167417a..2b3970e 100644
--- a/demo/src/main/scala/demo/RangeSliderExample.scala
+++ b/demo/src/main/scala/demo/RangeSliderExample.scala
@@ -3,10 +3,62 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object RangeSliderExample extends Example("RangeSlider") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Range Slider") {
+ //-- Begin: Basic Range Slider
+ val startValueVar: Var[Double] = Var(0)
+ val endValueVar: Var[Double] = Var(20)
+
+ div(
+ Label(_ =>
+ child.text <-- startValueVar.signal
+ .combineWith(endValueVar.signal)
+ .map((start, end) => s"Currently selected range: ($start, $end)")
+ ),
+ RangeSlider(
+ _.endValue <-- endValueVar.signal,
+ _.startValue <-- startValueVar.signal,
+ _.events.onChange.map(_.target.startValue) --> startValueVar.writer,
+ _.events.onChange.map(_.target.endValue) --> endValueVar.writer
+ )
+ )
+ //-- End
+ },
+ DemoPanel("Range Slider with Custom 'min', 'max', 'startValue' and 'endValue' Properties") {
+ //-- Begin: Range Slider with Custom 'min', 'max', 'startValue' and 'endValue' Properties
+ RangeSlider(_.min := 100, _.max := 200, _.startValue := 120, _.endValue := 150)
+ //-- End
+ },
+ DemoPanel("Range Slider with Tooltips") {
+ //-- Begin: Range Slider with Tooltips
+ RangeSlider(_.startValue := 3, _.endValue := 13, _.showTooltip := true)
+ //-- End
+ },
+ DemoPanel("Range Slider with Tickmarks and Custom Step") {
+ //-- Begin: Range Slider with Tickmarks and Custom Step
+ RangeSlider(_.step := 2, _.startValue := 4, _.endValue := 12, _.showTickmarks := true)
+ //-- End
+ },
+ DemoPanel("Range Slider with Tooltips, Tickmarks and Labels") {
+ //-- Begin: Range Slider with Tooltips, Tickmarks and Labels
+ RangeSlider(
+ _.min := 0,
+ _.max := 112,
+ _.step := 2,
+ _.startValue := 4,
+ _.endValue := 12,
+ _.showTooltip := true,
+ _.labelInterval := 2,
+ _.showTickmarks := true
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/RatingIndicatorExample.scala b/demo/src/main/scala/demo/RatingIndicatorExample.scala
index a94f55e..34786c2 100644
--- a/demo/src/main/scala/demo/RatingIndicatorExample.scala
+++ b/demo/src/main/scala/demo/RatingIndicatorExample.scala
@@ -3,46 +3,52 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object RatingIndicatorExample extends Example("RatingIndicator") {
- def component: HtmlElement = div(
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
styleTag("""
|div > ui5-rating-indicator {
| margin-right: 2em;
|}
|""".stripMargin),
- DemoPanel(
- "Basic Rating Indicator",
+ DemoPanel("Basic Rating Indicator")(
+ //-- Begin: Basic Rating Indicator
div(
RatingIndicator(_.events.onChange.map(_.target.value) --> Observer(println)),
RatingIndicator(_.value := 3),
RatingIndicator(_.value := 3.7)
)
+ //-- End
),
- DemoPanel(
- "Rating Indicator With Different Max Value",
+ DemoPanel("Rating Indicator With Different Max Value")(
+ //-- Begin: Rating Indicator With Different Max Value
div(
RatingIndicator(_.max := 10, _.value := 5),
RatingIndicator(_.max := 3, _.value := 3)
)
+ //-- End
),
- DemoPanel(
- "Disabled Rating Indicator",
+ DemoPanel("Disabled Rating Indicator")(
+ //-- Begin: Disabled Rating Indicator
div(
RatingIndicator(_.value := 4, _.disabled := true),
RatingIndicator(_.max := 10, _.value := 5, _.disabled := true),
RatingIndicator(_.value := 6, _.max := 6, _.disabled := true)
)
+ //-- End
),
- DemoPanel(
- "Readonly Rating Indicator",
+ DemoPanel("Readonly Rating Indicator")(
+ //-- Begin: Readonly Rating Indicator
div(
RatingIndicator(_.value := 4, _.readonly := true),
RatingIndicator(_.max := 10, _.value := 5, _.readonly := true),
RatingIndicator(_.value := 6, _.max := 6, _.readonly := true)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/ResponsivePopoverExample.scala b/demo/src/main/scala/demo/ResponsivePopoverExample.scala
index de095ea..6d49226 100644
--- a/demo/src/main/scala/demo/ResponsivePopoverExample.scala
+++ b/demo/src/main/scala/demo/ResponsivePopoverExample.scala
@@ -3,10 +3,50 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import org.scalajs.dom
object ResponsivePopoverExample extends Example("ResponsivePopover") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic ResponsivePopover") {
+ //-- Begin: Basic ResponsivePopover
+ val openPopoverBus: EventBus[dom.HTMLElement] = new EventBus
+ val closePopoverBus: EventBus[Unit] = new EventBus
+
+ div(
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Open Popover",
+ _.events.onClick.map(_.target) --> openPopoverBus.writer
+ ),
+ ResponsivePopover(
+ _.showAtFromEvents(openPopoverBus.events),
+ _.closeFromEvents(closePopoverBus.events),
+ _.headerText := "Newsletter subscription",
+ _ =>
+ div(
+ className := loginFormClass,
+ styleTagForLoginFormClass,
+ div(
+ Label(_.forId := "emailInput", _.required := true, _ => "Email"),
+ Input(_.id := "emailInput", _.placeholder := "Enter Email", _.tpe := InputType.Email)
+ )
+ ),
+ _.slots.footer := div(
+ div(flex := "1"),
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Subscribe",
+ _.events.onClick.mapTo(()) --> closePopoverBus.writer
+ )
+ )
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/SegmentedButtonExample.scala b/demo/src/main/scala/demo/SegmentedButtonExample.scala
index ac0e392..c886c30 100644
--- a/demo/src/main/scala/demo/SegmentedButtonExample.scala
+++ b/demo/src/main/scala/demo/SegmentedButtonExample.scala
@@ -3,30 +3,34 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object SegmentedButtonExample extends Example("SegmentedButton") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic SegmentedButton",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic SegmentedButton")(
+ //-- Begin: Basic SegmentedButton
SegmentedButton(
_.accessibleName := "Geographic location",
_.item(_ => "Map"),
_.item(_ => "Satellite", _.pressed := true),
_.item(_ => "Terrain")
)
+ //-- End
),
- DemoPanel(
- "SegmentedButton with Icons",
+ DemoPanel("SegmentedButton with Icons")(
+ //-- Begin: SegmentedButton with Icons
SegmentedButton(
_.item(_.icon := IconName.employee, _.pressed := true),
_.item(_.icon := IconName.menu),
_.item(_.icon := IconName.factory)
)
+ //-- End
),
- DemoPanel(
- "SegmentedButton with 5 SegmentedButtonItems",
+ DemoPanel("SegmentedButton with 5 SegmentedButtonItems")(
+ //-- Begin: SegmentedButton with 5 SegmentedButtonItems
SegmentedButton(
_.item(_ => "Item"),
_.item(_ => "Pressed SegmentedButtonItem With Bigger Text", _.pressed := true),
@@ -34,6 +38,7 @@ object SegmentedButtonExample extends Example("SegmentedButton") {
_.item(_ => "SegmentedButtonItem"),
_.item(_ => "Press me")
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/SelectExample.scala b/demo/src/main/scala/demo/SelectExample.scala
index 7c95f35..bfd5d83 100644
--- a/demo/src/main/scala/demo/SelectExample.scala
+++ b/demo/src/main/scala/demo/SelectExample.scala
@@ -3,16 +3,18 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object SelectExample extends Example("SelectExample") {
private val someCountries = List("Austria", "Belgium", "Bulgaria", "Germany", "United Kingdom", "Kazakhstan")
private val someCountryCodes = List("AT", "BE", "BG", "DE", "UK", "KZ")
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Select",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Select")(
+ //-- Begin: Basic Select
div(
Select(
_.option(_.icon := IconName.iphone, _ => "Phone"),
@@ -25,9 +27,10 @@ object SelectExample extends Example("SelectExample") {
_.option(_.icon := IconName.iphone, _ => "Phone")
)
)
+ //-- End
),
- DemoPanel(
- "Select with Two-Column Layout Items",
+ DemoPanel("Select with Two-Column Layout Items")(
+ //-- Begin: Select with Two-Column Layout Items
Select(_ =>
someCountries
.zip(someCountryCodes)
@@ -38,6 +41,7 @@ object SelectExample extends Example("SelectExample") {
)
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/ShellBarExample.scala b/demo/src/main/scala/demo/ShellBarExample.scala
index 33cad03..aeffb96 100644
--- a/demo/src/main/scala/demo/ShellBarExample.scala
+++ b/demo/src/main/scala/demo/ShellBarExample.scala
@@ -3,57 +3,59 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom.HTMLElement
object ShellBarExample extends Example("ShellBar") {
- def component: HtmlElement = div(
- DemoPanel(
- "ShellBar", {
- val openPopoverBus: EventBus[HTMLElement] = new EventBus
- div(
- ShellBar(
- _.primaryTitle := "Corporate Portal",
- _.secondaryTitle := "Secondary title",
- _.notificationsCount := "99+",
- _.showNotifications := true,
- _.showProductSwitch := true,
- _.showCoPilot := true,
- _.slots.profile := Avatar(_ => img(src := "/images/avatars/sherpal.png")),
- _.slots.logo := img(src := "/images/avatars/scala-logo.png"),
- _.slots.startButton := Button(_.icon := IconName.`nav-back`),
- _.item(_.icon := IconName.disconnected, _.text := "Disconnected"),
- _.item(_.icon := IconName.`incoming-call`, _.text := "Incoming Calls", _.count := "4"),
- _.slots.searchField := Input(),
- _.slots.menuItems := UList.Li(_ => "Application 1"),
- _.slots.menuItems := UList.Li(_ => "Application 2"),
- _.slots.menuItems := UList.Li(_ => "Application 3"),
- _.slots.menuItems := UList.Li(_ => "Application 4"),
- _.slots.menuItems := UList.Li(_ => "Application 5"),
- _.events.onProfileClick.map(_.detail.targetRef) --> openPopoverBus.writer
- ),
- Popover(
- _ => inContext(el => openPopoverBus.events --> Observer[HTMLElement](el.ref.showAt)),
- _.placementType := PopoverPlacementType.Bottom,
- _ => div(Title(_ => padding := "0.25rem 1rem 0rem 1rem", _ => "sherpal")),
- _ =>
- div(
- UList(
- _.separators := ListSeparator.None,
- _.Li(_.icon := IconName.`sys-find`, _ => "App Finder"),
- _.Li(_.icon := IconName.settings, _ => "Settings"),
- _.Li(_.icon := IconName.edit, _ => "Edit Home Page"),
- _.Li(_.icon := IconName.`sys-help`, _ => "Help"),
- _.Li(_.icon := IconName.log, _ => "Sign out")
- )
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("ShellBar") {
+ //-- Begin: ShellBar
+ val openPopoverBus: EventBus[HTMLElement] = new EventBus
+ div(
+ ShellBar(
+ _.primaryTitle := "Corporate Portal",
+ _.secondaryTitle := "Secondary title",
+ _.notificationsCount := "99+",
+ _.showNotifications := true,
+ _.showProductSwitch := true,
+ _.showCoPilot := true,
+ _.slots.profile := Avatar(_ => img(src := "/images/avatars/sherpal.png")),
+ _.slots.logo := img(src := "/images/avatars/scala-logo.png"),
+ _.slots.startButton := Button(_.icon := IconName.`nav-back`),
+ _.item(_.icon := IconName.disconnected, _.text := "Disconnected"),
+ _.item(_.icon := IconName.`incoming-call`, _.text := "Incoming Calls", _.count := "4"),
+ _.slots.searchField := Input(),
+ _.slots.menuItems := UList.item(_ => "Application 1"),
+ _.slots.menuItems := UList.item(_ => "Application 2"),
+ _.slots.menuItems := UList.item(_ => "Application 3"),
+ _.slots.menuItems := UList.item(_ => "Application 4"),
+ _.slots.menuItems := UList.item(_ => "Application 5"),
+ _.events.onProfileClick.map(_.detail.targetRef) --> openPopoverBus.writer
+ ),
+ Popover(
+ _ => inContext(el => openPopoverBus.events --> Observer[HTMLElement](el.ref.showAt)),
+ _.placementType := PopoverPlacementType.Bottom,
+ _ => div(Title(_ => padding := "0.25rem 1rem 0rem 1rem", _ => "sherpal")),
+ _ =>
+ div(
+ UList(
+ _.separators := ListSeparator.None,
+ _.item(_.icon := IconName.`sys-find`, _ => "App Finder"),
+ _.item(_.icon := IconName.settings, _ => "Settings"),
+ _.item(_.icon := IconName.edit, _ => "Edit Home Page"),
+ _.item(_.icon := IconName.`sys-help`, _ => "Help"),
+ _.item(_.icon := IconName.log, _ => "Sign out")
)
- )
+ )
)
- }
- ),
- DemoPanel(
- "Basic ShellBar",
+ )
+ //-- End
+ },
+ DemoPanel("Basic ShellBar")(
+ //-- Begin: Basic ShellBar
ShellBar(
_.primaryTitle := "Corporate Portal",
_.secondaryTitle := "secondary title",
@@ -61,6 +63,7 @@ object ShellBarExample extends Example("ShellBar") {
_.slots.logo := img(src := "/images/avatars/scala-logo.png"),
_.slots.startButton := Button(_.icon := IconName.`nav-back`)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/SideNavigationExample.scala b/demo/src/main/scala/demo/SideNavigationExample.scala
index 38ff0ac..102b8d6 100644
--- a/demo/src/main/scala/demo/SideNavigationExample.scala
+++ b/demo/src/main/scala/demo/SideNavigationExample.scala
@@ -3,50 +3,52 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom.HTMLElement
object SideNavigationExample extends Example("SideNavigation") {
- def component: HtmlElement = div(
- DemoPanel(
- "Side Navigation in Application", {
- val toggleCollapseBus: EventBus[Unit] = new EventBus
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Side Navigation in Application") {
+ //-- Begin: Side Navigation in Application
+ val toggleCollapseBus: EventBus[Unit] = new EventBus
- val collapsedSignal = toggleCollapseBus.events.foldLeft(false)((collapsed, _) => !collapsed)
- div(
- ShellBar(
- _.primaryTitle := "UI5 Web Components",
- _.secondaryTitle := "The Best Run SAP",
- _.showCoPilot := true,
- _.slots.startButton := Button(
- _.icon := IconName.menu,
- _.events.onClick.mapTo(()) --> toggleCollapseBus.writer
- )
- ),
- SideNavigation(
- _.collapsed <-- collapsedSignal,
- _.item(_.text := "Home", _.icon := IconName.home),
- _.item(
- _.text := "People",
- _.expanded := true,
- _.icon := IconName.group,
- _.subItem(_.text := "From My Team"),
- _.subItem(_.text := "From Other Team")
- ),
- _.item(_.text := "Locations", _.icon := IconName.`locate-me`, _.selected := true),
- _.item(
- _.text := "Events",
- _.icon := IconName.calendar,
- _.subItem(_.text := "Local"),
- _.subItem(_.text := "Others")
- ),
- _.slots.fixedItems := SideNavigation.item(_.text := "Useful Links", _.icon := IconName.`chain-link`),
- _.slots.fixedItems := SideNavigation.item(_.text := "History", _.icon := IconName.history)
+ val collapsedSignal = toggleCollapseBus.events.foldLeft(false)((collapsed, _) => !collapsed)
+ div(
+ ShellBar(
+ _.primaryTitle := "UI5 Web Components",
+ _.secondaryTitle := "The Best Run SAP",
+ _.showCoPilot := true,
+ _.slots.startButton := Button(
+ _.icon := IconName.menu,
+ _.events.onClick.mapTo(()) --> toggleCollapseBus.writer
)
+ ),
+ SideNavigation(
+ _.collapsed <-- collapsedSignal,
+ _.item(_.text := "Home", _.icon := IconName.home),
+ _.item(
+ _.text := "People",
+ _.expanded := true,
+ _.icon := IconName.group,
+ _.subItem(_.text := "From My Team"),
+ _.subItem(_.text := "From Other Team")
+ ),
+ _.item(_.text := "Locations", _.icon := IconName.`locate-me`, _.selected := true),
+ _.item(
+ _.text := "Events",
+ _.icon := IconName.calendar,
+ _.subItem(_.text := "Local"),
+ _.subItem(_.text := "Others")
+ ),
+ _.slots.fixedItems := SideNavigation.item(_.text := "Useful Links", _.icon := IconName.`chain-link`),
+ _.slots.fixedItems := SideNavigation.item(_.text := "History", _.icon := IconName.history)
)
- }
- )
+ )
+ //-- End
+ }
)
}
diff --git a/demo/src/main/scala/demo/SliderExample.scala b/demo/src/main/scala/demo/SliderExample.scala
index d617573..7f20172 100644
--- a/demo/src/main/scala/demo/SliderExample.scala
+++ b/demo/src/main/scala/demo/SliderExample.scala
@@ -3,10 +3,46 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object SliderExample extends Example("Slider") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Slider") {
+ //-- Begin: Basic Slider
+ val selectedValueVar: Var[Double] = Var(0)
+ div(
+ Label(_ => child.text <-- selectedValueVar.signal.map(value => s"Selected value: $value")),
+ br(),
+ Slider(_.value <-- selectedValueVar.signal, _.events.onInput.map(_.target.value) --> selectedValueVar.writer)
+ )
+ //-- End
+ },
+ DemoPanel("Slider with Tooltip") {
+ //-- Begin: Slider with Tooltip
+ Slider(_.min := 0, _.max := 20, _.showTooltip := true)
+ //-- End
+ },
+ DemoPanel("Disabled Slider with Tickmarks and Labels") {
+ //-- Begin: Disabled Slider with Tickmarks and Labels
+ Slider(_.min := 20, _.max := 100, _.disabled := true, _.labelInterval := 5, _.showTickmarks := true)
+ //-- End
+ },
+ DemoPanel("Slider Tooltip, Tickmarks and Labels") {
+ //-- Begin: Slider Tooltip, Tickmarks and Labels
+ Slider(
+ _.min := -20,
+ _.max := 20,
+ _.step := 2,
+ _.value := 12,
+ _.showTooltip := true,
+ _.labelInterval := 2,
+ _.showTickmarks := true
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/SplitButtonExample.scala b/demo/src/main/scala/demo/SplitButtonExample.scala
index 9b5aeb1..72ebd52 100644
--- a/demo/src/main/scala/demo/SplitButtonExample.scala
+++ b/demo/src/main/scala/demo/SplitButtonExample.scala
@@ -3,10 +3,48 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import org.scalajs.dom
object SplitButtonExample extends Example("SplitButton") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Default SplitButton") {
+ //-- Begin: Default SplitButton
+ div(SplitButton(_ => "Default"), SplitButton(_.disabled := true, _ => "Default"))
+ //-- End
+ },
+ DemoPanel("SplitButton with Design") {
+ //-- Begin: SplitButton with Design
+ div(ButtonDesign.allValues.map(design => SplitButton(_.design := design, _ => design.value)))
+ //-- End
+ },
+ DemoPanel("SplitButton with Icons") {
+ //-- Begin: SplitButton with Icons
+ div(
+ SplitButton(_ => "Icon", _.icon := IconName.add),
+ SplitButton(_ => "Icon + Active Icon", _.icon := IconName.add, _.activeIcon := IconName.accept)
+ )
+ //-- End
+ },
+ DemoPanel("SplitButton opening Popover on arrow-click") {
+ //-- Begin: SplitButton opening Popover on arrow-click
+ val arrowClickBus: EventBus[dom.HTMLElement] = new EventBus
+
+ div(
+ Popover(
+ _.showAtFromEvents(arrowClickBus.events),
+ _ => "Put whatever you want do show on arrow-click."
+ ),
+ SplitButton(
+ _ => "Expand ->",
+ _.events.onArrowClick.map(_.target) --> arrowClickBus.writer
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/StepInputExample.scala b/demo/src/main/scala/demo/StepInputExample.scala
index 15d64d8..f21d533 100644
--- a/demo/src/main/scala/demo/StepInputExample.scala
+++ b/demo/src/main/scala/demo/StepInputExample.scala
@@ -3,10 +3,59 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object StepInputExample extends Example("StepInput") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ //-- Begin Common
+ styleTag("""
+ |.shorter {
+ | width: 300px;
+ |}
+ |""".stripMargin)
+ //-- End Common
+ ,
+ DemoPanel("Basic Step Input") {
+ //-- Begin: Basic Step Input
+ div(
+ className := "shorter",
+ StepInput(_.value := 5),
+ StepInput(_.readonly := true, _.value := 5),
+ StepInput(_.disabled := true, _.value := 5)
+ )
+ //-- End
+ },
+ DemoPanel("Step Input with alignment") {
+ //-- Begin: Step Input with alignment
+ div(
+ className := "shorter",
+ StepInput(_.value := 5),
+ StepInput(_.value := 5, _ => textAlign := "center"),
+ StepInput(_.value := 5, _ => textAlign := "right")
+ )
+ //-- End
+ },
+ DemoPanel("Step Input with min, max, step and valuePrecision") {
+ //-- Begin: Step Input with min, max, step and valuePrecision
+ div(
+ className := "shorter",
+ StepInput(_.value := 5, _.min := 0, _.max := 10, _.step := 1),
+ StepInput(_.value := 0, _.min := -100, _.max := 100, _.step := 10),
+ StepInput(_.value := 10, _.min := 0, _.max := 20, _.valuePrecision := 1)
+ )
+ //-- End
+ },
+ DemoPanel("Step Input with Value State") {
+ //-- Begin: Step Input with Value State
+ div(
+ className := "shorter",
+ ValueState.allValues.map(state => StepInput(_.valueState := state, _ => marginTop := "0.5rem"))
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/SwitchExample.scala b/demo/src/main/scala/demo/SwitchExample.scala
index 1780fbc..4b97260 100644
--- a/demo/src/main/scala/demo/SwitchExample.scala
+++ b/demo/src/main/scala/demo/SwitchExample.scala
@@ -3,18 +3,20 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object SwitchExample extends Example("Switch") {
- def component: HtmlElement = div(
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
styleTag("""
|div > ui5-switch {
| margin-right: 1em;
|}
|""".stripMargin),
- DemoPanel(
- "BasicSwitch",
+ DemoPanel("Basic Switch")(
+ //-- Begin: Basic Switch
div(
Switch(_.textOn := "On", _.textOff := "Off"),
Switch(_.textOn := "On", _.textOff := "Off", _.checked := true),
@@ -22,15 +24,17 @@ object SwitchExample extends Example("Switch") {
Switch(_.textOn := "Yes", _.textOff := "No", _.disabled := true),
Switch(_.textOn := "Yes", _.textOff := "No", _.checked := true, _.disabled := true)
)
+ //-- End
),
- DemoPanel(
- "Graphical Switch",
+ DemoPanel("Graphical Switch")(
+ //-- Begin: Graphical Switch
div(
for {
disabled <- List(false, true)
checked <- List(false, true)
} yield Switch(_.design := SwitchDesign.Graphical, _.checked := checked, _.disabled := disabled)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/TabContainerExample.scala b/demo/src/main/scala/demo/TabContainerExample.scala
index d32fe0e..afad086 100644
--- a/demo/src/main/scala/demo/TabContainerExample.scala
+++ b/demo/src/main/scala/demo/TabContainerExample.scala
@@ -3,13 +3,15 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object TabContainerExample extends Example("TabContainer") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic TabContainer",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic TabContainer")(
+ //-- Begin: Basic TabContainer
TabContainer(
_ => width := "100%",
_.tab(
@@ -57,9 +59,10 @@ object TabContainerExample extends Example("TabContainer") {
)
)
)
+ //-- End
),
- DemoPanel(
- "TabContainer with text only tabs",
+ DemoPanel("TabContainer with text only tabs")(
+ //-- Begin: TabContainer with text only tabs
TabContainer(
_.collapsed := true,
_.fixed := true,
@@ -69,18 +72,20 @@ object TabContainerExample extends Example("TabContainer") {
_.tab(_.text := "About"),
_.tab(_.text := "Contacts")
)
+ //-- End
),
- DemoPanel(
- "Text only End Overflow",
+ DemoPanel("Text only End Overflow")(
+ //-- Begin: Text only End Overflow
TabContainer(
_ => width := "100%",
_.collapsed := true,
_.fixed := true,
_ => (1 to 23).toList.map(index => TabContainer.tab(_.text := s"Tab $index", _.selected := (index == 13)))
)
+ //-- End
),
- DemoPanel(
- "Text only Start and End Overflow",
+ DemoPanel("Text only Start and End Overflow")(
+ //-- Begin: Text only Start and End Overflow
TabContainer(
_ => width := "100%",
_.collapsed := true,
@@ -88,9 +93,10 @@ object TabContainerExample extends Example("TabContainer") {
_.tabsOverflowMode := TabsOverflowMode.StartAndEnd,
_ => (1 to 33).toList.map(index => TabContainer.tab(_.text := s"Tab $index", _.selected := (index == 17)))
)
+ //-- End
),
- DemoPanel(
- "TabContainer with text and additional-text",
+ DemoPanel("TabContainer with text and additional-text")(
+ //-- Begin: TabContainer with text and additional-text
TabContainer(
_.collapsed := true,
_.fixed := true,
@@ -100,9 +106,10 @@ object TabContainerExample extends Example("TabContainer") {
_.tab(_.text := "Notes", _.additionalText := "16"),
_.tab(_.text := "People", _.additionalText := "34")
)
+ //-- End
),
- DemoPanel(
- "TabContainer with nested tabs",
+ DemoPanel("TabContainer with nested tabs")(
+ //-- Begin: TabContainer with nested tabs
TabContainer(
_.collapsed := true,
_.tab(_.text := "Nodes", _ => "Notes go here ..."),
@@ -127,6 +134,7 @@ object TabContainerExample extends Example("TabContainer") {
_.slots.subTabs := TabContainer.tab(_.text := "Attachments", _ => "Attachments go here ...")
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/TableExample.scala b/demo/src/main/scala/demo/TableExample.scala
index c9293dc..4c7b9f8 100644
--- a/demo/src/main/scala/demo/TableExample.scala
+++ b/demo/src/main/scala/demo/TableExample.scala
@@ -3,14 +3,16 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example, MTG}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub, MTG}
import org.scalajs.dom
import scala.scalajs.js
object TableExample extends Example("Table") {
- def component: HtmlElement = div(
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
styleTag(s"""
|.header {
| display: flex;
@@ -21,42 +23,42 @@ object TableExample extends Example("Table") {
| padding: 0.5rem;
|}
|""".stripMargin),
- DemoPanel(
- "Basic Table", {
- val toggleStickyHeaderBus: EventBus[Unit] = new EventBus
- val stickyHeaderSignal = toggleStickyHeaderBus.events.foldLeft(false)((isSticky, _) => !isSticky)
+ DemoPanel("Basic Table") {
+ //-- Begin: Basic Table
+ val toggleStickyHeaderBus: EventBus[Unit] = new EventBus
+ val stickyHeaderSignal = toggleStickyHeaderBus.events.foldLeft(false)((isSticky, _) => !isSticky)
+ div(
div(
- div(
- className := "header",
- span("Cards table - resize your browser to make some columns pop-in"),
- Button(_.events.onClick.mapTo(()) --> toggleStickyHeaderBus, _ => "Toggle Sticky Column Header")
+ className := "header",
+ span("Cards table - resize your browser to make some columns pop-in"),
+ Button(_.events.onClick.mapTo(()) --> toggleStickyHeaderBus, _ => "Toggle Sticky Column Header")
+ ),
+ Table(
+ _.stickyColumnHeader <-- stickyHeaderSignal,
+ _.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
+ _.slots.columns := Table.column(_.minWidth := 800, _ => span(lineHeight := "1.4rem", "Type")),
+ _.slots.columns := Table.column(
+ _.minWidth := 600,
+ _.popinText := "Comment",
+ _.demandPopin := true,
+ _ => span(lineHeight := "1.4rem", "Comment")
),
- Table(
- _.stickyColumnHeader <-- stickyHeaderSignal,
- _.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
- _.slots.columns := Table.column(_.minWidth := 800, _ => span(lineHeight := "1.4rem", "Type")),
- _.slots.columns := Table.column(
- _.minWidth := 600,
- _.popinText := "Comment",
- _.demandPopin := true,
- _ => span(lineHeight := "1.4rem", "Comment")
- ),
- _.slots.columns := Table.column(_ => span(lineHeight := "1.4rem", "Cost")),
- _ =>
- MTG.cards.map(card =>
- Table.row(
- _.cell(_ => card.name),
- _.cell(_ => card.tpe),
- _.cell(_ => card.comment),
- _.cell(_ => card.cost)
- )
+ _.slots.columns := Table.column(_ => span(lineHeight := "1.4rem", "Cost")),
+ _ =>
+ MTG.cards.map(card =>
+ Table.row(
+ _.cell(_ => card.name),
+ _.cell(_ => card.tpe),
+ _.cell(_ => card.comment),
+ _.cell(_ => card.cost)
)
- )
+ )
)
- }
- ),
- DemoPanel(
- "Table in SingleSelect-mode",
+ )
+ //-- End
+ },
+ DemoPanel("Table in SingleSelect-mode")(
+ //-- Begin: Table in SingleSelect-mode
Table(
_.mode := TableMode.SingleSelect,
_.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
@@ -78,9 +80,10 @@ object TableExample extends Example("Table") {
)
)
)
+ //-- End
),
- DemoPanel(
- "Table in MultiSelect mode",
+ DemoPanel("Table in MultiSelect mode")(
+ //-- Begin: Table in MultiSelect mode"
Table(
_.events.onSelectionChange.map(_.detail.selectedRows.map(_.dataset.toMap)) --> Observer(println),
_.mode := TableMode.MultiSelect,
@@ -104,9 +107,10 @@ object TableExample extends Example("Table") {
)
)
)
+ //-- End
),
- DemoPanel(
- "Table with No Data",
+ DemoPanel("Table with No Data")(
+ //-- Begin: Table with No Data
Table(
_.noDataText := "No Data",
_.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
@@ -119,29 +123,77 @@ object TableExample extends Example("Table") {
),
_.slots.columns := Table.column(_ => span(lineHeight := "1.4rem", "Cost"))
)
+ //-- End
),
- DemoPanel(
- "Growing Table with 'More' button", {
- val loadMoreBus: EventBus[Unit] = new EventBus
- val totalNumberOfCards = MTG.cards.length
- val numberOfLoadedCards = loadMoreBus.events.delay(3000).mapTo(4).foldLeft(4)(_ + _)
+ DemoPanel("Growing Table with 'More' button") {
+ //-- Begin: Growing Table with 'More' button
+ val loadMoreBus: EventBus[Unit] = new EventBus
+ val totalNumberOfCards = MTG.cards.length
+ val numberOfLoadedCards = loadMoreBus.events.delay(3000).mapTo(4).foldLeft(4)(_ + _)
+
+ val cardsToDisplay = numberOfLoadedCards.map(MTG.cards.take)
- val cardsToDisplay = numberOfLoadedCards.map(MTG.cards.take)
+ val busyState = EventStream
+ .merge(
+ loadMoreBus.events.mapTo(+1),
+ numberOfLoadedCards.changes.mapTo(-1)
+ )
+ .foldLeft(0)(_ + _)
+ .map(_ > 0)
- val busyState = EventStream
- .merge(
- loadMoreBus.events.mapTo(+1),
- numberOfLoadedCards.changes.mapTo(-1)
+ Table(
+ _.busy <-- busyState,
+ _.growing := TableGrowingMode.Button,
+ _.growingButtonSubtext <-- numberOfLoadedCards
+ .map(_ min totalNumberOfCards)
+ .map(n => s"[$n / $totalNumberOfCards]"),
+ _.events.onLoadMore.mapTo(()) --> loadMoreBus,
+ _.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
+ _.slots.columns := Table.column(_.minWidth := 800, _ => span(lineHeight := "1.4rem", "Type")),
+ _.slots.columns := Table.column(
+ _.minWidth := 600,
+ _.popinText := "Comment",
+ _.demandPopin := true,
+ _ => span(lineHeight := "1.4rem", "Comment")
+ ),
+ _.slots.columns := Table.column(_ => span(lineHeight := "1.4rem", "Cost")),
+ _ =>
+ children <-- cardsToDisplay.map(
+ _.map(card =>
+ Table.row(
+ _ => dataAttr("card-name") := card.name,
+ _.cell(_ => card.name),
+ _.cell(_ => card.tpe),
+ _.cell(_ => card.comment),
+ _.cell(_ => card.cost)
+ )
+ )
)
- .foldLeft(0)(_ + _)
- .map(_ > 0)
+ )
+ //-- End
+ },
+ DemoPanel("Growing Table on Scroll") {
+ //-- Begin: Growing Table on Scroll
+ val loadMoreBus: EventBus[Unit] = new EventBus
+ val totalNumberOfCards = MTG.cards.length
+ val numberOfLoadedCards = loadMoreBus.events.delay(3000).mapTo(4).foldLeft(4)(_ + _)
+
+ val cardsToDisplay = numberOfLoadedCards.map(MTG.cards.take)
+
+ val busyState = EventStream
+ .merge(
+ loadMoreBus.events.mapTo(+1),
+ numberOfLoadedCards.changes.mapTo(-1)
+ )
+ .foldLeft(0)(_ + _)
+ .map(_ > 0)
+ div(
+ overflowY := "scroll",
+ height := "400px",
Table(
_.busy <-- busyState,
- _.growing := TableGrowingMode.Button,
- _.growingButtonSubtext <-- numberOfLoadedCards
- .map(_ min totalNumberOfCards)
- .map(n => s"[$n / $totalNumberOfCards]"),
+ _.growing := TableGrowingMode.Scroll,
_.events.onLoadMore.mapTo(()) --> loadMoreBus,
_.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
_.slots.columns := Table.column(_.minWidth := 800, _ => span(lineHeight := "1.4rem", "Type")),
@@ -165,58 +217,11 @@ object TableExample extends Example("Table") {
)
)
)
- }
- ),
- DemoPanel(
- "Growing Table on Scroll", {
- val loadMoreBus: EventBus[Unit] = new EventBus
- val totalNumberOfCards = MTG.cards.length
- val numberOfLoadedCards = loadMoreBus.events.delay(3000).mapTo(4).foldLeft(4)(_ + _)
-
- val cardsToDisplay = numberOfLoadedCards.map(MTG.cards.take)
-
- val busyState = EventStream
- .merge(
- loadMoreBus.events.mapTo(+1),
- numberOfLoadedCards.changes.mapTo(-1)
- )
- .foldLeft(0)(_ + _)
- .map(_ > 0)
-
- div(
- overflowY := "scroll",
- height := "400px",
- Table(
- _.busy <-- busyState,
- _.growing := TableGrowingMode.Scroll,
- _.events.onLoadMore.mapTo(()) --> loadMoreBus,
- _.slots.columns := Table.column(_ => width := "12rem", _ => span(lineHeight := "1.4rem", "Card")),
- _.slots.columns := Table.column(_.minWidth := 800, _ => span(lineHeight := "1.4rem", "Type")),
- _.slots.columns := Table.column(
- _.minWidth := 600,
- _.popinText := "Comment",
- _.demandPopin := true,
- _ => span(lineHeight := "1.4rem", "Comment")
- ),
- _.slots.columns := Table.column(_ => span(lineHeight := "1.4rem", "Cost")),
- _ =>
- children <-- cardsToDisplay.map(
- _.map(card =>
- Table.row(
- _ => dataAttr("card-name") := card.name,
- _.cell(_ => card.name),
- _.cell(_ => card.tpe),
- _.cell(_ => card.comment),
- _.cell(_ => card.cost)
- )
- )
- )
- )
- )
- }
- ),
- DemoPanel(
- "Table with grouping (SingleSelect)",
+ )
+ //-- End
+ },
+ DemoPanel("Table with grouping (SingleSelect)")(
+ //-- Begin: Table with grouping (SingleSelect)
Table(
_.mode := TableMode.SingleSelect,
_.slots.columns := Table.column(_ => Label(_ => "City")),
@@ -229,6 +234,7 @@ object TableExample extends Example("Table") {
_.row(_.cell(_ => "Paris"), _.cell(_ => "10 millions"), _.cell(_ => "France")),
_.row(_.cell(_ => "Lille"), _.cell(_ => "1 million"), _.cell(_ => "France"))
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/TextAreaExample.scala b/demo/src/main/scala/demo/TextAreaExample.scala
index 9577b20..6189ecf 100644
--- a/demo/src/main/scala/demo/TextAreaExample.scala
+++ b/demo/src/main/scala/demo/TextAreaExample.scala
@@ -3,19 +3,26 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object TextAreaExample extends Example("TextArea") {
- def component: HtmlElement = div(
- DemoPanel("Basic TextArea", TextArea(_.placeholder := "Type as much text as you wish")),
- DemoPanel(
- "TextArea with Maximum length",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic TextArea")(
+ //-- Begin: Basic TextArea
+ TextArea(_.placeholder := "Type as much text as you wish")
+ //-- End
+ ),
+ DemoPanel("TextArea with Maximum length")(
+ //-- Begin: TextArea with Maximum length
TextArea(
_.placeholder := "Type some text",
_.maxLength := 10,
_.showExceededText := true
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/TimePickerExample.scala b/demo/src/main/scala/demo/TimePickerExample.scala
index 4fe37c2..4afa174 100644
--- a/demo/src/main/scala/demo/TimePickerExample.scala
+++ b/demo/src/main/scala/demo/TimePickerExample.scala
@@ -3,10 +3,43 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object TimePickerExample extends Example("TimePicker") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic TimePicker") {
+ //-- Begin: Basic TimePicker
+ val selectedValueBus: EventBus[String] = new EventBus
+ div(
+ Label(_ => child.text <-- selectedValueBus.events.map(value => s"Currently selected: $value")),
+ br(),
+ TimePicker(
+ _.events.onChange.map(_.target.value) --> selectedValueBus
+ )
+ )
+ //-- End
+ },
+ DemoPanel("TimePicker in twelve hours format") {
+ //-- Begin: TimePicker in twelve hours format
+ val selectedValueBus: EventBus[String] = new EventBus
+ div(
+ Label(_ => child.text <-- selectedValueBus.events.map(value => s"Currently selected: $value")),
+ br(),
+ TimePicker(
+ _.formatPattern := "hh:mm:ss a",
+ _.events.onChange.map(_.target.value) --> selectedValueBus
+ )
+ )
+ //-- End
+ },
+ DemoPanel("TimePicker with value-state and valueStateMessage") {
+ //-- Begin: TimePicker with value-state and valueStateMessage
+ TimePicker(_.valueState := ValueState.Error, _.slots.valueStateMessage := div("Please provide a valid value"))
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/TimelineExample.scala b/demo/src/main/scala/demo/TimelineExample.scala
index 86e7b88..cb95116 100644
--- a/demo/src/main/scala/demo/TimelineExample.scala
+++ b/demo/src/main/scala/demo/TimelineExample.scala
@@ -3,13 +3,15 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object TimelineExample extends Example("Timeline") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Timeline",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Timeline")(
+ //-- Begin: Basic Timeline
Timeline(
_.item(
_.titleText := "called",
@@ -32,9 +34,10 @@ object TimelineExample extends Example("Timeline") {
_ => div("Online meeting")
)
)
+ //-- End
),
- DemoPanel(
- "Horizontal timeline",
+ DemoPanel("Horizontal timeline")(
+ //-- Begin: Horizontal timeline
Timeline(
_.layout := TimelineLayout.Horizontal,
_.item(
@@ -57,6 +60,7 @@ object TimelineExample extends Example("Timeline") {
_ => div("Online meeting")
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/TitleExample.scala b/demo/src/main/scala/demo/TitleExample.scala
index 6b7255a..01be82f 100644
--- a/demo/src/main/scala/demo/TitleExample.scala
+++ b/demo/src/main/scala/demo/TitleExample.scala
@@ -3,16 +3,17 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object TitleExample extends Example("Title") {
- def component: HtmlElement = div(
- DemoPanel(
- "Title in All Available Levels",
- div(
- TitleLevel.allValues.map(level => Title(_.level := level, _ => s"Title level ${level.value.tail}"))
- )
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Title in All Available Levels")(
+ //-- Begin: Title in All Available Levels
+ div(TitleLevel.allValues.map(level => Title(_.level := level, _ => s"Title level ${level.value.tail}")))
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/ToastExample.scala b/demo/src/main/scala/demo/ToastExample.scala
index 581d3cc..5fe9987 100644
--- a/demo/src/main/scala/demo/ToastExample.scala
+++ b/demo/src/main/scala/demo/ToastExample.scala
@@ -3,71 +3,73 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import scala.concurrent.duration.DurationInt
object ToastExample extends Example("Toast") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Toast", {
- val toastBus: EventBus[Unit] = new EventBus
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Toast") {
+ //-- Begin: Basic Toast
+ val toastBus: EventBus[Unit] = new EventBus
- div(
- Button(_ => "Basic Toast", _.events.onClick.mapTo(()) --> toastBus.writer),
- Toast(
- _ => inContext(el => toastBus.events.mapTo(()) --> Observer[Unit](_ => el.ref.show())),
- _ => "Basic Toast"
- ),
- MessageStrip(
- _.design := MessageStripDesign.Information,
- _ => "This toast pops up thanks to the click event going through an EventBus."
- )
+ div(
+ Button(_ => "Basic Toast", _.events.onClick.mapTo(()) --> toastBus.writer),
+ Toast(
+ _ => inContext(el => toastBus.events.mapTo(()) --> Observer[Unit](_ => el.ref.show())),
+ _ => "Basic Toast"
+ ),
+ MessageStrip(
+ _.design := MessageStripDesign.Information,
+ _ => "This toast pops up thanks to the click event going through an EventBus."
)
- }
- ),
- DemoPanel(
- "Toast Duration", {
- val shortToastId = "short-toast-id"
- val longToastId = "long-toast-id"
+ )
+ //-- End
+ },
+ DemoPanel("Toast Duration") {
+ //-- Begin: Toast Duration
+ val shortToastId = "short-toast-id"
+ val longToastId = "long-toast-id"
- div(
- Button(
- _ => "Short Toast",
- _.events.onClick.mapTo(Toast.getToastById(shortToastId)) --> Observer[Option[Toast.Ref]] {
- case Some(toast) => toast.show()
- case None => throw new IllegalStateException(s"The dom does not contain any toast with id $shortToastId")
- }
- ),
- Toast(
- _.id := shortToastId,
- _.duration := 1500.millis,
- _.placement := ToastPlacement.BottomStart,
- _ => "Short Toast"
- ),
- Button(
- _ => "Long Toast",
- _.events.onClick.mapTo(Toast.getToastById(longToastId)) --> Observer[Option[Toast.Ref]] {
- case Some(toast) => toast.show()
- case None => throw new IllegalStateException(s"The dom does not contain any toast with id $longToastId")
- }
- ),
- Toast(
- _.id := longToastId,
- _.duration := 4500.millis,
- _.placement := ToastPlacement.BottomEnd,
- _ => "Long Toast"
- ),
- MessageStrip(
- _.design := MessageStripDesign.Information,
- _ => "These toasts pops up by grabbing their reference using the Toast.getToastById function."
- )
+ div(
+ Button(
+ _ => "Short Toast",
+ _.events.onClick.mapTo(Toast.getToastById(shortToastId)) --> Observer[Option[Toast.Ref]] {
+ case Some(toast) => toast.show()
+ case None => throw new IllegalStateException(s"The dom does not contain any toast with id $shortToastId")
+ }
+ ),
+ Toast(
+ _.id := shortToastId,
+ _.duration := 1500.millis,
+ _.placement := ToastPlacement.BottomStart,
+ _ => "Short Toast"
+ ),
+ Button(
+ _ => "Long Toast",
+ _.events.onClick.mapTo(Toast.getToastById(longToastId)) --> Observer[Option[Toast.Ref]] {
+ case Some(toast) => toast.show()
+ case None => throw new IllegalStateException(s"The dom does not contain any toast with id $longToastId")
+ }
+ ),
+ Toast(
+ _.id := longToastId,
+ _.duration := 4500.millis,
+ _.placement := ToastPlacement.BottomEnd,
+ _ => "Long Toast"
+ ),
+ MessageStrip(
+ _.design := MessageStripDesign.Information,
+ _ => "These toasts pops up by grabbing their reference using the Toast.getToastById function."
)
- }
- ),
- DemoPanel(
- "Toast Placements",
+ )
+ //-- End
+ },
+ DemoPanel("Toast Placements")(
+ //-- Begin: Toast Placements
div(
ToastPlacement.allValues.flatMap { placement =>
val toastBus: EventBus[Unit] = new EventBus
@@ -82,6 +84,7 @@ object ToastExample extends Example("Toast") {
)
}
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/ToggleButtonExample.scala b/demo/src/main/scala/demo/ToggleButtonExample.scala
index 586bbd2..dffc13a 100644
--- a/demo/src/main/scala/demo/ToggleButtonExample.scala
+++ b/demo/src/main/scala/demo/ToggleButtonExample.scala
@@ -3,10 +3,61 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
object ToggleButtonExample extends Example("ToggleButton") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ styleTag("""
+ |ui5-toggle-button {
+ | margin-right: 0.5rem;
+ |}
+ |""".stripMargin),
+ DemoPanel("ToggleButton States") {
+ //-- Begin: ToggleButton States
+ div(
+ ToggleButton(_ => "ToggleButton"),
+ ToggleButton(_.pressed := true, _ => "Pressed ToggleButton"),
+ ToggleButton(_.disabled := true, _ => "Disabled ToggleButton"),
+ ToggleButton(_.disabled := true, _.pressed := true, _ => "Disabled pressed ToggleButton"),
+ ToggleButton(_.design := ButtonDesign.Positive, _ => "Accept ToggleButton"),
+ ToggleButton(_.design := ButtonDesign.Positive, _.pressed := true, _ => "Accept pressed ToggleButton"),
+ ToggleButton(_.design := ButtonDesign.Negative, _ => "Reject ToggleButton"),
+ ToggleButton(_.design := ButtonDesign.Negative, _.pressed := true, _ => "Reject pressed ToggleButton"),
+ ToggleButton(_.design := ButtonDesign.Transparent, _ => "Transparent ToggleButton"),
+ ToggleButton(_.design := ButtonDesign.Transparent, _.pressed := true, _ => "Transparent pressed ToggleButton")
+ )
+ //-- End
+ },
+ DemoPanel("ToggleButton with Icon") {
+ //-- Begin: ToggleButton with Icon
+ div(
+ ToggleButton(_.icon := IconName.menu, _ => "Menu"),
+ ToggleButton(_.design := ButtonDesign.Emphasized, _.icon := IconName.add, _ => "Add"),
+ ToggleButton(_.design := ButtonDesign.Default, _.icon := IconName.`nav-back`, _ => "Back"),
+ ToggleButton(_.design := ButtonDesign.Positive, _.icon := IconName.accept, _ => "Accept"),
+ ToggleButton(_.design := ButtonDesign.Negative, _.icon := IconName.`sys-cancel`, _ => "Deny")
+ )
+ //-- End
+ },
+ DemoPanel("ToggleButton with Icon Only") {
+ //-- Begin: ToggleButton with Icon Only
+ div(
+ ToggleButton(_.icon := IconName.accept),
+ ToggleButton(_.icon := IconName.`action-settings`, _.pressed := true),
+ ToggleButton(_.icon := IconName.add),
+ ToggleButton(_.icon := IconName.alert, _.pressed := true),
+ ToggleButton(_.icon := IconName.away, _.design := ButtonDesign.Positive),
+ ToggleButton(_.icon := IconName.bookmark, _.design := ButtonDesign.Positive),
+ ToggleButton(_.icon := IconName.cancel, _.design := ButtonDesign.Negative),
+ ToggleButton(_.icon := IconName.call, _.design := ButtonDesign.Negative, _.pressed := true),
+ ToggleButton(_.icon := IconName.camera, _.design := ButtonDesign.Transparent),
+ ToggleButton(_.icon := IconName.cart, _.design := ButtonDesign.Transparent, _.pressed := true)
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/TreeExample.scala b/demo/src/main/scala/demo/TreeExample.scala
index b63594a..0d0ac6c 100644
--- a/demo/src/main/scala/demo/TreeExample.scala
+++ b/demo/src/main/scala/demo/TreeExample.scala
@@ -3,14 +3,16 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
import org.scalajs.dom
object TreeExample extends Example("Tree") {
- def component: HtmlElement = div(
- DemoPanel(
- "Basic Tree",
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Basic Tree")(
+ //-- Begin: Basic Tree
Tree(
_ => width := "100%",
_.item(
@@ -40,9 +42,10 @@ object TreeExample extends Example("Tree") {
_.expanded := true
)
)
+ //-- End
),
- DemoPanel(
- "Tree with multiple selection",
+ DemoPanel("Tree with multiple selection")(
+ //-- Begin: Tree with multiple selection
Tree(
_.mode := ListMode.MultiSelect,
_.events.onSelectionChange
@@ -75,6 +78,7 @@ object TreeExample extends Example("Tree") {
_.expanded := true
)
)
+ //-- End
)
)
diff --git a/demo/src/main/scala/demo/UploadCollectionExample.scala b/demo/src/main/scala/demo/UploadCollectionExample.scala
index 0ac51cd..8ff628b 100644
--- a/demo/src/main/scala/demo/UploadCollectionExample.scala
+++ b/demo/src/main/scala/demo/UploadCollectionExample.scala
@@ -3,10 +3,96 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import org.scalajs.dom
+import scala.scalajs.js
object UploadCollectionExample extends Example("UploadCollection") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ DemoPanel("Upload Collection") {
+ //-- Begin: Upload Collection
+ val allStagedFilesVar: Var[List[(dom.File, js.Date)]] = Var(Nil)
+
+ val newFilesArrivedObserver = allStagedFilesVar.updater[List[(dom.File, js.Date)]](_ ++ _)
+
+ val stagedFilesCount = allStagedFilesVar.signal.map(_.length)
+
+ val uploadAllBus: EventBus[Unit] = new EventBus
+
+ UploadCollection(
+ _.slots.header := div(
+ Title.h2(_ => child.text <-- stagedFilesCount.map(count => s"Uploaded ($count)")),
+ Label(_ => "Add new files and press to start uploading pending files:"),
+ child <-- allStagedFilesVar.signal.mapTo(
+ // Note that we need to re-create a new file uploader each time, otherwise there is a glitch preventing
+ // from adding twice the same group of file in a row. (The glitch actually exists on their official docs)
+ FileUploader(
+ _.hideInput := true,
+ _.multiple := true,
+ _ => Button(_.icon := IconName.add, _.design := ButtonDesign.Transparent),
+ _.events.onChange.map(_.target.files.map(_ -> new js.Date())) --> newFilesArrivedObserver
+ )
+ ),
+ Button(_ => "Upload all", _.events.onClick.mapTo(()) --> uploadAllBus.writer)
+ ),
+ _.mode := ListMode.Delete,
+ _.events.onItemDelete.map(_.detail.item.dataset("index").toInt) --> allStagedFilesVar.updater[Int](
+ _.patch(_, Nil, 1)
+ ),
+ _ =>
+ // This is where the logic of actually uploading files should live
+ uploadAllBus.events.sample(allStagedFilesVar.signal) --> Observer[List[(dom.File, js.Date)]](files =>
+ files.foreach((file, date) => dom.console.log(file, date))
+ ),
+ _ =>
+ children <-- allStagedFilesVar.signal.map(_.zipWithIndex.map { case ((file, selectedAt), index) =>
+ UploadCollection.item(
+ _.fileName := file.name,
+ _.fileNameClickable := true,
+ _ => s"Selected at: $selectedAt",
+ _ => dataAttr("index") := index.toString,
+ _.slots.thumbnail := img(
+ src := dom.URL.createObjectURL(file),
+ inContext(el => onLoad --> Observer[Any](_ => dom.URL.revokeObjectURL(el.ref.src)))
+ )
+ )
+ }),
+ _.events.onDrop.preventDefault.map(
+ _.dataTransfer.files.toList.map(_ -> new js.Date)
+ ) --> newFilesArrivedObserver
+ )
+ //-- End
+ },
+ DemoPanel("UploadCollection With Drag and Drop and No Initial Data") {
+ //-- Begin: UploadCollection With Drag and Drop and No Initial Data
+ val allStagedFilesVar: Var[List[(dom.File, js.Date)]] = Var(Nil)
+
+ val newFilesArrivedObserver = allStagedFilesVar.updater[List[(dom.File, js.Date)]](_ ++ _)
+
+ UploadCollection(
+ _.slots.header := div(Title.h2(_ => "Attachments")),
+ _.events.onDrop.preventDefault.map(
+ _.dataTransfer.files.toList.map(_ -> new js.Date)
+ ) --> newFilesArrivedObserver,
+ _ =>
+ children <-- allStagedFilesVar.signal.map(_.zipWithIndex.map { case ((file, selectedAt), index) =>
+ UploadCollection.item(
+ _.fileName := file.name,
+ _.fileNameClickable := true,
+ _ => s"Selected at: $selectedAt",
+ _ => dataAttr("index") := index.toString,
+ _.slots.thumbnail := img(
+ src := dom.URL.createObjectURL(file),
+ inContext(el => onLoad --> Observer[Any](_ => dom.URL.revokeObjectURL(el.ref.src)))
+ )
+ )
+ })
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/ViewSettingsDialogExample.scala b/demo/src/main/scala/demo/ViewSettingsDialogExample.scala
index fa5944a..55d3a12 100644
--- a/demo/src/main/scala/demo/ViewSettingsDialogExample.scala
+++ b/demo/src/main/scala/demo/ViewSettingsDialogExample.scala
@@ -3,10 +3,63 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import scala.scalajs.js.JSON
object ViewSettingsDialogExample extends Example("ViewSettingsDialog") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ MessageStrip(
+ _.design := MessageStripDesign.Information,
+ _ =>
+ "Using the ViewSettingsDialog is straightforward on paper as shown below. However, don't be fooled. You still" +
+ " need to process by hand whatever it spits out. Given it's dynamic nature, it's not as trivial as it " +
+ "may seem. (But perhaps adding some magic on top would make it more delightful to use.)"
+ ),
+ DemoPanel("Usage") {
+ //-- Begin: Usage
+ val settingsBus: EventBus[ViewSettingsDialog.ViewSettings] = new EventBus
+ val showSettingsDialogBus: EventBus[Unit] = new EventBus
+ div(
+ Button(_ => "Open ViewSettingsDialog", _.events.onClick.mapTo(()) --> showSettingsDialogBus.writer),
+ ViewSettingsDialog(
+ _.showFromEvents(showSettingsDialogBus.events),
+ _.events.onCancel.map(_.detail) --> settingsBus.writer,
+ _.events.onConfirm.map(_.detail) --> settingsBus.writer,
+ _.slots.sortItems := List(
+ SortItem(_.text := "Name", _.selected := true),
+ SortItem(_.text := "Position"),
+ SortItem(_.text := "Company"),
+ SortItem(_.text := "Department")
+ ),
+ _.slots.filterItems := List(
+ FilterItem(
+ _.text := "Position",
+ _.slots.values := List("CTO", "CPO", "VP").map(position => FilterItem.option(_.text := position))
+ ),
+ FilterItem(
+ _.text := "Department",
+ _.slots.values := List("Sales", "Management", "PR").map(position => FilterItem.option(_.text := position))
+ ),
+ FilterItem(
+ _.text := "Location",
+ _.slots.values := List("Walldorf", "New York", "London").map(position =>
+ FilterItem.option(_.text := position)
+ )
+ ),
+ FilterItem(
+ _.text := "Report to",
+ _.slots.values := List("CTO", "CPO", "VP").map(position => FilterItem.option(_.text := position))
+ )
+ )
+ ),
+ pre(child.text <-- settingsBus.events.map(settings => JSON.stringify(settings, space = 2))),
+ pre(child.text <-- settingsBus.events.map(settings => settings.filters.toString))
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/WizardExample.scala b/demo/src/main/scala/demo/WizardExample.scala
index 5d7648c..e3c5471 100644
--- a/demo/src/main/scala/demo/WizardExample.scala
+++ b/demo/src/main/scala/demo/WizardExample.scala
@@ -3,10 +3,139 @@ package demo
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
-import demo.helpers.{DemoPanel, Example}
+import demo.helpers.{DemoPanel, Example, FetchDemoPanelFromGithub}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
object WizardExample extends Example("Wizard") {
- def component: HtmlElement = missing
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
+ styleTagForLoginFormClass,
+ DemoPanel("Wizard") {
+ //-- Begin: Wizard
+ val currentStepVar: Var[Int] = Var(1)
+
+ // Singal indicating whether the specified step number is currently selected.
+ def isSelectedStepSignal(stepNumber: Int) = currentStepVar.signal.map(_ == stepNumber)
+
+ // Biggest step that was seen up to now
+ val maxSeenStep = currentStepVar.signal.foldLeft(identity)(_ max _)
+
+ // Modifiers for all the steps
+ def commonModifiers(stepNumber: Int): Mod[ReactiveHtmlElement[WizardStep.Ref]] = List[WizardStep.ModFunction](
+ _.selected <-- isSelectedStepSignal(stepNumber),
+ _.disabled <-- maxSeenStep.map(stepNumber > _),
+ _ => dataAttr("step-number") := stepNumber.toString
+ ).map(_(WizardStep))
+
+ // Creates a button that, when clicking, go to the specified step. Can be hidden temporarily with the observable
+ def goToStepButton(goTo: Int, hiddenObservable: Observable[Boolean] = Val(false)) = Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => s"Go to step $goTo",
+ _.events.onClick.mapTo(goTo) --> currentStepVar.writer,
+ _ => hidden <-- hiddenObservable
+ )
+
+ // User will not be able to go to step 3 before this is filled.
+ val maybeInfoForStepThreeVar: Var[Option[String]] = Var(None)
+
+ // This bus will be fed at the very end when the process is done.
+ val finishDialogBus: EventBus[Unit] = new EventBus
+ val finishDialogCloseBus: EventBus[Unit] = new EventBus
+
+ div(
+ Wizard(
+ _.events.onStepChange.map(_.detail.step.dataset("stepNumber").toInt) --> currentStepVar.writer,
+ _.step(
+ _.icon := IconName.home,
+ _.titleText := "Greeting",
+ _ => commonModifiers(1),
+ _ => Title.h3(_ => "1. Greeting"),
+ _ =>
+ MessageStrip(
+ _.hideCloseButton := true,
+ _.design := MessageStripDesign.Information,
+ _ =>
+ "The Wizard control is supposed to break down large tasks, into smaller steps, easier for the " +
+ "user to work with."
+ ),
+ _ =>
+ div(
+ "This document is the ultimate authority for Magic: The Gathering® competitive game play. It " +
+ "consists of a series of numbered rules followed by a glossary. Many of the numbered rules are " +
+ "divided into subrules, and each separate rule and subrule of the game has its own number. (Note " +
+ "that subrules skip the letters “l” and “o” due to potential confusion with the numbers “1” and “0”;" +
+ " subrule 704.5k is followed by 704.5m, then 704.5n, then 704.5p, for example.)"
+ ),
+ _ => goToStepButton(2)
+ ),
+ _.step(
+ _.icon := IconName.employee,
+ _.titleText := "2. User name",
+ _ => commonModifiers(2),
+ _ => Title.h3(_ => "2. User name"),
+ _ =>
+ div(
+ "Changes may have been made to this document since its publication. You can download the most " +
+ "recent version from the Magic rules website at Magic.Wizards.com/Rules. If you have questions, " +
+ "you can get the answers from us at Support.Wizards.com."
+ ),
+ _ =>
+ div(
+ className := loginFormClass,
+ div(
+ Label(_ => "Fill in your name to continue:", _.required := true),
+ Input(
+ _.events.onChange
+ .map(_.target.value)
+ .filter(_.trim.nonEmpty) --> maybeInfoForStepThreeVar.writer.contramapSome
+ )
+ )
+ ),
+ _ => goToStepButton(3, maybeInfoForStepThreeVar.signal.map(_.isEmpty))
+ ),
+ _.step(
+ _.icon := IconName.`action-settings`,
+ _.titleText := "3. User profile",
+ _ => commonModifiers(3),
+ _ =>
+ Title.h3(_ =>
+ child.text <-- maybeInfoForStepThreeVar.signal.changes
+ .collect { case Some(name) => name }
+ .map(name => s"3. User profile: $name")
+ ),
+ _ => div("Here the user could fill some optional settings for their profile..."),
+ _ => goToStepButton(4)
+ ),
+ _.step(
+ _.icon := IconName.`chart-table-view`,
+ _.titleText := "4. Last Details",
+ _ => commonModifiers(4),
+ _ => Title.h4(_ => "4. Last Details"),
+ _ => div("Here we ask a few last things and then move along with their lives."),
+ _ =>
+ Button(
+ _.design := ButtonDesign.Positive,
+ _ => "Finish!",
+ _.events.onClick.mapTo(()) --> finishDialogBus.writer
+ )
+ )
+ ),
+ Dialog(
+ _.showFromEvents(finishDialogBus.events),
+ _.closeFromEvents(finishDialogCloseBus.events),
+ _ => div("Process finished!"),
+ _ =>
+ Button(
+ _.design := ButtonDesign.Emphasized,
+ _ => "Done",
+ _.events.onClick.mapTo(()) --> finishDialogCloseBus.writer
+ )
+ )
+ )
+ //-- End
+ }
+ )
}
diff --git a/demo/src/main/scala/demo/facades/highlightjs/HljsLanguage.scala b/demo/src/main/scala/demo/facades/highlightjs/HljsLanguage.scala
new file mode 100644
index 0000000..56f2589
--- /dev/null
+++ b/demo/src/main/scala/demo/facades/highlightjs/HljsLanguage.scala
@@ -0,0 +1,7 @@
+package demo.facades.highlightjs
+
+import scala.scalajs.js
+
+/** Marker trait for all js Object that represents languages to be registered with hljs
+ */
+trait HljsLanguage extends js.Object
diff --git a/demo/src/main/scala/demo/facades/highlightjs/hljs.scala b/demo/src/main/scala/demo/facades/highlightjs/hljs.scala
new file mode 100644
index 0000000..ed0f468
--- /dev/null
+++ b/demo/src/main/scala/demo/facades/highlightjs/hljs.scala
@@ -0,0 +1,16 @@
+package demo.facades.highlightjs
+
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+
+@js.native
+@JSImport("highlight.js/lib/core", JSImport.Default)
+object hljs extends js.Object {
+
+ def highlightElement(element: dom.HTMLElement): Unit = js.native
+
+ def registerLanguage(name: String, language: HljsLanguage): Unit = js.native
+
+}
diff --git a/demo/src/main/scala/demo/facades/highlightjs/hljsScala.scala b/demo/src/main/scala/demo/facades/highlightjs/hljsScala.scala
new file mode 100644
index 0000000..e3cc30f
--- /dev/null
+++ b/demo/src/main/scala/demo/facades/highlightjs/hljsScala.scala
@@ -0,0 +1,8 @@
+package demo.facades.highlightjs
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+
+@js.native
+@JSImport("highlight.js/lib/languages/scala", JSImport.Default)
+object hljsScala extends HljsLanguage
diff --git a/demo/src/main/scala/demo/helpers/DemoPanel.scala b/demo/src/main/scala/demo/helpers/DemoPanel.scala
index ec41067..e81c954 100644
--- a/demo/src/main/scala/demo/helpers/DemoPanel.scala
+++ b/demo/src/main/scala/demo/helpers/DemoPanel.scala
@@ -3,19 +3,41 @@ package demo.helpers
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
import com.raquo.laminar.api.L.*
+import demo.facades.highlightjs.{hljs, hljsScala}
object DemoPanel {
- def apply(title: String, body: => HtmlElement): HtmlElement = div(
+
+ hljs.registerLanguage("scala", hljsScala)
+
+ def apply(title: String)(body: HtmlElement)(using
+ demoPanelInfo: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement = div(
h2(title),
div(
padding := "1em",
border := "0.0625rem solid #C1C1C1",
backgroundColor := "#f7f7f7",
body
- )
- // div(
- // border := "0.0625rem solid #C1C1C1",
- // backgroundColor := "#f5f6fa"
- // )
+ ),
+ demoPanelInfo.demoPanelInfo
+ .get(title)
+ .map(thisExampleInfo =>
+ div(
+ marginTop := "1em",
+ overflowX := "auto",
+ border := "0.0625rem solid #C1C1C1",
+ backgroundColor := "#f5f6fa",
+ padding := "1rem",
+ Title.h3(_ => "Source code"),
+ pre(
+ code(
+ className := "language-scala",
+ demoPanelInfo.maybeStripIndentCommon.map(_ ++ "\n\n").getOrElse("") ++
+ thisExampleInfo.stripIndent,
+ onMountCallback(ctx => hljs.highlightElement(ctx.thisNode.ref))
+ )
+ )
+ )
+ )
)
}
diff --git a/demo/src/main/scala/demo/helpers/Example.scala b/demo/src/main/scala/demo/helpers/Example.scala
index d75973a..fcfcc4f 100644
--- a/demo/src/main/scala/demo/helpers/Example.scala
+++ b/demo/src/main/scala/demo/helpers/Example.scala
@@ -3,12 +3,15 @@ package demo.helpers
import com.raquo.laminar.api.L.*
import be.doeraene.webcomponents.ui5.*
import be.doeraene.webcomponents.ui5.configkeys.*
+import scala.concurrent.ExecutionContext.Implicits.global
/** An instance [[Example]] is bound to a specific component, and is used to display its functionalities.
*/
trait Example(val name: String) {
- def component: HtmlElement
+ def component(using
+ demoPanelInfoMap: FetchDemoPanelFromGithub.CompleteDemoPanelInfo
+ ): HtmlElement
def completeComponent = div(
Title(_.level := TitleLevel.H1, _ => name),
@@ -21,7 +24,12 @@ trait Example(val name: String) {
),
"."
),
- component
+ div(
+ child <-- EventStream
+ .fromFuture(FetchDemoPanelFromGithub.fetchAllDemoPanelInfo(name))
+ .startWith(FetchDemoPanelFromGithub.CompleteDemoPanelInfo(None, Map.empty))
+ .map(info => component(using info))
+ )
)
def missing: HtmlElement = MessageStrip(
@@ -40,6 +48,11 @@ trait Example(val name: String) {
|}
|""".stripMargin)
+ def mtgImageWarning = MessageStrip(
+ _.design := MessageStripDesign.Warning,
+ _ => "All images displayed on this page are the property of Wizard of the Coast."
+ )
+
}
object Example {
diff --git a/demo/src/main/scala/demo/helpers/FetchDemoPanelFromGithub.scala b/demo/src/main/scala/demo/helpers/FetchDemoPanelFromGithub.scala
new file mode 100644
index 0000000..a202657
--- /dev/null
+++ b/demo/src/main/scala/demo/helpers/FetchDemoPanelFromGithub.scala
@@ -0,0 +1,131 @@
+package demo.helpers
+
+import scala.concurrent.Future
+import scala.concurrent.ExecutionContext
+import org.scalajs.dom.fetch
+import org.scalajs.dom
+import java.lang.module.ModuleDescriptor.Opens
+
+object FetchDemoPanelFromGithub {
+
+ private def stripIndentOfString(contents: String): String = {
+ val lines = contents.split("\n")
+ val minIndent = lines.filter(_.trim.nonEmpty).map(_.takeWhile(_.isSpaceChar).length).minOption.getOrElse(0)
+ lines.map(_.drop(minIndent)).mkString("\n")
+ }
+ case class CompleteDemoPanelInfo(
+ maybeCommonContents: Option[String],
+ demoPanelInfo: Map[String, DemoPanelInfo]
+ ) {
+ def maybeStripIndentCommon: Option[String] = maybeCommonContents.map(stripIndentOfString)
+ }
+
+ case class DemoPanelInfo(title: String, contents: String) {
+ def stripIndent: String = stripIndentOfString(contents)
+ }
+
+ trait Parser[T] { self =>
+ def parse(str: String): Option[(T, Int, Int)]
+
+ final def map[U](f: T => U): Parser[U] = new Parser[U] {
+ def parse(str: String): Option[(U, Int, Int)] =
+ self.parse(str).map((t, beginIndex, endIndex) => (f(t), beginIndex, endIndex))
+ }
+
+ final def flatMap[U](f: T => Parser[U]): Parser[U] = new Parser[U] {
+ def parse(str: String): Option[(U, Int, Int)] = for {
+ tResult <- self.parse(str)
+ (t, beginIndex, endIndex) = tResult
+ remainingStr = str.drop(endIndex)
+ uParser = f(t)
+ uResult <- uParser.parse(remainingStr)
+ (u, _, endUIndex) = uResult
+ } yield (u, beginIndex, endUIndex)
+ }
+
+ final def orElse(default: T): Parser[T] = new Parser[T] {
+ def parse(str: String): Option[(T, Int, Int)] = Some(self.parse(str).getOrElse((default, 0, str.length)))
+ }
+
+ final def extract(str: String): Option[T] = parse(str).map(_._1)
+ }
+
+ /** [[Parser]] succeeding iff `value` is contained as is in the string. */
+ def exactMatch(value: String): Parser[Unit] = new Parser[Unit] {
+ def parse(str: String): Option[(Unit, Int, Int)] = str.indexOf(value) match {
+ case index if index >= 0 => Some(((), index, index + value.length))
+ case _ => None
+ }
+ }
+
+ val findCommonExample: Parser[String] = new Parser[String] {
+ val beginCommonString = "//-- Begin Common"
+ val endCommonString = "//-- End Common"
+
+ def parse(str: String): Option[(String, Int, Int)] = {
+ val lines = str.split("\n")
+ for {
+ beginLineIndex <- Some(lines.indexWhere(_.trim.startsWith(beginCommonString))).filter(_ >= 0)
+ numberOfLines <- Some(lines.drop(beginLineIndex).indexWhere(_.trim.startsWith(endCommonString))).filter(_ >= 0)
+ startIndexInStr = lines.take(beginLineIndex).map(_.length + 1).sum
+ endIndexInStr = lines.take(beginLineIndex + 1 + numberOfLines).map(_.length + 1).sum
+ } yield (lines.drop(beginLineIndex + 1).take(numberOfLines - 1).mkString("\n"), startIndexInStr, endIndexInStr)
+ }
+ }
+
+ val findNextExample: Parser[DemoPanelInfo] = new Parser[DemoPanelInfo] {
+ val beginExampleString = "//-- Begin:"
+ val endExampleString = "//-- End"
+
+ def parse(str: String): Option[(DemoPanelInfo, Int, Int)] = {
+ val lines = str.split("\n").toVector
+ Some(lines.indexWhere(_.trim.startsWith(beginExampleString))).filter(_ >= 0).map { lineIndex =>
+ val exampleName = lines(lineIndex).trim.drop(beginExampleString.length).trim
+ val exampleLines = lines.drop(lineIndex + 1).takeWhile(!_.trim.startsWith(endExampleString))
+ val startIndexInStr = lines.take(lineIndex + 1).map(_.length + 1).sum
+ val endIndexInStr =
+ lines.drop(lineIndex + 1).take(exampleLines.length + 1).map(_.length + 1).sum + startIndexInStr
+ (DemoPanelInfo(exampleName, exampleLines.mkString("\n")), startIndexInStr, endIndexInStr)
+ }
+ }
+ }
+
+ def findAll[A](parser: Parser[A]): Parser[List[A]] = new Parser[List[A]] {
+ def parse(str: String): Option[(List[A], Int, Int)] =
+ parser.parse(str) match {
+ case Some((info, beginIndex, endIndex)) =>
+ findAll(parser)
+ .parse(str.drop(endIndex))
+ .map((otherExamples, _, finalIndex) => (info +: otherExamples, beginIndex, endIndex))
+ case None => Some((Nil, 0, 0))
+ }
+ }
+
+ val findAllExamples = findAll(findNextExample).orElse(Nil)
+ val findAllCommonExamples =
+ findAll(findCommonExample).map(_.mkString("\n\n")).map(Some(_).filter(_.nonEmpty)).orElse(None)
+
+ val completeDemoPanelInfoParser = for {
+ maybeCommonExamples <- findAllCommonExamples
+ demoPanelInfo <- findAllExamples
+ } yield CompleteDemoPanelInfo(maybeCommonExamples, demoPanelInfo.map(info => info.title -> info).toMap)
+
+ private def parseFileContents(content: String): CompleteDemoPanelInfo =
+ completeDemoPanelInfoParser.extract(content).getOrElse(CompleteDemoPanelInfo(None, Map.empty))
+
+ def fetchAllDemoPanelInfo(exampleName: String)(using ExecutionContext): Future[CompleteDemoPanelInfo] = (for {
+ response <- fetch(
+ s"https://raw.githubusercontent.com/sherpal/LaminarSAPUI5Bindings/master/demo/src/main/scala/demo/${exampleName}Example.scala"
+ ).toFuture
+ content <- response.text().toFuture
+ _ <-
+ if response.status >= 400 then
+ Future.failed(new RuntimeException(s"Non successful http status (${response.status}). Body was: $content."))
+ else Future.successful(())
+ } yield parseFileContents(content)).recover { case throwable: Throwable =>
+ dom.console.error(s"Error while fetching demo panel info: ${throwable.getMessage}")
+ throwable.printStackTrace()
+ CompleteDemoPanelInfo(None, Map.empty)
+ }
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/WebComponent.scala b/web-components/src/main/scala/be/doeraene/webcomponents/WebComponent.scala
new file mode 100644
index 0000000..a657e8d
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/WebComponent.scala
@@ -0,0 +1,12 @@
+package be.doeraene.webcomponents
+
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.keys.ReactiveProp
+
+/** Marker trait that all web components inherit.
+ *
+ * This can allow you to implement some shenanigans and abstract over some thins.
+ */
+trait WebComponent {
+ val id: ReactiveProp[String, String] = idAttr
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Avatar.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Avatar.scala
index db5e40c..f6c50ec 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Avatar.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Avatar.scala
@@ -10,6 +10,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** An image-like component that has different display options for representing images and icons in different shapes and
* sizes, depending on the use case. The shape can be circular or square. There are several predefined sizes, as well
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Avatar extends HasIcon {
+object Avatar extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -35,22 +36,24 @@ object Avatar extends HasIcon {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-avatar")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
- val raised: ReactiveHtmlAttr[Boolean] =
+ lazy val interactive: ReactiveHtmlAttr[Boolean] = customHtmlAttr("interactive", BooleanAsAttrPresenceCodec)
+
+ lazy val raised: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("raised", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] =
+ lazy val disabled: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val colorScheme: ReactiveHtmlAttr[AvatarColorScheme] =
+ lazy val colorScheme: ReactiveHtmlAttr[AvatarColorScheme] =
customHtmlAttr("color-scheme", AvatarColorScheme.AsStringCodec)
- val shape: ReactiveHtmlAttr[AvatarShape] = customHtmlAttr("shape", AvatarShape.AsStringCodec)
+ lazy val shape: ReactiveHtmlAttr[AvatarShape] = customHtmlAttr("shape", AvatarShape.AsStringCodec)
- val initials: ReactiveHtmlAttr[AvatarInitials] =
+ lazy val initials: ReactiveHtmlAttr[AvatarInitials] =
customHtmlAttr("initials", AvatarInitials.AsStringCodec)
- val size: ReactiveHtmlAttr[AvatarSize] = customHtmlAttr("size", AvatarSize.AsStringCodec)
+ lazy val size: ReactiveHtmlAttr[AvatarSize] = customHtmlAttr("size", AvatarSize.AsStringCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Avatar)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/AvatarGroup.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/AvatarGroup.scala
new file mode 100644
index 0000000..a447d14
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/AvatarGroup.scala
@@ -0,0 +1,76 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.AvatarGroupType
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** Displays a group of avatars arranged horizontally. It is useful to visually showcase a group of related avatars,
+ * such as, project team members or employees. The component allows you to display the avatars in different sizes,
+ * depending on your use case.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object AvatarGroup extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ @JSName("colorScheme")
+ def colourSchemeJS: js.Array[String] = js.native
+
+ @JSName("hiddenItems")
+ def hiddenItemsJS: js.Array[Avatar.Ref] = js.native
+ }
+
+ object RawElement {
+ extension (element: RawElement)
+ def colourScheme: List[String] = element.colourSchemeJS.toList
+ def hiddenItems: List[Avatar.Ref] = element.hiddenItemsJS.toList
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/AvatarGroup.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = AvatarGroup.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-avatar-group")
+
+ lazy val tpe: ReactiveHtmlAttr[AvatarGroupType] = customHtmlAttr("type", AvatarGroupType.AsStringCodec)
+
+ object slots {
+ val overflowButton: Slot = Slot("overflowButton")
+ }
+
+ object events {
+ trait AvatarClickInfo extends js.Object {
+ def targetRef: dom.HTMLElement
+ def overflowButtonClicked: Boolean
+ }
+
+ val onClick: EventProp[EventWithPreciseTarget[Ref] & HasDetail[AvatarClickInfo]] = new EventProp("click")
+
+ val onOverflow: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("overflow")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(AvatarGroup)): _*)
+
+ def avatar: Avatar.type = Avatar
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Badge.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Badge.scala
index 88e694f..260bf02 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Badge.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Badge.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-badge is a small non-interactive component which contains text information and color chosen from a list of
* predefined color schemes. It serves the purpose to attract the user attention to some piece of information (state,
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Badge {
+object Badge extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -36,9 +37,7 @@ object Badge {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-badge")
- val id: ReactiveProp[String, String] = idAttr
-
- val colourScheme: ReactiveHtmlAttr[ColourScheme] = customHtmlAttr("color-scheme", ColourScheme.AsStringCodec)
+ lazy val colourScheme: ReactiveHtmlAttr[ColourScheme] = customHtmlAttr("color-scheme", ColourScheme.AsStringCodec)
object slots {
// note that unlike most elements that have an attribute Icon, this element has a slot icon instead.
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Bar.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Bar.scala
index e676855..fd0e889 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Bar.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Bar.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The Bar is a container which is primarily used to hold titles, buttons and input elements and its design and
* functionality is the basis for page headers and footers. The component consists of three areas to hold its content -
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Bar {
+object Bar extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -37,9 +38,7 @@ object Bar {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-bar")
- val id: ReactiveProp[String, String] = idAttr
-
- val design: ReactiveHtmlAttr[BarDesign] = customHtmlAttr("design", BarDesign.AsStringCodec)
+ lazy val design: ReactiveHtmlAttr[BarDesign] = customHtmlAttr("design", BarDesign.AsStringCodec)
object slots {
val endContent: Slot = new Slot("endContent")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BarcodeScannerDialog.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BarcodeScannerDialog.scala
new file mode 100644
index 0000000..a50d74e
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BarcodeScannerDialog.scala
@@ -0,0 +1,78 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The BarcodeScannerDialog component provides barcode scanning functionality for all devices that support the
+ * MediaDevices.getUserMedia() native API. Opening the dialog launches the device camera and scans for known barcode
+ * formats.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object BarcodeScannerDialog extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def close(): Unit = js.native
+ def show(): Unit = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/BarcodeScannerDialog.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = BarcodeScannerDialog.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-barcode-scanner-dialog")
+
+ object slots {}
+
+ object events {
+ trait ErrorInfo extends js.Object {
+ def message: String
+ }
+
+ val onScanError: EventProp[EventWithPreciseTarget[Ref] & HasDetail[ErrorInfo]] = new EventProp("scan-error")
+
+ trait SuccessInfo extends js.Object {
+ def text: String
+ def rawBytes: js.typedarray.Uint8Array
+ }
+
+ val onScanSuccess: EventProp[EventWithPreciseTarget[Ref] & HasDetail[SuccessInfo]] = new EventProp("scan-success")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(BarcodeScannerDialog)): _*)
+
+ /** You can feed this [[Observer]] with a barcode scanner [[Ref]]s in order to close it. */
+ val closeObserver: Observer[Ref] = Observer(_.close())
+
+ /** Can be used as modifier to close the Barcode Scanner every time the stream emits. */
+ def closeOnEvents(stream: EventStream[Unit]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => stream.mapTo(el.ref) --> closeObserver)
+
+ /** You can feed this [[Observer]] with a barcode scanner [[Ref]]s in order to open it. */
+ val showObserver: Observer[Ref] = Observer(_.show())
+
+ /** Can be used as modifier to open the Barcode Scanner every time the stream emits. */
+ def showOnEvents(stream: EventStream[Unit]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => stream.mapTo(el.ref) --> showObserver)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Breadcrumbs.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Breadcrumbs.scala
index c2af9f4..27822a5 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Breadcrumbs.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Breadcrumbs.scala
@@ -13,6 +13,7 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import be.doeraene.webcomponents.WebComponent
/** Breadcrumbs menu for navigation.
*
@@ -20,7 +21,7 @@ import be.doeraene.webcomponents.ui5.eventtypes.HasItem
* the doc for more
* information.
*/
-object Breadcrumbs extends HasIcon with HasOnClick {
+object Breadcrumbs extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -44,12 +45,10 @@ object Breadcrumbs extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-breadcrumbs")
- val id: ReactiveProp[String, String] = idAttr
-
- val separatorStyle: ReactiveHtmlAttr[BreadcrumbsSeparatorStyle] =
+ lazy val separatorStyle: ReactiveHtmlAttr[BreadcrumbsSeparatorStyle] =
customHtmlAttr("separator-style", BreadcrumbsSeparatorStyle.AsStringCodec)
- val design: ReactiveHtmlAttr[BreadcrumbsDesign] =
+ lazy val design: ReactiveHtmlAttr[BreadcrumbsDesign] =
customHtmlAttr("design", BreadcrumbsDesign.AsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BreadcrumbsItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BreadcrumbsItem.scala
index 93e8239..0c017ca 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BreadcrumbsItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BreadcrumbsItem.scala
@@ -10,14 +10,15 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
-/** Simple UI button
+/** The ui5-breadcrumbs-item component defines the content of an item in ui5-breadcumbs.
*
* @see
* the doc for more
* information.
*/
-object BreadcrumbsItem extends HasIcon with HasOnClick {
+object BreadcrumbsItem extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -34,14 +35,12 @@ object BreadcrumbsItem extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-breadcrumbs-item")
- val id: ReactiveProp[String, String] = idAttr
-
- val accessibleName: ReactiveHtmlAttr[String] =
+ lazy val accessibleName: ReactiveHtmlAttr[String] =
customHtmlAttr("accessible-name", StringAsIsCodec)
- val href: ReactiveHtmlAttr[String] = customHtmlAttr("href", StringAsIsCodec)
+ lazy val href: ReactiveHtmlAttr[String] = customHtmlAttr("href", StringAsIsCodec)
- val target: ReactiveHtmlAttr[LinkTarget] = customHtmlAttr("target", LinkTarget.AsStringCodec)
+ lazy val target: ReactiveHtmlAttr[LinkTarget] = customHtmlAttr("target", LinkTarget.AsStringCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(BreadcrumbsItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BusyIndicator.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BusyIndicator.scala
index 2235e48..e1ba5df 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BusyIndicator.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/BusyIndicator.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.concurrent.duration.FiniteDuration
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-busy-indicator signals that some operation is going on and that the user must wait. It does not block the
* current UI screen so other operations could be triggered in parallel. It displays 3 dots and each dot expands and
@@ -21,7 +22,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object BusyIndicator extends HasText {
+object BusyIndicator extends WebComponent with HasText {
@js.native
trait RawElement extends js.Object {}
@@ -38,13 +39,11 @@ object BusyIndicator extends HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-busy-indicator")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val active: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("active", BooleanAsAttrPresenceCodec)
- val active: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("active", BooleanAsAttrPresenceCodec)
+ lazy val delay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr[FiniteDuration]("delay", FiniteDurationCodec)
- val delay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr[FiniteDuration]("delay", FiniteDurationCodec)
-
- val size: ReactiveHtmlAttr[BusyIndicatorSize] =
+ lazy val size: ReactiveHtmlAttr[BusyIndicatorSize] =
customHtmlAttr[BusyIndicatorSize]("size", BusyIndicatorSize.AsStringCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(BusyIndicator)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Button.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Button.scala
index f835ac6..7ab2fe6 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Button.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Button.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-button component represents a simple push button. It enables users to trigger actions by clicking or tapping
* the ui5-button, or by pressing certain keyboard keys, such as Enter.
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Button extends HasIcon with HasOnClick {
+object Button extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -35,19 +36,15 @@ object Button extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-button")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
- val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
+ lazy val tooltip: ReactiveHtmlAttr[String] = customHtmlAttr("tooltip", StringAsIsCodec)
- val tooltip: ReactiveHtmlAttr[String] = customHtmlAttr("tooltip", StringAsIsCodec)
+ lazy val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
- val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
-
- @js.native
- @JSImport("@ui5/webcomponents/dist/features/InputElementsFormSupport.js", JSImport.Default)
- object SubmitsSupport extends js.Object
+ lazy val iconOnly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-only", BooleanAsAttrPresenceCodec)
lazy val submits: ReactiveHtmlAttr[Boolean] = {
SubmitsSupport
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Calendar.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Calendar.scala
new file mode 100644
index 0000000..840f290
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Calendar.scala
@@ -0,0 +1,91 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{CalendarSelectionMode, CalendarType}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-calendar component allows users to select one or more dates.
+ *
+ * Currently selected dates are represented with instances of ui5-date as children of the ui5-calendar. The value
+ * property of each ui5-date must be a date string, correctly formatted according to the ui5-calendar's formatPattern
+ * property. Whenever the user changes the date selection, ui5-calendar will automatically create/remove instances of
+ * ui5-date in itself, unless you prevent this behavior by calling preventDefault() for the selected-dates-change
+ * event. This is useful if you want to control the selected dates externally.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object Calendar extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/Calendar.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = Calendar.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-calendar")
+
+ lazy val hideWeekNumbers: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-week-numbers", BooleanAsAttrPresenceCodec)
+
+ lazy val selectionMode: ReactiveHtmlAttr[CalendarSelectionMode] =
+ customHtmlAttr("selection-mode", CalendarSelectionMode.AsStringCodec)
+
+ lazy val formatPattern: ReactiveHtmlAttr[String] = customHtmlAttr("format-pattern", StringAsIsCodec)
+
+ lazy val maxDateRaw: ReactiveHtmlAttr[String] = customHtmlAttr("max-date", StringAsIsCodec)
+
+ lazy val minDateRaw: ReactiveHtmlAttr[String] = customHtmlAttr("min-date", StringAsIsCodec)
+
+ lazy val primaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ customHtmlAttr("primary-calendar-type", CalendarType.AsStringCodec)
+
+ lazy val secondaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ customHtmlAttr("secondary-calendar-type", CalendarType.AsStringCodec)
+
+ object slots {}
+
+ object events {
+ trait SelectedDatesChangeInfo extends js.Object {
+ @JSName("values")
+ def valuesJS: js.Array[String]
+
+ @JSName("dates")
+ def datesJS: js.Array[Long]
+ }
+
+ object SelectedDatesChangeInfo {
+ extension (info: SelectedDatesChangeInfo)
+ def values: List[String] = info.valuesJS.toList
+ def dates: List[Long] = info.datesJS.toList
+ }
+
+ val onSelectedDatesChange: EventProp[EventWithPreciseTarget[Ref] & HasDetail[SelectedDatesChangeInfo]] =
+ new EventProp(
+ "selected-dates-change"
+ )
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Calendar)): _*)
+
+ def date: CalendarDate.type = CalendarDate
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CalendarDate.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CalendarDate.scala
new file mode 100644
index 0000000..5dc9661
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CalendarDate.scala
@@ -0,0 +1,39 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-date component defines a calendar date to be used inside ui5-calendar
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object CalendarDate extends WebComponent with HasValue {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def value: String = js.native
+ }
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = CalendarDate.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-date")
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(CalendarDate)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Card.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Card.scala
index caaf4f4..17a766a 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Card.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Card.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-card is a component that represents information in the form of a tile with separate header and content
* areas.
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Card extends HasAccessibleName {
+object Card extends WebComponent with HasAccessibleName {
@js.native
trait RawElement extends js.Object {}
@@ -35,8 +36,6 @@ object Card extends HasAccessibleName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-card")
- val id: ReactiveProp[String, String] = idAttr
-
object slots {
/** Note: Use ui5-card-header for the intended design. */
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CardHeader.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CardHeader.scala
index 46c54ea..f1e6c0b 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CardHeader.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CardHeader.scala
@@ -11,13 +11,14 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-card-header is a component, meant to be used as a header of the ui5-card component.
*
* @see
* the doc for more information.
*/
-object CardHeader {
+object CardHeader extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -34,15 +35,13 @@ object CardHeader {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-card-header")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val interactive: ReactiveHtmlAttr[Boolean] = customHtmlAttr("interactive", BooleanAsAttrPresenceCodec)
- val interactive: ReactiveHtmlAttr[Boolean] = customHtmlAttr("interactive", BooleanAsAttrPresenceCodec)
+ lazy val status: ReactiveHtmlAttr[String] = customHtmlAttr("status", StringAsIsCodec)
- val status: ReactiveHtmlAttr[String] = customHtmlAttr("status", StringAsIsCodec)
+ lazy val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
- val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
-
- val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
object slots {
val action: Slot = new Slot("action")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Carousel.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Carousel.scala
index 1a583aa..8a8c63e 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Carousel.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Carousel.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The Carousel allows the user to browse through a set of items. The component is mostly used for showing a gallery of
* images, but can hold any other HTML element.
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object Carousel {
+object Carousel extends WebComponent {
//noinspection ScalaUnusedSymbol
@js.native
@@ -40,21 +41,20 @@ object Carousel {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-carousel")
- val id: ReactiveProp[String, String] = idAttr
-
- val arrowsPlacement: ReactiveHtmlAttr[CarouselArrowsPlacement] =
+ lazy val arrowsPlacement: ReactiveHtmlAttr[CarouselArrowsPlacement] =
customHtmlAttr("arrows-placement", CarouselArrowsPlacement.AsStringCodec)
- val cyclic: ReactiveHtmlAttr[Boolean] = customHtmlAttr("cyclic", BooleanAsAttrPresenceCodec)
+ lazy val cyclic: ReactiveHtmlAttr[Boolean] = customHtmlAttr("cyclic", BooleanAsAttrPresenceCodec)
- val hideNavigationArrows: ReactiveHtmlAttr[Boolean] =
+ lazy val hideNavigationArrows: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("hide-navigation-arrows", BooleanAsAttrPresenceCodec)
- val hidePageIndicator: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-page-indicator", BooleanAsAttrPresenceCodec)
+ lazy val hidePageIndicator: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("hide-page-indicator", BooleanAsAttrPresenceCodec)
- val itemsPerPageL: ReactiveHtmlAttr[Int] = customHtmlAttr("items-per-page-l", IntAsStringCodec)
- val itemsPerPageM: ReactiveHtmlAttr[Int] = customHtmlAttr("items-per-page-m", IntAsStringCodec)
- val itemsPerPageS: ReactiveHtmlAttr[Int] = customHtmlAttr("items-per-page-s", IntAsStringCodec)
+ lazy val itemsPerPageL: ReactiveHtmlAttr[Int] = customHtmlAttr("items-per-page-l", IntAsStringCodec)
+ lazy val itemsPerPageM: ReactiveHtmlAttr[Int] = customHtmlAttr("items-per-page-m", IntAsStringCodec)
+ lazy val itemsPerPageS: ReactiveHtmlAttr[Int] = customHtmlAttr("items-per-page-s", IntAsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CheckBox.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CheckBox.scala
index 3c19443..41070a7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CheckBox.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CheckBox.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Allows the user to set a binary value, such as true/false or yes/no for an item.
*
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object CheckBox extends HasIcon with HasAccessibleName with HasName with HasText {
+object CheckBox extends WebComponent with HasIcon with HasAccessibleName with HasName with HasText {
@js.native
trait RawElement extends js.Object {
@@ -37,17 +38,15 @@ object CheckBox extends HasIcon with HasAccessibleName with HasName with HasText
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-checkbox")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val checked: ReactiveHtmlAttr[Boolean] = customHtmlAttr("checked", BooleanAsAttrPresenceCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val indeterminate: ReactiveHtmlAttr[Boolean] = customHtmlAttr("indeterminate", BooleanAsAttrPresenceCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val checked: ReactiveHtmlAttr[Boolean] = customHtmlAttr("checked", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val indeterminate: ReactiveHtmlAttr[Boolean] = customHtmlAttr("indeterminate", BooleanAsAttrPresenceCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
-
- val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+ lazy val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalette.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalette.scala
index 7d99740..baa2b52 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalette.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalette.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-color-palette provides the users with a range of predefined colors. The colors are fixed and do not change
* with the theme.
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ColourPalette {
+object ColourPalette extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -37,8 +38,6 @@ object ColourPalette {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-color-palette")
- val id: ReactiveProp[String, String] = idAttr
-
object slots {}
object events {
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPaletteItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPaletteItem.scala
index f80faa2..288f131 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPaletteItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPaletteItem.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-color-palette-item component represents a color in the the ui5-color-palette.
*
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ColourPaletteItem {
+object ColourPaletteItem extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -36,9 +37,7 @@ object ColourPaletteItem {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-color-palette-item")
- val id: ReactiveProp[String, String] = idAttr
-
- val value: ReactiveHtmlAttr[Colour] = customHtmlAttr("value", Colour.AsStringCodec)
+ lazy val value: ReactiveHtmlAttr[Colour] = customHtmlAttr("value", Colour.AsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalettePopover.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalettePopover.scala
index 08b5d0d..1b41b54 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalettePopover.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPalettePopover.scala
@@ -13,6 +13,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Represents a predefined range of colors for easier selection. Overview The ColorPalettePopover provides the users
* with a slot to predefine colors. You can customize them with the use of the colors property. You can specify a
@@ -24,7 +25,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ColourPalettePopover {
+object ColourPalettePopover extends WebComponent {
//noinspection ScalaUnusedSymbol
@js.native
@@ -44,11 +45,10 @@ object ColourPalettePopover {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-color-palette-popover")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val defaultColour: ReactiveHtmlAttr[Colour] = customHtmlAttr("default-color", Colour.AsStringCodec)
- val defaultColour: ReactiveHtmlAttr[Colour] = customHtmlAttr("default-color", Colour.AsStringCodec)
-
- val showDefaultColour: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-default-color", BooleanAsAttrPresenceCodec)
+ lazy val showDefaultColour: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("show-default-color", BooleanAsAttrPresenceCodec)
/** This import is required for the `showMoreColours` property to work. */
@js.native
@@ -60,7 +60,8 @@ object ColourPalettePopover {
customHtmlAttr("show-more-colors", BooleanAsAttrPresenceCodec)
}
- val showRecentColours: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-recent-colors", BooleanAsAttrPresenceCodec)
+ lazy val showRecentColours: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("show-recent-colors", BooleanAsAttrPresenceCodec)
object slots {}
@@ -75,4 +76,11 @@ object ColourPalettePopover {
def getColourPalettePopoverById(id: String): Option[Ref] =
Option(dom.document.getElementById(id)).map(_.asInstanceOf[Ref])
+ /** [[Observer]] you can feed a popover ref and a [[dom.HTMLElement]] to open the popover at the element. */
+ val showAtObserver: Observer[(Ref, dom.HTMLElement)] = Observer(_ showAt _)
+
+ /** [[Mod]] for [[ColourPalettePopover]]s opening them each time the stream emits an opener [[dom.HTMLElement]] */
+ def showAtFromEvents(openerEvents: EventStream[dom.HTMLElement]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => openerEvents.map(el.ref -> _) --> showAtObserver)
+
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPicker.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPicker.scala
index e885a00..524cd99 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPicker.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ColourPicker.scala
@@ -13,6 +13,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.WebComponent
/** The ui5-badge is a small non-interactive component which contains text information and color chosen from a list of
* predefined color schemes. It serves the purpose to attract the user attention to some piece of information (state,
@@ -22,7 +23,7 @@ import scala.scalajs.js.annotation.{JSImport, JSName}
* the doc for more
* information.
*/
-object ColourPicker {
+object ColourPicker extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -48,9 +49,7 @@ object ColourPicker {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-color-picker")
- val id: ReactiveProp[String, String] = idAttr
-
- val colour: ReactiveHtmlAttr[Colour] = customHtmlAttr("color", Colour.AsStringCodec)
+ lazy val colour: ReactiveHtmlAttr[Colour] = customHtmlAttr("color", Colour.AsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBox.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBox.scala
index b48fe8c..b0b31e3 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBox.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBox.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-combobox component represents a drop-down menu with a list of the available options and a text input field
* to narrow down the options. It is commonly used to enable users to select an option from a predefined list.
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ComboBox extends HasAccessibleName with HasValue {
+object ComboBox extends WebComponent with HasAccessibleName with HasValue {
@js.native
trait RawElement extends js.Object {}
@@ -37,15 +38,13 @@ object ComboBox extends HasAccessibleName with HasValue {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-combobox")
- val id: ReactiveProp[String, String] = idAttr
-
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val filter: ReactiveHtmlAttr[ComboBoxFilter] = customHtmlAttr("filter", ComboBoxFilter.AsStringCodec)
- val loading: ReactiveHtmlAttr[Boolean] = customHtmlAttr("loading", BooleanAsAttrPresenceCodec)
- val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val filter: ReactiveHtmlAttr[ComboBoxFilter] = customHtmlAttr("filter", ComboBoxFilter.AsStringCodec)
+ lazy val loading: ReactiveHtmlAttr[Boolean] = customHtmlAttr("loading", BooleanAsAttrPresenceCodec)
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
object slots {
val default: Slot = new Slot("default")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxGroupItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxGroupItem.scala
index e441512..58ac700 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxGroupItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxGroupItem.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-combobox-group-item is type of suggestion item, that can be used to split the ui5-combobox suggestions into
* groups.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ComboBoxGroupItem extends HasText {
+object ComboBoxGroupItem extends WebComponent with HasText {
@js.native
trait RawElement extends js.Object {
@@ -39,8 +40,6 @@ object ComboBoxGroupItem extends HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-cb-group-item")
- val id: ReactiveProp[String, String] = idAttr
-
object slots {}
object events {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxItem.scala
index b30050b..a3fef41 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ComboBoxItem.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-cb-item represents the item for a ui5-combobox.
*
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ComboBoxItem {
+object ComboBoxItem extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -38,10 +39,8 @@ object ComboBoxItem {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-cb-item")
- val id: ReactiveProp[String, String] = idAttr
-
- val text: ReactiveHtmlAttr[String] = customHtmlAttr("text", StringAsIsCodec)
- val additionalText: ReactiveHtmlAttr[String] = customHtmlAttr("additional-text", StringAsIsCodec)
+ lazy val text: ReactiveHtmlAttr[String] = customHtmlAttr("text", StringAsIsCodec)
+ lazy val additionalText: ReactiveHtmlAttr[String] = customHtmlAttr("additional-text", StringAsIsCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CustomListItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CustomListItem.scala
new file mode 100644
index 0000000..f0bf51f
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/CustomListItem.scala
@@ -0,0 +1,55 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+import be.doeraene.webcomponents.ui5.configkeys.ListItemType
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+
+/** A component to be used as custom list item within the ui5-list the same way as the standard ui5-li. The component
+ * accepts arbitrary HTML content to allow full customization.
+ *
+ * @see
+ * the doc for more information.
+ */
+object CustomListItem extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/CustomListItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = CustomListItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-li-custom")
+
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+
+ lazy val tpe: ReactiveHtmlAttr[ListItemType] = customHtmlAttr("type", ListItemType.AsStringCodec)
+
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+
+ object slots {}
+
+ object events {
+ val onDetailClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("detail-click")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(CustomListItem)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DatePicker.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DatePicker.scala
index 4e82b33..075eee3 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DatePicker.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DatePicker.scala
@@ -13,6 +13,8 @@ import org.scalajs.dom
import java.time.LocalDate
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** The ui5-date-picker component provides an input field with assigned calendar which opens on user action.
*
@@ -20,7 +22,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object DatePicker extends HasOnClick with HasAccessibleName with HasName with HasValue {
+object DatePicker extends WebComponent with HasAccessibleName with HasName with HasValue {
//noinspection ScalaUnusedSymbol
@js.native
@@ -63,28 +65,26 @@ object DatePicker extends HasOnClick with HasAccessibleName with HasName with Ha
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-date-picker")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val hideWeekNumbers: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-week-numbers", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val hideWeekNumbers: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-week-numbers", BooleanAsAttrPresenceCodec)
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
- val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+ lazy val formatPattern: ReactiveHtmlAttr[String] = customHtmlAttr("format-pattern", StringAsIsCodec)
- val formatPattern: ReactiveHtmlAttr[String] = customHtmlAttr("format-pattern", StringAsIsCodec)
+ lazy val maxDate: ReactiveHtmlAttr[LocalDate] = customHtmlAttr("max-date", LocalDateCodec)
+ lazy val minDate: ReactiveHtmlAttr[LocalDate] = customHtmlAttr("min-date", LocalDateCodec)
- val maxDate: ReactiveHtmlAttr[LocalDate] = customHtmlAttr("max-date", LocalDateCodec)
- val minDate: ReactiveHtmlAttr[LocalDate] = customHtmlAttr("min-date", LocalDateCodec)
-
- val primaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ lazy val primaryCalendarType: ReactiveHtmlAttr[CalendarType] =
customHtmlAttr("primary-calendar-type", CalendarType.AsStringCodec)
- val secondaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ lazy val secondaryCalendarType: ReactiveHtmlAttr[CalendarType] =
customHtmlAttr("secondary-calendar-type", CalendarType.AsStringCodec)
object slots {
@@ -92,19 +92,19 @@ object DatePicker extends HasOnClick with HasAccessibleName with HasName with Ha
}
object events {
- @js.native
- sealed trait EventData extends js.Object {
- def value: String = js.native
- def valid: Boolean = js.native
+
+ trait DateEventData extends js.Object {
+ def value: String
+ def valid: Boolean
}
- val onChange = new EventProp[dom.Event & HasDetail[EventData]]("change")
- val onInput = new EventProp[dom.Event & HasDetail[EventData]]("input")
+ val onChange = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[DateEventData]]("change")
+ val onInput = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[DateEventData]]("input")
- val onValidDateChange: EventProcessor[dom.Event & HasDetail[EventData], LocalDate] =
+ val onValidDateChange: EventProcessor[EventWithPreciseTarget[Ref] & HasDetail[DateEventData], LocalDate] =
onChange.map(_.detail).filter(_.valid).map(_.value).map(stringToLocalDate)
- val onValidDateInput: EventProcessor[dom.Event & HasDetail[EventData], LocalDate] =
+ val onValidDateInput: EventProcessor[EventWithPreciseTarget[Ref] & HasDetail[DateEventData], LocalDate] =
onInput.map(_.detail).filter(_.valid).map(_.value).map(stringToLocalDate)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DateRangePicker.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DateRangePicker.scala
new file mode 100644
index 0000000..b9561d1
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DateRangePicker.scala
@@ -0,0 +1,112 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.*
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The DateRangePicker enables the users to enter a localized date range using touch, mouse, keyboard input, or by
+ * selecting a date range in the calendar.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object DateRangePicker extends WebComponent with HasAccessibleName with HasName with HasValue {
+
+ @js.native
+ trait RawElement extends js.Object {
+ @JSName("endDateValue")
+ def endDateValueJS: js.Date = js.native
+
+ @JSName("startDateValue")
+ def startDateValueJS: js.Date = js.native
+
+ def closePicker(): Unit = js.native
+
+ def formatValue(date: js.Date): String = js.native
+
+ def isInValidRange(value: String): Boolean = js.native
+
+ def isOpen(): Boolean = js.native
+
+ def isValid(value: String): Boolean = js.native
+
+ def openPicker(): Unit = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/DateRangePicker.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = DateRangePicker.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-daterange-picker")
+
+ lazy val delimiter: ReactiveHtmlAttr[String] = customHtmlAttr("delimiter", StringAsIsCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val hideWeekNumbers: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-week-numbers", BooleanAsAttrPresenceCodec)
+
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+
+ lazy val formatPattern: ReactiveHtmlAttr[String] = customHtmlAttr("format-pattern", StringAsIsCodec)
+
+ lazy val maxDateRaw: ReactiveHtmlAttr[String] = customHtmlAttr("max-date", StringAsIsCodec)
+
+ lazy val minDateRaw: ReactiveHtmlAttr[String] = customHtmlAttr("min-date", StringAsIsCodec)
+
+ lazy val primaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ customHtmlAttr("primary-calendar-type", CalendarType.AsStringCodec)
+
+ lazy val secondaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ customHtmlAttr("secondary-calendar-type", CalendarType.AsStringCodec)
+
+ object slots {
+ val valueStateMessage: Slot = Slot("valueStateMessage")
+ }
+
+ object events {
+ import DatePicker.events.DateEventData
+ val onChange = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[DateEventData]]("change")
+ val onInput = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[DateEventData]]("input")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(DateRangePicker)): _*)
+
+ /** You can feed [[DateRangePicker]] refs to this observer in order to close them. */
+ val closePickerObserver: Observer[Ref] = Observer(_.closePicker())
+
+ /** creates a [[Mod]] for your [[DateRangePicker]]s to close them when the stream emit. */
+ def closePickerFromEvents(stream: EventStream[Unit]) =
+ inContext[ReactiveHtmlElement[Ref]](el => stream.mapTo(el.ref) --> closePickerObserver)
+
+ /** You can feed [[DateRangePicker]] refs to this observer in order to open them. */
+ val openPickerObserver: Observer[Ref] = Observer(_.openPicker())
+
+ /** creates a [[Mod]] for your [[DateRangePicker]]s to close them when the stream emit. */
+ def openPickerFromEvents(stream: EventStream[Unit]) =
+ inContext[ReactiveHtmlElement[Ref]](el => stream.mapTo(el.ref) --> openPickerObserver)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DateTimePicker.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DateTimePicker.scala
new file mode 100644
index 0000000..ef38c6c
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DateTimePicker.scala
@@ -0,0 +1,107 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.*
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The DateTimePicker component alows users to select both date (day, month and year) and time (hours, minutes and
+ * seconds) and for the purpose it consists of input field and Date/Time picker.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object DateTimePicker extends WebComponent with HasAccessibleName with HasName with HasValue {
+
+ @js.native
+ trait RawElement extends js.Object {
+ @JSName("dateValue")
+ def dateValueJS: js.Date = js.native
+
+ def closePicker(): Unit = js.native
+
+ def formatValue(date: js.Date): String = js.native
+
+ def isInValidRange(value: String): Boolean = js.native
+
+ def isOpen(): Boolean = js.native
+
+ def isValid(value: String): Boolean = js.native
+
+ def openPicker(): Unit = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/DateTimePicker.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = DateTimePicker.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-datetime-picker")
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val hideWeekNumbers: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-week-numbers", BooleanAsAttrPresenceCodec)
+
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+
+ lazy val formatPattern: ReactiveHtmlAttr[String] = customHtmlAttr("format-pattern", StringAsIsCodec)
+
+ lazy val maxDateRaw: ReactiveHtmlAttr[String] = customHtmlAttr("max-date", StringAsIsCodec)
+
+ lazy val minDateRaw: ReactiveHtmlAttr[String] = customHtmlAttr("min-date", StringAsIsCodec)
+
+ lazy val primaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ customHtmlAttr("primary-calendar-type", CalendarType.AsStringCodec)
+
+ lazy val secondaryCalendarType: ReactiveHtmlAttr[CalendarType] =
+ customHtmlAttr("secondary-calendar-type", CalendarType.AsStringCodec)
+
+ object slots {
+ val valueStateMessage: Slot = Slot("valueStateMessage")
+ }
+
+ object events {
+ import DatePicker.events.DateEventData
+ val onChange = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[DateEventData]]("change")
+ val onInput = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[DateEventData]]("input")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(DateTimePicker)): _*)
+
+ /** You can feed [[DateTimePicker]] refs to this observer in order to close them. */
+ val closePickerObserver: Observer[Ref] = Observer(_.closePicker())
+
+ /** creates a [[Mod]] for your [[DateTimePicker]]s to close them when the stream emit. */
+ def closePickerFromEvents(stream: EventStream[Unit]) =
+ inContext[ReactiveHtmlElement[Ref]](el => stream.mapTo(el.ref) --> closePickerObserver)
+
+ /** You can feed [[DateTimePicker]] refs to this observer in order to open them. */
+ val openPickerObserver: Observer[Ref] = Observer(_.openPicker())
+
+ /** creates a [[Mod]] for your [[DateTimePicker]]s to close them when the stream emit. */
+ def openPickerFromEvents(stream: EventStream[Unit]) =
+ inContext[ReactiveHtmlElement[Ref]](el => stream.mapTo(el.ref) --> openPickerObserver)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Dialog.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Dialog.scala
index b81c3e2..4ef33ac 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Dialog.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Dialog.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-dialog component is used to temporarily display some information in a size-limited window in front of the
* regular app screen.
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Dialog {
+object Dialog extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -43,17 +44,15 @@ object Dialog {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-dialog")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
- val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
-
- val resizable: ReactiveHtmlAttr[Boolean] = customHtmlAttr("resizable", BooleanAsAttrPresenceCodec)
- val stretch: ReactiveHtmlAttr[Boolean] = customHtmlAttr("stretch", BooleanAsAttrPresenceCodec)
- val draggable: ReactiveHtmlAttr[Boolean] = customHtmlAttr("draggable", BooleanAsAttrPresenceCodec)
- val open: ReactiveHtmlAttr[Boolean] = customHtmlAttr("open", BooleanAsAttrPresenceCodec)
- val preventFocusRestore: ReactiveHtmlAttr[Boolean] =
+ lazy val resizable: ReactiveHtmlAttr[Boolean] = customHtmlAttr("resizable", BooleanAsAttrPresenceCodec)
+ lazy val stretch: ReactiveHtmlAttr[Boolean] = customHtmlAttr("stretch", BooleanAsAttrPresenceCodec)
+ lazy val draggable: ReactiveHtmlAttr[Boolean] = customHtmlAttr("draggable", BooleanAsAttrPresenceCodec)
+ lazy val open: ReactiveHtmlAttr[Boolean] = customHtmlAttr("open", BooleanAsAttrPresenceCodec)
+ lazy val preventFocusRestore: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("prevent-focus-restore", BooleanAsAttrPresenceCodec)
- val initialFocus: ReactiveHtmlAttr[String] = customHtmlAttr("initial-focus", StringAsIsCodec)
+ lazy val initialFocus: ReactiveHtmlAttr[String] = customHtmlAttr("initial-focus", StringAsIsCodec)
//noinspection TypeAnnotation
object slots {
@@ -63,7 +62,22 @@ object Dialog {
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Dialog)): _*)
- def getDialogById(id: String): Option[dom.HTMLElement & RawElement] =
- Option(dom.document.getElementById(id)).map(_.asInstanceOf[dom.HTMLElement & RawElement])
+ def getDialogById(id: String): Option[Ref] =
+ Option(dom.document.getElementById(id)).map(_.asInstanceOf[Ref])
+
+ /** [[Observer]] you can feed to open the Dialog. */
+ val showObserver: Observer[Ref] = Observer(_.show())
+
+ def showFromEvents(openerEvents: EventStream[Unit]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => openerEvents.mapTo(el.ref) --> showObserver)
+
+ /** [[Observer]] you can feed a [[Dialog]] ref to close it. */
+ val closeObserver: Observer[Ref] = Observer(_.close())
+
+ def closeFromEvents(closeEvents: EventStream[Unit]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => closeEvents.mapTo(el.ref) --> closeObserver)
+
+ /** [[Observer]] you can feed a [[Dialog]] ref to apply focus to it. */
+ val applyFocusObserver: Observer[Ref] = Observer(_.applyFocus())
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DynamicSideContent.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DynamicSideContent.scala
new file mode 100644
index 0000000..776aab4
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/DynamicSideContent.scala
@@ -0,0 +1,81 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{SideContentFallDown, SideContentPosition, SideContentVisibility}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The DynamicSideContent (ui5-dynamic-side-content) is a layout component that allows additional content to be
+ * displayed in a way that flexibly adapts to different screen sizes. The side content appears in a container next to
+ * or directly below the main content (it doesn't overlay). When the side content is triggered, the main content
+ * becomes narrower (if appearing side-by-side). The side content contains a separate scrollbar when appearing next to
+ * the main content.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object DynamicSideContent extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def toggleContents(): Unit = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/DynamicSideContent.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = DynamicSideContent.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-dynamic-side-content")
+
+ lazy val equalSplit: ReactiveHtmlAttr[Boolean] = customHtmlAttr("equal-split", BooleanAsAttrPresenceCodec)
+
+ lazy val hideMainContent: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-main-content", BooleanAsAttrPresenceCodec)
+
+ lazy val hideSideContent: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-side-content", BooleanAsAttrPresenceCodec)
+
+ lazy val sideContentFallDown: ReactiveHtmlAttr[SideContentFallDown] =
+ customHtmlAttr("side-content-fall-down", SideContentFallDown.AsStringCodec)
+
+ lazy val sideContentPosition: ReactiveHtmlAttr[SideContentPosition] =
+ customHtmlAttr("side-content-position", SideContentPosition.AsStringCodec)
+
+ lazy val sideContentVisibility: ReactiveHtmlAttr[SideContentVisibility] =
+ customHtmlAttr("side-content-visibility", SideContentVisibility.AsStringCodec)
+
+ object slots {
+ val sideContent: Slot = new Slot("sideContent")
+ }
+
+ object events {
+ trait LayoutChangeInfo extends js.Object {
+ def currentBreakpoint: String
+ def previousBreakpoint: String
+ def mainContentVisible: Boolean
+ def sideContentVisible: Boolean
+ }
+
+ val onLayoutChange: EventProp[EventWithPreciseTarget[Ref] & HasDetail[LayoutChangeInfo]] = new EventProp(
+ "layout-change"
+ )
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(DynamicSideContent)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FileUploader.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FileUploader.scala
new file mode 100644
index 0000000..43ad34f
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FileUploader.scala
@@ -0,0 +1,87 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import org.scalajs.dom.FileList
+import be.doeraene.webcomponents.ui5.configkeys.ValueState
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-file-uploader opens a file explorer dialog and enables users to upload files. The component consists of
+ * input field, but you can provide an HTML element by your choice to trigger the file upload, by using the default
+ * slot. Furthermore, you can set the property "hideInput" to "true" to hide the input field. To get all selected
+ * files, you can simply use the read-only "files" property. To restrict the types of files the user can select, you
+ * can use the "accept" property. And, similar to all input based components, the FileUploader supports "valueState",
+ * "placeholder", "name", and "disabled" properties. For the ui5-file-uploader
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object FileUploader extends WebComponent with HasName with HasValue {
+
+ @js.native
+ trait RawElement extends js.Object {
+ @JSName("files")
+ def filesJS: FileList = js.native
+ }
+
+ object RawElement {
+ extension (element: RawElement) def files: List[dom.File] = element.filesJS.toList
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/FileUploader.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = FileUploader.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-file-uploader")
+
+ lazy val accept: ReactiveHtmlAttr[List[String]] = customHtmlAttr("accept", ListCodec(StringAsIsCodec))
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val hideInput: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-input", BooleanAsAttrPresenceCodec)
+
+ lazy val multiple: ReactiveHtmlAttr[Boolean] = customHtmlAttr("multiple", BooleanAsAttrPresenceCodec)
+
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+
+ object slots {
+ val valueStateMessage: Slot = new Slot("valueStateMessage")
+ }
+
+ object events {
+
+ trait HasFiles extends js.Object {
+ @JSName("files")
+ def filesJS: FileList
+ }
+
+ object HasFiles {
+ extension (element: HasFiles) def files: List[dom.File] = element.filesJS.toList
+ }
+
+ val onChange: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasFiles]] = new EventProp("change")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(FileUploader)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FilterItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FilterItem.scala
new file mode 100644
index 0000000..33bcb51
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FilterItem.scala
@@ -0,0 +1,48 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** For the ui5-filter-item
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object FilterItem extends WebComponent with HasText {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/FilterItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = FilterItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-filter-item")
+
+ object slots {
+ val values: Slot = Slot("values")
+ }
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(FilterItem)): _*)
+
+ def option: FilterItemOption.type = FilterItemOption
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FilterItemOption.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FilterItemOption.scala
new file mode 100644
index 0000000..b1f3453
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FilterItemOption.scala
@@ -0,0 +1,46 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** For the filter-item-option
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object FilterItemOption extends WebComponent with HasText {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/FilterItemOption.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = FilterItemOption.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-filter-item-option")
+
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(FilterItemOption)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FlexibleColumnLayout.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FlexibleColumnLayout.scala
index 1739bfd..58fa8ab 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FlexibleColumnLayout.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/FlexibleColumnLayout.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The FlexibleColumnLayout implements the master-detail-detail paradigm by displaying up to three pages in separate
* columns.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for
* more information.
*/
-object FlexibleColumnLayout {
+object FlexibleColumnLayout extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -54,11 +55,9 @@ object FlexibleColumnLayout {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-flexible-column-layout")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val hideArrows: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-arrows", BooleanAsAttrPresenceCodec)
- val hideArrows: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-arrows", BooleanAsAttrPresenceCodec)
-
- val layout: ReactiveHtmlAttr[FCLLayout] = customHtmlAttr("layout", FCLLayout.AsStringCodec)
+ lazy val layout: ReactiveHtmlAttr[FCLLayout] = customHtmlAttr("layout", FCLLayout.AsStringCodec)
val onLayoutChanged = new EventProp[dom.Event]("layout-change")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasAccessibleName.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasAccessibleName.scala
index 1a1c957..4938fc6 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasAccessibleName.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasAccessibleName.scala
@@ -5,6 +5,6 @@ import com.raquo.laminar.api.L.*
import com.raquo.laminar.keys.ReactiveHtmlAttr
trait HasAccessibleName {
- val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
- val accessibleNameRef: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name-ref", StringAsIsCodec)
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+ lazy val accessibleNameRef: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name-ref", StringAsIsCodec)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDescription.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDescription.scala
index 216fccb..a0b531c 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDescription.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDescription.scala
@@ -5,5 +5,5 @@ import com.raquo.laminar.api.L.*
import com.raquo.laminar.keys.ReactiveHtmlAttr
trait HasDescription {
- val description: ReactiveHtmlAttr[String] = customHtmlAttr("description", StringAsIsCodec)
+ lazy val description: ReactiveHtmlAttr[String] = customHtmlAttr("description", StringAsIsCodec)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDesign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDesign.scala
deleted file mode 100644
index 7aaa28a..0000000
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasDesign.scala
+++ /dev/null
@@ -1,7 +0,0 @@
-package be.doeraene.webcomponents.ui5
-
-import be.doeraene.webcomponents.ui5.configkeys.ButtonDesign
-import com.raquo.laminar.api.L.customHtmlAttr
-import com.raquo.laminar.keys.ReactiveHtmlAttr
-
-trait HasDesign {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasIcon.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasIcon.scala
index 9cff1ba..e38eac7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasIcon.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasIcon.scala
@@ -5,5 +5,5 @@ import com.raquo.laminar.keys.ReactiveHtmlAttr
import com.raquo.laminar.api.L.*
trait HasIcon {
- val icon: ReactiveHtmlAttr[IconName] = customHtmlAttr("icon", IconName.AsStringCodec)
+ lazy val icon: ReactiveHtmlAttr[IconName] = customHtmlAttr("icon", IconName.AsStringCodec)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasName.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasName.scala
index 5206a69..277588a 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasName.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasName.scala
@@ -5,5 +5,5 @@ import com.raquo.laminar.api.L.*
import com.raquo.laminar.keys.ReactiveHtmlAttr
trait HasName {
- val name: ReactiveHtmlAttr[String] = customHtmlAttr("name", StringAsIsCodec)
+ lazy val name: ReactiveHtmlAttr[String] = customHtmlAttr("name", StringAsIsCodec)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnChange.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnChange.scala
deleted file mode 100644
index 24cead5..0000000
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnChange.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package be.doeraene.webcomponents.ui5
-
-import com.raquo.laminar.api.L.EventProp
-import org.scalajs.dom
-
-trait HasOnChange {
- val onChange = new EventProp[dom.Event]("change")
-}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnClick.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnClick.scala
deleted file mode 100644
index e434a53..0000000
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnClick.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package be.doeraene.webcomponents.ui5
-
-import com.raquo.laminar.api.L.EventProp
-import org.scalajs.dom
-
-trait HasOnClick {
- val onClick = new EventProp[dom.MouseEvent]("click")
-}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnInput.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnInput.scala
deleted file mode 100644
index ea61b0e..0000000
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasOnInput.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package be.doeraene.webcomponents.ui5
-
-import com.raquo.laminar.api.L.EventProp
-import org.scalajs.dom
-
-trait HasOnInput {
- val onInput = new EventProp[dom.Event]("input")
-}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasText.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasText.scala
index cc43661..7765572 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasText.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasText.scala
@@ -5,5 +5,5 @@ import com.raquo.laminar.api.L.*
import com.raquo.laminar.keys.ReactiveHtmlAttr
trait HasText {
- val text: ReactiveHtmlAttr[String] = customHtmlAttr("text", StringAsIsCodec)
+ lazy val text: ReactiveHtmlAttr[String] = customHtmlAttr("text", StringAsIsCodec)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasValue.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasValue.scala
index 05dda25..ae8fff6 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasValue.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/HasValue.scala
@@ -5,5 +5,5 @@ import com.raquo.laminar.api.L.*
import com.raquo.laminar.keys.ReactiveHtmlAttr
trait HasValue {
- val value: ReactiveHtmlAttr[String] = customHtmlAttr("value", StringAsIsCodec)
+ lazy val value: ReactiveHtmlAttr[String] = customHtmlAttr("value", StringAsIsCodec)
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Icon.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Icon.scala
index b7c63fc..0f78b0c 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Icon.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Icon.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-icon component represents an SVG icon. There are two main scenarios how the ui5-icon component is used: as a
* purely decorative element; or as a visually appealing clickable area in the form of an icon button.
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Icon extends HasAccessibleName {
+object Icon extends WebComponent with HasAccessibleName {
@js.native
trait RawElement extends js.Object {}
@@ -35,14 +36,12 @@ object Icon extends HasAccessibleName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-icon")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val name: ReactiveHtmlAttr[IconName] = customHtmlAttr("name", IconName.AsStringCodec)
- val name: ReactiveHtmlAttr[IconName] = customHtmlAttr("name", IconName.AsStringCodec)
-
- val interactive: ReactiveHtmlAttr[Boolean] =
+ lazy val interactive: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("interactive", BooleanAsAttrPresenceCodec)
- val showTooltip: ReactiveHtmlAttr[Boolean] =
+ lazy val showTooltip: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("show-tooltip", BooleanAsAttrPresenceCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Icon)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/IllustratedMessage.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/IllustratedMessage.scala
index bcdee26..37b7db7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/IllustratedMessage.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/IllustratedMessage.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.internal.Slot
+import be.doeraene.webcomponents.WebComponent
/** Simple UI button
*
@@ -22,7 +23,7 @@ import be.doeraene.webcomponents.ui5.internal.Slot
* the doc for more
* information.
*/
-object IllustratedMessage extends HasIcon with HasOnClick {
+object IllustratedMessage extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -39,11 +40,10 @@ object IllustratedMessage extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-illustrated-message")
- val id: ReactiveProp[String, String] = idAttr
-
- val name: ReactiveHtmlAttr[IllustrationMessageType] = customHtmlAttr("name", IllustrationMessageType.AsStringCodec)
- val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
- val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
+ lazy val name: ReactiveHtmlAttr[IllustrationMessageType] =
+ customHtmlAttr("name", IllustrationMessageType.AsStringCodec)
+ lazy val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
object slots {
val subtitle: Slot = new Slot("subtitle")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Input.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Input.scala
index 2437c99..b0b1622 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Input.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Input.scala
@@ -12,16 +12,22 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.internal.Slot
import be.doeraene.webcomponents.ui5.eventtypes.{HasDetail, HasItem, HasTargetRef}
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
-/** Simple UI button
+/** The ui5-input component allows the user to enter and edit text or numeric values in one line.
+ *
+ * Additionally, you can provide suggestionItems, that are displayed in a popover right under the input.
*
* @see
* the doc for more information.
*/
-object Input extends HasOnClick with HasOnInput with HasOnChange with HasValue with HasAccessibleName {
+object Input extends WebComponent with HasValue with HasAccessibleName {
@js.native
trait RawElement extends js.Object {
+ def value: String = js.native
+
def openPicker(): Unit = js.native
}
@@ -37,22 +43,20 @@ object Input extends HasOnClick with HasOnInput with HasOnChange with HasValue w
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-input")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
- val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val tpe: ReactiveHtmlAttr[InputType] = customHtmlAttr("type", InputType.AsStringCodec)
- val tpe: ReactiveHtmlAttr[InputType] = customHtmlAttr("type", InputType.AsStringCodec)
+ lazy val maxLength: ReactiveHtmlAttr[Int] = customHtmlAttr("maxlength", IntAsStringCodec)
- val maxLength: ReactiveHtmlAttr[Int] = customHtmlAttr("maxlength", IntAsStringCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
-
- val showClearIcon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-clear-icon", BooleanAsAttrPresenceCodec)
- val showSuggestions: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-suggestions", BooleanAsAttrPresenceCodec)
+ lazy val showClearIcon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-clear-icon", BooleanAsAttrPresenceCodec)
+ lazy val showSuggestions: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-suggestions", BooleanAsAttrPresenceCodec)
object slots {
val valueStateMessage: Slot = new Slot("valueStateMessage")
@@ -61,7 +65,10 @@ object Input extends HasOnClick with HasOnInput with HasOnChange with HasValue w
val icon: Slot = new Slot("icon")
}
- object events extends HasOnChange with HasOnInput {
+ object events {
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+ val onInput: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("input")
+
val onSuggestionItemPreview =
new EventProp[dom.Event & HasDetail[HasTargetRef[dom.HTMLElement] & HasItem[SuggestionItem.RawElement]]](
"suggestion-item-preview"
@@ -72,6 +79,7 @@ object Input extends HasOnClick with HasOnInput with HasOnChange with HasValue w
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Input)): _*)
- def suggestion: SuggestionItem.type = SuggestionItem
+ def suggestion: SuggestionItem.type = SuggestionItem
+ def suggestionGroup: SuggestionGroupItem.type = SuggestionGroupItem
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Label.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Label.scala
index 40eb046..c550371 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Label.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Label.scala
@@ -10,13 +10,14 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.configkeys.WrappingType
+import be.doeraene.webcomponents.WebComponent
/** Simple UI button
*
* @see
* the doc for more information.
*/
-object Label extends HasIcon with HasOnClick {
+object Label extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -33,12 +34,10 @@ object Label extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-label")
- val id: ReactiveProp[String, String] = idAttr
-
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val showColon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-colon", BooleanAsAttrPresenceCodec)
- val forId: ReactiveHtmlAttr[String] = customHtmlAttr("for", StringAsIsCodec)
- val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val showColon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-colon", BooleanAsAttrPresenceCodec)
+ lazy val forId: ReactiveHtmlAttr[String] = customHtmlAttr("for", StringAsIsCodec)
+ lazy val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
val isRequired: Setter[HtmlElement] = required := true
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Link.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Link.scala
index eabbc5d..61f3ecd 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Link.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Link.scala
@@ -11,13 +11,15 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.configkeys.WrappingType
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** A link to another page.
*
* @see
* the doc for more information.
*/
-object Link extends HasDesign with HasIcon with HasOnClick with HasAccessibleName {
+object Link extends WebComponent with HasIcon with HasAccessibleName {
@js.native
trait RawElement extends js.Object {}
@@ -34,17 +36,21 @@ object Link extends HasDesign with HasIcon with HasOnClick with HasAccessibleNam
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-link")
- val id: ReactiveProp[String, String] = idAttr
-
- val disabled: ReactiveHtmlAttr[Boolean] =
+ lazy val disabled: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val href: ReactiveHtmlAttr[String] = customHtmlAttr("href", StringAsIsCodec)
- val target: ReactiveHtmlAttr[LinkTarget] = customHtmlAttr("target", LinkTarget.AsStringCodec)
+ lazy val href: ReactiveHtmlAttr[String] = customHtmlAttr("href", StringAsIsCodec)
+ lazy val target: ReactiveHtmlAttr[LinkTarget] = customHtmlAttr("target", LinkTarget.AsStringCodec)
- val design: ReactiveHtmlAttr[LinkDesign] =
+ lazy val design: ReactiveHtmlAttr[LinkDesign] =
customHtmlAttr("design", LinkDesign.AsStringCodec)
- val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+ lazy val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+
+ object slots {}
+
+ object events {
+ val onClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("click")
+ }
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Link)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ListItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ListItem.scala
index d37fcba..b165d5d 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ListItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ListItem.scala
@@ -10,6 +10,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-li represents the simplest type of item for a ui5-list. This is a list item, providing the most common use
* cases such as text, image and icon.
@@ -17,7 +18,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object ListItem extends HasIcon with HasDescription with HasAdditionalText {
+object ListItem extends WebComponent with HasIcon with HasDescription with HasAdditionalText {
@js.native
trait RawElement extends js.Object {}
@@ -34,17 +35,15 @@ object ListItem extends HasIcon with HasDescription with HasAdditionalText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-li")
- val id: ReactiveProp[String, String] = idAttr
-
- val additionalTextState: ReactiveHtmlAttr[ValueState] =
+ lazy val additionalTextState: ReactiveHtmlAttr[ValueState] =
customHtmlAttr("additional-text-state", ValueState.AsStringCodec)
- val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
- val image: ReactiveHtmlAttr[String] = customHtmlAttr("image", StringAsIsCodec)
+ lazy val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
+ lazy val image: ReactiveHtmlAttr[String] = customHtmlAttr("image", StringAsIsCodec)
- val tpe: ReactiveHtmlAttr[ListItemType] = customHtmlAttr("type", ListItemType.AsStringCodec)
+ lazy val tpe: ReactiveHtmlAttr[ListItemType] = customHtmlAttr("type", ListItemType.AsStringCodec)
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ListItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MediaGallery.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MediaGallery.scala
new file mode 100644
index 0000000..5f32814
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MediaGallery.scala
@@ -0,0 +1,81 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{
+ MediaGalleryLayout,
+ MediaGalleryMenuHorizontalAlign,
+ MediaGalleryMenuVerticalAlign
+}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-media-gallery component allows the user to browse through multimedia items. Currently, the supported items
+ * are images and videos. The items should be defined using the ui5-media-gallery-item component. The items are
+ * initially displayed as thumbnails. When the user selects a thumbnail, the corresponding item is displayed in larger
+ * size.
+ *
+ * The component is responsive by default and adjusts the position of the menu with respect to viewport size, but the
+ * application is able to further customize the layout via the provided API.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object MediaGallery extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/MediaGallery.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = MediaGallery.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-media-gallery")
+
+ lazy val interactiveDisplayArea: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("interactive-display-area", BooleanAsAttrPresenceCodec)
+
+ lazy val layout: ReactiveHtmlAttr[MediaGalleryLayout] =
+ customHtmlAttr("layout", MediaGalleryLayout.AsStringCodec)
+
+ lazy val menuHorizontalAlign: ReactiveHtmlAttr[MediaGalleryMenuHorizontalAlign] =
+ customHtmlAttr("menu-horizontal-align", MediaGalleryMenuHorizontalAlign.AsStringCodec)
+
+ lazy val menuVerticalAlign: ReactiveHtmlAttr[MediaGalleryMenuVerticalAlign] =
+ customHtmlAttr("menu-vertical-align", MediaGalleryMenuVerticalAlign.AsStringCodec)
+
+ lazy val showAllThumbnails: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("show-all-thumbnails", BooleanAsAttrPresenceCodec)
+
+ object slots {}
+
+ object events {
+ val onDisplayAreaClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("display-area-click")
+ val onOverflowClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("overflow-click")
+ val onSelectionChange: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[item.Ref]]] = new EventProp(
+ "selection-change"
+ )
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(MediaGallery)): _*)
+
+ def item: MediaGalleryItem.type = MediaGalleryItem
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MediaGalleryItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MediaGalleryItem.scala
new file mode 100644
index 0000000..2f324db
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MediaGalleryItem.scala
@@ -0,0 +1,58 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.MediaGalleryItemLayout
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-media-gallery-item web component represents the items displayed in the ui5-media-gallery web component.
+ *
+ * Note: ui5-media-gallery-item is not supported when used outside of ui5-media-gallery.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object MediaGalleryItem extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def selected: Boolean = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/MediaGalleryItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = MediaGalleryItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-media-gallery-item")
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val layout: ReactiveHtmlAttr[MediaGalleryItemLayout] =
+ customHtmlAttr("layout", MediaGalleryItemLayout.AsStringCodec)
+
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+
+ object slots {
+ val thumbnail: Slot = new Slot("thumbnail")
+ }
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(MediaGalleryItem)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Menu.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Menu.scala
index d163153..f9ccf00 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Menu.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Menu.scala
@@ -10,13 +10,14 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Simple UI button
*
* @see
* the doc for more information.
*/
-object Menu {
+object Menu extends WebComponent {
//noinspection ScalaUnusedSymbol
@js.native
@@ -38,9 +39,7 @@ object Menu {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-menu")
- val id: ReactiveProp[String, String] = idAttr
-
- val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("headerText", StringAsIsCodec)
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("headerText", StringAsIsCodec)
object events {
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MenuItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MenuItem.scala
index f350b25..58fe312 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MenuItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MenuItem.scala
@@ -9,6 +9,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** ui5-menu-item is the item to use inside a ui5-menu. An arbitrary hierarchy structure can be represented by
* recursively nesting menu items.
@@ -16,7 +17,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object MenuItem extends HasIcon with HasOnClick with HasText {
+object MenuItem extends WebComponent with HasIcon with HasText {
@js.native
trait RawElement extends js.Object {}
@@ -33,11 +34,9 @@ object MenuItem extends HasIcon with HasOnClick with HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-menu-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
-
- val startsSection: ReactiveHtmlAttr[Boolean] = customHtmlAttr("starts-section", BooleanAsAttrPresenceCodec)
+ lazy val startsSection: ReactiveHtmlAttr[Boolean] = customHtmlAttr("starts-section", BooleanAsAttrPresenceCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(MenuItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MessageStrip.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MessageStrip.scala
index 0402c8c..c3752a0 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MessageStrip.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MessageStrip.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-message-strip component enables the embedding of app-related messages. It displays 4 designs of messages,
* each with corresponding semantic color and icon: Information, Positive, Warning and Negative. Each message can have
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object MessageStrip {
+object MessageStrip extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -37,13 +38,11 @@ object MessageStrip {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-message-strip")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val design: ReactiveHtmlAttr[MessageStripDesign] = customHtmlAttr("design", MessageStripDesign.AsStringCodec)
- val design: ReactiveHtmlAttr[MessageStripDesign] = customHtmlAttr("design", MessageStripDesign.AsStringCodec)
+ lazy val hideCloseButton: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-close-button", BooleanAsAttrPresenceCodec)
- val hideCloseButton: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-close-button", BooleanAsAttrPresenceCodec)
-
- val hideIcon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-icon", BooleanAsAttrPresenceCodec)
+ lazy val hideIcon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-icon", BooleanAsAttrPresenceCodec)
object slots {
val icon: Slot = new Slot("icon")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBox.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBox.scala
index 1df404f..b0447e2 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBox.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBox.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-combobox component represents a drop-down menu with a list of the available options and a text input field
* to narrow down the options. It is commonly used to enable users to select an option from a predefined list.
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object MultiComboBox extends HasAccessibleName with HasValue {
+object MultiComboBox extends WebComponent with HasAccessibleName with HasValue {
@js.native
trait RawElement extends js.Object {}
@@ -37,15 +38,14 @@ object MultiComboBox extends HasAccessibleName with HasValue {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-multi-combobox")
- val id: ReactiveProp[String, String] = idAttr
-
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val filter: ReactiveHtmlAttr[ComboBoxFilter] = customHtmlAttr("filter", ComboBoxFilter.AsStringCodec)
- val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val allowCustomValues: ReactiveHtmlAttr[Boolean] = customHtmlAttr("allow-custom-values", BooleanAsAttrPresenceCodec)
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val filter: ReactiveHtmlAttr[ComboBoxFilter] = customHtmlAttr("filter", ComboBoxFilter.AsStringCodec)
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val allowCustomValues: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("allow-custom-values", BooleanAsAttrPresenceCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
object slots {
val default: Slot = new Slot("default")
@@ -63,6 +63,6 @@ object MultiComboBox extends HasAccessibleName with HasValue {
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(MultiComboBox)): _*)
- def item: MultiComboBoxItem.type = MultiComboBoxItem
- //def group: ComboBoxGroupItem.type = ComboBoxGroupItem // TODO
+ def item: MultiComboBoxItem.type = MultiComboBoxItem
+ def group: MultiComboBoxGroupItem.type = MultiComboBoxGroupItem
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxGroupItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxGroupItem.scala
new file mode 100644
index 0000000..7519b2d
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxGroupItem.scala
@@ -0,0 +1,39 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-mcb-group-item is type of suggestion item, that can be used to split the ui5-multi-combobox suggestions into
+ * groups.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object MultiComboBoxGroupItem extends WebComponent with HasText {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = MultiComboBoxGroupItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-mcb-group-item")
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(MultiComboBoxGroupItem)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxItem.scala
index 20940ab..6baa268 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiComboBoxItem.scala
@@ -2,7 +2,7 @@ package be.doeraene.webcomponents.ui5
import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
import be.doeraene.webcomponents.ui5.internal.Slot
-import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec, BooleanAsIsCodec}
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, BooleanAsIsCodec, StringAsIsCodec}
import com.raquo.laminar.api.L.*
import com.raquo.laminar.builders.HtmlTag
import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-cb-item represents the item for a ui5-combobox.
*
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object MultiComboBoxItem {
+object MultiComboBoxItem extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -38,10 +39,9 @@ object MultiComboBoxItem {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-mcb-item")
- val id: ReactiveProp[String, String] = idAttr
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
- val text: ReactiveHtmlAttr[String] = customHtmlAttr("text", StringAsIsCodec)
- val additionalText: ReactiveHtmlAttr[String] = customHtmlAttr("additional-text", StringAsIsCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+ lazy val text: ReactiveHtmlAttr[String] = customHtmlAttr("text", StringAsIsCodec)
+ lazy val additionalText: ReactiveHtmlAttr[String] = customHtmlAttr("additional-text", StringAsIsCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiInput.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiInput.scala
new file mode 100644
index 0000000..6672fa1
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/MultiInput.scala
@@ -0,0 +1,126 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import com.raquo.domtypes.generic.codecs.IntAsStringCodec
+import be.doeraene.webcomponents.ui5.configkeys.InputType
+import be.doeraene.webcomponents.ui5.configkeys.ValueState
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import be.doeraene.webcomponents.WebComponent
+
+/** A ui5-multi-input field allows the user to enter multiple values, which are displayed as ui5-token. User can choose
+ * interaction for creating tokens. Fiori Guidelines say that user should create tokens when:
+ *
+ * - Type a value in the input and press enter or focus out the input field (change event is fired)
+ * - Select a value from the suggestion list (suggestion-item-select event is fired)
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object MultiInput extends WebComponent with HasAccessibleName with HasName with HasValue {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def previewItem: suggestion.Ref = js.native
+
+ def openPicker(): Unit = js.native
+
+ def tokensJS: js.Array[token.Ref] = js.native
+
+ def value: String = js.native
+ }
+
+ object RawElement {
+ extension (element: RawElement) def tokens: List[token.Ref] = element.tokensJS.toList
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/MultiInput.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = MultiInput.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-multi-input")
+
+ lazy val showValueHelpIcon: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("show-value-help-icon", BooleanAsAttrPresenceCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val maxlength: ReactiveHtmlAttr[Int] = customHtmlAttr("maxlength", IntAsStringCodec)
+
+ // todo[1.4.0]
+ //lazy val noTypeahead: ReactiveHtmlAttr[Boolean] = customHtmlAttr("no-typeahead", BooleanAsAttrPresenceCodec)
+
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+
+ lazy val showClearIcon: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-clear-icon", BooleanAsAttrPresenceCodec)
+
+ lazy val showSuggestions: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-suggestions", BooleanAsAttrPresenceCodec)
+
+ lazy val tpe: ReactiveHtmlAttr[InputType] = customHtmlAttr("type", InputType.AsStringCodec)
+
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+
+ object slots {
+
+ val tokens: Slot = new Slot("tokens")
+
+ // note that unlike most elements that have an attribute Icon, this element has a slot icon instead.
+ // most of the time you will want to use a ui5-icon for this slot.
+ val icon: Slot = new Slot("icon")
+
+ val valueStateMessage: Slot = new Slot("valueStateMessage")
+ }
+
+ object events {
+ trait HasToken extends js.Object {
+ def token: MultiInput.token.Ref
+ }
+
+ val onTokenDelete: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasToken]] = new EventProp("token-delete")
+
+ val onValueHelpTrigger: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("value-help-trigger")
+
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+
+ val onInput: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("input")
+
+ trait SuggestionItemPreviewInfo extends js.Object {
+ def item: suggestion.Ref
+
+ def targetRef: suggestion.Ref
+ }
+
+ val onSuggestionItemPreview: EventProp[EventWithPreciseTarget[Ref] & HasDetail[SuggestionItemPreviewInfo]] =
+ new EventProp("suggestion-item-preview")
+
+ val onSuggestionItemSelect: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[suggestion.Ref]]] =
+ new EventProp("suggestion-item-select")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(MultiInput)): _*)
+
+ def suggestion: SuggestionItem.type = SuggestionItem
+
+ def token: Token.type = Token
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationAction.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationAction.scala
new file mode 100644
index 0000000..ba6071c
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationAction.scala
@@ -0,0 +1,50 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.ButtonDesign
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-notification-action represents an abstract action, used in the ui5-li-notification and the
+ * ui5-li-notification-group items.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object NotificationAction extends WebComponent with HasText with HasIcon {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/NotificationAction.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = NotificationAction.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-notification-action")
+
+ lazy val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(NotificationAction)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationListGroupItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationListGroupItem.scala
new file mode 100644
index 0000000..31fd801
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationListGroupItem.scala
@@ -0,0 +1,72 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, IconName, Priority}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import scala.concurrent.duration.FiniteDuration
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+import be.doeraene.webcomponents.ui5.configkeys.AvatarSize.L
+
+/** The ui5-li-notification-group is a special type of list item, that unlike others can group items within self,
+ * usually ui5-li-notification items.
+ *
+ * @see
+ * the doc for
+ * more information.
+ */
+object NotificationListGroupItem extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/NotificationListGroupItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = NotificationListGroupItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-li-notification-group")
+
+ lazy val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("collapsed", BooleanAsAttrPresenceCodec)
+
+ lazy val showCounter: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-counter", BooleanAsAttrPresenceCodec)
+
+ lazy val busy: ReactiveHtmlAttr[Boolean] = customHtmlAttr("busy", BooleanAsAttrPresenceCodec)
+
+ lazy val busyDelay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("busy-delay", FiniteDurationCodec)
+
+ lazy val priority: ReactiveHtmlAttr[Priority] = customHtmlAttr("priority", Priority.AsStringCodec)
+
+ lazy val read: ReactiveHtmlAttr[Boolean] = customHtmlAttr("read", BooleanAsAttrPresenceCodec)
+
+ lazy val showClose: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-close", BooleanAsAttrPresenceCodec)
+
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
+
+ object slots {
+ val actions: Slot = Slot("actions")
+ }
+
+ object events {
+ val onToggle: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("toggle")
+ val onClose: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("close")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(NotificationListGroupItem)): _*)
+
+ def item: NotificationListItem.type = NotificationListItem
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationListItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationListItem.scala
new file mode 100644
index 0000000..70871d0
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/NotificationListItem.scala
@@ -0,0 +1,73 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.Priority
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.configkeys.WrappingType
+import scala.concurrent.duration.FiniteDuration
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-li-notification is a type of list item, meant to display notifications.
+ *
+ * The component has a rich set of various properties that allows the user to set avatar, titleText, descriptive
+ * content and footnotes to fully describe a notification.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object NotificationListItem extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/NotificationListItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = NotificationListItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-li-notification")
+
+ lazy val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+
+ lazy val busy: ReactiveHtmlAttr[Boolean] = customHtmlAttr("busy", BooleanAsAttrPresenceCodec)
+
+ lazy val busyDelay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("busy-delay", FiniteDurationCodec)
+
+ lazy val priority: ReactiveHtmlAttr[Priority] = customHtmlAttr("priority", Priority.AsStringCodec)
+
+ lazy val read: ReactiveHtmlAttr[Boolean] = customHtmlAttr("read", BooleanAsAttrPresenceCodec)
+
+ lazy val showClose: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-close", BooleanAsAttrPresenceCodec)
+
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
+
+ object slots {
+ val avatar: Slot = Slot("avatar")
+ val footnotes: Slot = Slot("footnotes")
+ val actions: Slot = Slot("actions")
+ }
+
+ object events {
+ val onClose: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("close")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(NotificationListItem)): _*)
+
+ def action: NotificationAction.type = NotificationAction
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Page.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Page.scala
new file mode 100644
index 0000000..6dd22a0
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Page.scala
@@ -0,0 +1,60 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.PageBackgroundDesign
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-page is a container component that holds one whole screen of an application. The page has three distinct
+ * areas that can hold content - a header, content area and a footer.
+ *
+ * @see
+ * the doc for more information.
+ */
+object Page extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/Page.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = Page.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-page")
+
+ lazy val backgroundDesign: ReactiveHtmlAttr[PageBackgroundDesign] =
+ customHtmlAttr("background-design", PageBackgroundDesign.AsStringCodec)
+
+ lazy val disableScrolling: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("disable-scrolling", BooleanAsAttrPresenceCodec)
+
+ lazy val floatingFooter: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("floating-footer", BooleanAsAttrPresenceCodec)
+
+ lazy val hideFooter: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("hide-footer", BooleanAsAttrPresenceCodec)
+
+ object slots {
+ val footer: Slot = new Slot("footer")
+ val header: Slot = new Slot("header")
+ }
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Page)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Panel.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Panel.scala
index 27713a5..33356e2 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Panel.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Panel.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-panel component is a container which has a header and a content area and is used for grouping and displaying
* information. It can be collapsed to save space on the screen.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Panel extends HasAccessibleName {
+object Panel extends WebComponent with HasAccessibleName {
@js.native
trait RawElement extends js.Object {
@@ -38,18 +39,16 @@ object Panel extends HasAccessibleName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-panel")
- val id: ReactiveProp[String, String] = idAttr
-
- val accessibleRole: ReactiveHtmlAttr[PanelAccessibleRole] =
+ lazy val accessibleRole: ReactiveHtmlAttr[PanelAccessibleRole] =
customHtmlAttr("accessible-role", PanelAccessibleRole.AsStringCodec)
- val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("collapsed", BooleanAsAttrPresenceCodec)
- val fixed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("fixed", BooleanAsAttrPresenceCodec)
+ lazy val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("collapsed", BooleanAsAttrPresenceCodec)
+ lazy val fixed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("fixed", BooleanAsAttrPresenceCodec)
- val headerLevel: ReactiveHtmlAttr[TitleLevel] = customHtmlAttr("header-level", TitleLevel.AsStringCodec)
- val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
+ lazy val headerLevel: ReactiveHtmlAttr[TitleLevel] = customHtmlAttr("header-level", TitleLevel.AsStringCodec)
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
- val noAnimation: ReactiveHtmlAttr[Boolean] = customHtmlAttr("no-animation", BooleanAsAttrPresenceCodec)
+ lazy val noAnimation: ReactiveHtmlAttr[Boolean] = customHtmlAttr("no-animation", BooleanAsAttrPresenceCodec)
object slots {
val header: Slot = new Slot("header")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Popover.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Popover.scala
index a7df1e6..54cf1de 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Popover.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Popover.scala
@@ -1,6 +1,6 @@
package be.doeraene.webcomponents.ui5
-import be.doeraene.webcomponents.ui5.configkeys.PopoverPlacementType
+import be.doeraene.webcomponents.ui5.configkeys.{PopoverHorizontalAlign, PopoverPlacementType, PopoverVerticalAlign}
import be.doeraene.webcomponents.ui5.internal.Slot
import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
import com.raquo.laminar.api.L.*
@@ -11,6 +11,9 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
/** The ui5-popover component displays additional information for an object in a compact way and without leaving the
* page.
@@ -18,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Popover extends HasIcon with HasOnClick {
+object Popover extends WebComponent with HasAccessibleName {
@js.native
trait RawElement extends js.Object {
@@ -43,18 +46,35 @@ object Popover extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-popover")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val allowTargetOverlap: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("allow-target-overlap", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
- val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
+ lazy val hideArrow: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-arrow", BooleanAsAttrPresenceCodec)
- val placementType: ReactiveHtmlAttr[PopoverPlacementType] =
- customHtmlAttr("placement-type", PopoverPlacementType.AsStringCodec)
+ lazy val hideBackdrop: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-backdrop", BooleanAsAttrPresenceCodec)
+
+ lazy val horizontalAlign: ReactiveHtmlAttr[PopoverHorizontalAlign] =
+ customHtmlAttr("horizontal-align", PopoverHorizontalAlign.AsStringCodec)
+
+ lazy val modal: ReactiveHtmlAttr[Boolean] = customHtmlAttr("modal", BooleanAsAttrPresenceCodec)
/** id of the element that opens the popover */
- val opener: ReactiveHtmlAttr[String] = customHtmlAttr("opener", StringAsIsCodec)
- val open: ReactiveHtmlAttr[Boolean] = customHtmlAttr("open", BooleanAsAttrPresenceCodec)
+ lazy val opener: ReactiveHtmlAttr[String] = customHtmlAttr("opener", StringAsIsCodec)
+
+ lazy val placementType: ReactiveHtmlAttr[PopoverPlacementType] =
+ customHtmlAttr("placement-type", PopoverPlacementType.AsStringCodec)
+
+ lazy val verticalAlign: ReactiveHtmlAttr[PopoverVerticalAlign] =
+ customHtmlAttr("vertical-align", PopoverVerticalAlign.AsStringCodec)
+
+ lazy val initialFocus: ReactiveHtmlAttr[String] = customHtmlAttr("initial-focus", StringAsIsCodec)
+
+ lazy val open: ReactiveHtmlAttr[Boolean] = customHtmlAttr("open", BooleanAsAttrPresenceCodec)
+
+ lazy val preventFocusRestore: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("prevent-focus-restore", BooleanAsAttrPresenceCodec)
object slots {
def header: Slot = new Slot("header")
@@ -62,10 +82,17 @@ object Popover extends HasIcon with HasOnClick {
}
object events {
- val onAfterClose: EventProp[dom.Event] = new EventProp("after-close")
- val onAfterOpen: EventProp[dom.Event] = new EventProp("after-open")
- val onBeforeClose: EventProp[dom.Event] = new EventProp("before-close")
- val onBeforeOpen: EventProp[dom.Event] = new EventProp("before-open")
+ val onAfterClose: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("after-close")
+ val onAfterOpen: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("after-open")
+
+ trait BeforeCloseInfo extends js.Object {
+ def escPressed: Boolean
+ }
+
+ val onBeforeClose: EventProp[EventWithPreciseTarget[Ref] & HasDetail[BeforeCloseInfo]] = new EventProp(
+ "before-close"
+ )
+ val onBeforeOpen: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("before-open")
}
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Popover)): _*)
@@ -73,4 +100,21 @@ object Popover extends HasIcon with HasOnClick {
def getPopoverById(id: String): Option[Ref] =
Option(dom.document.getElementById(id)).map(_.asInstanceOf[dom.HTMLElement & RawElement])
+ /** [[Observer]] you can feed a popover ref and a [[dom.HTMLElement]] to open the popover at the element. */
+ val showAtObserver: Observer[(Ref, dom.HTMLElement)] = Observer(_ showAt _)
+
+ /** [[Mod]] for [[Popover]]s opening them each time the stream emits an opener [[dom.HTMLElement]] */
+ def showAtFromEvents(openerEvents: EventStream[dom.HTMLElement]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => openerEvents.map(el.ref -> _) --> showAtObserver)
+
+ /** [[Observer]] you can feed a popover ref to close it. */
+ val closeObserver: Observer[Ref] = Observer(_.close())
+
+ /** [[Mod]] for [[Popover]]s closing them each time the stream emits. */
+ def closeFromEvents(closeEvents: EventStream[Unit]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => closeEvents.mapTo(el.ref) --> closeObserver)
+
+ /** [[Observer]] you can feed a popover ref to apply focus to it. */
+ val applyFocusObserver: Observer[Ref] = Observer(_.applyFocus())
+
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitch.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitch.scala
index 4a85f63..524eb22 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitch.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitch.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-product-switch is an SAP Fiori specific web component that is used in ui5-shellbar and allows the user to
* easily switch between products.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ProductSwitch {
+object ProductSwitch extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -36,8 +37,6 @@ object ProductSwitch {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-product-switch")
- val id: ReactiveProp[String, String] = idAttr
-
object slots {}
object events {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitchItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitchItem.scala
index 5f1e169..e033f30 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitchItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProductSwitchItem.scala
@@ -11,6 +11,8 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** The ui5-product-switch-item web component represents the items displayed in the ui5-product-switch web component.
*
@@ -20,7 +22,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object ProductSwitchItem extends HasIcon {
+object ProductSwitchItem extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -37,19 +39,19 @@ object ProductSwitchItem extends HasIcon {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-product-switch-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr[String]("subtitle-text", StringAsIsCodec)
- val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr[String]("subtitle-text", StringAsIsCodec)
+ lazy val target: ReactiveHtmlAttr[LinkTarget] = customHtmlAttr[LinkTarget]("target", LinkTarget.AsStringCodec)
- val target: ReactiveHtmlAttr[LinkTarget] = customHtmlAttr[LinkTarget]("target", LinkTarget.AsStringCodec)
+ lazy val targetSrc: ReactiveHtmlAttr[String] = customHtmlAttr[String]("target-src", StringAsIsCodec)
- val targetSrc: ReactiveHtmlAttr[String] = customHtmlAttr[String]("target-src", StringAsIsCodec)
-
- val titleText: ReactiveHtmlAttr[String] = customHtmlAttr[String]("title-text", StringAsIsCodec)
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr[String]("title-text", StringAsIsCodec)
object slots {}
- object events extends HasOnClick {}
+ object events {
+ val onClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("click")
+ }
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ProductSwitchItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProgressIndicator.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProgressIndicator.scala
index f3f2875..85c584f 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProgressIndicator.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ProgressIndicator.scala
@@ -12,6 +12,7 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import com.raquo.domtypes.generic.codecs.IntAsStringCodec
import be.doeraene.webcomponents.ui5.configkeys.ValueState
+import be.doeraene.webcomponents.WebComponent
/** Shows the progress of a process in a graphical way. To indicate the progress, the inside of the component is filled
* with a color.
@@ -20,7 +21,7 @@ import be.doeraene.webcomponents.ui5.configkeys.ValueState
* the doc for more
* information.
*/
-object ProgressIndicator {
+object ProgressIndicator extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -37,20 +38,18 @@ object ProgressIndicator {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-progress-indicator")
- val id: ReactiveProp[String, String] = idAttr
-
- val disabled: ReactiveHtmlAttr[Boolean] =
+ lazy val disabled: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val displayValue: ReactiveHtmlAttr[String] =
+ lazy val displayValue: ReactiveHtmlAttr[String] =
customHtmlAttr("display-value", StringAsIsCodec)
- val hideValue: ReactiveHtmlAttr[Boolean] =
+ lazy val hideValue: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("hide-value", BooleanAsAttrPresenceCodec)
- val value: ReactiveHtmlAttr[Int] = customHtmlAttr("value", IntAsStringCodec)
+ lazy val value: ReactiveHtmlAttr[Int] = customHtmlAttr("value", IntAsStringCodec)
- val valueState: ReactiveHtmlAttr[ValueState] =
+ lazy val valueState: ReactiveHtmlAttr[ValueState] =
customHtmlAttr("value-state", ValueState.AsStringCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ProgressIndicator)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RadioButton.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RadioButton.scala
index d0af2de..5fb1df4 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RadioButton.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RadioButton.scala
@@ -12,6 +12,7 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.configkeys.ValueState
import be.doeraene.webcomponents.ui5.configkeys.WrappingType
+import be.doeraene.webcomponents.WebComponent
/** The ui5-radio-button component enables users to select a single option from a set of options. When a
* ui5-radio-button is selected by the user, the change event is fired. When a ui5-radio-button that is within a group
@@ -23,7 +24,7 @@ import be.doeraene.webcomponents.ui5.configkeys.WrappingType
* the doc for more
* information.
*/
-object RadioButton extends HasAccessibleName with HasName with HasText {
+object RadioButton extends WebComponent with HasAccessibleName with HasName with HasText {
@js.native
trait RawElement extends js.Object {}
@@ -40,15 +41,13 @@ object RadioButton extends HasAccessibleName with HasName with HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-radio-button")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val checked: ReactiveHtmlAttr[Boolean] = customHtmlAttr("checked", BooleanAsAttrPresenceCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val checked: ReactiveHtmlAttr[Boolean] = customHtmlAttr("checked", BooleanAsAttrPresenceCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
-
- val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+ lazy val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RangeSlider.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RangeSlider.scala
new file mode 100644
index 0000000..aa55050
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RangeSlider.scala
@@ -0,0 +1,75 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import com.raquo.domtypes.generic.codecs.DoubleAsStringCodec
+import com.raquo.domtypes.generic.codecs.IntAsStringCodec
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** Represents a numerical interval and two handles (grips) to select a sub-range within it. The purpose of the
+ * component to enable visual selection of sub-ranges within a given interval.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object RangeSlider extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def endValue: Double = js.native
+ def startValue: Double = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/RangeSlider.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = RangeSlider.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-range-slider")
+
+ lazy val endValue: ReactiveHtmlAttr[Double] = customHtmlAttr("end-value", DoubleAsStringCodec)
+
+ lazy val startValue: ReactiveHtmlAttr[Double] = customHtmlAttr("start-value", DoubleAsStringCodec)
+
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val labelInterval: ReactiveHtmlAttr[Int] = customHtmlAttr("label-interval", IntAsStringCodec)
+
+ lazy val max: ReactiveHtmlAttr[Double] = customHtmlAttr("max", DoubleAsStringCodec)
+
+ lazy val min: ReactiveHtmlAttr[Double] = customHtmlAttr("min", DoubleAsStringCodec)
+
+ lazy val showTickmarks: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-tickmarks", BooleanAsAttrPresenceCodec)
+
+ lazy val showTooltip: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-tooltip", BooleanAsAttrPresenceCodec)
+
+ lazy val step: ReactiveHtmlAttr[Int] = customHtmlAttr("step", IntAsStringCodec)
+
+ object slots {}
+
+ object events {
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+ val onInput: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("input")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(RangeSlider)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RatingIndicator.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RatingIndicator.scala
index e373446..d29365b 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RatingIndicator.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/RatingIndicator.scala
@@ -14,6 +14,7 @@ import scala.scalajs.js.annotation.JSImport
import com.raquo.domtypes.generic.codecs.IntAsStringCodec
import com.raquo.domtypes.generic.codecs.DoubleAsStringCodec
import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** The Rating Indicator is used to display a specific number of icons that are used to rate an item. Additionally, it
* is also used to display the average and overall ratings.
@@ -22,7 +23,7 @@ import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
* the doc for more
* information.
*/
-object RatingIndicator {
+object RatingIndicator extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -41,13 +42,11 @@ object RatingIndicator {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-rating-indicator")
- val id: ReactiveProp[String, String] = idAttr
-
- val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val max: ReactiveHtmlAttr[Int] = customHtmlAttr("max", IntAsStringCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val value: ReactiveHtmlAttr[Double] = customHtmlAttr("value", DoubleAsStringCodec)
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val max: ReactiveHtmlAttr[Int] = customHtmlAttr("max", IntAsStringCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val value: ReactiveHtmlAttr[Double] = customHtmlAttr("value", DoubleAsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ResponsivePopover.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ResponsivePopover.scala
new file mode 100644
index 0000000..2f307b6
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ResponsivePopover.scala
@@ -0,0 +1,121 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.*
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-responsive-popover acts as a Popover on desktop and tablet, while on phone it acts as a Dialog. The
+ * component improves tremendously the user experience on mobile.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object ResponsivePopover extends WebComponent with HasAccessibleName {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def showAt(opener: dom.HTMLElement): Unit = js.native
+
+ def applyFocus(): Unit = js.native
+
+ def close(): Unit = js.native
+
+ def isOpen(): Boolean = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/ResponsivePopover.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = ResponsivePopover.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-responsive-popover")
+
+ lazy val allowTargetOverlap: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("allow-target-overlap", BooleanAsAttrPresenceCodec)
+
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
+
+ lazy val hideArrow: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-arrow", BooleanAsAttrPresenceCodec)
+
+ lazy val hideBackdrop: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-backdrop", BooleanAsAttrPresenceCodec)
+
+ lazy val horizontalAlign: ReactiveHtmlAttr[PopoverHorizontalAlign] =
+ customHtmlAttr("horizontal-align", PopoverHorizontalAlign.AsStringCodec)
+
+ lazy val modal: ReactiveHtmlAttr[Boolean] = customHtmlAttr("modal", BooleanAsAttrPresenceCodec)
+
+ /** id of the element that opens the ResponsivePopover */
+ lazy val opener: ReactiveHtmlAttr[String] = customHtmlAttr("opener", StringAsIsCodec)
+
+ lazy val placementType: ReactiveHtmlAttr[PopoverPlacementType] =
+ customHtmlAttr("placement-type", PopoverPlacementType.AsStringCodec)
+
+ lazy val verticalAlign: ReactiveHtmlAttr[PopoverVerticalAlign] =
+ customHtmlAttr("vertical-align", PopoverVerticalAlign.AsStringCodec)
+
+ lazy val initialFocus: ReactiveHtmlAttr[String] = customHtmlAttr("initial-focus", StringAsIsCodec)
+
+ lazy val open: ReactiveHtmlAttr[Boolean] = customHtmlAttr("open", BooleanAsAttrPresenceCodec)
+
+ lazy val preventFocusRestore: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("prevent-focus-restore", BooleanAsAttrPresenceCodec)
+
+ object slots {
+ def header: Slot = new Slot("header")
+ def footer: Slot = new Slot("footer")
+ }
+
+ object events {
+ val onAfterClose: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("after-close")
+ val onAfterOpen: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("after-open")
+
+ trait BeforeCloseInfo extends js.Object {
+ def escPressed: Boolean
+ }
+
+ val onBeforeClose: EventProp[EventWithPreciseTarget[Ref] & HasDetail[BeforeCloseInfo]] = new EventProp(
+ "before-close"
+ )
+ val onBeforeOpen: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("before-open")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ResponsivePopover)): _*)
+
+ def getResponsivePopoverById(id: String): Option[Ref] =
+ Option(dom.document.getElementById(id)).map(_.asInstanceOf[dom.HTMLElement & RawElement])
+
+ /** [[Observer]] you can feed a ResponsivePopover ref and a [[dom.HTMLElement]] to open the ResponsivePopover at the
+ * element.
+ */
+ val showAtObserver: Observer[(Ref, dom.HTMLElement)] = Observer(_ showAt _)
+
+ def showAtFromEvents(openerEvents: EventStream[dom.HTMLElement]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => openerEvents.map(el.ref -> _) --> showAtObserver)
+
+ /** [[Observer]] you can feed a ResponsivePopover ref to close it. */
+ val closeObserver: Observer[Ref] = Observer(_.close())
+
+ def closeFromEvents(closeEvents: EventStream[Unit]): Mod[ReactiveHtmlElement[Ref]] =
+ inContext[ReactiveHtmlElement[Ref]](el => closeEvents.mapTo(el.ref) --> closeObserver)
+
+ /** [[Observer]] you can feed a ResponsivePopover ref to apply focus to it. */
+ val applyFocusObserver: Observer[Ref] = Observer(_.applyFocus())
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButton.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButton.scala
index 8948306..95918b3 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButton.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButton.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-segmented-button shows a group of items. When the user clicks or taps one of the items, it stays in a
* pressed state. It automatically resizes the items to fit proportionally within the component. When no width is set,
@@ -21,7 +22,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object SegmentedButton {
+object SegmentedButton extends WebComponent {
@js.native
trait RawElement extends js.Object {
@@ -40,9 +41,7 @@ object SegmentedButton {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-segmented-button")
- val id: ReactiveProp[String, String] = idAttr
-
- val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButtonItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButtonItem.scala
index 2b26cc8..9f4ef43 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButtonItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SegmentedButtonItem.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Users can use the ui5-segmented-button-item as part of a ui5-segmented-button.
*
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object SegmentedButtonItem extends HasAccessibleName with HasIcon {
+object SegmentedButtonItem extends WebComponent with HasAccessibleName with HasIcon {
@js.native
trait RawElement extends js.Object {}
@@ -36,21 +37,19 @@ object SegmentedButtonItem extends HasAccessibleName with HasIcon {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-segmented-button-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
- val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
+ lazy val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
- val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
+ lazy val submits: ReactiveHtmlAttr[Boolean] = customHtmlAttr("submits", BooleanAsAttrPresenceCodec)
- val submits: ReactiveHtmlAttr[Boolean] = customHtmlAttr("submits", BooleanAsAttrPresenceCodec)
-
- val pressed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("pressed", BooleanAsAttrPresenceCodec)
+ lazy val pressed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("pressed", BooleanAsAttrPresenceCodec)
lazy val accessibilityAttributes: ReactiveHtmlAttr[js.Object] = ??? // todo
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val tooltip: ReactiveHtmlAttr[String] = customHtmlAttr("tooltip", StringAsIsCodec)
+ lazy val tooltip: ReactiveHtmlAttr[String] = customHtmlAttr("tooltip", StringAsIsCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Select.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Select.scala
index 9cd56e1..5300445 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Select.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Select.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-select component is used to create a drop-down list. The items inside the ui5-select define the available
* options by using the ui5-option component.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object Select extends HasIcon with HasAccessibleName with HasName {
+object Select extends WebComponent with HasIcon with HasAccessibleName with HasName {
@js.native
trait RawElement extends js.Object {
@@ -38,12 +39,10 @@ object Select extends HasIcon with HasAccessibleName with HasName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-select")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
-
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
object slots {
val valueStateMessage: Slot = new Slot("valueStateMessage")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SelectOption.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SelectOption.scala
index 6f97a3f..42bd004 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SelectOption.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SelectOption.scala
@@ -9,13 +9,14 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-option component defines the content of an option in the ui5-select.
*
* @see
* the doc for more information.
*/
-object SelectOption extends HasIcon with HasAdditionalText with HasValue {
+object SelectOption extends WebComponent with HasIcon with HasAdditionalText with HasValue {
@js.native
trait RawElement extends js.Object {
@@ -36,10 +37,8 @@ object SelectOption extends HasIcon with HasAdditionalText with HasValue {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-option")
- val id: ReactiveProp[String, String] = idAttr
-
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(SelectOption)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBar.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBar.scala
index e838546..aafd7ec 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBar.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBar.scala
@@ -13,6 +13,8 @@ import be.doeraene.webcomponents.ui5.internal.Slot
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** Simple UI button
*
@@ -20,7 +22,7 @@ import be.doeraene.webcomponents.ui5.eventtypes.HasItem
* the doc for more
* information.
*/
-object ShellBar extends HasIcon with HasOnClick {
+object ShellBar extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {
@@ -39,34 +41,36 @@ object ShellBar extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-shellbar")
- val id: ReactiveProp[String, String] = idAttr
-
- val primaryTitle: ReactiveHtmlAttr[String] =
+ lazy val primaryTitle: ReactiveHtmlAttr[String] =
customHtmlAttr("primary-title", StringAsIsCodec)
- val secondaryTitle: ReactiveHtmlAttr[String] =
+ lazy val secondaryTitle: ReactiveHtmlAttr[String] =
customHtmlAttr("secondary-title", StringAsIsCodec)
- val notificationsCount: ReactiveHtmlAttr[String] =
+ lazy val notificationsCount: ReactiveHtmlAttr[String] =
customHtmlAttr("notifications-count", StringAsIsCodec)
- val showProductSwitch: ReactiveHtmlAttr[Boolean] =
+ lazy val showProductSwitch: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("show-product-switch", BooleanAsAttrPresenceCodec)
- val showCoPilot: ReactiveHtmlAttr[Boolean] =
+ lazy val showCoPilot: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("show-copilot", BooleanAsAttrPresenceCodec)
- val showNotifications: ReactiveHtmlAttr[Boolean] =
+ lazy val showNotifications: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("show-notifications", BooleanAsAttrPresenceCodec)
object events {
- val onCopilotClick = new EventProp[dom.Event & HasDetail[HasTargetRef[dom.HTMLElement]]]("co-pilot-click")
- val onProfileClick = new EventProp[dom.Event & HasDetail[HasTargetRef[dom.HTMLElement]]]("profile-click")
- val onLogoClick = new EventProp[dom.Event & HasDetail[HasTargetRef[dom.HTMLElement]]]("logo-click")
- val onMenuItemClick = new EventProp[dom.Event & HasDetail[HasItem[dom.HTMLElement]]]("menu-item-click")
+ val onCopilotClick =
+ new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasTargetRef[dom.HTMLElement]]]("co-pilot-click")
+ val onProfileClick =
+ new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasTargetRef[dom.HTMLElement]]]("profile-click")
+ val onLogoClick =
+ new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasTargetRef[dom.HTMLElement]]]("logo-click")
+ val onMenuItemClick =
+ new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[dom.HTMLElement]]]("menu-item-click")
val onNotificationsClick =
- new EventProp[dom.Event & HasDetail[HasTargetRef[dom.HTMLElement]]]("notifications-click")
+ new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasTargetRef[dom.HTMLElement]]]("notifications-click")
val onProductSwitchClick =
- new EventProp[dom.Event & HasDetail[HasTargetRef[dom.HTMLElement]]]("product-switch-click")
+ new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasTargetRef[dom.HTMLElement]]]("product-switch-click")
}
object slots {
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBarItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBarItem.scala
index dc90a50..b230267 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBarItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ShellBarItem.scala
@@ -9,14 +9,18 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.HasTargetRef
+import be.doeraene.webcomponents.WebComponent
-/** Simple UI button
+/** The ui5-shellbar-item represents a custom item, that might be added to the ui5-shellbar.
*
* @see
* the doc for more
* information.
*/
-object ShellBarItem extends HasIcon with HasOnClick with HasText {
+object ShellBarItem extends WebComponent with HasIcon with HasText {
@js.native
trait RawElement extends js.Object {}
@@ -33,9 +37,14 @@ object ShellBarItem extends HasIcon with HasOnClick with HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-shellbar-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val count: HtmlAttr[String] = customHtmlAttr("count", StringAsIsCodec)
- val count: HtmlAttr[String] = customHtmlAttr("count", StringAsIsCodec)
+ object slots {}
+
+ object events {
+ val onClick: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasTargetRef[dom.HTMLElement]]] =
+ new EventProp("click")
+ }
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ShellBarItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigation.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigation.scala
index 3bb8be1..7ae5042 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigation.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigation.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The SideNavigation is used as a standard menu in applications. It consists of three containers: header
* (top-aligned), main navigation section (top-aligned) and the secondary section (bottom-aligned).
@@ -20,7 +21,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object SideNavigation {
+object SideNavigation extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -37,9 +38,7 @@ object SideNavigation {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-side-navigation")
- val id: ReactiveProp[String, String] = idAttr
-
- val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("collapsed", BooleanAsAttrPresenceCodec)
+ lazy val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("collapsed", BooleanAsAttrPresenceCodec)
object slots {
val fixedItems: Slot = new Slot("fixedItems")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationItem.scala
index f12c741..ea6ddc7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationItem.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-side-navigation-item is used within ui5-side-navigation only. Via the ui5-side-navigation-item you control
* the content of the SideNavigation.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object SideNavigationItem extends HasText {
+object SideNavigationItem extends WebComponent with HasText {
@js.native
trait RawElement extends SideNavigation.events.SideNavigationItemRawElement {}
@@ -36,15 +37,13 @@ object SideNavigationItem extends HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-side-navigation-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val expanded: ReactiveHtmlAttr[Boolean] = customHtmlAttr("expanded", BooleanAsAttrPresenceCodec)
- val expanded: ReactiveHtmlAttr[Boolean] = customHtmlAttr("expanded", BooleanAsAttrPresenceCodec)
+ lazy val icon: ReactiveHtmlAttr[IconName] = customHtmlAttr("icon", IconName.AsStringCodec)
- val icon: ReactiveHtmlAttr[IconName] = customHtmlAttr("icon", IconName.AsStringCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
-
- val wholeItemToggleable: ReactiveHtmlAttr[Boolean] =
+ lazy val wholeItemToggleable: ReactiveHtmlAttr[Boolean] =
customHtmlAttr("whole-item-toggleable", BooleanAsAttrPresenceCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationSubItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationSubItem.scala
index a97f179..c7b88c3 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationSubItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SideNavigationSubItem.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** The ui5-side-navigation-item is used within ui5-side-navigation only. Via the ui5-side-navigation-item you control
* the content of the SideNavigation.
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for
* more information.
*/
-object SideNavigationSubItem extends HasText {
+object SideNavigationSubItem extends WebComponent with HasText {
@js.native
trait RawElement extends SideNavigation.events.SideNavigationItemRawElement {}
@@ -36,11 +37,9 @@ object SideNavigationSubItem extends HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-side-navigation-sub-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val icon: ReactiveHtmlAttr[IconName] = customHtmlAttr("icon", IconName.AsStringCodec)
- val icon: ReactiveHtmlAttr[IconName] = customHtmlAttr("icon", IconName.AsStringCodec)
-
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Slider.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Slider.scala
new file mode 100644
index 0000000..327c672
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Slider.scala
@@ -0,0 +1,71 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import com.raquo.domtypes.generic.codecs.DoubleAsStringCodec
+import com.raquo.domtypes.generic.codecs.IntAsStringCodec
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The Slider component represents a numerical range and a handle (grip). The purpose of the component is to enable
+ * visual selection of a value in a continuous numerical range by moving an adjustable handle.
+ *
+ * @see
+ * the doc for more information.
+ */
+object Slider extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def value: Double = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/Slider.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = Slider.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-slider")
+
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val labelInterval: ReactiveHtmlAttr[Int] = customHtmlAttr("label-interval", IntAsStringCodec)
+
+ lazy val max: ReactiveHtmlAttr[Double] = customHtmlAttr("max", DoubleAsStringCodec)
+
+ lazy val min: ReactiveHtmlAttr[Double] = customHtmlAttr("min", DoubleAsStringCodec)
+
+ lazy val showTickmarks: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-tickmarks", BooleanAsAttrPresenceCodec)
+
+ lazy val showTooltip: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-tooltip", BooleanAsAttrPresenceCodec)
+
+ lazy val step: ReactiveHtmlAttr[Int] = customHtmlAttr("step", IntAsStringCodec)
+
+ lazy val value: ReactiveHtmlAttr[Double] = customHtmlAttr("value", DoubleAsStringCodec)
+
+ object slots {}
+
+ object events {
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+ val onInput: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("input")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Slider)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SortItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SortItem.scala
new file mode 100644
index 0000000..7b4eb79
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SortItem.scala
@@ -0,0 +1,46 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** For the ui5-sort-item
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object SortItem extends WebComponent with HasText {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/SortItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = SortItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-sort-item")
+
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(SortItem)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SplitButton.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SplitButton.scala
new file mode 100644
index 0000000..f8f0695
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SplitButton.scala
@@ -0,0 +1,59 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** ui5-split-button enables users to trigger actions. It is constructed of two separate actions - default action and
+ * arrow action that can be activated by clicking or tapping, or by pressing certain keyboard keys - Space or Enter for
+ * default action, and Arrow Down or Arrow Up for arrow action.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object SplitButton extends WebComponent with HasIcon {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/SplitButton.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = SplitButton.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-split-button")
+
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+
+ lazy val activeIcon: ReactiveHtmlAttr[IconName] = customHtmlAttr("active-icon", IconName.AsStringCodec)
+
+ lazy val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ object slots {}
+
+ object events {
+ val onArrowClick: EventProp[EventWithPreciseTarget[dom.HTMLElement]] = new EventProp("arrow-click")
+ val onClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("click")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(SplitButton)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/StepInput.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/StepInput.scala
new file mode 100644
index 0000000..3432051
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/StepInput.scala
@@ -0,0 +1,80 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import com.raquo.domtypes.generic.codecs.DoubleAsStringCodec
+import com.raquo.domtypes.generic.codecs.IntAsStringCodec
+import be.doeraene.webcomponents.ui5.configkeys.ValueState
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-step-input consists of an input field and buttons with icons to increase/decrease the value with the
+ * predefined step.
+ *
+ * The user can change the value of the component by pressing the increase/decrease buttons, by typing a number
+ * directly, by using the keyboard up/down and page up/down, or by using the mouse scroll wheel. Decimal values are
+ * supported.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object StepInput extends WebComponent with HasAccessibleName with HasName {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def value: Double = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/StepInput.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = StepInput.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-step-input")
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val max: ReactiveHtmlAttr[Double] = customHtmlAttr("max", DoubleAsStringCodec)
+
+ lazy val min: ReactiveHtmlAttr[Double] = customHtmlAttr("min", DoubleAsStringCodec)
+
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+
+ lazy val step: ReactiveHtmlAttr[Double] = customHtmlAttr("step", DoubleAsStringCodec)
+
+ lazy val value: ReactiveHtmlAttr[Double] = customHtmlAttr("value", DoubleAsStringCodec)
+
+ lazy val valuePrecision: ReactiveHtmlAttr[Int] = customHtmlAttr("value-precision", IntAsStringCodec)
+
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+
+ object slots {
+ val valueStateMessage: Slot = Slot("valueStateMessage")
+ }
+
+ object events {
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(StepInput)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionGroupItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionGroupItem.scala
new file mode 100644
index 0000000..6c49052
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionGroupItem.scala
@@ -0,0 +1,44 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-SuggestionGroupItem
+ *
+ * @see
+ * the doc for more information.
+ */
+object SuggestionGroupItem extends WebComponent with HasText {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/features/InputSuggestions.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = SuggestionGroupItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-ui5-suggestion-group-item")
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(SuggestionGroupItem)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionItem.scala
index 8a3771a..3ad593e 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/SuggestionItem.scala
@@ -11,13 +11,14 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.configkeys.ListItemType
import be.doeraene.webcomponents.ui5.configkeys.ValueState
+import be.doeraene.webcomponents.WebComponent
/** The ui5-suggestion-item represents the suggestion item of the ui5-input.
*
* @see
* the doc for more information.
*/
-object SuggestionItem extends HasIcon with HasDescription with HasText with HasAdditionalText {
+object SuggestionItem extends WebComponent with HasIcon with HasDescription with HasText with HasAdditionalText {
@js.native
trait RawElement extends js.Object {
@@ -36,15 +37,13 @@ object SuggestionItem extends HasIcon with HasDescription with HasText with HasA
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-suggestion-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
- val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
+ lazy val image: ReactiveHtmlAttr[String] = customHtmlAttr("image", StringAsIsCodec)
- val image: ReactiveHtmlAttr[String] = customHtmlAttr("image", StringAsIsCodec)
+ lazy val tpe: ReactiveHtmlAttr[ListItemType] = customHtmlAttr("tpe", ListItemType.AsStringCodec)
- val tpe: ReactiveHtmlAttr[ListItemType] = customHtmlAttr("tpe", ListItemType.AsStringCodec)
-
- val additionalTextState: ReactiveHtmlAttr[ValueState] =
+ lazy val additionalTextState: ReactiveHtmlAttr[ValueState] =
customHtmlAttr("additional-text-state", ValueState.AsStringCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(SuggestionItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Switch.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Switch.scala
index 55434d8..7431889 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Switch.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Switch.scala
@@ -11,16 +11,20 @@ import org.scalajs.dom.Event
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** The ui5-switch component is used for changing between binary states.
*
* @see
* the doc for more information.
*/
-object Switch extends HasAccessibleName {
+object Switch extends WebComponent with HasAccessibleName {
@js.native
- trait RawElement extends js.Object {}
+ trait RawElement extends js.Object {
+ def checked: Boolean = js.native
+ }
@js.native
@JSImport("@ui5/webcomponents/dist/Switch.js", JSImport.Default)
@@ -34,20 +38,19 @@ object Switch extends HasAccessibleName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-switch")
- val id: ReactiveProp[String, String] = idAttr
-
- val textOn: ReactiveHtmlAttr[String] = customHtmlAttr("text-on", StringAsIsCodec)
- val textOff: ReactiveHtmlAttr[String] = customHtmlAttr("text-off", StringAsIsCodec)
+ lazy val textOn: ReactiveHtmlAttr[String] = customHtmlAttr("text-on", StringAsIsCodec)
+ lazy val textOff: ReactiveHtmlAttr[String] = customHtmlAttr("text-off", StringAsIsCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val checked: ReactiveHtmlAttr[Boolean] = customHtmlAttr("checked", BooleanAsAttrPresenceCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val checked: ReactiveHtmlAttr[Boolean] = customHtmlAttr("checked", BooleanAsAttrPresenceCodec)
- val design: ReactiveHtmlAttr[SwitchDesign] = customHtmlAttr("design", SwitchDesign.AsStringCodec)
+ lazy val design: ReactiveHtmlAttr[SwitchDesign] = customHtmlAttr("design", SwitchDesign.AsStringCodec)
object slots {}
- object events extends HasOnChange {
- val onCheckedChange: EventProcessor[Event, Boolean] = onChange.mapToChecked
+ object events {
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+ val onCheckedChange: EventProcessor[EventWithPreciseTarget[Ref], Boolean] = onChange.map(_.target.checked)
}
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Switch)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tab.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tab.scala
index 68180a6..237bd49 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tab.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tab.scala
@@ -11,6 +11,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Element contained in a [[TabContainer]].
*
@@ -18,7 +19,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object Tab extends HasIcon with HasText {
+object Tab extends WebComponent with HasIcon with HasText {
@js.native
trait RawElement extends js.Object {
@@ -37,14 +38,12 @@ object Tab extends HasIcon with HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-tab")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+ lazy val design: ReactiveHtmlAttr[SemanticColour] = customHtmlAttr("design", SemanticColour.AsStringCodec)
- val design: ReactiveHtmlAttr[SemanticColour] = customHtmlAttr("design", SemanticColour.AsStringCodec)
-
- val additionalText: ReactiveHtmlAttr[String] = customHtmlAttr("additional-text", StringAsIsCodec)
+ lazy val additionalText: ReactiveHtmlAttr[String] = customHtmlAttr("additional-text", StringAsIsCodec)
object slots {
val subTabs: Slot = new Slot("subTabs")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TabContainer.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TabContainer.scala
index ab4d7bf..89b1d25 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TabContainer.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TabContainer.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Tab container
*
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object TabContainer {
+object TabContainer extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -36,14 +37,12 @@ object TabContainer {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-tabcontainer")
- val id: ReactiveProp[String, String] = idAttr
-
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("collapsed", BooleanAsAttrPresenceCodec)
- val fixed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("fixed", BooleanAsAttrPresenceCodec)
- val showOverflow: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-overflow", BooleanAsAttrPresenceCodec)
- val tabLayout: ReactiveHtmlAttr[TabLayout] = customHtmlAttr("tab-layout", TabLayout.AsStringCodec)
- val tabsOverflowMode: ReactiveHtmlAttr[TabsOverflowMode] =
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val collapsed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("collapsed", BooleanAsAttrPresenceCodec)
+ lazy val fixed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("fixed", BooleanAsAttrPresenceCodec)
+ lazy val showOverflow: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-overflow", BooleanAsAttrPresenceCodec)
+ lazy val tabLayout: ReactiveHtmlAttr[TabLayout] = customHtmlAttr("tab-layout", TabLayout.AsStringCodec)
+ lazy val tabsOverflowMode: ReactiveHtmlAttr[TabsOverflowMode] =
customHtmlAttr("tabs-overflow-mode", TabsOverflowMode.AsStringCodec)
object slots {
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Table.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Table.scala
index 5eb531c..aec60dd 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Table.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Table.scala
@@ -15,8 +15,9 @@ import scala.compiletime.ops.int.<=
import scala.concurrent.duration.FiniteDuration
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
-object Table {
+object Table extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -33,17 +34,16 @@ object Table {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-table")
- val id: ReactiveProp[String, String] = idAttr
-
- val busy: ReactiveHtmlAttr[Boolean] = customHtmlAttr("busy", BooleanAsAttrPresenceCodec)
- val busyDelay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("busy-delay", FiniteDurationCodec)
- val growing: ReactiveHtmlAttr[TableGrowingMode] = customHtmlAttr("growing", TableGrowingMode.AsStringCodec)
- val growingButtonSubtext: ReactiveHtmlAttr[String] = customHtmlAttr("growing-button-subtext", StringAsIsCodec)
- val growingButtonText: ReactiveHtmlAttr[String] = customHtmlAttr("growing-button-text", StringAsIsCodec)
- val hideNoData: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-no-data", BooleanAsAttrPresenceCodec)
- val mode: ReactiveHtmlAttr[TableMode] = customHtmlAttr("mode", TableMode.AsStringCodec)
- val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
- val stickyColumnHeader: ReactiveHtmlAttr[Boolean] = customHtmlAttr("sticky-column-header", BooleanAsAttrPresenceCodec)
+ lazy val busy: ReactiveHtmlAttr[Boolean] = customHtmlAttr("busy", BooleanAsAttrPresenceCodec)
+ lazy val busyDelay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("busy-delay", FiniteDurationCodec)
+ lazy val growing: ReactiveHtmlAttr[TableGrowingMode] = customHtmlAttr("growing", TableGrowingMode.AsStringCodec)
+ lazy val growingButtonSubtext: ReactiveHtmlAttr[String] = customHtmlAttr("growing-button-subtext", StringAsIsCodec)
+ lazy val growingButtonText: ReactiveHtmlAttr[String] = customHtmlAttr("growing-button-text", StringAsIsCodec)
+ lazy val hideNoData: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-no-data", BooleanAsAttrPresenceCodec)
+ lazy val mode: ReactiveHtmlAttr[TableMode] = customHtmlAttr("mode", TableMode.AsStringCodec)
+ lazy val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
+ lazy val stickyColumnHeader: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("sticky-column-header", BooleanAsAttrPresenceCodec)
object slots {
val columns: Slot = new Slot("columns")
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableCell.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableCell.scala
index e7ac6b3..2f89bf7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableCell.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableCell.scala
@@ -7,8 +7,9 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
-object TableCell {
+object TableCell extends WebComponent {
@js.native
trait RawElement extends js.Object {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableColumn.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableColumn.scala
index 7fb3eb8..1ec5dde 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableColumn.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableColumn.scala
@@ -12,8 +12,9 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import com.raquo.domtypes.generic.codecs.IntAsStringCodec
import com.raquo.domtypes.generic.codecs.BooleanAsAttrPresenceCodec
+import be.doeraene.webcomponents.WebComponent
-object TableColumn {
+object TableColumn extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -30,12 +31,9 @@ object TableColumn {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-table-column")
- val demandPopin: ReactiveHtmlAttr[Boolean] = customHtmlAttr("demand-popin", BooleanAsAttrPresenceCodec)
- val minWidth: ReactiveHtmlAttr[Int] = customHtmlAttr("min-width", IntAsStringCodec)
- val popinText: ReactiveHtmlAttr[String] = customHtmlAttr("popin-text", StringAsIsCodec)
-
- val slot: ReactiveHtmlAttr["columns" | "default"] =
- customHtmlAttr("slot", EmbeddingAsIsCodec.apply)
+ lazy val demandPopin: ReactiveHtmlAttr[Boolean] = customHtmlAttr("demand-popin", BooleanAsAttrPresenceCodec)
+ lazy val minWidth: ReactiveHtmlAttr[Int] = customHtmlAttr("min-width", IntAsStringCodec)
+ lazy val popinText: ReactiveHtmlAttr[String] = customHtmlAttr("popin-text", StringAsIsCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(TableColumn)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableRow.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableRow.scala
index f4d612e..26f108f 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableRow.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TableRow.scala
@@ -7,8 +7,9 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
-object TableRow {
+object TableRow extends WebComponent {
@js.native
trait RawElement extends js.Object {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TextArea.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TextArea.scala
index 6c7358f..fe396b7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TextArea.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TextArea.scala
@@ -13,14 +13,15 @@ import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
import be.doeraene.webcomponents.ui5.configkeys.ValueState
import com.raquo.domtypes.generic.codecs.IntAsStringCodec
import be.doeraene.webcomponents.ui5.internal.Slot
+import be.doeraene.webcomponents.WebComponent
-/** TextArea
+/** The ui5-textarea component is used to enter multiple lines of text.
*
* @see
* the doc for more
* information.
*/
-object TextArea extends HasValue with HasAccessibleName with HasName {
+object TextArea extends WebComponent with HasValue with HasAccessibleName with HasName {
@js.native
trait RawElement extends js.Object {}
@@ -37,18 +38,17 @@ object TextArea extends HasValue with HasAccessibleName with HasName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-textarea")
- val id: ReactiveProp[String, String] = idAttr
-
- val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
- val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
- val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
- val growing: ReactiveHtmlAttr[Boolean] = customHtmlAttr("growing", BooleanAsAttrPresenceCodec)
- val showExceededText: ReactiveHtmlAttr[Boolean] = customHtmlAttr("show-exceeded-text", BooleanAsAttrPresenceCodec)
- val growingMaxLines: ReactiveHtmlAttr[Int] = customHtmlAttr("growing-max-lines", IntAsStringCodec)
- val maxLength: ReactiveHtmlAttr[Int] = customHtmlAttr("maxlength", IntAsStringCodec)
- val rows: ReactiveHtmlAttr[Int] = customHtmlAttr("rows", IntAsStringCodec)
- val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
- val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+ lazy val required: ReactiveHtmlAttr[Boolean] = customHtmlAttr("required", BooleanAsAttrPresenceCodec)
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+ lazy val growing: ReactiveHtmlAttr[Boolean] = customHtmlAttr("growing", BooleanAsAttrPresenceCodec)
+ lazy val showExceededText: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("show-exceeded-text", BooleanAsAttrPresenceCodec)
+ lazy val growingMaxLines: ReactiveHtmlAttr[Int] = customHtmlAttr("growing-max-lines", IntAsStringCodec)
+ lazy val maxLength: ReactiveHtmlAttr[Int] = customHtmlAttr("maxlength", IntAsStringCodec)
+ lazy val rows: ReactiveHtmlAttr[Int] = customHtmlAttr("rows", IntAsStringCodec)
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
val isRequired: Setter[HtmlElement] = required := true
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimePicker.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimePicker.scala
new file mode 100644
index 0000000..cc984d0
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimePicker.scala
@@ -0,0 +1,78 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.configkeys.ValueState
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-time-picker component provides an input field with assigned sliders which are opened on user action. The
+ * ui5-time-picker allows users to select a localized time using touch, mouse, or keyboard input. It consists of two
+ * parts: the time input field and the sliders.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object TimePicker extends WebComponent with HasValue {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def dateValue: js.Date = js.native
+
+ def value: String = js.native
+
+ def closePicker(): Unit = js.native
+
+ def formatValue(date: js.Date): String = js.native
+
+ def isOpen(): Boolean = js.native
+
+ def isValid(value: String): Boolean = js.native
+
+ def openPicker(): Unit = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/TimePicker.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = TimePicker.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-time-picker")
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val formatPattern: ReactiveHtmlAttr[String] = customHtmlAttr("format-pattern", StringAsIsCodec)
+
+ lazy val placeholder: ReactiveHtmlAttr[String] = customHtmlAttr("placeholder", StringAsIsCodec)
+
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+
+ lazy val valueState: ReactiveHtmlAttr[ValueState] = customHtmlAttr("value-state", ValueState.AsStringCodec)
+
+ object slots {
+ val valueStateMessage: Slot = Slot("valueStateMessage")
+ }
+
+ object events {
+ val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("change")
+ val onInput: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("input")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(TimePicker)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Timeline.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Timeline.scala
index f38bf92..e7921e6 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Timeline.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Timeline.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
/** The ui5-timeline component shows entries (such as objects, events, or posts) in chronological order. A common use
* case is to provide information about changes to an object, or events related to an object. These entries can be
@@ -23,7 +24,7 @@ import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
* the doc for more
* information.
*/
-object Timeline {
+object Timeline extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -40,11 +41,9 @@ object Timeline {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-timeline")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
- val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
-
- val layout: ReactiveHtmlAttr[TimelineLayout] = customHtmlAttr("layout", TimelineLayout.AsStringCodec)
+ lazy val layout: ReactiveHtmlAttr[TimelineLayout] = customHtmlAttr("layout", TimelineLayout.AsStringCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimelineItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimelineItem.scala
index 386fc2a..729a76c 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimelineItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TimelineItem.scala
@@ -12,6 +12,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** An entry posted on the timeline.
*
@@ -19,7 +20,7 @@ import scala.scalajs.js.annotation.JSImport
* the doc for more
* information.
*/
-object TimelineItem extends HasIcon with HasName {
+object TimelineItem extends WebComponent with HasIcon with HasName {
@js.native
trait RawElement extends js.Object {
@@ -44,13 +45,11 @@ object TimelineItem extends HasIcon with HasName {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-timeline-item")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val nameClickable: ReactiveHtmlAttr[Boolean] = customHtmlAttr("name-clickable", BooleanAsAttrPresenceCodec)
- val nameClickable: ReactiveHtmlAttr[Boolean] = customHtmlAttr("name-clickable", BooleanAsAttrPresenceCodec)
+ lazy val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
- val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
-
- val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Title.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Title.scala
index 34b2029..1a523e7 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Title.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Title.scala
@@ -13,6 +13,7 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.configkeys.TitleLevel
import be.doeraene.webcomponents.ui5.configkeys.WrappingType
+import be.doeraene.webcomponents.WebComponent
/** The ui5-title component is used to display titles inside a page. It is a simple, large-sized text with explicit
* header/title semantics.
@@ -20,7 +21,7 @@ import be.doeraene.webcomponents.ui5.configkeys.WrappingType
* @see
* the doc for more information.
*/
-object Title {
+object Title extends WebComponent {
@js.native
trait RawElement extends js.Object {}
@@ -37,10 +38,8 @@ object Title {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-title")
- val id: ReactiveProp[String, String] = idAttr
-
- val level: ReactiveHtmlAttr[TitleLevel] = customHtmlAttr("level", TitleLevel.AsStringCodec)
- val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
+ lazy val level: ReactiveHtmlAttr[TitleLevel] = customHtmlAttr("level", TitleLevel.AsStringCodec)
+ lazy val wrappingType: ReactiveHtmlAttr[WrappingType] = customHtmlAttr("wrapping-type", WrappingType.AsStringCodec)
object slots {}
@@ -48,4 +47,22 @@ object Title {
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Title)): _*)
+ /** Creates Title of H1 level. */
+ def h1(mods: ModFunction*): HtmlElement = apply(mods :+ (_.level := TitleLevel.H1): _*)
+
+ /** Creates Title of H2 level. */
+ def h2(mods: ModFunction*): HtmlElement = apply(mods :+ (_.level := TitleLevel.H2): _*)
+
+ /** Creates Title of H3 level. */
+ def h3(mods: ModFunction*): HtmlElement = apply(mods :+ (_.level := TitleLevel.H3): _*)
+
+ /** Creates Title of H4 level. */
+ def h4(mods: ModFunction*): HtmlElement = apply(mods :+ (_.level := TitleLevel.H4): _*)
+
+ /** Creates Title of H4 level. */
+ def h5(mods: ModFunction*): HtmlElement = apply(mods :+ (_.level := TitleLevel.H5): _*)
+
+ /** Creates Title of H6 level. */
+ def h6(mods: ModFunction*): HtmlElement = apply(mods :+ (_.level := TitleLevel.H6): _*)
+
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Toast.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Toast.scala
index 6547cff..c20d6c2 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Toast.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Toast.scala
@@ -11,13 +11,14 @@ import org.scalajs.dom
import scala.concurrent.duration.FiniteDuration
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** Simple UI button
*
* @see
* the doc for more information.
*/
-object Toast extends HasIcon with HasOnClick {
+object Toast extends WebComponent with HasIcon {
@js.native
trait RawElement extends js.Object {
@@ -36,11 +37,13 @@ object Toast extends HasIcon with HasOnClick {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-toast")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val placement: ReactiveHtmlAttr[ToastPlacement] = customHtmlAttr("placement", ToastPlacement.AsStringCodec)
- val placement: ReactiveHtmlAttr[ToastPlacement] = customHtmlAttr("placement", ToastPlacement.AsStringCodec)
+ lazy val duration: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("duration", FiniteDurationCodec)
- val duration: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("duration", FiniteDurationCodec)
+ object slots {}
+
+ object events {}
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Toast)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ToggleButton.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ToggleButton.scala
new file mode 100644
index 0000000..20fdabe
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ToggleButton.scala
@@ -0,0 +1,69 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-toggle-button component is an enhanced ui5-button that can be toggled between pressed and normal states.
+ * Users can use the ui5-toggle-button as a switch to turn a setting on or off. It can also be used to represent an
+ * independent choice similar to a check box.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object ToggleButton extends WebComponent with HasAccessibleName with HasIcon {
+
+ @js.native
+ trait RawElement extends js.Object {
+ var accessibilityAttributes: js.Object = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/ToggleButton.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = ToggleButton.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-toggle-button")
+
+ lazy val pressed: ReactiveHtmlAttr[Boolean] = customHtmlAttr("pressed", BooleanAsAttrPresenceCodec)
+
+ // This component has accessibilityAttributes but I currently don't know how to implement it
+
+ lazy val design: ReactiveHtmlAttr[ButtonDesign] = customHtmlAttr("design", ButtonDesign.AsStringCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val iconEnd: ReactiveHtmlAttr[Boolean] = customHtmlAttr("icon-end", BooleanAsAttrPresenceCodec)
+
+ lazy val submits: ReactiveHtmlAttr[Boolean] = {
+ SubmitsSupport
+ customHtmlAttr("submits", BooleanAsAttrPresenceCodec)
+ }
+
+ lazy val tooltip: ReactiveHtmlAttr[String] = customHtmlAttr("tooltip", StringAsIsCodec)
+
+ object slots {}
+
+ object events {
+ val onClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("click")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ToggleButton)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Token.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Token.scala
new file mode 100644
index 0000000..fd1f3ef
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Token.scala
@@ -0,0 +1,57 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.WebComponent
+
+/** Tokens are small items of information (similar to tags) that mainly serve to visualize previously selected items.
+ *
+ * @see
+ * the doc for more information.
+ */
+object Token extends WebComponent with HasText {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def selected: Boolean = js.native
+
+ def text: String = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/Token.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = Token.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-token")
+
+ lazy val readonly: ReactiveHtmlAttr[Boolean] = customHtmlAttr("readonly", BooleanAsAttrPresenceCodec)
+
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+
+ object slots {
+ val closeIcon: Slot = new Slot("closeIcon")
+ }
+
+ object events {
+ val onSelect: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("select")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Token)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tree.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tree.scala
index 0e2c007..300ea09 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tree.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Tree.scala
@@ -14,13 +14,14 @@ import scala.scalajs.js.annotation.{JSImport, JSName}
import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import be.doeraene.webcomponents.WebComponent
/** The ui5-tree component provides a tree structure for displaying data in a hierarchy.
*
* @see
* the doc for more information.
*/
-object Tree {
+object Tree extends WebComponent {
//noinspection ScalaUnusedSymbol
@js.native
@@ -40,13 +41,11 @@ object Tree {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-tree")
- val id: ReactiveProp[String, String] = idAttr
+ lazy val footerText: ReactiveHtmlAttr[String] = customHtmlAttr("footer-text", StringAsIsCodec)
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
+ lazy val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
- val footerText: ReactiveHtmlAttr[String] = customHtmlAttr("footer-text", StringAsIsCodec)
- val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
- val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
-
- val mode: ReactiveHtmlAttr[ListMode] = customHtmlAttr("mode", ListMode.AsStringCodec)
+ lazy val mode: ReactiveHtmlAttr[ListMode] = customHtmlAttr("mode", ListMode.AsStringCodec)
object events {
val onItemClick: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[TreeItem.Ref]]] = new EventProp(
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TreeItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TreeItem.scala
index 43faa5f..063fcde 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TreeItem.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/TreeItem.scala
@@ -10,6 +10,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
/** This is the item to use inside a ui5-tree. You can represent an arbitrary tree structure by recursively nesting tree
* items.
@@ -17,7 +18,7 @@ import scala.scalajs.js.annotation.JSImport
* @see
* the doc for more information.
*/
-object TreeItem extends HasIcon with HasText {
+object TreeItem extends WebComponent with HasIcon with HasText {
@js.native
trait RawElement extends js.Object {
@@ -36,12 +37,10 @@ object TreeItem extends HasIcon with HasText {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-tree-item")
- val id: ReactiveProp[String, String] = idAttr
-
- val expanded: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("expanded", BooleanAsAttrPresenceCodec)
- val hasChildren: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("has-children", BooleanAsAttrPresenceCodec)
- val intermediate: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("intermediate", BooleanAsAttrPresenceCodec)
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("selected", BooleanAsAttrPresenceCodec)
+ lazy val expanded: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("expanded", BooleanAsAttrPresenceCodec)
+ lazy val hasChildren: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("has-children", BooleanAsAttrPresenceCodec)
+ lazy val intermediate: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("intermediate", BooleanAsAttrPresenceCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr[Boolean]("selected", BooleanAsAttrPresenceCodec)
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(TreeItem)): _*)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UList.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UList.scala
index c8a1f2c..256c1cf 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UList.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UList.scala
@@ -13,6 +13,7 @@ import org.scalajs.dom
import scala.scalajs.js
import scala.scalajs.js.annotation.{JSImport, JSName}
import scala.concurrent.duration.FiniteDuration
+import be.doeraene.webcomponents.WebComponent
/** The ui5-list component allows displaying a list of items, advanced keyboard handling support for navigating between
* items, and predefined modes to improve the development efficiency.
@@ -20,7 +21,7 @@ import scala.concurrent.duration.FiniteDuration
* @see
* the doc for more information.
*/
-object UList {
+object UList extends WebComponent with HasAccessibleName {
@js.native
trait RawElement extends js.Object {}
@@ -37,44 +38,43 @@ object UList {
private val tag: HtmlTag[Ref] = customHtmlTag("ui5-list")
- val id: ReactiveProp[String, String] = idAttr
-
- val busy: ReactiveHtmlAttr[Boolean] = customHtmlAttr("busy", BooleanAsAttrPresenceCodec)
- val busyDelay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("busy-delay", FiniteDurationCodec)
- val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
- val footerText: ReactiveHtmlAttr[String] = customHtmlAttr("footer-text", StringAsIsCodec)
- val mode: ReactiveHtmlAttr[ListMode] = customHtmlAttr("mode", ListMode.AsStringCodec)
- val separators: ReactiveHtmlAttr[ListSeparator] = customHtmlAttr("separators", ListSeparator.AsStringCodec)
- val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
- val growing: ReactiveHtmlAttr[ListGrowingMode] = customHtmlAttr("growing", ListGrowingMode.AsStringCodec)
- val indent: ReactiveHtmlAttr[Boolean] = customHtmlAttr("indent", BooleanAsAttrPresenceCodec)
+ lazy val accessibleRole: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-role", StringAsIsCodec)
+ lazy val busy: ReactiveHtmlAttr[Boolean] = customHtmlAttr("busy", BooleanAsAttrPresenceCodec)
+ lazy val busyDelay: ReactiveHtmlAttr[FiniteDuration] = customHtmlAttr("busy-delay", FiniteDurationCodec)
+ lazy val footerText: ReactiveHtmlAttr[String] = customHtmlAttr("footer-text", StringAsIsCodec)
+ lazy val growing: ReactiveHtmlAttr[ListGrowingMode] = customHtmlAttr("growing", ListGrowingMode.AsStringCodec)
+ lazy val headerText: ReactiveHtmlAttr[String] = customHtmlAttr("header-text", StringAsIsCodec)
+ lazy val indent: ReactiveHtmlAttr[Boolean] = customHtmlAttr("indent", BooleanAsAttrPresenceCodec)
+ lazy val mode: ReactiveHtmlAttr[ListMode] = customHtmlAttr("mode", ListMode.AsStringCodec)
+ lazy val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
+ lazy val separators: ReactiveHtmlAttr[ListSeparator] = customHtmlAttr("separators", ListSeparator.AsStringCodec)
object events {
- val onItemClick = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[Li.Ref]]]("item-click")
- val onItemClose = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[Li.Ref]]]("item-close")
- val onItemDelete = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[Li.Ref]]]("item-delete")
- val onItemToggle = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[Li.Ref]]]("item-toggle")
+ val onItemClick = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[item.Ref]]]("item-click")
+ val onItemClose = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[item.Ref]]]("item-close")
+ val onItemDelete = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[item.Ref]]]("item-delete")
+ val onItemToggle = new EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[item.Ref]]]("item-toggle")
val onLoadMore = new EventProp[EventWithPreciseTarget[Ref]]("load-more")
@js.native
trait SelectionChangeDetail extends js.Object {
@JSName("selectedItems")
- def selectedItemsJS: js.Array[Li.Ref] = js.native
+ def selectedItemsJS: js.Array[item.Ref] = js.native
@JSName("previouslySelectedItems")
- def previouslySelectedItemsJS: js.Array[Li.Ref] = js.native
+ def previouslySelectedItemsJS: js.Array[item.Ref] = js.native
}
object SelectionChangeDetail {
extension (detail: SelectionChangeDetail)
- def selectedItems: List[Li.Ref] = detail.selectedItemsJS.toList
- def previouslySelectedItems: List[Li.Ref] = detail.previouslySelectedItemsJS.toList
+ def selectedItems: List[item.Ref] = detail.selectedItemsJS.toList
+ def previouslySelectedItems: List[item.Ref] = detail.previouslySelectedItemsJS.toList
/** Returns the first selected item when it exists (useful in [[ListMode.SingleSelect]]) */
- def maybeSelectedItem: Option[Li.Ref] = detail.selectedItemsJS.headOption
+ def maybeSelectedItem: Option[item.Ref] = detail.selectedItemsJS.headOption
/** Returns the first previously selected item when it exists (useful in [[ListMode.SingleSelect]])) */
- def maybePreviouslySelectedItem: Option[Li.Ref] = detail.previouslySelectedItemsJS.headOption
+ def maybePreviouslySelectedItem: Option[item.Ref] = detail.previouslySelectedItemsJS.headOption
}
val onSelectionChange =
@@ -87,7 +87,14 @@ object UList {
def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(UList)): _*)
- val Li: ListItem.type = ListItem
- def group: UListGroupHeader.type = UListGroupHeader
+ @deprecated("Li was a badly designed name. Use `item` instead", "15/08/2022")
+ def Li: ListItem.type = ListItem
+
+ def item: ListItem.type = ListItem
+ def customItem: CustomListItem.type = CustomListItem
+ def group: UListGroupHeader.type = UListGroupHeader
+
+ def notificationItem: NotificationListItem.type = NotificationListItem
+ def notificationGroup: NotificationListGroupItem.type = NotificationListGroupItem
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UListGroupHeader.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UListGroupHeader.scala
index dca8fcc..89e3f74 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UListGroupHeader.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UListGroupHeader.scala
@@ -11,29 +11,32 @@ import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import be.doeraene.webcomponents.ui5.internal.Slot
import be.doeraene.webcomponents.ui5.eventtypes.{HasDetail, HasItem, HasTargetRef}
+import be.doeraene.webcomponents.WebComponent
-/** Simple UI button
+/** The ui5-li-groupheader is a special list item, used only to separate other list items into logical groups.
*
* @see
* the doc for more information.
*/
-object UListGroupHeader {
+object UListGroupHeader extends WebComponent {
@js.native
trait RawElement extends js.Object {}
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/List.js", JSImport.Default)
+ object RawImport extends js.Object
+
// object-s are lazy so you need to actually use them in your code to prevent dead code elimination
used(RawImport)
type Ref = dom.html.Element with RawElement
type ModFunction = UListGroupHeader.type => Mod[ReactiveHtmlElement[Ref]]
- private val tag: HtmlTag[Ref] = customHtmlTag("ui5-groupheader")
-
- val id: ReactiveProp[String, String] = idAttr
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-li-groupheader")
- val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
- val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
object slots {}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UploadCollection.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UploadCollection.scala
new file mode 100644
index 0000000..6b0779b
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UploadCollection.scala
@@ -0,0 +1,87 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.configkeys.ListMode
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import be.doeraene.webcomponents.WebComponent
+
+/** This component allows you to represent files before uploading them to a server, with the help of
+ * ui5-upload-collection-item. It also allows you to show already uploaded files.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object UploadCollection extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/UploadCollection.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = UploadCollection.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-upload-collection")
+
+ lazy val accessibleName: ReactiveHtmlAttr[String] = customHtmlAttr("accessible-name", StringAsIsCodec)
+
+ lazy val hideDragOverlay: ReactiveHtmlAttr[Boolean] = customHtmlAttr("hide-drag-overlay", BooleanAsAttrPresenceCodec)
+
+ lazy val mode: ReactiveHtmlAttr[ListMode] = customHtmlAttr("mode", ListMode.AsStringCodec)
+
+ lazy val noDataDescription: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-description", StringAsIsCodec)
+
+ lazy val noDataText: ReactiveHtmlAttr[String] = customHtmlAttr("no-data-text", StringAsIsCodec)
+
+ object slots {
+ val header: Slot = new Slot("header")
+ }
+
+ object events {
+
+ trait HasDataTransfer extends js.Object {
+ def dataTransfer: dom.DataTransfer
+ }
+
+ val onDrop: EventProp[EventWithPreciseTarget[Ref] & HasDataTransfer] = new EventProp("drop")
+ val onItemDelete: EventProp[EventWithPreciseTarget[Ref] & HasDetail[HasItem[dom.HTMLElement]]] = new EventProp(
+ "item-delete"
+ )
+
+ trait SelectionChangeInfo extends js.Object {
+ @JSName("selectedItems")
+ def selectedItemsJS: Array[dom.HTMLElement]
+ }
+
+ object SelectionChangeInfo {
+ extension (info: SelectionChangeInfo) def selectedItems: List[dom.HTMLElement] = info.selectedItemsJS.toList
+ }
+
+ val onSelectionChange: EventProp[EventWithPreciseTarget[Ref] & HasDetail[SelectionChangeInfo]] = new EventProp(
+ "selection-change"
+ )
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(UploadCollection)): _*)
+
+ def item: UploadCollectionItem.type = UploadCollectionItem
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UploadCollectionItem.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UploadCollectionItem.scala
new file mode 100644
index 0000000..a4dfa95
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/UploadCollectionItem.scala
@@ -0,0 +1,88 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.{ButtonDesign, ColourScheme, IconName}
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.configkeys.{ListMode, UploadState}
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.ui5.eventtypes.HasItem
+import com.raquo.domtypes.generic.codecs.IntAsStringCodec
+import be.doeraene.webcomponents.WebComponent
+
+/** This component allows you to represent files before uploading them to a server, with the help of
+ * ui5-upload-collection-item. It also allows you to show already uploaded files.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object UploadCollectionItem extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def fileJS: dom.File | Null = js.native
+
+ def progress: Int = js.native
+
+ def fileName: String = js.native
+ }
+
+ object RawElement {
+ extension (element: RawElement) def file: Option[dom.File] = Option(element.fileJS)
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/UploadCollectionItem.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = UploadCollectionItem.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-upload-collection-item")
+
+ lazy val disableDeleteButton: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("disable-delete-button", BooleanAsAttrPresenceCodec)
+
+ lazy val fileName: ReactiveHtmlAttr[String] =
+ customHtmlAttr("file-name", StringAsIsCodec)
+
+ lazy val fileNameClickable: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("file-name-clickable", BooleanAsAttrPresenceCodec)
+
+ lazy val hideRetryButton: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("hide-retry-button", BooleanAsAttrPresenceCodec)
+
+ lazy val hideTerminateButton: ReactiveHtmlAttr[Boolean] =
+ customHtmlAttr("hide-terminate-button", BooleanAsAttrPresenceCodec)
+
+ lazy val progress: ReactiveHtmlAttr[Int] = customHtmlAttr("progress", IntAsStringCodec)
+
+ lazy val uploadState: ReactiveHtmlAttr[UploadState] =
+ customHtmlAttr("upload-state", UploadState.AsStringCodec)
+
+ object slots {
+ val thumbnail: Slot = new Slot("thumbnail")
+ }
+
+ object events {
+ val onFileNameClick: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("file-name-click")
+ val onRename: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("rename")
+ val onRetry: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("retry")
+ val onTerminate: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("terminate")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(UploadCollectionItem)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ViewSettingsDialog.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ViewSettingsDialog.scala
new file mode 100644
index 0000000..92badfe
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/ViewSettingsDialog.scala
@@ -0,0 +1,105 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.*
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.{JSImport, JSName}
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-view-settings-dialog component helps the user to sort data within a list or a table. It consists of several
+ * lists like Sort order which is built-in and Sort By and Filter By lists, for which you must be provide
+ * items(ui5-sort-item & ui5-filter-item respectively) These options can be used to create sorters for a table. The
+ * ui5-view-settings-dialog interrupts the current application processing as it is the only focused UI element and the
+ * main screen is dimmed/blocked. The ui5-view-settings-dialog is modal, which means that user action is required
+ * before returning to the parent window is possible.
+ *
+ * @see
+ * the doc for more
+ * information.
+ */
+object ViewSettingsDialog extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {
+ def setConfirmedSettings(settings: ViewSettings): Unit = js.native
+
+ def show(): Unit = js.native
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/ViewSettingsDialog.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = ViewSettingsDialog.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-view-settings-dialog")
+
+ lazy val sortDescending: ReactiveHtmlAttr[Boolean] = customHtmlAttr("sort-descending", BooleanAsAttrPresenceCodec)
+
+ object slots {
+ val filterItems: Slot = Slot("filterItems")
+ val sortItems: Slot = Slot("sortItems")
+ }
+
+ trait ViewSettings extends js.Object {
+ def sortOrder: "Ascending" | "Descending"
+
+ def sortBy: String
+
+ def sortDescending: Boolean
+
+ @JSName("filters")
+ def filtersJS: js.Array[js.Dictionary[js.Array[String]]]
+ }
+
+ object ViewSettings {
+ extension (settings: ViewSettings)
+ def filters: Map[String, List[String]] =
+ settings.filtersJS.flatMap(_.toMap.map((key, values) => (key, values.toList))).toMap
+ }
+
+ object events {
+ val onBeforeOpen: EventProp[EventWithPreciseTarget[Ref]] = new EventProp("before-open")
+
+ trait HasSortByItem extends js.Object {
+ def sortByItem: dom.HTMLElement
+ }
+
+ val onCancel: EventProp[EventWithPreciseTarget[Ref] & HasDetail[ViewSettings & HasSortByItem]] = new EventProp(
+ "cancel"
+ )
+ val onConfirm: EventProp[EventWithPreciseTarget[Ref] & HasDetail[ViewSettings & HasSortByItem]] = new EventProp(
+ "confirm"
+ )
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(ViewSettingsDialog)): _*)
+
+ /** Feed an instance of [[ViewSettingsDialog]] ref to this observer in order to show it. */
+ val showObserver: Observer[Ref] = Observer(_.show())
+
+ /** [[Mod]] showing the [[ViewSettingsDialog]] when the specified stream emits. */
+ def showFromEvents(viewSettingsDialogShowEvents: EventStream[Unit]) =
+ inContext[ReactiveHtmlElement[Ref]](el => viewSettingsDialogShowEvents.mapTo(el.ref) --> showObserver)
+
+ /** Feed an instance of [[ViewSettingsDialog]] ref with the desired [[ViewSettings]] to set these to it. */
+ val setConfirmedSettingsObserver: Observer[(Ref, ViewSettings)] = Observer(_.setConfirmedSettings(_))
+
+ /** [[Mod]] settings the [[ViewSettings]] to the [[ViewSettingsDialog]]. */
+ def setConfirmedSettingsFromEvents(settingsEvent: EventStream[ViewSettings]) =
+ inContext[ReactiveHtmlElement[Ref]](el => settingsEvent.map(el.ref -> _) --> setConfirmedSettingsObserver)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Wizard.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Wizard.scala
new file mode 100644
index 0000000..dd47d49
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/Wizard.scala
@@ -0,0 +1,57 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.*
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.ui5.eventtypes.EventWithPreciseTarget
+import be.doeraene.webcomponents.ui5.eventtypes.HasDetail
+import be.doeraene.webcomponents.WebComponent
+
+/** The ui5-wizard helps users to complete a complex task by dividing it into sections and guiding them through it. It
+ * has two main areas - a navigation area at the top showing the step sequence and a content area below it.
+ *
+ * @see
+ * the doc for more information.
+ */
+object Wizard extends WebComponent {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ @js.native
+ @JSImport("@ui5/webcomponents-fiori/dist/Wizard.js", JSImport.Default)
+ object RawImport extends js.Object
+
+ // object-s are lazy so you need to actually use them in your code to prevent dead code elimination
+ used(RawImport)
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = Wizard.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-wizard")
+
+ object slots {}
+
+ object events {
+ trait StepChangeInfo extends js.Object {
+ def step: WizardStep.Ref
+ def previousStep: WizardStep.Ref
+ def changeWithClick: Boolean
+ }
+
+ val onStepChange: EventProp[EventWithPreciseTarget[Ref] & HasDetail[StepChangeInfo]] = new EventProp("step-change")
+ }
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(Wizard)): _*)
+
+ def step: WizardStep.type = WizardStep
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/WizardStep.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/WizardStep.scala
new file mode 100644
index 0000000..9f3070e
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/WizardStep.scala
@@ -0,0 +1,48 @@
+package be.doeraene.webcomponents.ui5
+
+import be.doeraene.webcomponents.ui5.configkeys.*
+import be.doeraene.webcomponents.ui5.internal.Slot
+import com.raquo.domtypes.generic.codecs.{BooleanAsAttrPresenceCodec, StringAsIsCodec}
+import com.raquo.laminar.api.L.*
+import com.raquo.laminar.builders.HtmlTag
+import com.raquo.laminar.keys.{ReactiveHtmlAttr, ReactiveProp, ReactiveStyle}
+import com.raquo.laminar.nodes.ReactiveHtmlElement
+import org.scalajs.dom
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSImport
+import be.doeraene.webcomponents.WebComponent
+
+/** A component that represents a logical step as part of the ui5-wizard. It is meant to aggregate arbitrary HTML
+ * elements that form the content of a single step.
+ *
+ * @see
+ * the doc for more information.
+ */
+object WizardStep extends WebComponent with HasIcon {
+
+ @js.native
+ trait RawElement extends js.Object {}
+
+ type Ref = dom.html.Element with RawElement
+ type ModFunction = WizardStep.type => Mod[ReactiveHtmlElement[Ref]]
+
+ private val tag: HtmlTag[Ref] = customHtmlTag("ui5-wizard-step")
+
+ lazy val branching: ReactiveHtmlAttr[Boolean] = customHtmlAttr("branching", BooleanAsAttrPresenceCodec)
+
+ lazy val disabled: ReactiveHtmlAttr[Boolean] = customHtmlAttr("disabled", BooleanAsAttrPresenceCodec)
+
+ lazy val selected: ReactiveHtmlAttr[Boolean] = customHtmlAttr("selected", BooleanAsAttrPresenceCodec)
+
+ lazy val subtitleText: ReactiveHtmlAttr[String] = customHtmlAttr("subtitle-text", StringAsIsCodec)
+
+ lazy val titleText: ReactiveHtmlAttr[String] = customHtmlAttr("title-text", StringAsIsCodec)
+
+ object slots {}
+
+ object events {}
+
+ def apply(mods: ModFunction*): HtmlElement = tag(mods.map(_(WizardStep)): _*)
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/AvatarGroupType.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/AvatarGroupType.scala
new file mode 100644
index 0000000..32f140e
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/AvatarGroupType.scala
@@ -0,0 +1,15 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait AvatarGroupType {
+ def value: String = toString
+}
+
+object AvatarGroupType extends EnumerationString[AvatarGroupType] {
+ case object Group extends AvatarGroupType
+ case object Individual extends AvatarGroupType
+
+ val allValues: List[AvatarGroupType] = List(Group, Individual)
+
+ def valueOf(value: AvatarGroupType): String = value.value
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/ButtonDesign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/ButtonDesign.scala
index 02d1d36..6c52d2e 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/ButtonDesign.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/ButtonDesign.scala
@@ -2,7 +2,9 @@ package be.doeraene.webcomponents.ui5.configkeys
import com.raquo.domtypes.generic.codecs.Codec
-sealed trait ButtonDesign
+sealed trait ButtonDesign {
+ def value: String = toString
+}
object ButtonDesign extends EnumerationString[ButtonDesign] {
@@ -22,6 +24,6 @@ object ButtonDesign extends EnumerationString[ButtonDesign] {
Attention
)
- def valueOf(value: ButtonDesign): String = value.toString
+ def valueOf(value: ButtonDesign): String = value.value
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarSelectionMode.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarSelectionMode.scala
new file mode 100644
index 0000000..45cb28a
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarSelectionMode.scala
@@ -0,0 +1,15 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait CalendarSelectionMode {
+ def value: String = toString
+}
+
+object CalendarSelectionMode extends EnumerationString[CalendarSelectionMode] {
+ case object Single extends CalendarSelectionMode
+ case object Range extends CalendarSelectionMode
+ case object Multiple extends CalendarSelectionMode
+
+ val allValues: List[CalendarSelectionMode] = List(Single, Range, Multiple)
+
+ def valueOf(value: CalendarSelectionMode): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarType.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarType.scala
index 0293676..c7cc39f 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarType.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/CalendarType.scala
@@ -5,33 +5,52 @@ import scala.scalajs.js.annotation.JSImport
trait CalendarType {
def value: String = toString
+
+ /** You can call this object wherever in your code to be sure that you have support for this [[CalendarType]] */
+ def importObject: CalendarType.CalendarTypeImporter[this.type]
}
object CalendarType extends EnumerationString[CalendarType] {
// /!\ To use any but the Gregorian type, you need to call one of the imports below anywhere in your code.
- case object Gregorian extends CalendarType
- case object Buddhist extends CalendarType
- case object Islamic extends CalendarType
- case object Japanese extends CalendarType
- case object Persian extends CalendarType
+ case object Gregorian extends CalendarType {
+ def importObject: CalendarType.CalendarTypeImporter[this.type] = GregorianImport
+ }
+ case object Buddhist extends CalendarType {
+ def importObject: CalendarType.CalendarTypeImporter[this.type] = BuddhistImport
+ }
+ case object Islamic extends CalendarType {
+ def importObject: CalendarType.CalendarTypeImporter[this.type] = IslamicImport
+ }
+ case object Japanese extends CalendarType {
+ def importObject: CalendarType.CalendarTypeImporter[this.type] = JapaneseImport
+ }
+ case object Persian extends CalendarType {
+ def importObject: CalendarType.CalendarTypeImporter[this.type] = PersianImport
+ }
+
+ /** Marker trait to specify the the object is used to import a specific calendar. */
+ sealed trait CalendarTypeImporter[For <: CalendarType] extends js.Object
+
+ // Gregorian calendar is imported by default, so this object is here only for consistency
+ object GregorianImport extends CalendarTypeImporter[Gregorian.type]
@js.native
@JSImport("@ui5/webcomponents-localization/dist/features/calendar/Buddhist.js", JSImport.Default)
- object BuddhistImport extends js.Object
+ object BuddhistImport extends CalendarTypeImporter[Buddhist.type]
@js.native
@JSImport("@ui5/webcomponents-localization/dist/features/calendar/Islamic.js", JSImport.Default)
- object IslamicImport extends js.Object
+ object IslamicImport extends CalendarTypeImporter[Islamic.type]
@js.native
@JSImport("@ui5/webcomponents-localization/dist/features/calendar/Japanese.js", JSImport.Default)
- object JapaneseImport extends js.Object
+ object JapaneseImport extends CalendarTypeImporter[Japanese.type]
@js.native
@JSImport("@ui5/webcomponents-localization/dist/features/calendar/Persian.js", JSImport.Default)
- object PersianImport extends js.Object
+ object PersianImport extends CalendarTypeImporter[Persian.type]
val allValues: List[CalendarType] = List(Gregorian, Buddhist, Islamic, Japanese, Persian)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryItemLayout.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryItemLayout.scala
new file mode 100644
index 0000000..3ba68dc
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryItemLayout.scala
@@ -0,0 +1,14 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait MediaGalleryItemLayout {
+ def value: String = toString
+}
+
+object MediaGalleryItemLayout extends EnumerationString[MediaGalleryItemLayout] {
+ case object Square extends MediaGalleryItemLayout
+ case object Wide extends MediaGalleryItemLayout
+
+ val allValues: List[MediaGalleryItemLayout] = List(Square, Wide)
+
+ def valueOf(value: MediaGalleryItemLayout): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryLayout.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryLayout.scala
new file mode 100644
index 0000000..2da972b
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryLayout.scala
@@ -0,0 +1,16 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait MediaGalleryLayout {
+ def value: String = toString
+}
+
+object MediaGalleryLayout extends EnumerationString[MediaGalleryLayout] {
+ case object Auto extends MediaGalleryLayout
+ case object Vertical extends MediaGalleryLayout
+ case object Horizontal extends MediaGalleryLayout
+
+ val allValues: List[MediaGalleryLayout] = List(Auto, Vertical, Horizontal)
+
+ def valueOf(value: MediaGalleryLayout): String = value.value
+
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryMenuHorizontalAlign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryMenuHorizontalAlign.scala
new file mode 100644
index 0000000..4b74ce6
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryMenuHorizontalAlign.scala
@@ -0,0 +1,14 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait MediaGalleryMenuHorizontalAlign {
+ def value: String = toString
+}
+
+object MediaGalleryMenuHorizontalAlign extends EnumerationString[MediaGalleryMenuHorizontalAlign] {
+ case object Left extends MediaGalleryMenuHorizontalAlign
+ case object Right extends MediaGalleryMenuHorizontalAlign
+
+ val allValues: List[MediaGalleryMenuHorizontalAlign] = List(Left, Right)
+
+ def valueOf(value: MediaGalleryMenuHorizontalAlign): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryMenuVerticalAlign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryMenuVerticalAlign.scala
new file mode 100644
index 0000000..5351b70
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/MediaGalleryMenuVerticalAlign.scala
@@ -0,0 +1,14 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait MediaGalleryMenuVerticalAlign {
+ def value: String = toString
+}
+
+object MediaGalleryMenuVerticalAlign extends EnumerationString[MediaGalleryMenuVerticalAlign] {
+ case object Top extends MediaGalleryMenuVerticalAlign
+ case object Bottom extends MediaGalleryMenuVerticalAlign
+
+ val allValues: List[MediaGalleryMenuVerticalAlign] = List(Top, Bottom)
+
+ def valueOf(value: MediaGalleryMenuVerticalAlign): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PageBackgroundDesign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PageBackgroundDesign.scala
new file mode 100644
index 0000000..9c00838
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PageBackgroundDesign.scala
@@ -0,0 +1,17 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait PageBackgroundDesign {
+ def value: String = toString
+}
+
+object PageBackgroundDesign extends EnumerationString[PageBackgroundDesign] {
+ case object Solid extends PageBackgroundDesign
+ case object Transparent extends PageBackgroundDesign
+ case object ListPage extends PageBackgroundDesign {
+ override def value: String = "List"
+ }
+
+ val allValues: List[PageBackgroundDesign] = List(Solid, Transparent, ListPage)
+
+ def valueOf(value: PageBackgroundDesign): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PopoverHorizontalAlign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PopoverHorizontalAlign.scala
new file mode 100644
index 0000000..d6745d5
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PopoverHorizontalAlign.scala
@@ -0,0 +1,17 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait PopoverHorizontalAlign {
+ def value: String = toString
+
+}
+
+object PopoverHorizontalAlign extends EnumerationString[PopoverHorizontalAlign] {
+ case object Center extends PopoverHorizontalAlign
+ case object Left extends PopoverHorizontalAlign
+ case object Right extends PopoverHorizontalAlign
+ case object Stretch extends PopoverHorizontalAlign
+
+ val allValues: List[PopoverHorizontalAlign] = List(Center, Left, Right, Stretch)
+
+ def valueOf(value: PopoverHorizontalAlign): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PopoverVerticalAlign.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PopoverVerticalAlign.scala
new file mode 100644
index 0000000..e7286cc
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/PopoverVerticalAlign.scala
@@ -0,0 +1,17 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait PopoverVerticalAlign {
+ def value: String = toString
+
+}
+
+object PopoverVerticalAlign extends EnumerationString[PopoverVerticalAlign] {
+ case object Center extends PopoverVerticalAlign
+ case object Top extends PopoverVerticalAlign
+ case object Bottom extends PopoverVerticalAlign
+ case object Stretch extends PopoverVerticalAlign
+
+ val allValues: List[PopoverVerticalAlign] = List(Center, Top, Bottom, Stretch)
+
+ def valueOf(value: PopoverVerticalAlign): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/Priority.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/Priority.scala
new file mode 100644
index 0000000..b639560
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/Priority.scala
@@ -0,0 +1,16 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait Priority {
+ def value: String = toString
+}
+
+object Priority extends EnumerationString[Priority] {
+ case object None extends Priority
+ case object Low extends Priority
+ case object Medium extends Priority
+ case object High extends Priority
+
+ val allValues: List[Priority] = List(None, Low, Medium, High)
+
+ def valueOf(value: Priority): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentFallDown.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentFallDown.scala
new file mode 100644
index 0000000..8fc5f20
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentFallDown.scala
@@ -0,0 +1,16 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait SideContentFallDown {
+ def value: String = toString
+}
+
+object SideContentFallDown extends EnumerationString[SideContentFallDown] {
+ case object BelowXL extends SideContentFallDown
+ case object BelowL extends SideContentFallDown
+ case object BelowM extends SideContentFallDown
+ case object OnMinimumWidth extends SideContentFallDown
+
+ val allValues: List[SideContentFallDown] = List(BelowXL, BelowL, BelowM, OnMinimumWidth)
+
+ def valueOf(value: SideContentFallDown): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentPosition.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentPosition.scala
new file mode 100644
index 0000000..9ba59ca
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentPosition.scala
@@ -0,0 +1,14 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait SideContentPosition {
+ def value: String = toString
+}
+
+object SideContentPosition extends EnumerationString[SideContentPosition] {
+ case object Start extends SideContentPosition
+ case object End extends SideContentPosition
+
+ val allValues: List[SideContentPosition] = List(Start, End)
+
+ def valueOf(value: SideContentPosition): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentVisibility.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentVisibility.scala
new file mode 100644
index 0000000..b1d339f
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/SideContentVisibility.scala
@@ -0,0 +1,17 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait SideContentVisibility {
+ def value: String = toString
+}
+
+object SideContentVisibility extends EnumerationString[SideContentVisibility] {
+ case object AlwaysShow extends SideContentVisibility
+ case object ShowAboveL extends SideContentVisibility
+ case object ShowAboveM extends SideContentVisibility
+ case object ShowAboveS extends SideContentVisibility
+ case object NeverShow extends SideContentVisibility
+
+ val allValues: List[SideContentVisibility] = List(AlwaysShow, ShowAboveL, ShowAboveM, ShowAboveS, NeverShow)
+
+ def valueOf(value: SideContentVisibility): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/UploadState.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/UploadState.scala
new file mode 100644
index 0000000..d5262cd
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/configkeys/UploadState.scala
@@ -0,0 +1,15 @@
+package be.doeraene.webcomponents.ui5.configkeys
+
+sealed trait UploadState {
+ def value: String = toString
+}
+
+object UploadState extends EnumerationString[UploadState] {
+ case object Ready extends UploadState
+ case object Uploading extends UploadState
+ case object Error extends UploadState
+
+ val allValues: List[UploadState] = List(Ready, Uploading, Error)
+
+ def valueOf(value: UploadState): String = value.value
+}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/package.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/package.scala
index 97066c1..7ff46be 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/package.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/package.scala
@@ -34,4 +34,14 @@ package object ui5 {
override def encode(scalaValue: LocalDate): String = scalaValue.format(formatter)
}
+ case class ListCodec[A](codec: Codec[A, String]) extends Codec[List[A], String] {
+ def decode(domValue: String): List[A] = domValue.split(',').toList.map(codec.decode)
+
+ def encode(scalaValue: List[A]): String = scalaValue.map(codec.encode).mkString(",")
+ }
+
+ @js.native
+ @JSImport("@ui5/webcomponents/dist/features/InputElementsFormSupport.js", JSImport.Default)
+ object SubmitsSupport extends js.Object
+
}
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/Colour.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/Colour.scala
index 5684bcc..441122d 100644
--- a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/Colour.scala
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/Colour.scala
@@ -65,21 +65,7 @@ object Colour {
*
* Behaviour for invalid strings is undefined.
*/
- def fromString(str: String): Colour = {
- val canvas = dom.document.createElement("canvas").asInstanceOf[dom.HTMLCanvasElement]
- canvas.width = 1
- canvas.height = 1
- val ctx = canvas.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D]
- ctx.fillStyle = str
- ctx.fillRect(0, 0, 1, 1)
- val data = ctx.getImageData(0, 0, 1, 1).data
- val red = data(0)
- val green = data(1)
- val blue = data(2)
- val alpha = data(3) / 255.0
-
- apply(red, green, blue, alpha)
- }
+ def fromString(str: String)(using cache: FromStringColourCache): Colour = cache.fromString(str)
// some predefined colours
val black: Colour = fromIntColour(0)
diff --git a/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/FromStringColourCache.scala b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/FromStringColourCache.scala
new file mode 100644
index 0000000..eff074b
--- /dev/null
+++ b/web-components/src/main/scala/be/doeraene/webcomponents/ui5/scaladsl/colour/FromStringColourCache.scala
@@ -0,0 +1,67 @@
+package be.doeraene.webcomponents.ui5.scaladsl.colour
+
+import org.scalajs.dom
+
+import scala.collection.mutable
+
+/** The purpose of a [[FromStringColourCache]] is to circumvent the fact that actually pulling a [[Colour]] from a
+ * [[String]] is a quite expensive operation. Indeed, one has to create an actual canvas, paint to it, and retrieve the
+ * colour data back from it.
+ *
+ * In order to seemlessly use the fromString method in the companion object of [[Colour]], there is a default cache
+ * implementation (given -- pun intended -- below) that simply caches the last 100 different seen values.
+ *
+ * If you want, you can provide a custom implementation and use that one.
+ *
+ * Note that the mutability aspect, as implemented here, is not a liability since we run this thing on JS, where only
+ * one thread exists.
+ */
+trait FromStringColourCache {
+ def fromString(colourString: String): Colour
+}
+
+object FromStringColourCache {
+
+ val noCache: FromStringColourCache = new FromStringColourCache {
+ val canvas = dom.document.createElement("canvas").asInstanceOf[dom.HTMLCanvasElement]
+ canvas.width = 1
+ canvas.height = 1
+ val ctx = canvas.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D]
+
+ def fromString(colourString: String): Colour = {
+ ctx.fillStyle = colourString
+ ctx.fillRect(0, 0, 1, 1)
+ val data = ctx.getImageData(0, 0, 1, 1).data
+ val red = data(0)
+ val green = data(1)
+ val blue = data(2)
+ val alpha = data(3) / 255.0
+
+ Colour(red, green, blue, alpha)
+ }
+ }
+
+ def lastNCache(cacheSize: Int): FromStringColourCache = new FromStringColourCache {
+ var oldestCacheValue: String = "white"
+ var cachedValues: mutable.Map[String, Colour] = mutable.Map("white" -> Colour.white)
+ var numberOfCachedValues: Int = 0
+
+ def fromString(colourString: String): Colour =
+ cachedValues.get(colourString) match {
+ case Some(colour) => colour
+ case None =>
+ val colour = noCache.fromString(colourString)
+ if numberOfCachedValues == cacheSize then
+ cachedValues -= oldestCacheValue
+ oldestCacheValue = colourString
+ else numberOfCachedValues += 1
+
+ cachedValues += (colourString -> colour)
+
+ colour
+ }
+ }
+
+ given FromStringColourCache = lastNCache(100)
+
+}