Skip to content
This repository has been archived by the owner on Jan 14, 2025. It is now read-only.

Commit

Permalink
feat(select): enhanced select (#823)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Goo committed Apr 30, 2019
1 parent b400013 commit 14b22bc
Show file tree
Hide file tree
Showing 27 changed files with 1,704 additions and 571 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
"testdouble": "^3.6.0",
"ts-loader": "^3.5.0",
"ts-node": "^7.0.1",
"typescript": "^3.2.2",
"typescript": "^3.3.3",
"typescript-eslint-parser": "^21.0.1",
"utility-types": "^2.1.0",
"uuid": "^3.3.2",
Expand Down
5 changes: 0 additions & 5 deletions packages/list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,6 @@ export default class List extends React.Component<ListProps, ListState> {
return [];
}

// this is a proxy for ListItem
getListElements = () => {
return this.listElements;
}

get classes() {
const {
className,
Expand Down
5 changes: 5 additions & 0 deletions packages/menu-surface/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface MenuSurfaceProps extends React.HTMLProps<HTMLDivElement> {
};
onClose?: () => void;
onOpen?: () => void;
onMount?: (isMounted: boolean) => void;
quickOpen?: boolean;
open?: boolean;
fixed?: boolean;
Expand Down Expand Up @@ -148,6 +149,9 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
if (this.props.quickOpen !== prevProps.quickOpen) {
this.foundation.setQuickOpen(this.props.quickOpen!);
}
if (this.state.mounted !== prevState.mounted) {
this.props.onMount && this.props.onMount(this.state.mounted);
}
}

componentWillUnmount() {
Expand Down Expand Up @@ -344,6 +348,7 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
onKeyDown,
styles,
quickOpen,
onMount,
/* eslint-enable */
children,
...otherProps
Expand Down
15 changes: 4 additions & 11 deletions packages/menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const {cssClasses} = MDCMenuFoundation;
export interface MenuProps extends MenuSurfaceProps {
children: React.ReactElement<MenuList>;
onSelected?: (index: number, item: Element) => void;
ref?: React.Ref<any>;
};

export interface MenuState {
Expand Down Expand Up @@ -130,12 +131,7 @@ class Menu extends React.Component<MenuProps, MenuState> {

handleOpen: MenuSurfaceProps['onOpen'] = () => {
const {onOpen} = this.props;
if (onOpen) {
onOpen();
}
if (this.listElements.length > 0) {
(this.listElements[0] as HTMLElement).focus();
}
onOpen && onOpen();
}

render() {
Expand All @@ -147,11 +143,9 @@ class Menu extends React.Component<MenuProps, MenuState> {
onOpen,
children,
onSelected,
ref,
/* eslint-enable no-unused-vars */
...otherProps
} = this.props;

return (
<MenuSurface
tabIndex={-1}
Expand All @@ -166,7 +160,6 @@ class Menu extends React.Component<MenuProps, MenuState> {
);
}


renderChild() {
const {children} = this.props;
const {foundation} = this.state;
Expand Down Expand Up @@ -194,8 +187,8 @@ export {
ListDivider as MenuListDivider,
ListGroup as MenuListGroup,
ListGroupSubheader as MenuListGroupSubheader,
ListItemGraphic as MenuListGraphic,
ListItemMeta as MenuListMeta,
ListItemGraphic as MenuListItemGraphic,
ListItemMeta as MenuListItemMeta,
ListItemText as MenuListItemText,
} from '@material/react-list';
export {MenuListProps} from './MenuList';
Expand Down
158 changes: 158 additions & 0 deletions packages/select/BaseSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// The MIT License
//
// Copyright (c) 2019 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import * as React from 'react';
import NativeSelect, {
NativeSelectProps, // eslint-disable-line no-unused-vars
} from './NativeSelect';
import EnhancedSelect, {
EnhancedSelectProps, // eslint-disable-line no-unused-vars
} from './EnhancedSelect';
import {MDCSelectFoundation} from '@material/select/foundation';

export type BaseSelectProps<T extends HTMLElement>
= (T extends HTMLSelectElement ? NativeSelectProps : EnhancedSelectProps);

export interface CommonSelectProps {
enhanced: boolean;
className?: string;
disabled?: boolean;
foundation?: MDCSelectFoundation;
value?: string;
selectClassName?: string;
}

export class BaseSelect<T extends HTMLElement = HTMLSelectElement>
extends React.Component<BaseSelectProps<T>> {
static defaultProps = {
enhanced: false,
selectClassName: '',
};

handleFocus = (evt: React.FocusEvent<T>) => {
const {foundation, onFocus} = this.props;
if (foundation) {
foundation.handleFocus();
}
onFocus && onFocus(evt);
};

handleBlur = (evt: React.FocusEvent<T>) => {
const {foundation, onBlur} = this.props;
if (foundation) {
foundation.handleBlur();
}
onBlur && onBlur(evt);
};

handleTouchStart = (evt: React.TouchEvent<T>) => {
const {foundation, onTouchStart} = this.props;
if (foundation) {
foundation.handleClick(this.getNormalizedXCoordinate(evt));
}
onTouchStart && onTouchStart(evt);
}

handleMouseDown = (evt: React.MouseEvent<T>) => {
const {foundation, onMouseDown} = this.props;
if (foundation) {
foundation.handleClick(this.getNormalizedXCoordinate(evt));
}
onMouseDown && onMouseDown(evt);
}

handleClick = (evt: React.MouseEvent<T>) => {
const {foundation, onClick} = this.props;
if (foundation) {
foundation.handleClick(this.getNormalizedXCoordinate(evt));
}
onClick && onClick(evt);
}

handleKeyDown = (evt: React.KeyboardEvent<T>) => {
const {foundation, onKeyDown} = this.props;
if (foundation) {
foundation.handleKeydown(evt.nativeEvent);
}
onKeyDown && onKeyDown(evt);
}

private isTouchEvent = (evt: MouseEvent | TouchEvent): evt is TouchEvent => {
return Boolean((evt as TouchEvent).touches);
}

private getNormalizedXCoordinate
= (evt: React.MouseEvent<T> | React.TouchEvent<T>) => {
const targetClientRect = (evt.currentTarget as Element).getBoundingClientRect();
const xCoordinate
= this.isTouchEvent(evt.nativeEvent) ? evt.nativeEvent.touches[0].clientX : evt.nativeEvent.clientX;
return xCoordinate - targetClientRect.left;
}


render() {
const {
/* eslint-disable no-unused-vars */
onFocus,
onBlur,
onClick,
onMouseDown,
onTouchStart,
ref,
/* eslint-enable no-unused-vars */
enhanced,
children,
onKeyDown,
selectClassName,
...otherProps
} = this.props;

const props = {
onFocus: this.handleFocus,
onBlur: this.handleBlur,
onMouseDown: this.handleMouseDown,
onClick: this.handleClick,
onTouchStart: this.handleTouchStart,
className: selectClassName,
...otherProps,
};

if (enhanced) {
return (
<EnhancedSelect
onKeyDown={this.handleKeyDown}
{...props}
>
{children}
</EnhancedSelect>
);
}
return (
<NativeSelect
onKeyDown={onKeyDown}
{...props}
>
{children}
</NativeSelect>
);
}
}
Loading

0 comments on commit 14b22bc

Please sign in to comment.