{"id":13566550,"url":"https://github.com/sherpal/LaminarSAPUI5Bindings","last_synced_at":"2025-04-04T00:31:00.959Z","repository":{"id":45749757,"uuid":"497704152","full_name":"sherpal/LaminarSAPUI5Bindings","owner":"sherpal","description":"Laminar bindings for the SAP ui5 web components library","archived":false,"fork":false,"pushed_at":"2025-01-11T14:22:51.000Z","size":914,"stargazers_count":82,"open_issues_count":7,"forks_count":11,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-01-11T15:32:45.789Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sherpal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-29T20:37:24.000Z","updated_at":"2025-01-11T14:22:56.000Z","dependencies_parsed_at":"2024-03-19T20:30:50.362Z","dependency_job_id":"1825fcbf-4ea5-4c21-8530-1c825bcac46b","html_url":"https://github.com/sherpal/LaminarSAPUI5Bindings","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherpal%2FLaminarSAPUI5Bindings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherpal%2FLaminarSAPUI5Bindings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherpal%2FLaminarSAPUI5Bindings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sherpal%2FLaminarSAPUI5Bindings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sherpal","download_url":"https://codeload.github.com/sherpal/LaminarSAPUI5Bindings/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247103306,"owners_count":20884023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T13:02:11.845Z","updated_at":"2025-04-04T00:30:58.627Z","avatar_url":"https://github.com/sherpal.png","language":"Scala","funding_links":[],"categories":["Scala"],"sub_categories":[],"readme":"# Laminar bindings for SAP ui5 web-components library\n\nThis repository contains [Laminar](https://laminar.dev/) bindings for [SAP ui5 web-components](https://sap.github.io/ui5-webcomponents/).\n\nWeb components are a powerful technology to create near-native html elements. Laminar, for its part, is a Scala-js framework/library to manipulate the dom.\n\n## Demo\n\nYou can visit the [live demo](https://sherpal.github.io/laminar-ui5-demo/) if you want to see these bindings in action.\n\n## Installation\n\nIn order to use these bindings within your Scala.js project, you need to add the following to your `build.sbt`:\n\n```scala\n// for Scala 3\nlibraryDependencies ++= List(\n  \"be.doeraene\" %%% \"web-components-ui5\" % \"\u003ccurrently supported version\u003e\",\n  \"com.raquo\" %%% \"laminar\" % \"17.0.0\"\n)\n```\n\nor \n\n```scala\n// for Scala 2.13.10 (at least .10)\nscalacOptions ++= List(\"-Ytasty-reader\")\nlibraryDependencies ++= List(\n  \"be.doeraene\" % \"web-components-ui5_sjs1_3\" % \"\u003ccurrently supported version\u003e\",\n  \"com.raquo\" %%% \"laminar\" % \"17.0.0\"\n)\n```\n\nwhere `\u003ccurrently supported version\u003e` must be replaced with the version of the library that you want to use (see [the maven repo](https://mvnrepository.com/artifact/be.doeraene/web-components-ui5) page to check existing versions). Major and minor numbers will correspond to the major and minor version of the npm UI5 library.\n\nUsing these bindings with an earlier version of UI5 will probably show stuff that did not exist at the time. Similarly, using these bindings with an older version will imply that you will not find certain bindings. **Other than that, it is perfectly safe to mix mismatch versions.**\n\nImportant: this is only the \"Scala part\" of the installation. We do *not* manage the dependencies to the npm library itself. See the \"How to use\" section below.\n\n## Migration to Version 2.x\n\nPlease review our [migration note](./VERSION2MIGRATION.md).\n\n### Note on the Laminar version\n\nThis library is built against a certain version of Laminar, but it is set as \"provided\". That means that you have to depend on Laminar yourself, but (for a large deal) you are not bound to the specific version used here.\n\nImportant: from 1.9.2 onwards, you are forced to use Laminar \u003e= 15.0. Before 1.9.2, you are forced to use Laminar \u003c 15.0. If you would like to keep using Laminar \u003c 15.0 with newer versions of this library please open an issue.\n\n## What if I find a missing bindings?\n\nFirst of all, don't panic. You can do the following things:\n\n1. Open an issue in this repository, referencing the missing component from [the official docs](https://sap.github.io/ui5-webcomponents/playground/components)\n2. In order to not be stuck in your project right away, you can implement the bindings you need yourself. See the section below to see how to do that\n3. If you feel like it, create a PR to this repository with your new implementation.\n\n## How to use\n\nThese bindings are barely \"facades\" types for the official UI5 library. They _won't_ work if you don't handle that npm dependency on your own project. You will need the following imports in your `package.json` (or equivalent tool such as scala-js-bundler):\n\n```\n\"@ui5/webcomponents\": \"2.1.0\",\n\"@ui5/webcomponents-fiori\": \"2.1.0\",\n\"@ui5/webcomponents-icons\": \"2.1.0\"\n```\n\n(and thus `npm install` it). Then, you can use any of the components as defined in the `be.doeraene.webcomponents.ui5` package.\n\nFor example, you can add an `\u003cui5-input\u003e` in your Laminar code like this:\n\n```scala\ndiv(\n  Input(\n    _.required := true,\n    _.valueState := ValueState.Information,\n    _.placeholder := \"Enter your name\",\n    onChange.mapToValue --\u003e Observer(println)\n  )\n)\n```\n\nThis will be rendered as an input as documented [here](https://sap.github.io/ui5-webcomponents/playground/components/Input/).\n\n### Remark for Scala 2.13 users\n\nIn order to achieve the highest ergonomics possible, the `apply` method of components uses a union type as argument. But this only would completely sacrifice the ability of Scala 2.13 users to use the library.\n\nThat is why all components also have an `of` method, only accepting mod functions (as opposed to accepting both mods and mod functions). This reduces the ergonomics a bit for Scala 2.13, but it allows us to move forward.\n\nFor you, concretely, instead of having, for example,\n\n```scala\nInput(_.required := true, onChange.mapToValue --\u003e Observer(println))\n```\n\nyou will have to write\n\n```scala\nInput.of(_.required := true, _ =\u003e onChange.mapToValue --\u003e Observer(println))\n```\n\nFailing to do so will produce the following error message: \n\n\u003e Unsupported Scala 3 union in parameter value mods; found in method apply in trait be.doeraene.webcomponents.WebComponent.\n\n### Running the Demo\n\nThe project contains a demo file for each component. These examples are located in the `demo` sub-project.\n\nIn order to run those, you need to have\n\n- npm with a recent enough version (\u003e= 14 should do) (if you have nvm installed, you can do, e.g., `nvm use 17`)\n- sbt\n\nPerform the following steps:\n\nFirst time onl. Open a terminal. cd demo; npm install\n\n1. in one terminal, run `sbt ~demo/fastLinkJS`\n2. in another terminal, go to `demo` and run `npm install` then `npm run dev`\n3. when both steps are ready, go to `http://localhost:3000/laminar-ui5-demo/` and the demo should be there, waiting for you.\n\nIf you're in vscode, try running the [task](https://code.visualstudio.com/docs/editor/tasks), \"runDemo\" build task, it will do the above 3 steps for you. \n\n### How to use slots?\n\nIn web-components, certain components can have special dom children called \"slots\". In the dom, these \"special\" children are only singular in the fact that they have a property `slot` specifying which slot they fill in.\n\nFor example, the `Dialog` component as a `footer` slot. In raw html, this would look like\n\n```html\n\u003cui5-dialog\u003e\n  \u003cdiv slot=\"footer\"\u003eI'm a footer\u003c/div\u003e\n\u003c/ui5-dialog\u003e\n```\n\nThanks to the `slots` object in the `Dialog` component, this is written in Scala as\n\n```scala\nDialog(\n  _.slots.footer := div(\"I'm a footer\")\n)\n```\n\nSee the implementation of the `Slot` class to understand what it does.\n\n## Icons\n\nAs you may read from SAP's documentation, Icons need to be imported manually via something like:\n\n```typescript\nimport * as accidentalLeave from \"@ui5/webcomponents-icons/dist/accidental-leave.js\";\n```\n\nOr you can import all icons at once with\n\n```typescript\nimport * as allIcons from \"@ui5/webcomponents-icons/dist/AllIcons.js\";\n```\n\nWith these bindings, you don't need to do any of that. Scala.js will detect automatically the imports that are required based on your usage of the `IconName` values. For example, if you have somewhere in your code\n\n```scala\nIcon(\n  _.name := IconName.`accidental-leave`\n)\n```\n\nthen the above import will be added in the compiled JS file.\n\n## Illustrated Messages\n\n[Illustrated Messages](https://sap.github.io/ui5-webcomponents/playground/components/IllustratedMessage/) work the same way as icons (see above).\n\nAll possible choices are available in the `IllustratedMessageType` object. Illustrated messages within the `tnt` subdirectory are available in the `tnt` object thereof.\n\n## How to read the source code\n\nEvery component is implemented as an object with the following:\n\n- a `RawElement` (js-native) trait that defines methods that the underlying DOM element will have\n- a `RawImport` object only there to actually import the library\n- a few reactive html attributes specific to that component\n- a `slots` object that contains descriptions of the slots that the component can have. They allow syntax such as `Input(_.slots.valueStateMessage := span(\"Hello\"))`.\n- an `events` object containing specific events that the component can emit.\n- an `apply` method to build the component (following the [example from the Laminar website](https://laminar.dev/examples/web-components), they take functions from the object to mods)\n- some components will have links to other tightly coupled components (for example, the `TabContainer` has a `tab` link to the `Tab` component). They allow syntax such as `TabContainer(_.tab := ???)`.\n\nEvery object is named after the corresponding ui5 web-component, with the notable exception of `List` which is called `UList` to not interfere with the built-in Scala `List` type.\n\n### Config Keys package\n\nThe ui5 library contains a whole lot of \"enums\" that are used as properties of elements. In TypeScript, this would typically be represented as\n\n```typescript\ntype Stuff = \"foo\" | \"bar\" | \"baz\"\n```\n\nIn Scala 3, this approach could work in theory, but not I'm not really fond of. Instead, I prefer to define those as sealed traits like this\n\n```scala\nsealed trait Stuff\nobject Stuff {\n  case object Foo extends Stuff\n  case object Bar extends Stuff\n  case object Baz extends Stuff\n}\n```\n\nIn order to facilitate such usage with Laminar, these objects will typically extends `EnumerationString`, which helps by providing the codec required to represent this as a string.\n\n## Contributing: Implement bindings for a new component\n\nIf you found a missing component that you would like to see integrated in the repo, please find below small guidelines to help you with. There are quite a lot of steps but don't worry, in pratice you can do most of these by simply copy-pasting another existing component!\n\n#### Before you start\n\nBe sure to check the section about How to read the source code, above.\n\nAlso, verify that the component you are about to add is available in the current matching version defined in the `build.sbt` as\n\n```scala\nThisBuild / version := \"\u003ccurrent supported sap version\u003e\"\n```\n\nIf not, please open an issue to upgrade to the new version. This does not prevent you to already do the following steps.\n\n#### Create the scala file\n\nIn the `be.doeraene.webcomponents.ui5` package, create a new `object` called the same way as the component, as visible in the docs page (for example, for the [busy indicator](https://sap.github.io/ui5-webcomponents/playground/components/BusyIndicator/), it will be `BusyIndicator`)\n\n#### Make the docstring\n\nFill in the docstring for that object with the contents of the \"Overview\" section from the docs, and a `@see` referencing the official docs webpage\n\n#### Write the boilerplate\n\nIn the `object`, add the following things:\n\n- make the object extends `WebComponent`\n- create a trait `RawElement` extending `js.Object` and annotated with `@js.native`\n- add an object `RawImport` extending `js.Object` and annotated with both `@js.native` and `@JSImport`, specifying the correct import (available in the official docs), setting `JSImport.Default` as second argument\n- call `used(RawImport)` the line after (this is done to be sure that scala-js actually import the JS dependency)\n- define an alias `type Ref` as `dom.html.Element \u0026 RawElement`\n- define the protected `tag` variable of type `CustomHtmlTag[Ref]` specifying the ui5 tag name from the doc (for example, for the Button component, it's `protected val tag: CustomHtmlTag[Ref] = CustomHtmlTag(\"ui5-button\")`). ⚠️: when copy-pasting from an existing component, this is usually the one we forget! When that happens, you will observe a component doing basically nothing. It's a sign you put the wrong import.\n- create an empty object `slots`\n- create an empty object `events`\n- in the case where your component is linked to other components (for example a `TableCell` is always contained in `TableRow`, so the `TableRow` object will have a reference to the `TableCell` object)\n\n#### Filling the reactive attributes\n\nThe official docs always have a \"Properties/Attributes\" section. All these properties should be converted into `HtmlAttr`. For example, the `disabled` attribute of Button is defined as\n\n```scala\nval disabled: HtmlAttr[Boolean] = htmlAttr(\"disabled\", BooleanAsAttrPresenceCodec)\n```\n\nNote that while it's not mandatory that the name of the variable matches the name of the attribute, it's customary to use the same (camelCase) naming.\n\nFor \"primitive\" types (`Boolean`, `String`, `Int`), you can use the codecs provided by Laminar. For \"enums\", see below (essentially you will need to define an `EnumerationString` type as described in the next section).\n\nFor durations, _expressed in millis_, you can use the `FiniteDurationCodec` available for you in the package object.\n\nYou can decide to create more involved types a bit fancy and create custom codecs, but you need to decide whether it's worth it. Such example could be if you have a type representing numbers between 0 and 10 because that are the only values that are valid by ui5.\n\nSome common attribute are \"packaged\" in traits that your component object can inherit from (e.g., `HasIcon`).\n\nNote: in theory all these properties could (should?) also exist on the `RawElement` trait. However, it is not really idiomatic to Laminar to use these. If, for some reason, you still think some property should be there, it should be \"readonly\" (that is, defined as a `def`).\n\n#### Reactive attributes for enums\n\nA lot of attributes/properties of UI5 elements are enumerations. Usually, the official SAP docs itself gives a name to the \"type\" of the enum. For example, `ValueState` or `BreadcrumbsSeparatorStyle`.\n\nAlthough you could define these properties as `String`, this would be poorly typed and lack semantics. For that reason, we create a `sealed trait`, with plenty of `case object`s to represent those.\n\nThey are all defined in the `be.doeraene.webcomponents.ui5.configkeys` package.\n\nTo implement a new enum, follow these steps:\n\n1. create a sealed trait named the same way as in the sap docs\n2. give this sealed trait a companion object, and fill it with one `case object` for each possible value\n3. make the companion object extend `EnumerationString` (available in the same package)\n4. Implement the `valueOf` and `allValues` abstract members\n5. use the `AsStringCodec` in your reactive values\n\n#### Events\n\nSome UI5 components have custom events specific to them, usually with special type of emitted events.\n\nAll these events are represented as values of type `EventProp`. It takes a type parameter, which is the type of the values emitted by this event. This type has to be a subtype of `dom.Event`. Events from UI5 components are usually augmented with other properties. They are roughly documented, but most of the time you will have to trigger them manually and `dom.console.log` the emitted values to understand what they really are.\n\nSome common patterns are:\n\n- values have a `detail` field of a certain type. The library has a helper `HasDetail` trait, and in that case the complete type will look like `dom.Event \u0026 HasDetail[SomeOtherType]`. An example taken from the `Table` component is `val onSelectionChange = new EventProp[dom.Event \u0026 HasDetail[TableSelectionChangeDetail]](\"selection-change\")`.\n- events with a more precise target type (when you need the precise type instead of just `HtmlElement`). This type is `EventWithPreciseTarget` and already extends `dom.Event`. An example from the `CheckBox` component is `val onChange: EventProp[EventWithPreciseTarget[Ref]] = new EventProp(\"change\")`\n- a combination of the above (combined with `\u0026`)\n\n#### Slots\n\nIn the `slots` object, define a value for each of the slot this component has, except for the (always present) `default` one.\n\nAll the slots for a component are documented in the `Slots` section in the docs.\n\nFor example, the footer slot of the `Dialog` is defined as\n\n```scala\nval footer: Slot = new Slot(\"footer\")\n```\n\n#### Methods\n\nSome SAP components have special methods to interact with. Among the most common, you will find the `Dialog` which has a `show` method. Some others like `Popover` will have `showAt`.\n\nThese methods must be defined as `js.native` method of the `RawElement` trait.\n\nFor methods that take as input or return some \"not-so-scala-ish\" types (such as `js.Array`), it is best to call them `theMethodNameJS` (annotating by `JSName(\"theMethodName\")`) and to create an extension method called `theMethodName` taking/returning more Scala friendly types. For example, the `ColourPicker` `RawElement` is implemented as\n\n```scala\n@js.native\ntrait RawElement extends js.Object {\n  @JSName(\"color\")\n  def colourJS: String = js.native\n}\n\nobject RawElement {\n  extension (rawElement: RawElement)\n    /** The current colour as [[Colour]] instance. English UK spelling for consistency. */\n    def colour: Colour = Colour.fromString(rawElement.colourJS)\n}\n```\n\n#### Making the demos\n\nThe best way to make sure that your implementations work, and to also help future users at the same time, is to write a demo for the new component.\n\nIn the `demo` subproject, in the `demo` package, create an object `MyComponentExample`, extending `Example` with the SAP name of the component.\n\nYou will then need to fill the `component` def. You should make use of the `DemoPanel` and wrapping your example codes in `//-- Begin: name of your demo panel` and `//-- End`. Once your code will be merged on master, it will make the source code directly displayed beneath the examples.\n\nYou then need to add your example in the list in the `EntryPoint` in the `demo` package.\n\nFor the structure, you can take inspiration from the existing other examples. For the content of your demo, you can take inspiration from the official SAP docs.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsherpal%2FLaminarSAPUI5Bindings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsherpal%2FLaminarSAPUI5Bindings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsherpal%2FLaminarSAPUI5Bindings/lists"}