Skip to content

Commit

Permalink
feat(segmented-button): Implement components (#6223)
Browse files Browse the repository at this point in the history
  • Loading branch information
gschrag authored Jul 27, 2020
1 parent 8388a9b commit ac405ea
Show file tree
Hide file tree
Showing 17 changed files with 1,055 additions and 78 deletions.
3 changes: 2 additions & 1 deletion packages/mdc-segmented-button/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"access": "public"
},
"dependencies": {
"@material/base": "^7.0.0"
"@material/base": "^7.0.0",
"@material/ripple": "^7.0.0"
}
}
34 changes: 34 additions & 0 deletions packages/mdc-segmented-button/segment/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,51 @@
*/

export interface MDCSegmentedButtonSegmentAdapter {
/**
* @return Returns true if wrapping segmented button is single select
*/
isSingleSelect(): boolean;

/**
* @param attrName Attribute of interest
* @return Returns segment's attribute value if it is set, otherwise returns
* null
*/
getAttr(attrName: string): string | null;

/**
* Sets segment's attribute value to new value
*
* @param attrName Attribute of interest
* @param value New value of attribute
*/
setAttr(attrName: string, value: string): void;

/**
* Adds css class to segment
*
* @param className Class to add
*/
addClass(className: string): void;

/**
* Removes css class from segment
*
* @param className Class to remove
*/
removeClass(className: string): void;

/**
* @param className Class of interest
* @return Returns true if segment has css class, otherwise returns false
*/
hasClass(className: string): boolean;

/**
* Emits event about segment to wrapping segmented button
*
* @param selected Represents whether segment is currently selected
* @event selected With detail - SegmentDetail
*/
notifySelectedChange(selected: boolean): void;
}
98 changes: 90 additions & 8 deletions packages/mdc-segmented-button/segment/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,124 @@
*/

import {MDCComponent} from '@material/base/component';
import {SpecificEventListener} from '@material/base/types';
import {MDCRippleCapableSurface} from '@material/ripple/types';
import {MDCSegmentedButtonSegmentAdapter} from './adapter';
import {MDCSegmentedButtonSegmentFoundation} from './foundation';
import {SegmentDetail} from '../types';
import {events} from './constants';

export type MDCSegmentedButtonSegmentFactory =
(el: Element, foundation?: MDCSegmentedButtonSegmentFoundation) =>
MDCSegmentedButtonSegment;

