diff --git a/.changeset/giant-buses-hear.md b/.changeset/giant-buses-hear.md new file mode 100644 index 000000000..8c4e535db --- /dev/null +++ b/.changeset/giant-buses-hear.md @@ -0,0 +1,5 @@ +--- +'@baloise/ds-core': patch +--- + +**segment**: resolve custom elemen creation in angular applications diff --git a/packages/angular/src/components/bal-segment.ts b/packages/angular/src/components/bal-segment.ts index cc5b6a109..8d1f59cb8 100644 --- a/packages/angular/src/components/bal-segment.ts +++ b/packages/angular/src/components/bal-segment.ts @@ -12,7 +12,8 @@ import { import { NG_VALUE_ACCESSOR } from '@angular/forms' import type { Components } from '@baloise/ds-core' -import { defineCustomElement } from '@baloise/ds-core/components/bal-segment' +import { defineCustomElement as defineSegment } from '@baloise/ds-core/components/bal-segment' +import { defineCustomElement as defineSegmentItem } from '@baloise/ds-core/components/bal-segment-item' import { ProxyCmp, proxyOutputs } from '../generated/angular-component-lib/utils' import { ValueAccessor } from '../generated/value-accessor' @@ -25,7 +26,10 @@ const accessorProvider = { } @ProxyCmp({ - defineCustomElementFn: defineCustomElement, + defineCustomElementFn: () => { + defineSegment() + defineSegmentItem() + }, inputs: BalSegmentInputs, methods: BalSegmentMethods, }) diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index 258b208f7..11ab6a1c5 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -2741,6 +2741,7 @@ export namespace Components { * If `true`, and is vertical then the list height is limited and scrollable. */ "scrollable": boolean; + "setAriaForm": (ariaForm: BalAriaForm) => Promise; /** * the value of the segment. */ @@ -2765,6 +2766,7 @@ export namespace Components { * Label of the segment control */ "label": string; + "setAriaForm": (ariaForm: BalAriaForm) => Promise; "setFocus": () => Promise; /** * The value of the segment button. @@ -3700,10 +3702,6 @@ export interface BalSegmentCustomEvent extends CustomEvent { detail: T; target: HTMLBalSegmentElement; } -export interface BalSegmentItemCustomEvent extends CustomEvent { - detail: T; - target: HTMLBalSegmentItemElement; -} export interface BalSelectCustomEvent extends CustomEvent { detail: T; target: HTMLBalSelectElement; @@ -4725,6 +4723,7 @@ declare global { new (): HTMLBalRadioGroupElement; }; interface HTMLBalSegmentElementEventMap { + "balFocus": BalEvents.BalSegmentFocusDetail; "balBlur": BalEvents.BalSegmentBlurDetail; "balChange": BalEvents.BalSegmentChangeDetail; "balSelect": BalEvents.BalSegmentChangeDetail; @@ -4744,18 +4743,7 @@ declare global { prototype: HTMLBalSegmentElement; new (): HTMLBalSegmentElement; }; - interface HTMLBalSegmentItemElementEventMap { - "balBlur": BalEvents.BalSegmentBlurDetail; - } interface HTMLBalSegmentItemElement extends Components.BalSegmentItem, HTMLStencilElement { - addEventListener(type: K, listener: (this: HTMLBalSegmentItemElement, ev: BalSegmentItemCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; - removeEventListener(type: K, listener: (this: HTMLBalSegmentItemElement, ev: BalSegmentItemCustomEvent) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; } var HTMLBalSegmentItemElement: { prototype: HTMLBalSegmentItemElement; @@ -7915,6 +7903,10 @@ declare namespace LocalJSX { * Emitted when the value property has changed and any dragging pointer has been released from `bal-segment`. This event will not emit when programmatically setting the `value` property. */ "onBalChange"?: (event: BalSegmentCustomEvent) => void; + /** + * Emitted when the toggle has focus. + */ + "onBalFocus"?: (event: BalSegmentCustomEvent) => void; /** * Emitted when the value of the segment changes from user committed actions or from externally assigning a value. */ @@ -7951,10 +7943,6 @@ declare namespace LocalJSX { * Label of the segment control */ "label"?: string; - /** - * Emitted when the component was touched - */ - "onBalBlur"?: (event: BalSegmentItemCustomEvent) => void; /** * The value of the segment button. */ diff --git a/packages/core/src/components/bal-field/bal-field.tsx b/packages/core/src/components/bal-field/bal-field.tsx index 7af5e7efc..0385658ee 100644 --- a/packages/core/src/components/bal-field/bal-field.tsx +++ b/packages/core/src/components/bal-field/bal-field.tsx @@ -28,6 +28,7 @@ export class Field implements ComponentInterface, BalMutationObserver { 'bal-input-slider', 'bal-file-upload', 'bal-dropdown', + 'bal-segment', ] private formElements = [...this.formControlElement, 'bal-field-label', 'bal-field-message'] @@ -172,6 +173,8 @@ export class Field implements ComponentInterface, BalMutationObserver { 'bal-field-control bal-dropdown', 'bal-field-control bal-checkbox', 'bal-field-control bal-radio', + 'bal-field-control bal-segment-item', + 'bal-field-control bal-segment', 'bal-field-control bal-checkbox-group', 'bal-field-control bal-radio-group', 'bal-field-control bal-number-input', diff --git a/packages/core/src/components/bal-segment/bal-segment-item/bal-segment-item.tsx b/packages/core/src/components/bal-segment/bal-segment-item/bal-segment-item.tsx index 30c5cce25..a1d03719d 100644 --- a/packages/core/src/components/bal-segment/bal-segment-item/bal-segment-item.tsx +++ b/packages/core/src/components/bal-segment/bal-segment-item/bal-segment-item.tsx @@ -1,22 +1,11 @@ -import { - Component, - h, - ComponentInterface, - Host, - Element, - Prop, - State, - Watch, - Method, - EventEmitter, - Event, -} from '@stencil/core' +import { Component, h, ComponentInterface, Host, Element, Prop, State, Watch, Method } from '@stencil/core' import { BEM } from '../../../utils/bem' import { SegmentValue } from '../bal-segment.types' import { Attributes, inheritAttributes } from '../../../utils/attributes' import { addEventListener, raf, removeEventListener } from '../../../utils/helpers' +import { BalAriaForm, defaultBalAriaForm } from '../../../utils/form' -let ids = 0 +let SegmentItemIds = 0 @Component({ tag: 'bal-segment-item', @@ -26,13 +15,18 @@ export class SegmentItem implements ComponentInterface { private segmentEl: HTMLBalSegmentElement | null = null private nativeEl: HTMLButtonElement | undefined private inheritedAttributes: Attributes = {} - private id = ids++ + private internalId = SegmentItemIds++ + private inputId = `bal-si-${this.internalId}` @Element() el!: HTMLElement @State() hasSlotContent = false @State() isFocusable = false @State() isVertical = false + @State() isLast = false + @State() isFirst = false + @State() hasEmptyValue = true + @State() ariaForm: BalAriaForm = defaultBalAriaForm /** * If `true`, the user cannot interact with the segment button. @@ -64,24 +58,31 @@ export class SegmentItem implements ComponentInterface { /** * The value of the segment button. */ - @Prop() value: SegmentValue = 'bal-si-' + this.id + @Prop({ mutable: true }) value: SegmentValue = 'bal-si-' + this.internalId @Watch('value') - valueChanged() { - this.updateState() + valueChanged(newValue: SegmentValue, oldValue: SegmentValue) { + if (newValue !== oldValue) { + this.updateState() + } } - /** - * Emitted when the component was touched - */ - @Event() balBlur!: EventEmitter + componentWillLoad() { + this.inheritedAttributes = { + ...inheritAttributes(this.el, ['aria-label']), + } + } - connectedCallback() { + componentDidLoad() { const segmentEl = (this.segmentEl = this.el.closest('bal-segment')) if (segmentEl) { - this.updateState() addEventListener(segmentEl, 'balSelect', this.updateState) addEventListener(segmentEl, 'balVertical', this.updateVertical) } + + raf(() => { + this.checkSlotContent() + this.updateState() + }) } disconnectedCallback() { @@ -93,16 +94,15 @@ export class SegmentItem implements ComponentInterface { } } - componentWillLoad() { - this.inheritedAttributes = { - ...inheritAttributes(this.el, ['aria-label']), + private calculateEmptyValue() { + if (this.segmentEl) { + const segments = Array.from(this.segmentEl.querySelectorAll('bal-segment-item')) + this.hasEmptyValue = !segments.some(item => item.value === this.segmentEl.value) + } else { + this.hasEmptyValue = false } } - componentDidLoad() { - raf(() => this.checkSlotContent()) - } - /** * @internal * Focuses the native