Skip to content

Commit

Permalink
feat(select): match menu width to select and introduce clamp-menu-width
Browse files Browse the repository at this point in the history
fixes #5112
fixes #5133

upon opening the select, the menu with calculate the width of the select and set the min-width of the corresponding menu to match. If the user has an item with a really long headline, then clap-menu-width will clamp the max-width of the menu to that of the select.

PiperOrigin-RevId: 587862332
  • Loading branch information
Elliott Marquez authored and copybara-github committed Dec 4, 2023
1 parent 52e568d commit a5a40b6
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 1 deletion.
1 change: 1 addition & 0 deletions select/demo/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const collection = new MaterialCollection<KnobTypesToKnobs<StoryKnobs>>(
],
}),
}),
new Knob('clampMenuWidth', {ui: boolInput(), defaultValue: false}),
new Knob('error', {ui: boolInput(), defaultValue: false}),

new Knob('md-select Slots', {ui: title()}),
Expand Down
3 changes: 3 additions & 0 deletions select/demo/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface StoryKnobs {
errorText: string;
supportingText: string;
error: boolean;
clampMenuWidth: boolean;
menuPositioning: 'absolute' | 'fixed' | 'popover' | undefined;

'md-select Slots': void;
Expand All @@ -42,6 +43,7 @@ const selects: MaterialStoryInit<StoryKnobs> = {
.disabled=${knobs.disabled}
.errorText=${knobs.errorText}
.supportingText=${knobs.supportingText}
.clampMenuWidth=${knobs.clampMenuWidth}
.menuPositioning=${knobs.menuPositioning!}
.typeaheadDelay=${knobs.typeaheadDelay}
.error=${knobs.error}>
Expand All @@ -57,6 +59,7 @@ const selects: MaterialStoryInit<StoryKnobs> = {
.disabled=${knobs.disabled}
.errorText=${knobs.errorText}
.supportingText=${knobs.supportingText}
.clampMenuWidth=${knobs.clampMenuWidth}
.menuPositioning=${knobs.menuPositioning!}
.typeaheadDelay=${knobs.typeaheadDelay}
.error=${knobs.error}>
Expand Down
5 changes: 5 additions & 0 deletions select/internal/_shared.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@
display: flex;
}

md-menu {
min-width: var(--__menu-min-width, inherit);
max-width: var(--__menu-max-width, inherit);
}

md-menu ::slotted(:not[disabled]) {
cursor: pointer;
}
Expand Down
28 changes: 28 additions & 0 deletions select/internal/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import '../../menu/menu.js';
import {html, isServer, LitElement, nothing, PropertyValues} from 'lit';
import {property, query, queryAssignedElements, state} from 'lit/decorators.js';
import {ClassInfo, classMap} from 'lit/directives/class-map.js';
import {styleMap} from 'lit/directives/style-map.js';
import {html as staticHtml, StaticValue} from 'lit/static-html.js';

import {Field} from '../../field/internal/field.js';
Expand Down Expand Up @@ -128,6 +129,12 @@ export abstract class Select extends selectBaseClass {
@property({attribute: 'menu-positioning'})
menuPositioning: 'absolute' | 'fixed' | 'popover' = 'popover';

/**
* Clamps the menu-width to the width of the select.
*/
@property({type: Boolean, attribute: 'clamp-menu-width'})
clampMenuWidth = false;

/**
* The max time between the keystrokes of the typeahead select / menu behavior
* before it clears the typeahead buffer.
Expand Down Expand Up @@ -240,6 +247,10 @@ export abstract class Select extends selectBaseClass {
@query('#label') private readonly labelEl!: HTMLElement;
@queryAssignedElements({slot: 'leading-icon', flatten: true})
private readonly leadingIcons!: Element[];
// Have to keep track of previous open because it's state and private and thus
// cannot be tracked in PropertyValues<this> map.
private prevOpen = this.open;
private selectWidth = 0;

constructor() {
super();
Expand Down Expand Up @@ -316,6 +327,17 @@ export abstract class Select extends selectBaseClass {
this.initUserSelection();
}

// We have just opened the menu.
// We are only able to check for the select's rect in `update()` instead of
// having to wait for `updated()` because the menu can never be open on
// first render since it is not settable and Lit SSR does not support click
// events which would open the menu.
if (this.prevOpen !== this.open && this.open) {
const selectRect = this.getBoundingClientRect();
this.selectWidth = selectRect.width;
}

this.prevOpen = this.open;
super.update(changed);
}

Expand Down Expand Up @@ -445,6 +467,12 @@ export abstract class Select extends selectBaseClass {
part="menu"
exportparts="focus-ring: menu-focus-ring"
anchor="field"
style=${styleMap({
'--__menu-min-width': `${this.selectWidth}px`,
'--__menu-max-width': this.clampMenuWidth
? `${this.selectWidth}px`
: undefined,
})}
.open=${this.open}
.quick=${this.quick}
.positioning=${this.menuPositioning}
Expand Down
5 changes: 4 additions & 1 deletion testing/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ function getUsedTokensFromRule(
// Check them explicitly as well for properties like border-radius.
for (const property of [...rule.style, ...CSS_SHORTHAND_PROPERTIES]) {
const value = rule.style.getPropertyValue(property);
for (const match of value.matchAll(/--_[\w-]+/g)) {
// match css custom properties of --_ but not --__ as --_ are tokens and
// --__ are private custom properties used for our convenience not to be
// exposed to users.
for (const match of value.matchAll(/--_(?!_)[\w-]+/g)) {
used.add(match[0]);
}
}
Expand Down

0 comments on commit a5a40b6

Please sign in to comment.