From 6e54048f1eadb8234c3681009a5b8193f14f7c14 Mon Sep 17 00:00:00 2001 From: Elliott Marquez Date: Mon, 18 Sep 2023 17:07:44 -0700 Subject: [PATCH] refactor(menu)!: rename corner and focus state values lowercase with dashes BREAKING: for style guide enum consistency, we have renamed the corner values START_START etc. to be lowercase with dashes. e.g. start-start. The same has been done with MdMenu.defaultFocus' values. PiperOrigin-RevId: 566454879 --- catalog/src/components/top-app-bar.ts | 4 +- .../figures/menu/usage-submenu.html | 2 +- docs/components/menu.md | 4 +- menu/demo/demo.ts | 54 +++++++++---------- menu/demo/stories.ts | 4 +- menu/internal/menu.ts | 30 +++++++---- menu/internal/submenuitem/sub-menu-item.ts | 7 +-- menu/internal/surfacePositionController.ts | 49 ++++++++++------- menu/menu.ts | 2 +- select/internal/select.ts | 2 +- 10 files changed, 91 insertions(+), 67 deletions(-) diff --git a/catalog/src/components/top-app-bar.ts b/catalog/src/components/top-app-bar.ts index 1a3ad1e21..beedd24ce 100644 --- a/catalog/src/components/top-app-bar.ts +++ b/catalog/src/components/top-app-bar.ts @@ -60,8 +60,8 @@ import {materialDesign} from '../svg/material-design-logo.js'; > - + diff --git a/docs/components/menu.md b/docs/components/menu.md index be2ed7a76..82c177688 100644 --- a/docs/components/menu.md +++ b/docs/components/menu.md @@ -152,8 +152,8 @@ Granny Smith, and Red Delicious."](images/menu/usage-submenu.webp) + menu-corner="start-end" + anchor-corner="start-start"> diff --git a/menu/demo/demo.ts b/menu/demo/demo.ts index 2120095d7..65884aad1 100644 --- a/menu/demo/demo.ts +++ b/menu/demo/demo.ts @@ -8,7 +8,7 @@ import './index.js'; import './material-collection.js'; import {KnobTypesToKnobs, MaterialCollection, materialInitsToStoryInits, setUpDemo, title} from './material-collection.js'; -import {Corner, DefaultFocusState} from '@material/web/menu/menu.js'; +import {Corner, FocusState} from '@material/web/menu/menu.js'; import {boolInput, Knob, numberInput, selectDropdown, textInput} from './index.js'; import {stories, StoryKnobs} from './stories.js'; @@ -17,35 +17,35 @@ const collection = new MaterialCollection>('Menu', [ new Knob('menu', {ui: title()}), new Knob('anchorCorner', { - defaultValue: 'END_START' as Corner, + defaultValue: Corner.END_START as Corner, ui: selectDropdown({ options: [ - {label: 'START_START', value: 'START_START'}, - {label: 'START_END', value: 'START_END'}, - {label: 'END_START', value: 'END_START'}, - {label: 'END_END', value: 'END_END'}, + {label: Corner.START_START, value: Corner.START_START}, + {label: Corner.START_END, value: Corner.START_END}, + {label: Corner.END_START, value: Corner.END_START}, + {label: Corner.END_END, value: Corner.END_END}, ] }), }), new Knob('menuCorner', { - defaultValue: 'START_START' as Corner, + defaultValue: Corner.START_START as Corner, ui: selectDropdown({ options: [ - {label: 'START_START', value: 'START_START'}, - {label: 'START_END', value: 'START_END'}, - {label: 'END_START', value: 'END_START'}, - {label: 'END_END', value: 'END_END'}, + {label: Corner.START_START, value: Corner.START_START}, + {label: Corner.START_END, value: Corner.START_END}, + {label: Corner.END_START, value: Corner.END_START}, + {label: Corner.END_END, value: Corner.END_END}, ] }), }), new Knob('defaultFocus', { - defaultValue: 'FIRST_ITEM' as const, - ui: selectDropdown({ + defaultValue: FocusState.FIRST_ITEM as FocusState, + ui: selectDropdown({ options: [ - {label: 'FIRST_ITEM', value: 'FIRST_ITEM'}, - {label: 'LAST_ITEM', value: 'LAST_ITEM'}, - {label: 'LIST_ROOT', value: 'LIST_ROOT'}, - {label: 'NONE', value: 'NONE'}, + {label: FocusState.FIRST_ITEM, value: FocusState.FIRST_ITEM}, + {label: FocusState.LAST_ITEM, value: FocusState.LAST_ITEM}, + {label: FocusState.LIST_ROOT, value: FocusState.LIST_ROOT}, + {label: FocusState.NONE, value: FocusState.NONE}, ] }), }), @@ -125,24 +125,24 @@ const collection = // sub-menu-item knobs new Knob('sub-menu-item', {ui: title()}), new Knob('submenu.anchorCorner', { - defaultValue: 'START_END' as Corner, + defaultValue: Corner.START_END as Corner, ui: selectDropdown({ options: [ - {label: 'START_START', value: 'START_START'}, - {label: 'START_END', value: 'START_END'}, - {label: 'END_START', value: 'END_START'}, - {label: 'END_END', value: 'END_END'}, + {label: Corner.START_START, value: Corner.START_START}, + {label: Corner.START_END, value: Corner.START_END}, + {label: Corner.END_START, value: Corner.END_START}, + {label: Corner.END_END, value: Corner.END_END}, ] }), }), new Knob('submenu.menuCorner', { - defaultValue: 'START_START' as Corner, + defaultValue: Corner.START_START as Corner, ui: selectDropdown({ options: [ - {label: 'START_START', value: 'START_START'}, - {label: 'START_END', value: 'START_END'}, - {label: 'END_START', value: 'END_START'}, - {label: 'END_END', value: 'END_END'}, + {label: Corner.START_START, value: Corner.START_START}, + {label: Corner.START_END, value: Corner.START_END}, + {label: Corner.END_START, value: Corner.END_START}, + {label: Corner.END_END, value: Corner.END_END}, ] }), }), diff --git a/menu/demo/stories.ts b/menu/demo/stories.ts index 9ec801b91..cba96ff4e 100644 --- a/menu/demo/stories.ts +++ b/menu/demo/stories.ts @@ -14,7 +14,7 @@ import '@material/web/icon/icon.js'; import {MaterialStoryInit} from './material-collection.js'; import {CloseMenuEvent} from '@material/web/menu/internal/shared.js'; -import {Corner, DefaultFocusState, MdMenu, MenuItem} from '@material/web/menu/menu.js'; +import {Corner, FocusState, MdMenu, MenuItem} from '@material/web/menu/menu.js'; import {css, html} from 'lit'; /** Knob types for Menu stories. */ @@ -22,7 +22,7 @@ export interface StoryKnobs { menu: void; anchorCorner: Corner|undefined; menuCorner: Corner|undefined; - defaultFocus: DefaultFocusState|undefined; + defaultFocus: FocusState|undefined; open: boolean; fixed: boolean; quick: boolean; diff --git a/menu/internal/menu.ts b/menu/internal/menu.ts index 3e620ae8e..1dcb47636 100644 --- a/menu/internal/menu.ts +++ b/menu/internal/menu.ts @@ -32,7 +32,18 @@ export const DEFAULT_TYPEAHEAD_BUFFER_TIME = 200; /** * Element to focus on when menu is first opened. */ -export type DefaultFocusState = 'NONE'|'LIST_ROOT'|'FIRST_ITEM'|'LAST_ITEM'; +// tslint:disable-next-line:enforce-name-casing We are mimicking enum style +export const FocusState = { + NONE: 'none', + LIST_ROOT: 'list-root', + FIRST_ITEM: 'first-item', + LAST_ITEM: 'last-item' +} as const; + +/** + * Element to focus on when menu is first opened. + */ +export type FocusState = typeof FocusState[keyof typeof FocusState]; /** * Gets the currently focused element on the page. @@ -143,7 +154,8 @@ export abstract class Menu extends LitElement { * NOTE: This value may not be respected by the menu positioning algorithm * if the menu would render outisde the viewport. */ - @property({attribute: 'anchor-corner'}) anchorCorner: Corner = 'END_START'; + @property({attribute: 'anchor-corner'}) + anchorCorner: Corner = Corner.END_START; /** * The corner of the menu which to align the anchor in the standard logical * property style of _. @@ -151,7 +163,7 @@ export abstract class Menu extends LitElement { * NOTE: This value may not be respected by the menu positioning algorithm * if the menu would render outisde the viewport. */ - @property({attribute: 'menu-corner'}) menuCorner: Corner = 'START_START'; + @property({attribute: 'menu-corner'}) menuCorner: Corner = Corner.START_START; /** * Keeps the user clicks outside the menu. * @@ -181,7 +193,7 @@ export abstract class Menu extends LitElement { * `list-tabindex` to `0` when necessary. */ @property({attribute: 'default-focus'}) - defaultFocus: DefaultFocusState = 'FIRST_ITEM'; + defaultFocus: FocusState = FocusState.FIRST_ITEM; @state() private typeaheadActive = true; @@ -398,7 +410,7 @@ export abstract class Menu extends LitElement { const items = this.listElement.items; const activeItemRecord = List.getActiveItem(items); - if (activeItemRecord && this.defaultFocus !== 'NONE') { + if (activeItemRecord && this.defaultFocus !== FocusState.NONE) { activeItemRecord.item.tabIndex = -1; } @@ -414,7 +426,7 @@ export abstract class Menu extends LitElement { // the items before the animation has begun and causes the list to slide // (block-padding-of-the-menu)px at the end of the animation switch (this.defaultFocus) { - case 'FIRST_ITEM': + case FocusState.FIRST_ITEM: const first = List.getFirstActivatableItem(items); if (first) { first.tabIndex = 0; @@ -422,7 +434,7 @@ export abstract class Menu extends LitElement { await (first as LitElement & MenuItem).updateComplete; } break; - case 'LAST_ITEM': + case FocusState.LAST_ITEM: const last = List.getLastActivatableItem(items); if (last) { last.tabIndex = 0; @@ -430,11 +442,11 @@ export abstract class Menu extends LitElement { await (last as LitElement & MenuItem).updateComplete; } break; - case 'LIST_ROOT': + case FocusState.LIST_ROOT: this.listElement?.focus(); break; default: - case 'NONE': + case FocusState.NONE: // Do nothing. break; } diff --git a/menu/internal/submenuitem/sub-menu-item.ts b/menu/internal/submenuitem/sub-menu-item.ts index 9911642fd..00cccd395 100644 --- a/menu/internal/submenuitem/sub-menu-item.ts +++ b/menu/internal/submenuitem/sub-menu-item.ts @@ -28,11 +28,12 @@ export class SubMenuItem extends MenuItemEl { /** * The anchorCorner to set on the submenu. */ - @property({attribute: 'anchor-corner'}) anchorCorner: Corner = 'START_END'; + @property({attribute: 'anchor-corner'}) + anchorCorner: Corner = Corner.START_END; /** * The menuCorner to set on the submenu. */ - @property({attribute: 'menu-corner'}) menuCorner: Corner = 'START_START'; + @property({attribute: 'menu-corner'}) menuCorner: Corner = Corner.START_START; /** * The delay between mouseenter and submenu opening. */ @@ -267,7 +268,7 @@ export class SubMenuItem extends MenuItemEl { // We manually set focus with `active` on keyboard navigation. And we // want to focus the root on hover, so the user can pick up navigation with // keyboard after hover. - menu.defaultFocus = 'LIST_ROOT'; + menu.defaultFocus = 'list-root'; // This is required in the case where we have a leaf menu open and and the // user hovers a parent menu's item which is not an md-sub-menu item. // If this were set to true, then the menu would close and focus would be diff --git a/menu/internal/surfacePositionController.ts b/menu/internal/surfacePositionController.ts index 419f92aa0..3babdac00 100644 --- a/menu/internal/surfacePositionController.ts +++ b/menu/internal/surfacePositionController.ts @@ -7,10 +7,21 @@ import {ReactiveController, ReactiveControllerHost} from 'lit'; import {StyleInfo} from 'lit/directives/style-map.js'; +/** + * An enum of supported Menu corners + */ +// tslint:disable-next-line:enforce-name-casing We are mimicking enum style +export const Corner = { + END_START: 'end-start', + END_END: 'end-end', + START_START: 'start-start', + START_END: 'start-end', +} as const; + /** * A corner of a box in the standard logical property style of _ */ -export type Corner = 'END_START'|'END_END'|'START_START'|'START_END'; +export type Corner = typeof Corner[keyof typeof Corner]; /** * An interface that provides a method to customize the rect from which to @@ -147,8 +158,8 @@ export class SurfacePositionController implements ReactiveController { yOffset, repositionStrategy, } = this.getProperties(); - const anchorCorner = anchorCornerRaw.toUpperCase().trim(); - const surfaceCorner = surfaceCornerRaw.toUpperCase().trim(); + const anchorCorner = anchorCornerRaw.toLowerCase().trim(); + const surfaceCorner = surfaceCornerRaw.toLowerCase().trim(); if (!surfaceEl || !anchorEl) { return; @@ -172,9 +183,9 @@ export class SurfacePositionController implements ReactiveController { anchorEl.getSurfacePositionClientRect() : anchorEl.getBoundingClientRect(); const [surfaceBlock, surfaceInline] = - surfaceCorner.split('_') as Array<'START'|'END'>; + surfaceCorner.split('-') as Array<'start'|'end'>; const [anchorBlock, anchorInline] = - anchorCorner.split('_') as Array<'START'|'END'>; + anchorCorner.split('-') as Array<'start'|'end'>; // LTR depends on the direction of the SURFACE not the anchor. const isLTR = @@ -226,8 +237,8 @@ export class SurfacePositionController implements ReactiveController { // If the surface should be out of bounds in the block direction, flip the // surface and anchor corner block values and recalculate if (blockOutOfBoundsCorrection) { - const flippedSurfaceBlock = surfaceBlock === 'START' ? 'END' : 'START'; - const flippedAnchorBlock = anchorBlock === 'START' ? 'END' : 'START'; + const flippedSurfaceBlock = surfaceBlock === 'start' ? 'end' : 'start'; + const flippedAnchorBlock = anchorBlock === 'start' ? 'end' : 'start'; const flippedBlock = this.calculateBlock({ surfaceRect, @@ -263,8 +274,8 @@ export class SurfacePositionController implements ReactiveController { // If the surface should be out of bounds in the inline direction, flip the // surface and anchor corner inline values and recalculate if (inlineOutOfBoundsCorrection) { - const flippedSurfaceInline = surfaceInline === 'START' ? 'END' : 'START'; - const flippedAnchorInline = anchorInline === 'START' ? 'END' : 'START'; + const flippedSurfaceInline = surfaceInline === 'start' ? 'end' : 'start'; + const flippedAnchorInline = anchorInline === 'start' ? 'end' : 'start'; const flippedInline = this.calculateInline({ surfaceRect, @@ -326,8 +337,8 @@ export class SurfacePositionController implements ReactiveController { private calculateBlock(config: { surfaceRect: DOMRect, anchorRect: DOMRect, - anchorBlock: 'START'|'END', - surfaceBlock: 'START'|'END', + anchorBlock: 'start'|'end', + surfaceBlock: 'start'|'end', yOffset: number, isTopLayer: boolean, }) { @@ -342,8 +353,8 @@ export class SurfacePositionController implements ReactiveController { // We use number booleans to multiply values rather than `if` / ternary // statements because it _heavily_ cuts down on nesting and readability const isTopLayer = isTopLayerBool ? 1 : 0; - const isSurfaceBlockStart = surfaceBlock === 'START' ? 1 : 0; - const isSurfaceBlockEnd = surfaceBlock === 'END' ? 1 : 0; + const isSurfaceBlockStart = surfaceBlock === 'start' ? 1 : 0; + const isSurfaceBlockEnd = surfaceBlock === 'end' ? 1 : 0; const isOneBlockEnd = anchorBlock !== surfaceBlock ? 1 : 0; // Whether or not to apply the height of the anchor @@ -363,7 +374,7 @@ export class SurfacePositionController implements ReactiveController { const blockInset = isTopLayer * blockTopLayerOffset + blockAnchorOffset; const surfaceBlockProperty = - surfaceBlock === 'START' ? 'inset-block-start' : 'inset-block-end'; + surfaceBlock === 'start' ? 'inset-block-start' : 'inset-block-end'; return {blockInset, blockOutOfBoundsCorrection, surfaceBlockProperty}; } @@ -374,8 +385,8 @@ export class SurfacePositionController implements ReactiveController { */ private calculateInline(config: { isLTR: boolean, - surfaceInline: 'START'|'END', - anchorInline: 'START'|'END', + surfaceInline: 'start'|'end', + anchorInline: 'start'|'end', anchorRect: DOMRect, surfaceRect: DOMRect, xOffset: number, @@ -395,8 +406,8 @@ export class SurfacePositionController implements ReactiveController { const isTopLayer = isTopLayerBool ? 1 : 0; const isLTR = isLTRBool ? 1 : 0; const isRTL = isLTRBool ? 0 : 1; - const isSurfaceInlineStart = surfaceInline === 'START' ? 1 : 0; - const isSurfaceInlineEnd = surfaceInline === 'END' ? 1 : 0; + const isSurfaceInlineStart = surfaceInline === 'start' ? 1 : 0; + const isSurfaceInlineEnd = surfaceInline === 'end' ? 1 : 0; const isOneInlineEnd = anchorInline !== surfaceInline ? 1 : 0; // Whether or not to apply the width of the anchor @@ -424,7 +435,7 @@ export class SurfacePositionController implements ReactiveController { const inlineInset = isTopLayer * inlineTopLayerOffset + inlineAnchorOffset; const surfaceInlineProperty = - surfaceInline === 'START' ? 'inset-inline-start' : 'inset-inline-end'; + surfaceInline === 'start' ? 'inset-inline-start' : 'inset-inline-end'; return { inlineInset, diff --git a/menu/menu.ts b/menu/menu.ts index 1a5ffcbe7..5525370ce 100644 --- a/menu/menu.ts +++ b/menu/menu.ts @@ -11,7 +11,7 @@ import {Menu} from './internal/menu.js'; import {styles} from './internal/menu-styles.css.js'; export {ListItem} from '../list/internal/listitem/list-item.js'; -export {Corner, DefaultFocusState} from './internal/menu.js'; +export {Corner, FocusState} from './internal/menu.js'; export {CloseMenuEvent, MenuItem} from './internal/shared.js'; declare global { diff --git a/select/internal/select.ts b/select/internal/select.ts index 30b7cbd5e..85f897cd3 100644 --- a/select/internal/select.ts +++ b/select/internal/select.ts @@ -467,7 +467,7 @@ export abstract class Select extends LitElement { return html`