export class MDCSegmentedButtonSegment extends MDCComponent<MDCSegmentedButtonSegmentFoundation> {
/**
* Implementation of MDCSegmentedButtonSegmentFoundation
*/
export class MDCSegmentedButtonSegment extends MDCComponent<MDCSegmentedButtonSegmentFoundation> implements MDCRippleCapableSurface {
static attachTo(root: Element) {
return new MDCSegmentedButtonSegment(root);
}

private index!: number; // assigned in setIndex by parent
private isSingleSelect!: boolean; // assigned in setIsSingleSelect by parent
private handleClick!:
SpecificEventListener<'click'>; // assigned in initialSyncWithDOM

initialSyncWithDOM() {
return;
this.handleClick = () => this.foundation.handleClick();

this.listen(events.CLICK, this.handleClick);
}

destroy() {
this.unlisten(events.CLICK, this.handleClick);

super.destroy();
}

getDefaultFoundation(): MDCSegmentedButtonSegmentFoundation {
return new MDCSegmentedButtonSegmentFoundation();
// DO NOT INLINE this variable. For backward compatibility, foundations take a Partial<MDCFooAdapter>.
// To ensure we don't accidentally omit any methods, we need a separate, strongly typed adapter variable.
// tslint:disable:object-literal-sort-keys Methods should be in the same order as the adapter interface.
const adapter: MDCSegmentedButtonSegmentAdapter = {
isSingleSelect: () => {
return this.isSingleSelect;
},
getAttr: (attrName) => {
return this.root.getAttribute(attrName);
},
setAttr: (attrName, value) => {
this.root.setAttribute(attrName, value);
},
addClass: (className) => {
this.root.classList.add(className);
},
removeClass: (className) => {
this.root.classList.remove(className);
},
hasClass: (className) => {
return this.root.classList.contains(className);
},
notifySelectedChange: (selected) => {
this.emit<SegmentDetail>(
events.SELECTED,
{
index: this.index,
selected: selected,
segmentId: this.getSegmentId()
},
true /* shouldBubble */
);
}
};
return new MDCSegmentedButtonSegmentFoundation(adapter);
}

/**
* Sets segment's index value
*
* @param index Segment's index within wrapping segmented button
*/
setIndex(index: number) {
this.index = index;
}

/**
* Sets segment's isSingleSelect value
*
* @param isSingleSelect True if wrapping segmented button is single select
*/
setIsSingleSelect(isSingleSelect: boolean) {
this.isSingleSelect = isSingleSelect;
}

/**
* @return Returns true if segment is currently selected, otherwise returns
* false
*/
isSelected(): boolean {
return false;
return this.foundation.isSelected();
}

/**
* Sets segment to be selected
*/
setSelected() {
return;
this.foundation.setSelected();
}

/**
* Sets segment to be not selected
*/
setUnselected() {
return;
this.foundation.setUnselected();
}

getSegmentId(): string {
return '';
/**
* @return Returns segment's segmentId if it was set by client
*/
getSegmentId(): string | undefined {
return this.foundation.getSegmentId();
}
}
24 changes: 19 additions & 5 deletions packages/mdc-segmented-button/segment/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,30 @@
*/

/**
* Strings constants used by segment
* Boolean strings for segment
*/
export const strings = {
ARIA_CHECKED: 'aria-checked',
ARIA_PRESSED: 'aria-pressed',
DATA_SEGMENT_ID: 'data-segment-id',
export const booleans = {
TRUE: 'true',
FALSE: 'false'
};

/**
* Attributes referenced by segment
*/
export const attributes = {
ARIA_CHECKED: 'aria-checked',
ARIA_PRESSED: 'aria-pressed',
DATA_SEGMENT_ID: 'data-segment-id'
}

/**
* Events received or emitted by segment
*/
export const events = {
CLICK: 'click',
SELECTED: 'selected'
}

/**
* Style classes for segment
*/
Expand Down
44 changes: 37 additions & 7 deletions packages/mdc-segmented-button/segment/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import {MDCFoundation} from '@material/base/foundation';
import {MDCSegmentedButtonSegmentAdapter} from './adapter';
import {cssClasses, strings} from './constants';
import {cssClasses, booleans, attributes} from './constants';

export class MDCSegmentedButtonSegmentFoundation extends MDCFoundation<MDCSegmentedButtonSegmentAdapter> {
static get defaultAdapter(): MDCSegmentedButtonSegmentAdapter {
Expand All @@ -42,24 +42,45 @@ export class MDCSegmentedButtonSegmentFoundation extends MDCFoundation<MDCSegmen
super({...MDCSegmentedButtonSegmentFoundation.defaultAdapter, ...adapter});
}

/**
* @return Returns true if segment is currently selected, otherwise returns
* false
*/
isSelected(): boolean {
return this.adapter.hasClass(cssClasses.SELECTED);
}

/**
* Sets segment to be selected
*/
setSelected() {
this.adapter.addClass(cssClasses.SELECTED);
this.setAriaAttr(strings.TRUE);
this.setAriaAttr(booleans.TRUE);
}

/**
* Sets segment to be not selected
*/
setUnselected() {
this.adapter.removeClass(cssClasses.SELECTED);
this.setAriaAttr(strings.FALSE);
this.setAriaAttr(booleans.FALSE);
}

getSegmentId(): string | null {
return this.adapter.getAttr(strings.DATA_SEGMENT_ID);
/**
* @return Returns segment's segmentId if it was set by client
*/
getSegmentId(): string | undefined {
return this.adapter.getAttr(attributes.DATA_SEGMENT_ID) ?? undefined;
}

/**
* Called when segment is clicked. If the wrapping segmented button is single
* select, doesn't allow segment to be set to not selected. Otherwise, toggles
* segment's selected status. Finally, emits event to wrapping segmented
* button.
*
* @event selected With detail - SegmentDetail
*/
handleClick(): void {
if (this.adapter.isSingleSelect()) {
this.setSelected();
Expand All @@ -69,6 +90,9 @@ export class MDCSegmentedButtonSegmentFoundation extends MDCFoundation<MDCSegmen
this.adapter.notifySelectedChange(this.isSelected());
}

/**
* Sets segment from not selected to selected, or selected to not selected
*/
private toggleSelection() {
if (this.isSelected()) {
this.setUnselected();
Expand All @@ -77,11 +101,17 @@ export class MDCSegmentedButtonSegmentFoundation extends MDCFoundation<MDCSegmen
}
}

/**
* Sets appropriate aria attribute, based on wrapping segmented button's
* single selected value, to new value
*
* @param value Value that represents selected status
*/
private setAriaAttr(value: string) {
if (this.adapter.isSingleSelect()) {
this.adapter.setAttr(strings.ARIA_CHECKED, value);
this.adapter.setAttr(attributes.ARIA_CHECKED, value);
} else {
this.adapter.setAttr(strings.ARIA_PRESSED, value);
this.adapter.setAttr(attributes.ARIA_PRESSED, value);
}
}
}
1 change: 1 addition & 0 deletions packages/mdc-segmented-button/segment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@

export * from './adapter';
export * from './foundation';
export * from './component';
Loading

0 comments on commit ac405ea

Please sign in to comment.