@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.66 → 2.0.0-next.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +2738 -2408
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +347 -30
  4. package/dist/automation/automation-button/automation-button.css.js +9 -9
  5. package/dist/automation/valve-analoge-two-way-icon/valve-analog-two-way-icon.d.ts +1 -0
  6. package/dist/automation/valve-analoge-two-way-icon/valve-analog-two-way-icon.d.ts.map +1 -1
  7. package/dist/automation/valve-analoge-two-way-icon/valve-analog-two-way-icon.js +9 -28
  8. package/dist/automation/valve-analoge-two-way-icon/valve-analog-two-way-icon.js.map +1 -1
  9. package/dist/components/accordion-card/accordion-card.css.js +1 -1
  10. package/dist/components/dropdown-button/dropdown-button.d.ts +11 -0
  11. package/dist/components/dropdown-button/dropdown-button.d.ts.map +1 -1
  12. package/dist/components/dropdown-button/dropdown-button.js +24 -2
  13. package/dist/components/dropdown-button/dropdown-button.js.map +1 -1
  14. package/dist/components/keyboard-numeric/keyboard-numeric.d.ts +1 -2
  15. package/dist/components/keyboard-numeric/keyboard-numeric.d.ts.map +1 -1
  16. package/dist/components/keyboard-numeric/keyboard-numeric.js +10 -15
  17. package/dist/components/keyboard-numeric/keyboard-numeric.js.map +1 -1
  18. package/dist/components/number-input-field/number-input-field.d.ts +9 -0
  19. package/dist/components/number-input-field/number-input-field.d.ts.map +1 -1
  20. package/dist/components/number-input-field/number-input-field.js +30 -1
  21. package/dist/components/number-input-field/number-input-field.js.map +1 -1
  22. package/dist/components/number-input-field/number-input-format.d.ts +9 -0
  23. package/dist/components/number-input-field/number-input-format.d.ts.map +1 -1
  24. package/dist/components/number-input-field/number-input-format.js +33 -2
  25. package/dist/components/number-input-field/number-input-format.js.map +1 -1
  26. package/dist/components/stepper-box/stepper-box.css.js +0 -6
  27. package/dist/components/stepper-box/stepper-box.css.js.map +1 -1
  28. package/dist/components/stepper-box/stepper-box.d.ts +3 -13
  29. package/dist/components/stepper-box/stepper-box.d.ts.map +1 -1
  30. package/dist/components/stepper-box/stepper-box.js.map +1 -1
  31. package/dist/components/toggle-button-group/toggle-button-group.d.ts +14 -0
  32. package/dist/components/toggle-button-group/toggle-button-group.d.ts.map +1 -1
  33. package/dist/components/toggle-button-group/toggle-button-group.js +25 -5
  34. package/dist/components/toggle-button-group/toggle-button-group.js.map +1 -1
  35. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.css.js +4 -0
  36. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.css.js.map +1 -1
  37. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.d.ts +1 -0
  38. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.d.ts.map +1 -1
  39. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.js +13 -1
  40. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.js.map +1 -1
  41. package/dist/navigation-instruments/graph-mini/graph-mini.css.js +2 -1
  42. package/dist/navigation-instruments/graph-mini/graph-mini.css.js.map +1 -1
  43. package/dist/navigation-instruments/graph-mini/graph-mini.d.ts.map +1 -1
  44. package/dist/navigation-instruments/graph-mini/graph-mini.js +7 -5
  45. package/dist/navigation-instruments/graph-mini/graph-mini.js.map +1 -1
  46. package/dist/navigation-instruments/indicator-graph/indicator-graph.css.js +66 -0
  47. package/dist/navigation-instruments/indicator-graph/indicator-graph.css.js.map +1 -0
  48. package/dist/navigation-instruments/indicator-graph/indicator-graph.d.ts +54 -0
  49. package/dist/navigation-instruments/indicator-graph/indicator-graph.d.ts.map +1 -0
  50. package/dist/navigation-instruments/indicator-graph/indicator-graph.js +203 -0
  51. package/dist/navigation-instruments/indicator-graph/indicator-graph.js.map +1 -0
  52. package/docs/GettingStarted.mdx +28 -12
  53. package/package.json +1 -1
  54. package/src/automation/automation-button/automation-button.css +9 -9
  55. package/src/automation/valve-analoge-two-way-icon/valve-analog-two-way-icon.ts +9 -28
  56. package/src/components/accordion-card/accordion-card.css +1 -1
  57. package/src/components/dropdown-button/dropdown-button.stories.ts +22 -0
  58. package/src/components/dropdown-button/dropdown-button.ts +33 -4
  59. package/src/components/keyboard-numeric/keyboard-numeric.ts +12 -18
  60. package/src/components/number-input-field/number-input-field.spec.ts +134 -0
  61. package/src/components/number-input-field/number-input-field.ts +47 -0
  62. package/src/components/number-input-field/number-input-format.spec.ts +62 -0
  63. package/src/components/number-input-field/number-input-format.ts +59 -2
  64. package/src/components/stepper-box/stepper-box.css +0 -6
  65. package/src/components/stepper-box/stepper-box.ts +3 -13
  66. package/src/components/toggle-button-group/toggle-button-group.ts +36 -5
  67. package/src/components/toggle-button-group/toggle-button.stories.ts +31 -0
  68. package/src/integration-systems/integration-fleet-button/integration-fleet-button.css +4 -0
  69. package/src/integration-systems/integration-fleet-button/integration-fleet-button.stories.ts +9 -0
  70. package/src/integration-systems/integration-fleet-button/integration-fleet-button.ts +10 -1
  71. package/src/navigation-instruments/graph-mini/graph-mini.css +2 -1
  72. package/src/navigation-instruments/graph-mini/graph-mini.stories.ts +11 -3
  73. package/src/navigation-instruments/graph-mini/graph-mini.ts +7 -5
  74. package/src/navigation-instruments/indicator-graph/indicator-graph.css +55 -0
  75. package/src/navigation-instruments/indicator-graph/indicator-graph.stories.ts +116 -0
  76. package/src/navigation-instruments/indicator-graph/indicator-graph.ts +222 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indicator-graph.js","sources":["../../../src/navigation-instruments/indicator-graph/indicator-graph.ts"],"sourcesContent":["import {LitElement, PropertyValues, html, unsafeCSS} from 'lit';\nimport {property, query, state} from 'lit/decorators.js';\nimport compentStyle from './indicator-graph.css?inline';\nimport uPlot from 'uplot';\nimport {customElement} from '../../decorator.js';\n\nexport enum ObcIndicatorGraphSize {\n small = 'small',\n medium = 'medium',\n large = 'large',\n}\n\nexport enum ObcIndicatorGraphPriority {\n regular = 'regular',\n enhanced = 'enhanced',\n}\n\nexport interface ObcIndicatorGraphLayout {\n size?: ObcIndicatorGraphSize;\n priority?: ObcIndicatorGraphPriority;\n y?: {min?: number; max?: number};\n}\n\n/**\n * @element obc-indicator-graph\n * @description A mini graph component\n *\n * @property {Array} data - The data to display in the graph, first array is the x values, second array is the y values\n */\n@customElement('obc-indicator-graph')\nexport class ObcIndicatorGraph extends LitElement {\n @property({type: Array})\n data: [number[], number[]] = [[], []];\n\n @property({type: Object})\n layout: ObcIndicatorGraphLayout = {};\n\n @query('.chart-container')\n private chart!: HTMLDivElement;\n\n @state()\n private y: number = 24;\n\n @state()\n private zeroLineY: number = 0;\n\n private uplot: uPlot | null = null;\n private resizeObserver: ResizeObserver | null = null;\n\n private getCssColor(name: string) {\n const color = getComputedStyle(this).getPropertyValue(name).trim();\n return color;\n }\n\n override firstUpdated() {\n const opts = {\n width: this.chart.clientWidth,\n height: this.chart.clientHeight,\n scales: {\n x: {time: false, show: false},\n y: {\n auto: true,\n show: false,\n range: this._range.bind(this),\n },\n },\n series: [\n {},\n {\n stroke: this._getStrokeColor(),\n width: this._getStrokeWidth(),\n points: {show: false},\n },\n ],\n axes: [\n {show: false},\n {ticks: {show: false}, show: false, grid: {show: false}},\n ],\n legend: {show: false},\n cursor: {show: false},\n };\n\n this.uplot = new uPlot(opts, this.data, this.chart);\n requestAnimationFrame(() => this.updateY());\n\n this.resizeObserver = new ResizeObserver(() => this.updateSize());\n this.resizeObserver.observe(this.chart);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.uplot?.destroy();\n this.uplot = null;\n }\n\n private updateSize() {\n if (!this.uplot) {\n return;\n }\n this.uplot.setSize({\n width: this.chart.clientWidth,\n height: this.chart.clientHeight,\n });\n this.updateY();\n }\n\n private _range(\n _self: uPlot,\n initMin: number,\n initMax: number,\n _scaleKey: string\n ): [number, number] {\n const {min: minY, max: maxY} = this.layout.y ?? {};\n const range = maxY ?? initMax - (minY ?? initMin);\n let min = minY ?? initMin - range * 0.1;\n if (minY === undefined) {\n min = Math.min(0, min);\n }\n return [min, maxY ?? initMax + range * 0.1] as [number, number];\n }\n\n private get _effectiveSize() {\n return this.layout.size ?? ObcIndicatorGraphSize.medium;\n }\n\n private get _effectivePriority() {\n return this.layout.priority ?? ObcIndicatorGraphPriority.regular;\n }\n\n private _getStrokeWidth() {\n switch (this._effectiveSize) {\n case ObcIndicatorGraphSize.small:\n return 1;\n case ObcIndicatorGraphSize.medium:\n return 1.5;\n case ObcIndicatorGraphSize.large:\n return 2;\n }\n }\n\n private _getStrokeColor() {\n switch (this._effectivePriority) {\n case ObcIndicatorGraphPriority.regular:\n return this.getCssColor('--element-neutral-color');\n case ObcIndicatorGraphPriority.enhanced:\n return this.getCssColor('--element-neutral-enhanced-color');\n }\n }\n\n updatePalette() {\n if (!this.uplot) {\n return;\n }\n const stroke = this._getStrokeColor();\n this.uplot.setSeries(1, {\n // @ts-expect-error - stroke is not a property of the Series interface\n stroke: stroke,\n width: this._getStrokeWidth(),\n points: {show: false},\n });\n }\n\n private updateY() {\n if (!this.uplot) {\n return;\n }\n const lastY = this.data[1][this.data[1].length - 1];\n // @ts-expect-error - valToPct is not a property of the Scale interface\n const yRatio = this.uplot.scales.y.valToPct(lastY);\n this.y = yRatio * this.chart.clientHeight;\n this.updateZeroLine();\n }\n\n private updateZeroLine() {\n if (!this.uplot) {\n return;\n }\n // @ts-expect-error - valToPct is not a property of the Scale interface\n const yRatio = this.uplot.scales.y.valToPct(0);\n this.zeroLineY = yRatio * this.chart.clientHeight;\n }\n\n override updated(changedProperties: PropertyValues) {\n if (changedProperties.has('data') || changedProperties.has('layout')) {\n this.updatePalette();\n this.uplot?.setData(this.data);\n requestAnimationFrame(() => this.updateY());\n }\n }\n\n override render() {\n return html`\n <div\n class=\"chart-container ${this._effectivePriority} ${this\n ._effectiveSize}\"\n >\n <div\n id=\"zero-line\"\n style=\"transform: translateY(${-this.zeroLineY}px);\n display: ${this.zeroLineY > -0.5 ? 'block' : 'none'};\n \"\n ></div>\n <div\n id=\"dot\"\n style=\"transform: translateY(${-this.y}px);\n display: ${this.y > -0.5 ? 'block' : 'none'};\n \"\n ></div>\n </div>\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-indicator-graph': ObcIndicatorGraph;\n }\n}\n"],"names":["ObcIndicatorGraphSize","ObcIndicatorGraphPriority"],"mappings":"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"}
@@ -2,23 +2,19 @@ import {Meta} from '@storybook/addon-docs/blocks';
2
2
 
3
3
  <Meta title="Introduction/Getting Started" />
4
4
 
5
- # Getting Started
5
+ # 🚀 Project Status
6
6
 
7
- ## Access Requirements
7
+ **v1.0.0 is now released!** It has been an incredibly productive first year, with more than 200 components already
8
+ implemented. The code was officially opened by **Prince Sverre Magnus of Norway** and is now publicly available,
9
+ marking our first stable milestone.
8
10
 
9
- > **Note:** Access to the OpenBridge Web Components library is currently restricted to JIP partners and donors until the public release in March 2026. To gain access, you must join the project as a partner or donor. [Register your interest here.](https://docs.google.com/forms/d/e/1FAIpQLSd2H7bbL_duBTMhHzjw7W52H9XXAiJ9A3sL7PsrfCTW_bNUhw/viewform)
11
+ # Getting Started
10
12
 
11
13
  ## Installation
12
14
 
13
- To use the components in your project, you need access to the private package repository. Once you have access:
15
+ The package is publicly available on [npm](https://www.npmjs.com/package/@oicl/openbridge-webcomponents) no authentication required.
14
16
 
15
- 1. **Create a GitHub personal access token** with `read:packages` permission. [Create token here.](https://github.com/settings/tokens/new)
16
- 2. **Login to the GitHub package registry:**
17
- ```bash
18
- npm login --registry https://npm.pkg.github.com/ --scope=@oicl
19
- ```
20
- Use your GitHub username and the generated token as password.
21
- 3. **Install the package:**
17
+ 1. **Install the package:**
22
18
  ```bash
23
19
  npm install @oicl/openbridge-webcomponents
24
20
  ```
@@ -26,6 +22,11 @@ To use the components in your project, you need access to the private package re
26
22
  ```bash
27
23
  npm install @oicl/openbridge-webcomponents-vue
28
24
  ```
25
+ React, Angular, and Svelte wrappers are published as `@oicl/openbridge-webcomponents-react`, `-ng`, and `-svelte`.
26
+
27
+ 2. **Choose a release channel:**
28
+ - The `latest` tag tracks the stable, production-ready releases (updated from the `stable` branch).
29
+ - The `next` tag tracks the active development releases (from the `develop` branch) and may include breaking changes. Install it with `npm install @oicl/openbridge-webcomponents@next`.
29
30
 
30
31
  ## Usage
31
32
 
@@ -71,4 +72,19 @@ To use the components in your project, you need access to the private package re
71
72
  <ObcTopBar></ObcTopBar>
72
73
  ```
73
74
 
74
- For more details, see the [README](../../readme.md) or visit the [OpenBridge Design System website](https://www.openbridge.no/).
75
+ ## Licensing Model
76
+
77
+ The project will **always remain open source**, but with the 1.0.0 release it has transitioned to a new licensing system. Each release — based on both the `develop` and `stable` branches — follows a "delayed" permissive license cycle:
78
+
79
+ 1. **Initial Release:** Each new version is licensed under **AGPL** for the first 6 months.
80
+ 2. **Transition:** After 6 months, the license for that specific version automatically switches to **Apache 2.0**.
81
+ 3. **Supporter Access:** Project donors and sponsors receive **immediate access** to the code under the **Apache 2.0** license.
82
+
83
+ > **Example:**
84
+ > If version 1.2.2 is released on **August 14, 2026**:
85
+ >
86
+ > - **open-source projects and internal users** can use it immediately under AGPL.
87
+ > - **donors** can use it immediately under **Apache 2.0**.
88
+ > - From **February 14, 2027**, version 1.2.2 becomes available to everyone under the **Apache 2.0** license (e.g., for commercial use without AGPL restrictions).
89
+
90
+ For more details visit the [OpenBridge Design System website](https://www.openbridge.no/).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oicl/openbridge-webcomponents-full-bundle",
3
- "version": "2.0.0-next.66",
3
+ "version": "2.0.0-next.68",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -219,18 +219,18 @@
219
219
  }
220
220
 
221
221
  .state-closed .icon-holder {
222
- border-color: var(--automation-pipe-tertiary-inverted-color);
223
- background: var(--automation-pipe-primary-inverted-color);
224
-
225
- .static & {
226
- border-color: var(--automation-pipe-secondary-color);
227
- background: var(--automation-pipe-primary-inverted-color);
228
- }
222
+ border-color: var(--automation-button-off-border-color);
223
+ background: var(--automation-button-off-background-color);
229
224
  }
230
225
 
231
226
  .state-open .icon-holder {
232
- border-color: var(--automation-pipe-tertiary-color);
233
- background: var(--automation-pipe-primary-color);
227
+ border-color: var(--on-border-color);
228
+ background: var(--on-background-color);
229
+ }
230
+
231
+ .static .icon-holder {
232
+ border-color: var(--automation-button-static-broder-color);
233
+ background: var(--automation-button-static-background-color);
234
234
  }
235
235
 
236
236
  .alert-frame {
@@ -2,6 +2,7 @@ import {LitElement, html, svg, unsafeCSS} from 'lit';
2
2
  import {property} from 'lit/decorators.js';
3
3
  import compentStyle from './valve-analog-two-way-icon.css?inline';
4
4
  import {customElement} from '../../decorator.js';
5
+ import '../../icons/icon-twoway-analog-closed.js';
5
6
 
6
7
  @customElement('obc-valve-analog-two-way-icon')
7
8
  export class ObcValveAnalogTwoWayIcon extends LitElement {
@@ -13,36 +14,16 @@ export class ObcValveAnalogTwoWayIcon extends LitElement {
13
14
  const transform = this.vertical ? 'transform: rotate(90deg);' : '';
14
15
  if (this.closed) {
15
16
  return html` <div class="wrapper" style="${transform}">
16
- <svg
17
- width="24"
18
- height="24"
19
- viewBox="0 0 24 24"
20
- fill="none"
21
- xmlns="http://www.w3.org/2000/svg"
22
- >
23
- <path
24
- fill-rule="evenodd"
25
- clip-rule="evenodd"
26
- d="M3 16.8892L10 12.8059V10.4546L3 6.37127V16.8892ZM11 9.88024V13.3802L2.75194 18.1916C2.41861 18.386 2 18.1456 2 17.7597V5.50075C2 5.11486 2.41861 4.87442 2.75193 5.06886L11 9.88024ZM21 16.8892V6.37127L14 10.4546V12.8059L21 16.8892ZM13 13.3802L21.2481 18.1916C21.5814 18.386 22 18.1456 22 17.7597V5.50075C22 5.11486 21.5814 4.87442 21.2481 5.06886L13 9.88024V13.3802ZM12 22.6301C11.4477 22.6301 11 22.1824 11 21.6301V15.6301C11 15.0778 11.4477 14.6301 12 14.6301C12.5523 14.6301 13 15.0778 13 15.6301V21.6301C13 22.1824 12.5523 22.6301 12 22.6301Z"
27
- fill="var(--automation-device-secondary-color)"
28
- />
29
- <path
30
- d="M10 12.8059L3 16.8892V6.37127L10 10.4546V12.8059Z"
31
- fill="var(--automation-device-tertiary-color)"
32
- />
33
- <path
34
- d="M21 6.37127V16.8892L14 12.8059V10.4546L21 6.37127Z"
35
- fill="var(--automation-device-tertiary-color)"
36
- />
37
- </svg>
17
+ <obi-twoway-analog-closed useCssColor> </obi-twoway-analog-closed>
38
18
  </div>`;
39
19
  }
40
20
 
41
21
  const handleRotation = -(1 - this.value / 100) * 90;
22
+ const handleTranslation = (1 - this.value / 100) * 2;
42
23
  const handle = svg`
43
- <g transform="rotate(${handleRotation} 12 18.5)">
44
- <path fill-rule="evenodd" clip-rule="evenodd" d="M9.49942 20.1303H14.4994C15.3278 20.1303 15.9994 19.4587 15.9994 18.6303C15.9994 17.8019 15.3278 17.1303 14.4994 17.1303H9.49942C8.67099 17.1303 7.99942 17.8019 7.99942 18.6303C7.99942 19.4587 8.67099 20.1303 9.49942 20.1303ZM9.49942 19.1303L14.4994 19.1303C14.7756 19.1303 14.9994 18.9064 14.9994 18.6303C14.9994 18.3542 14.7756 18.1303 14.4994 18.1303L9.49942 18.1303C9.22328 18.1303 8.99942 18.3542 8.99942 18.6303C8.99942 18.9064 9.22328 19.1303 9.49942 19.1303Z" fill="var(--automation-device-tertiary-color)"/>
45
- <path d="M14.4994 19.1303L9.49942 19.1303C9.22328 19.1303 8.99942 18.9064 8.99942 18.6303C8.99942 18.3542 9.22328 18.1303 9.49942 18.1303L14.4994 18.1303C14.7756 18.1303 14.9994 18.3542 14.9994 18.6303C14.9994 18.9064 14.7756 19.1303 14.4994 19.1303Z" fill="var(--automation-device-primary-color)"/>
24
+ <g transform="translate(0, ${handleTranslation}) rotate(${handleRotation} 12 3.5) ">
25
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 5H14.5C15.3284 5 16 4.32843 16 3.5C16 2.67157 15.3284 2 14.5 2H9.5C8.67157 2 8 2.67157 8 3.5C8 4.32843 8.67157 5 9.5 5ZM9.5 4L14.5 4C14.7761 4 15 3.77614 15 3.5C15 3.22386 14.7761 3 14.5 3L9.5 3C9.22386 3 9 3.22386 9 3.5C9 3.77614 9.22386 4 9.5 4Z" fill="var(--automation-device-tertiary-color)"/>
26
+ <path d="M9.5 4L14.5 4C14.7761 4 15 3.77614 15 3.5C15 3.22386 14.7761 3 14.5 3L9.5 3C9.22386 3 9 3.22386 9 3.5C9 3.77614 9.22386 4 9.5 4Z" fill="var(--automation-device-primary-color)"/>
46
27
  </g>
47
28
  `;
48
29
 
@@ -60,11 +41,11 @@ export class ObcValveAnalogTwoWayIcon extends LitElement {
60
41
  xmlns="http://www.w3.org/2000/svg"
61
42
  >
62
43
  <path
63
- d="M2 5.50075V17.7597C2 18.1456 2.41861 18.386 2.75194 18.1916L10.5714 13.6302H13.4286L21.2481 18.1916C21.5814 18.386 22 18.1456 22 17.7597V5.50075C22 5.11486 21.5814 4.87442 21.2481 5.06886L13.4286 9.63024H10.5714L2.75193 5.06886C2.41861 4.87442 2 5.11486 2 5.50075Z"
44
+ d="M11 11L3.5547 6.03645C2.89015 5.59342 2 6.06981 2 6.8685V19.1315C2 19.9302 2.89015 20.4066 3.5547 19.9635L11 15H13L20.4453 19.9635C21.1099 20.4066 22 19.9302 22 19.1315V6.8685C22 6.06981 21.1099 5.59342 20.4453 6.03645L13 11H11ZM3 6.8685L3 19.1315L10.6972 14H13.3028L21 19.1315V6.8685L13.3028 12H10.6972L3 6.8685Z"
64
45
  fill="var(--automation-device-tertiary-color)"
65
46
  />
66
47
  <path
67
- d="M3 6.3712V16.8891L10.3011 12.6302H13.6989L21 16.8891V6.3712L13.6989 10.6302H10.3011L3 6.3712Z"
48
+ d="M10.6972 12H13.3028L21 6.8685V19.1315L13.3028 14H10.6972L3 19.1315V6.8685L10.6972 12Z"
68
49
  fill="var(--automation-device-primary-color)"
69
50
  />
70
51
  ${handle}
@@ -104,7 +85,7 @@ export class ObcValveAnalogTwoWayIcon extends LitElement {
104
85
  <defs>
105
86
  <clipPath id="clip0">
106
87
  <path
107
- d="M3 6.3712V16.8891L10.3011 12.6302H13.6989L21 16.8891V6.3712L13.6989 10.6302H10.3011L3 6.3712Z"
88
+ d="M10.6972 12H13.3028L21 6.8685V19.1315L13.3028 14H10.6972L3 19.1315V6.8685L10.6972 12Z"
108
89
  />
109
90
  </clipPath>
110
91
  </defs>
@@ -158,7 +158,7 @@
158
158
 
159
159
  .label-description {
160
160
  @mixin font-label;
161
- flex: 1 0 0;
161
+ flex: 0 0 auto;
162
162
  width: 100%;
163
163
  overflow: hidden;
164
164
  color: var(--element-neutral-color);
@@ -25,6 +25,12 @@ const meta: Meta<ObcDropdownButton> = {
25
25
  openTop: {
26
26
  control: 'boolean',
27
27
  },
28
+ allowEmptySelection: {
29
+ control: 'boolean',
30
+ },
31
+ placeholder: {
32
+ control: 'text',
33
+ },
28
34
  },
29
35
  args: {
30
36
  options: [
@@ -171,3 +177,19 @@ export const DisabledWithIcon: Story = {
171
177
  </obc-dropdown-button>
172
178
  `,
173
179
  };
180
+
181
+ export const EmptySelection: Story = {
182
+ args: {
183
+ value: 'none',
184
+ allowEmptySelection: true,
185
+ placeholder: 'Select a brand',
186
+ },
187
+ render: (args) => html`
188
+ <obc-dropdown-button
189
+ .options=${args.options}
190
+ .value=${args.value}
191
+ .allowEmptySelection=${args.allowEmptySelection}
192
+ .placeholder=${args.placeholder}
193
+ ></obc-dropdown-button>
194
+ `,
195
+ };
@@ -49,6 +49,8 @@ export type DropdownButtonOption = {
49
49
  * - `options` (Array): List of selectable options, each with a `value` (string), `label` (string), and optional `level` (number) for indentation.
50
50
  * - `value` (string): The currently selected option's value. If not set, defaults to the first option.
51
51
  * - `fullWidth` (boolean): Expands the component to fill its container when true. Default is false.
52
+ * - `allowEmptySelection` (boolean): When true, a `value` that does not match any option leaves the button with no option selected, showing `placeholder` instead of defaulting to the first option. Default is false.
53
+ * - `placeholder` (string): Text shown when nothing is selected and `allowEmptySelection` is true. Default is an empty string.
52
54
  *
53
55
  * ### Events
54
56
  * - `dropdown-change` – Fired when the user selects a different option. The event detail includes `{ value, label }` of the selected option.
@@ -102,6 +104,17 @@ export class ObcDropdownButton extends LitElement {
102
104
  */
103
105
  @property({type: Boolean}) fullWidth = false;
104
106
 
107
+ /**
108
+ * If true, a `value` that does not match any option leaves the button with no option selected,
109
+ * showing `placeholder` instead of defaulting to the first option. Default is false.
110
+ */
111
+ @property({type: Boolean}) allowEmptySelection = false;
112
+
113
+ /**
114
+ * Text shown when nothing is selected and `allowEmptySelection` is true. Default is an empty string.
115
+ */
116
+ @property({type: String}) placeholder = '';
117
+
105
118
  /**
106
119
  * Controls the button's display type.
107
120
  * - `label`: Text label only (default)
@@ -142,10 +155,21 @@ export class ObcDropdownButton extends LitElement {
142
155
  this.selectedLabel = '';
143
156
  return;
144
157
  }
145
- this.selectedValue = this.value || this.options[0].value;
146
- this.selectedLabel = this.value
147
- ? this.options.find((item) => item.value === this.value)?.label || ''
148
- : this.options[0].label;
158
+ const match = this.value
159
+ ? this.options.find((item) => item.value === this.value)
160
+ : undefined;
161
+ if (match) {
162
+ this.selectedValue = match.value;
163
+ this.selectedLabel = match.label;
164
+ return;
165
+ }
166
+ if (this.allowEmptySelection) {
167
+ this.selectedValue = '';
168
+ this.selectedLabel = this.placeholder;
169
+ return;
170
+ }
171
+ this.selectedValue = this.options[0].value;
172
+ this.selectedLabel = this.options[0].label;
149
173
  }
150
174
 
151
175
  override render() {
@@ -172,6 +196,11 @@ export class ObcDropdownButton extends LitElement {
172
196
  </div>
173
197
  </div>
174
198
  <select @change=${this.changeHandler} ?disabled=${this.disabled}>
199
+ ${this.allowEmptySelection && this.selectedValue === ''
200
+ ? html`<option value="" disabled selected hidden>
201
+ ${this.placeholder}
202
+ </option>`
203
+ : nothing}
175
204
  ${this.options.map((item) => {
176
205
  const indent = item.level ? (item.level - 1) * 2 : 0;
177
206
  const indentText = [];
@@ -79,6 +79,10 @@ const ALLOWED_CHARS = [
79
79
 
80
80
  const OPERATORS = ['+', '-', '–', '×', '÷', '*', '/'];
81
81
 
82
+ const ALLOWED_CHARS_PATTERN = `^[${ALLOWED_CHARS.map((c) =>
83
+ c.replace(/[-.*+?^${}()|[\]\\]/g, '\\$&')
84
+ ).join('')}]*$`;
85
+
82
86
  @customElement('obc-keyboard-numeric')
83
87
  export class ObcKeyboardNumeric extends LitElement {
84
88
  @property({type: String}) type: ObcKeyboardNumericType =
@@ -118,6 +122,13 @@ export class ObcKeyboardNumeric extends LitElement {
118
122
  @state() private content: ObcKeyboardNumericContent =
119
123
  ObcKeyboardNumericContent.Numbers;
120
124
 
125
+ private get effectiveValidationPattern(): string {
126
+ if (!this.validationPattern) {
127
+ return ALLOWED_CHARS_PATTERN;
128
+ }
129
+ return `^(?=${ALLOWED_CHARS_PATTERN})(?=${this.validationPattern}).*$`;
130
+ }
131
+
121
132
  /** Validates if a character can be added to the current value */
122
133
  private canAddCharacter(char: string): boolean {
123
134
  const potentialValue = this.value + char;
@@ -150,11 +161,6 @@ export class ObcKeyboardNumeric extends LitElement {
150
161
  return true;
151
162
  }
152
163
 
153
- /** Validates that all characters in a value are allowed */
154
- private isValidValue(value: string): boolean {
155
- return [...value].every((char) => ALLOWED_CHARS.includes(char));
156
- }
157
-
158
164
  private handleCloseClick() {
159
165
  this.dispatchEvent(
160
166
  new CustomEvent('close-click', {bubbles: true, composed: true})
@@ -254,19 +260,6 @@ export class ObcKeyboardNumeric extends LitElement {
254
260
  const input = e.target as ObcNumberInputField;
255
261
  const newValue = input.displayValue;
256
262
 
257
- if (this.validationPattern && newValue) {
258
- const regex = new RegExp(this.validationPattern);
259
- if (!regex.test(newValue)) {
260
- input.displayOverride = this.value;
261
- return;
262
- }
263
- }
264
-
265
- if (newValue && !this.isValidValue(newValue)) {
266
- input.displayOverride = this.value;
267
- return;
268
- }
269
-
270
263
  this.value = newValue;
271
264
  this.dispatchValueChange();
272
265
  }
@@ -359,6 +352,7 @@ export class ObcKeyboardNumeric extends LitElement {
359
352
  .textAlign=${this.inputFieldTextAlign}
360
353
  .size=${ObcNumberInputFieldSize.Large}
361
354
  .helperText=${this.helperText}
355
+ .validationPattern=${this.effectiveValidationPattern}
362
356
  ?hasLeadingIcon=${this.hasLeadingIcon}
363
357
  placeholder="00.0"
364
358
  @input=${this.handleInput}
@@ -218,4 +218,138 @@ describe('obc-number-input-field', () => {
218
218
  expect(input.value).toBe('10.');
219
219
  });
220
220
  });
221
+
222
+ describe('beforeinput filtering', () => {
223
+ function dispatchBeforeInput(
224
+ target: HTMLInputElement,
225
+ data: string | null,
226
+ inputType = 'insertText'
227
+ ): InputEvent {
228
+ const event = new InputEvent('beforeinput', {
229
+ data,
230
+ inputType,
231
+ cancelable: true,
232
+ bubbles: true,
233
+ });
234
+ target.dispatchEvent(event);
235
+ return event;
236
+ }
237
+
238
+ beforeEach(async () => {
239
+ el.decimalSeparator = '.';
240
+ el.groupSeparator = ',';
241
+ input.focus();
242
+ input.value = '';
243
+ await el.updateComplete;
244
+ });
245
+
246
+ it('blocks letters', () => {
247
+ const event = dispatchBeforeInput(input, 'a');
248
+ expect(event.defaultPrevented).toBe(true);
249
+ });
250
+
251
+ it('blocks scientific notation', () => {
252
+ input.value = '1';
253
+ input.setSelectionRange(1, 1);
254
+ const event = dispatchBeforeInput(input, 'e');
255
+ expect(event.defaultPrevented).toBe(true);
256
+ });
257
+
258
+ it('blocks symbols', () => {
259
+ const event = dispatchBeforeInput(input, '!');
260
+ expect(event.defaultPrevented).toBe(true);
261
+ });
262
+
263
+ it('allows digits', () => {
264
+ const event = dispatchBeforeInput(input, '1');
265
+ expect(event.defaultPrevented).toBe(false);
266
+ });
267
+
268
+ it('allows decimal separator', () => {
269
+ const event = dispatchBeforeInput(input, '.');
270
+ expect(event.defaultPrevented).toBe(false);
271
+ });
272
+
273
+ it('allows comma when locale uses it', () => {
274
+ const event = dispatchBeforeInput(input, ',');
275
+ expect(event.defaultPrevented).toBe(false);
276
+ });
277
+
278
+ it('allows leading sign', () => {
279
+ const event = dispatchBeforeInput(input, '-');
280
+ expect(event.defaultPrevented).toBe(false);
281
+ });
282
+
283
+ it('allows deletions (null data)', () => {
284
+ input.value = '12';
285
+ input.setSelectionRange(2, 2);
286
+ const event = dispatchBeforeInput(input, null, 'deleteContentBackward');
287
+ expect(event.defaultPrevented).toBe(false);
288
+ });
289
+
290
+ it('blocks paste containing letters', () => {
291
+ const event = new InputEvent('beforeinput', {
292
+ data: null,
293
+ inputType: 'insertFromPaste',
294
+ cancelable: true,
295
+ bubbles: true,
296
+ });
297
+ const dt = new DataTransfer();
298
+ dt.setData('text/plain', '12abc34');
299
+ Object.defineProperty(event, 'dataTransfer', {value: dt});
300
+ input.dispatchEvent(event);
301
+ expect(event.defaultPrevented).toBe(true);
302
+ });
303
+
304
+ it('allows paste of numeric string with separators', () => {
305
+ const event = new InputEvent('beforeinput', {
306
+ data: null,
307
+ inputType: 'insertFromPaste',
308
+ cancelable: true,
309
+ bubbles: true,
310
+ });
311
+ const dt = new DataTransfer();
312
+ dt.setData('text/plain', '1,234.5');
313
+ Object.defineProperty(event, 'dataTransfer', {value: dt});
314
+ input.dispatchEvent(event);
315
+ expect(event.defaultPrevented).toBe(false);
316
+ });
317
+
318
+ it('honours custom decimalSeparator', async () => {
319
+ el.decimalSeparator = ',';
320
+ el.groupSeparator = ' ';
321
+ await el.updateComplete;
322
+
323
+ const dot = dispatchBeforeInput(input, '.');
324
+ expect(dot.defaultPrevented).toBe(true);
325
+
326
+ const comma = dispatchBeforeInput(input, ',');
327
+ expect(comma.defaultPrevented).toBe(false);
328
+ });
329
+
330
+ it('honours custom validationPattern', async () => {
331
+ el.validationPattern = '^[0-9]*$';
332
+ await el.updateComplete;
333
+
334
+ const minus = dispatchBeforeInput(input, '-');
335
+ expect(minus.defaultPrevented).toBe(true);
336
+
337
+ const digit = dispatchBeforeInput(input, '1');
338
+ expect(digit.defaultPrevented).toBe(false);
339
+ });
340
+
341
+ it('does not block when disabled', async () => {
342
+ el.disabled = true;
343
+ await el.updateComplete;
344
+ const event = dispatchBeforeInput(input, 'a');
345
+ expect(event.defaultPrevented).toBe(false);
346
+ });
347
+
348
+ it('does not block when readonly', async () => {
349
+ el.readonly = true;
350
+ await el.updateComplete;
351
+ const event = dispatchBeforeInput(input, 'a');
352
+ expect(event.defaultPrevented).toBe(false);
353
+ });
354
+ });
221
355
  });
@@ -13,6 +13,7 @@ import {classMap} from 'lit/directives/class-map.js';
13
13
  import {customElement} from '../../decorator.js';
14
14
  import {
15
15
  formatNumberForDisplay,
16
+ isAllowedIntermediateInput,
16
17
  NumberInputFormatOptions,
17
18
  parseNumberInput,
18
19
  removeGroupingFromDisplay,
@@ -113,6 +114,14 @@ export class ObcNumberInputField extends LitElement {
113
114
  @property({type: Number}) minFractionDigits = 0;
114
115
  @property({type: Number}) maxFractionDigits?: number | undefined;
115
116
 
117
+ /**
118
+ * Optional regex pattern. When set, any keystroke or paste whose resulting
119
+ * value does not match this pattern is blocked at the `beforeinput` stage.
120
+ * When unset, a permissive numeric filter (digits, sign, active separators,
121
+ * whitespace) is applied instead.
122
+ */
123
+ @property({type: String}) validationPattern = '';
124
+
116
125
  @state() private hasFocus = false;
117
126
  @state() private displayText = '';
118
127
  @state() private previousValue = NaN;
@@ -150,6 +159,43 @@ export class ObcNumberInputField extends LitElement {
150
159
  this.dispatchInput();
151
160
  }
152
161
 
162
+ private isProjectedValueAllowed(projected: string): boolean {
163
+ if (this.validationPattern) {
164
+ let regex: RegExp;
165
+ try {
166
+ // Anchor the consumer pattern so it must match the whole projected
167
+ // value rather than any substring (e.g. `[0-9]` must not pass `a1`).
168
+ regex = new RegExp(`^(?:${this.validationPattern})$`);
169
+ } catch {
170
+ // Invalid pattern: fall back to the numeric filter instead of
171
+ // allowing arbitrary input.
172
+ return isAllowedIntermediateInput(projected, this.getFormatOptions());
173
+ }
174
+ return regex.test(projected);
175
+ }
176
+ return isAllowedIntermediateInput(projected, this.getFormatOptions());
177
+ }
178
+
179
+ private onBeforeInput(e: InputEvent) {
180
+ if (this.disabled || this.readonly) return;
181
+ const input = e.target as HTMLInputElement;
182
+
183
+ const incoming = e.data ?? e.dataTransfer?.getData('text/plain') ?? null;
184
+ // Deletions, history, and formatting (`insertLineBreak`, `historyUndo`,
185
+ // `deleteContentBackward`, etc.) have `data === null` and no transferable
186
+ // text — always allow them.
187
+ if (incoming === null) return;
188
+
189
+ const start = input.selectionStart ?? input.value.length;
190
+ const end = input.selectionEnd ?? input.value.length;
191
+ const projected =
192
+ input.value.slice(0, start) + incoming + input.value.slice(end);
193
+
194
+ if (!this.isProjectedValueAllowed(projected)) {
195
+ e.preventDefault();
196
+ }
197
+ }
198
+
153
199
  private onFocus() {
154
200
  this.hasFocus = true;
155
201
  const source = this.displayOverride || this.displayText;
@@ -444,6 +490,7 @@ export class ObcNumberInputField extends LitElement {
444
490
  hasHelperOrError ? 'helper-text' : undefined
445
491
  )}
446
492
  autocomplete="off"
493
+ @beforeinput=${this.onBeforeInput}
447
494
  @input=${this.onInput}
448
495
  />
449
496
  ${unitInside