Skip to content

Commit

Permalink
refactor(dropdown): align DOM with form-field DOM
Browse files Browse the repository at this point in the history
  • Loading branch information
eTallang committed Jan 9, 2025
1 parent 6cb755c commit aeb0e23
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 25 deletions.
65 changes: 42 additions & 23 deletions packages/lib/components/dropdown/dropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,43 @@ import a11yStyles from '../../global-css/a11y.css?inline';
import formFieldStyles from '../form-field/form-field.css?inline';
import type { Option } from './option';
import type { OptionValue } from './types';
import { FormControl } from './formControl';

@customElement('cx-dropdown')
export class Dropdown extends LitElement {
export class Dropdown extends FormControl(LitElement) {
static styles = [
unsafeCSS(a11yStyles),
unsafeCSS(formFieldStyles),
css`
label {
cursor: pointer;
min-width: 200px;
font: inherit;
}
.trigger {
.cx-form-field__input-container {
anchor-name: --cx-trigger;
}
.trigger {
border: none;
background-color: transparent;
padding: 0;
display: flex;
gap: var(--cx-spacing-2);
align-items: center;
justify-content: space-between;
gap: var(--cx-spacing-2);
cursor: inherit;
width: 100%;
color: var(--cx-color-text-primary)
color: var(--cx-color-text-primary);
font: inherit;
font-size: 1rem;
font-weight: 400;
&:focus {
outline: none;
}
}
cx-icon {
Expand All @@ -39,7 +55,6 @@ export class Dropdown extends LitElement {
}
}
.trigger-content {
display: flex;
align-items: center;
Expand All @@ -50,13 +65,11 @@ export class Dropdown extends LitElement {
[popover] {
--translate-curve: ease;
--translate-duration: 200ms;
position-anchor: --cx-trigger;
border: none;
position: absolute;
flex-direction: column;
opacity: 0;
translate: 0px 6px;
inset: unset;
left: anchor(left);
top: anchor(bottom);
Expand All @@ -76,7 +89,6 @@ export class Dropdown extends LitElement {
&:popover-open {
--translate-curve: var(--ease-spring-3);
--translate-duration: 500ms;
display: flex;
opacity: 1;
translate: 0px;
Expand All @@ -97,12 +109,16 @@ export class Dropdown extends LitElement {
];

/**
* @description The selected value
* @default 0
* @description The selected value. The selected value must be defined as a value for one of the options.
* @default ''
*/
@property({ type: String, reflect: true })
value: OptionValue = '';

/**
* @description The dropdown label
* @default ''
*/
@property({ type: String, reflect: true })
label = '';

Expand Down Expand Up @@ -213,18 +229,21 @@ export class Dropdown extends LitElement {
return html`
<label class=${classMap({ 'cx-form-field': true, 'cx-form-field--focused': this.isExpanded })}>
<div class="cx-form-field__label">${this.label}</div>
<button
class="cx-form-field__input-container trigger"
popovertarget="popover"
role="combobox"
aria-haspopup="listbox"
aria-expanded=${this.isExpanded}
aria-controls="popover"
@keydown=${this.onTriggerKeyDown}
>
<span class="trigger-content" .innerHTML=${selectedOption?.innerHTML ?? ''}></span>
<cx-icon name="down" class=${classMap({ rotated: this.isExpanded })}></cx-icon>
</button>
<div class="cx-form-field__input-container">
<button
class="trigger"
popovertarget="popover"
role="combobox"
aria-haspopup="listbox"
aria-expanded=${this.isExpanded}
aria-controls="popover"
type="button"
@keydown=${this.onTriggerKeyDown}
>
<span class="trigger-content" .innerHTML=${selectedOption?.innerHTML ?? ''}></span>
<cx-icon name="down" class=${classMap({ rotated: this.isExpanded })}></cx-icon>
</button>
</div>
</label>
<div
Expand Down
29 changes: 29 additions & 0 deletions packages/lib/components/dropdown/formControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { LitElement } from 'lit';

// biome-ignore lint/complexity/noBannedTypes: <explanation>
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
type Constructor<T = {}> = new (...args: any[]) => T;

export const FormControl = <T extends Constructor<LitElement>>(superClass: T) => {
class FormControlClass extends superClass {
/**
* This is a magic prop that identifies the element
* as a form-associated custom element.
**/
static formAssociated = true;

public elementInternals: ElementInternals;

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
constructor(...args: any[]) {
super(...args);
this.elementInternals = this.attachInternals();
}

formAssociatedCallback(form: HTMLFormElement) {
console.log('Connected! Form: ', form);
}
}

return FormControlClass as T;
};
2 changes: 0 additions & 2 deletions packages/lib/components/form-field/form-field.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ label.cx-form-field {
--border-color: var(--cx-color-border-primary);
}

/* The input-container can both be a child, and the element itself */
&:focus,
&:has(:focus) {
background-color: transparent;
--border-color: var(--cx-color-border-primary);
Expand Down

0 comments on commit aeb0e23

Please sign in to comment.