From 356743c8c5e48e41140a790261771697e5ea75ee Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 09:42:03 -0700 Subject: [PATCH 01/81] Remove non-existent tsconfig include dir --- packages/labs/examples/tsconfig.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/labs/examples/tsconfig.json b/packages/labs/examples/tsconfig.json index 71c9c93bd9b..0b6071960d7 100644 --- a/packages/labs/examples/tsconfig.json +++ b/packages/labs/examples/tsconfig.json @@ -20,8 +20,5 @@ "@blueprintjs/labs": ["../dist/index"], "@blueprintjs/docs": ["../../docs/dist/index"] } - }, - "include": [ - "../types/*" - ] + } } From 66c6edc927df1eaeeda14f9e27f719547a667a21 Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 10:17:44 -0700 Subject: [PATCH 02/81] Install moment and moment-timezone --- packages/labs/package.json | 3 +++ packages/labs/yarn.lock | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/labs/package.json b/packages/labs/package.json index 5e8fc81c19b..8210775e480 100644 --- a/packages/labs/package.json +++ b/packages/labs/package.json @@ -9,12 +9,15 @@ "dependencies": { "@blueprintjs/core": "^1.26.0", "classnames": "^2.2", + "moment": "^2.14.1", + "moment-timezone": "^0.5.13", "popper.js": "1.11.0", "pure-render-decorator": "^1.2.1", "react-popper": "^0.7.2", "tslib": "^1.5.0" }, "devDependencies": { + "@types/moment-timezone": "^0.2.35", "bourbon": "^4.3.4", "react": "^15.6.1", "react-addons-css-transition-group": "^15.6.0", diff --git a/packages/labs/yarn.lock b/packages/labs/yarn.lock index ac19a2e8992..6bb85580183 100644 --- a/packages/labs/yarn.lock +++ b/packages/labs/yarn.lock @@ -2,6 +2,12 @@ # yarn lockfile v1 +"@types/moment-timezone@^0.2.35": + version "0.2.35" + resolved "https://registry.yarnpkg.com/@types/moment-timezone/-/moment-timezone-0.2.35.tgz#3fbbcb035e66aa5589a9198a6eec8d4dd9a701a5" + dependencies: + moment ">=2.14.0" + asap@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" @@ -87,6 +93,16 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: dependencies: js-tokens "^3.0.0" +moment-timezone@^0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.13.tgz#99ce5c7d827262eb0f1f702044177f60745d7b90" + dependencies: + moment ">= 2.9.0" + +"moment@>= 2.9.0", moment@>=2.14.0, moment@^2.14.1: + version "2.18.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" + node-fetch@^1.0.1: version "1.7.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.1.tgz#899cb3d0a3c92f952c47f1b876f4c8aeabd400d5" From 4a4aef23470522e2a23f65402e78754bebe431a3 Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 10:18:18 -0700 Subject: [PATCH 03/81] Remove unused import --- packages/labs/examples/suggestExample.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/labs/examples/suggestExample.tsx b/packages/labs/examples/suggestExample.tsx index 086b564de25..4327a26133e 100644 --- a/packages/labs/examples/suggestExample.tsx +++ b/packages/labs/examples/suggestExample.tsx @@ -8,7 +8,7 @@ import * as classNames from "classnames"; import * as React from "react"; -import { Button, Classes, MenuItem, Switch } from "@blueprintjs/core"; +import { Classes, MenuItem, Switch } from "@blueprintjs/core"; import { BaseExample } from "@blueprintjs/docs"; import { ISelectItemRendererProps, Suggest } from "../src"; import { Film, TOP_100_FILMS } from "./data"; From b6d830217917ebaabe8dc7b9d5d3122748608248 Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 10:18:06 -0700 Subject: [PATCH 04/81] Shorten imports --- packages/core/src/common/index.ts | 4 ++-- packages/table/src/interactions/reorderable.tsx | 2 +- packages/table/src/interactions/selectable.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/common/index.ts b/packages/core/src/common/index.ts index d1e7a94370e..22eec1110c0 100644 --- a/packages/core/src/common/index.ts +++ b/packages/core/src/common/index.ts @@ -12,8 +12,8 @@ export * from "./position"; export * from "./props"; export * from "./tetherUtils" -import * as classes from "../common/classes"; -import * as keys from "../common/keys"; +import * as classes from "./classes"; +import * as keys from "./keys"; import * as utils from "./utils"; export const Classes = classes; diff --git a/packages/table/src/interactions/reorderable.tsx b/packages/table/src/interactions/reorderable.tsx index e26224a422f..4065ecfa265 100644 --- a/packages/table/src/interactions/reorderable.tsx +++ b/packages/table/src/interactions/reorderable.tsx @@ -8,7 +8,7 @@ import * as PureRender from "pure-render-decorator"; import * as React from "react"; import { Utils } from "../common/utils"; -import { Draggable, ICoordinateData, IDraggableProps } from "../interactions/draggable"; +import { Draggable, ICoordinateData, IDraggableProps } from "./draggable"; import { IRegion, RegionCardinality, Regions } from "../regions"; export interface IReorderableProps { diff --git a/packages/table/src/interactions/selectable.tsx b/packages/table/src/interactions/selectable.tsx index a935bf9f8f4..479b6ff1cf9 100644 --- a/packages/table/src/interactions/selectable.tsx +++ b/packages/table/src/interactions/selectable.tsx @@ -10,8 +10,8 @@ import * as PureRender from "pure-render-decorator"; import * as React from "react"; import { IFocusedCellCoordinates } from "../common/cell"; import { Utils } from "../common/utils"; -import { DragEvents } from "../interactions/dragEvents"; -import { Draggable, ICoordinateData, IDraggableProps } from "../interactions/draggable"; +import { DragEvents } from "./dragEvents"; +import { Draggable, ICoordinateData, IDraggableProps } from "./draggable"; import { IRegion, RegionCardinality, Regions } from "../regions"; export type ISelectedRegionTransform = (region: IRegion, event: MouseEvent, coords?: ICoordinateData) => IRegion; From 456303aa2a14a7458a8fcaf8bec0486724917fac Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 11:42:09 -0700 Subject: [PATCH 05/81] Timezone Input V0 --- packages/labs/examples/index.ts | 1 + .../labs/examples/timezoneInputExample.tsx | 41 +++++ packages/labs/src/common/classes.ts | 1 + packages/labs/src/components/_index.scss | 1 + packages/labs/src/components/index.ts | 1 + .../timezone-input/_timezone-input.scss | 12 ++ .../timezone-input/timezone-input.md | 3 + .../timezone-input/timezoneInput.tsx | 147 ++++++++++++++++++ packages/labs/src/index.md | 3 + 9 files changed, 210 insertions(+) create mode 100644 packages/labs/examples/timezoneInputExample.tsx create mode 100644 packages/labs/src/components/timezone-input/_timezone-input.scss create mode 100644 packages/labs/src/components/timezone-input/timezone-input.md create mode 100644 packages/labs/src/components/timezone-input/timezoneInput.tsx diff --git a/packages/labs/examples/index.ts b/packages/labs/examples/index.ts index d2d87b890ca..849f51474fc 100644 --- a/packages/labs/examples/index.ts +++ b/packages/labs/examples/index.ts @@ -12,3 +12,4 @@ export * from "./selectExample"; export * from "./suggestExample"; export * from "./tagInputExample"; export * from "./tooltip2Example"; +export * from "./timezoneInputExample"; diff --git a/packages/labs/examples/timezoneInputExample.tsx b/packages/labs/examples/timezoneInputExample.tsx new file mode 100644 index 00000000000..8b34638a42f --- /dev/null +++ b/packages/labs/examples/timezoneInputExample.tsx @@ -0,0 +1,41 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy + * of the license at https://github.com/palantir/blueprint/blob/master/LICENSE + * and https://github.com/palantir/blueprint/blob/master/PATENTS + */ + +import * as classNames from "classnames"; +import * as React from "react"; + +import { Classes, Icon, Tag } from "@blueprintjs/core"; +import { BaseExample } from "@blueprintjs/docs"; + +import { ITimezone, TimezoneInput } from "../src"; + +export interface ITimezoneInputExampleState { + timezone?: ITimezone; +} + +export class TimezoneInputExample extends BaseExample { + public state: ITimezoneInputExampleState = {}; + + protected renderExample() { + const { timezone } = this.state; + return ( +
+ + + + {timezone ? timezone.name : "No timezone selected"} + +
+ ); + } + + private handleTimezoneSelect = (timezone: ITimezone) => { + this.setState({ timezone }); + } +} diff --git a/packages/labs/src/common/classes.ts b/packages/labs/src/common/classes.ts index 8cfe601c0bf..cb5cb606d24 100644 --- a/packages/labs/src/common/classes.ts +++ b/packages/labs/src/common/classes.ts @@ -14,3 +14,4 @@ export const SELECT = "pt-select"; export const SELECT_POPOVER = `${SELECT}-popover`; export const TAG_INPUT = "pt-tag-input"; export const TAG_INPUT_ICON = `${TAG_INPUT}-icon`; +export const TIMEZONE_INPUT = "pt-timezone-input"; diff --git a/packages/labs/src/components/_index.scss b/packages/labs/src/components/_index.scss index b913ffc5471..888204219ba 100644 --- a/packages/labs/src/components/_index.scss +++ b/packages/labs/src/components/_index.scss @@ -9,3 +9,4 @@ @import "select/multi-select"; @import "select/select"; @import "tag-input/tag-input"; +@import "timezone-input/timezone-input"; diff --git a/packages/labs/src/components/index.ts b/packages/labs/src/components/index.ts index 3956ecaff10..57f042da26c 100644 --- a/packages/labs/src/components/index.ts +++ b/packages/labs/src/components/index.ts @@ -13,3 +13,4 @@ export * from "./select/multiSelect"; export * from "./select/select"; export * from "./select/suggest"; export * from "./tag-input/tagInput"; +export * from "./timezone-input/timezoneInput"; diff --git a/packages/labs/src/components/timezone-input/_timezone-input.scss b/packages/labs/src/components/timezone-input/_timezone-input.scss new file mode 100644 index 00000000000..4ca2b9c9ba4 --- /dev/null +++ b/packages/labs/src/components/timezone-input/_timezone-input.scss @@ -0,0 +1,12 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy + * of the license at https://github.com/palantir/blueprint/blob/master/LICENSE + * and https://github.com/palantir/blueprint/blob/master/PATENTS + */ + +@import "~@blueprintjs/core/src/common/variables"; +@import "~@blueprintjs/core/src/components/forms/common"; +@import "~@blueprintjs/core/src/components/tag/common"; + +.pt-timezone-input {} diff --git a/packages/labs/src/components/timezone-input/timezone-input.md b/packages/labs/src/components/timezone-input/timezone-input.md new file mode 100644 index 00000000000..f9cb71f166b --- /dev/null +++ b/packages/labs/src/components/timezone-input/timezone-input.md @@ -0,0 +1,3 @@ +@# Timezone Input + +@reactExample TimezoneInputExample diff --git a/packages/labs/src/components/timezone-input/timezoneInput.tsx b/packages/labs/src/components/timezone-input/timezoneInput.tsx new file mode 100644 index 00000000000..1ce394a13b8 --- /dev/null +++ b/packages/labs/src/components/timezone-input/timezoneInput.tsx @@ -0,0 +1,147 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy + * of the license at https://github.com/palantir/blueprint/blob/master/LICENSE + * and https://github.com/palantir/blueprint/blob/master/PATENTS + */ + +import * as classNames from "classnames"; +import * as moment from "moment-timezone"; +import * as PureRender from "pure-render-decorator"; +import * as React from "react"; + +import { + AbstractComponent, + Classes as CoreClasses, + InputGroup, + IProps, + Menu, + MenuItem, + Utils, +} from "@blueprintjs/core"; +import { + IQueryListRendererProps, + Popover2, + QueryList, +} from ".."; +import * as Classes from "../../common/classes"; + +export interface ITimezoneInputProps extends IProps { + onTimezoneSelect: (timezone: ITimezone | undefined, event?: React.SyntheticEvent) => void; +} + +export interface ITimezone { + name: string; +} + +export interface ITimezoneInputState { + activeItem?: ITimezone; + query?: string; + isOpen?: boolean; +} + +const TypedQueryList = QueryList.ofType(); + +@PureRender +export class TimezoneInput extends AbstractComponent { + public static displayName = "Blueprint.TimezoneInput"; + + public static defaultProps: Partial = {}; + + public state: ITimezoneInputState = { + isOpen: false, + query: "", + }; + + private queryList: QueryList; + private refHandlers = { + queryList: (ref: QueryList) => this.queryList = ref, + }; + + private items: ITimezone[] = moment.tz.names().map((name) => ({ name })); + + public render() { + const { className } = this.props; + + return ( + + ); + } + + private renderQueryList = (listProps: IQueryListRendererProps) => { + const { query, activeItem, isOpen } = this.state; + + return ( + + + + + {this.renderItems(listProps)} + + + ); + } + + private renderItems(listProps: IQueryListRendererProps) { + const { activeItem } = this.state; + + return listProps.filteredItems.map((item, index) => { + const classes = classNames({ + [CoreClasses.ACTIVE]: item === activeItem, + }); + const handleClick = (event: React.SyntheticEvent) => { + listProps.handleItemSelect(item, event); + }; + + return ( + + ); + }); + } + + private handleActiveItemChange = (activeItem: ITimezone) => { + this.setState({ activeItem }); + } + + private handleItemSelect = (item: ITimezone, event?: React.SyntheticEvent) => { + this.setState({ isOpen: false }); + Utils.safeInvoke(this.props.onTimezoneSelect, item, event); + } + + private handleQueryChange = (event: React.FormEvent) => { + const query = event.currentTarget.value; + this.setState({ query }); + } + + private handlePopoverInteraction = (isOpen: boolean) => { + this.setState({ isOpen }); + } +} diff --git a/packages/labs/src/index.md b/packages/labs/src/index.md index 658d026474f..e6b9f131092 100644 --- a/packages/labs/src/index.md +++ b/packages/labs/src/index.md @@ -25,6 +25,8 @@ reference: labs - [`Tooltip2`](#labs/tooltip2) is like [`Tooltip`](#core/components/tooltip), but uses [`Popover2`](#labs/popover2). +- [`Timezone Input`](#labs/timezone-input) for inputting timezones. + @page select-component @page suggest @page multi-select @@ -33,3 +35,4 @@ reference: labs @page tag-input @page popover2 @page tooltip2 +@page timezone-input From 7de36a167225fc85e72fcb01aae07059a9474b72 Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 15:39:33 -0700 Subject: [PATCH 06/81] Use Select component --- .../timezone-input/timezoneInput.tsx | 119 ++++++------------ 1 file changed, 36 insertions(+), 83 deletions(-) diff --git a/packages/labs/src/components/timezone-input/timezoneInput.tsx b/packages/labs/src/components/timezone-input/timezoneInput.tsx index 1ce394a13b8..a6417a3a3de 100644 --- a/packages/labs/src/components/timezone-input/timezoneInput.tsx +++ b/packages/labs/src/components/timezone-input/timezoneInput.tsx @@ -12,17 +12,15 @@ import * as React from "react"; import { AbstractComponent, + Button, Classes as CoreClasses, - InputGroup, IProps, - Menu, MenuItem, Utils, } from "@blueprintjs/core"; import { - IQueryListRendererProps, - Popover2, - QueryList, + ISelectItemRendererProps, + Select, } from ".."; import * as Classes from "../../common/classes"; @@ -35,12 +33,10 @@ export interface ITimezone { } export interface ITimezoneInputState { - activeItem?: ITimezone; - query?: string; - isOpen?: boolean; + selectedItem?: ITimezone; } -const TypedQueryList = QueryList.ofType(); +const TimezoneSelect = Select.ofType(); @PureRender export class TimezoneInput extends AbstractComponent { @@ -48,100 +44,57 @@ export class TimezoneInput extends AbstractComponent = {}; - public state: ITimezoneInputState = { - isOpen: false, - query: "", - }; - - private queryList: QueryList; - private refHandlers = { - queryList: (ref: QueryList) => this.queryList = ref, - }; + public state: ITimezoneInputState = {}; private items: ITimezone[] = moment.tz.names().map((name) => ({ name })); public render() { const { className } = this.props; + const { selectedItem } = this.state; return ( - } onItemSelect={this.handleItemSelect} - query={this.state.query} - ref={this.refHandlers.queryList} - renderer={this.renderQueryList} - /> - ); - } - - private renderQueryList = (listProps: IQueryListRendererProps) => { - const { query, activeItem, isOpen } = this.state; - - return ( - - - - - {this.renderItems(listProps)} - - + ); } - private renderItems(listProps: IQueryListRendererProps) { - const { activeItem } = this.state; - - return listProps.filteredItems.map((item, index) => { - const classes = classNames({ - [CoreClasses.ACTIVE]: item === activeItem, - }); - const handleClick = (event: React.SyntheticEvent) => { - listProps.handleItemSelect(item, event); - }; - - return ( - - ); - }); + private filterTimezones(query: string, timezone: ITimezone) { + return timezone.name.toLowerCase().indexOf(query.toLowerCase()) >= 0; } - private handleActiveItemChange = (activeItem: ITimezone) => { - this.setState({ activeItem }); + private renderTimezone(itemProps: ISelectItemRendererProps) { + const classes = classNames({ + [CoreClasses.ACTIVE]: itemProps.isActive, + [CoreClasses.INTENT_PRIMARY]: itemProps.isActive, + }); + const timezone = itemProps.item.name; + const offset = moment.tz(timezone).format("Z"); + return ( + + ); } private handleItemSelect = (item: ITimezone, event?: React.SyntheticEvent) => { - this.setState({ isOpen: false }); + this.setState({ selectedItem: item }); Utils.safeInvoke(this.props.onTimezoneSelect, item, event); } - - private handleQueryChange = (event: React.FormEvent) => { - const query = event.currentTarget.value; - this.setState({ query }); - } - - private handlePopoverInteraction = (isOpen: boolean) => { - this.setState({ isOpen }); - } } From 67ca676472ea6227cf6c27d2bfff88bcd33fc43c Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 18:14:57 -0700 Subject: [PATCH 07/81] Seed list with representative timezones --- .../labs/examples/timezoneInputExample.tsx | 8 +- .../timezone-input/timezoneInput.tsx | 84 ++++++++++++++++++- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/packages/labs/examples/timezoneInputExample.tsx b/packages/labs/examples/timezoneInputExample.tsx index 8b34638a42f..cbaf157a851 100644 --- a/packages/labs/examples/timezoneInputExample.tsx +++ b/packages/labs/examples/timezoneInputExample.tsx @@ -14,17 +14,21 @@ import { BaseExample } from "@blueprintjs/docs"; import { ITimezone, TimezoneInput } from "../src"; export interface ITimezoneInputExampleState { + date?: Date; timezone?: ITimezone; } export class TimezoneInputExample extends BaseExample { - public state: ITimezoneInputExampleState = {}; + public state: ITimezoneInputExampleState = { + date: new Date(), + }; protected renderExample() { - const { timezone } = this.state; + const { date, timezone } = this.state; return (
diff --git a/packages/labs/src/components/timezone-input/timezoneInput.tsx b/packages/labs/src/components/timezone-input/timezoneInput.tsx index a6417a3a3de..c94b0506428 100644 --- a/packages/labs/src/components/timezone-input/timezoneInput.tsx +++ b/packages/labs/src/components/timezone-input/timezoneInput.tsx @@ -25,6 +25,7 @@ import { import * as Classes from "../../common/classes"; export interface ITimezoneInputProps extends IProps { + date: Date; onTimezoneSelect: (timezone: ITimezone | undefined, event?: React.SyntheticEvent) => void; } @@ -46,7 +47,15 @@ export class TimezoneInput extends AbstractComponent ({ name })); + private items: ITimezone[]; + private initialItems: ITimezone[]; + + constructor(props: ITimezoneInputProps, context?: any) { + super(props, context); + + this.items = getTimezones(); + this.initialItems = getRepresentativeTimezones(props.date); + } public render() { const { className } = this.props; @@ -56,7 +65,7 @@ export class TimezoneInput extends AbstractComponent} onItemSelect={this.handleItemSelect} @@ -71,8 +80,20 @@ export class TimezoneInput extends AbstractComponent= 0; + public componentWillReceiveProps(nextProps: ITimezoneInputProps) { + if (this.props.date.valueOf() !== nextProps.date.valueOf()) { + this.initialItems = getRepresentativeTimezones(nextProps.date); + } + } + + private filterTimezones = (query: string, items: ITimezone[]): ITimezone[] => { + if (!query) { + return this.initialItems; + } + + return items.filter((item) => { + return item.name.toLowerCase().indexOf(query.toLowerCase()) >= 0; + }); } private renderTimezone(itemProps: ISelectItemRendererProps) { @@ -98,3 +119,58 @@ export class TimezoneInput extends AbstractComponent ({ name })); +} + +function getRepresentativeTimezones(date: Date): ITimezone[] { + interface IZoneWithMeta { + name: string; + offset: number; + offsetStr: string; + population: number; + } + + const zones: IZoneWithMeta[] = moment.tz.names() + .filter((name) => ( + // https://github.com/moment/moment-timezone/issues/227 + /\//.test(name) && + !/Etc\//.test(name) + )) + .map((name) => { + const zone = moment.tz.zone(name); + const zonedDate = moment.tz(date, name); + + const offset = zonedDate.utcOffset(); + const offsetStr = zonedDate.format("Z"); + + const population = (zone as any).population || 0; + + return { name, offset, offsetStr, population }; + }); + + const zonesByOffset: { [offset: string]: IZoneWithMeta[] } = {}; + for (const zone of zones) { + zonesByOffset[zone.offsetStr] = zonesByOffset[zone.offsetStr] || []; + zonesByOffset[zone.offsetStr].push(zone); + } + + const offsetZones: IZoneWithMeta[] = []; + for (const offset of Object.keys(zonesByOffset)) { + if (zonesByOffset[offset].length > 0) { + zonesByOffset[offset].sort((tz1, tz2) => { + if (tz1.population === tz2.population) { + return tz1.name < tz2.name ? -1 : 1; + } + return tz2.population - tz1.population; + }); + + offsetZones.push(zonesByOffset[offset][0]); + } + } + + offsetZones.sort((tz1, tz2) => tz1.offset - tz2.offset); + + return offsetZones.map(({ name }) => ({ name })); +} From dad05d1aecb1cfc4b63f7185b3ed406d5ef15288 Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 18:42:33 -0700 Subject: [PATCH 08/81] Additional timezone metadata --- .../timezone-input/timezoneInput.tsx | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/labs/src/components/timezone-input/timezoneInput.tsx b/packages/labs/src/components/timezone-input/timezoneInput.tsx index c94b0506428..73479ccec0a 100644 --- a/packages/labs/src/components/timezone-input/timezoneInput.tsx +++ b/packages/labs/src/components/timezone-input/timezoneInput.tsx @@ -31,6 +31,9 @@ export interface ITimezoneInputProps extends IProps { export interface ITimezone { name: string; + abbreviation: string; + offset: number; + offsetAsString: string; } export interface ITimezoneInputState { @@ -53,7 +56,7 @@ export class TimezoneInput extends AbstractComponent ({ name })); +function getTimezones(date: Date): ITimezone[] { + return moment.tz.names().map((name) => createTimezone(date.getTime(), name)); } function getRepresentativeTimezones(date: Date): ITimezone[] { @@ -172,5 +176,30 @@ function getRepresentativeTimezones(date: Date): ITimezone[] { offsetZones.sort((tz1, tz2) => tz1.offset - tz2.offset); - return offsetZones.map(({ name }) => ({ name })); + return offsetZones.map(({ name }) => createTimezone(date.getTime(), name)); +} + +function createTimezone(timestamp: number, timezoneName: string): ITimezone { + const zonedDate = moment.tz(timestamp, timezoneName); + const offset = zonedDate.utcOffset(); + const offsetAsString = zonedDate.format("Z"); + const abbreviation = getAbbreviation(timestamp, timezoneName); + return { + name: timezoneName, + abbreviation, + offset, + offsetAsString, + }; +} + +function getAbbreviation(timestamp: number, timezoneName: string): string { + const zone = moment.tz.zone(timezoneName); + if (zone) { + const abbreviation = zone.abbr(timestamp); + if (abbreviation.length > 0 && abbreviation[0] !== "-" && abbreviation[0] !== "+") { + return abbreviation; + } + } + + return ""; } From 75dd8661596c2d37a2eb9f15761a4a433d2547d6 Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 19:46:56 -0700 Subject: [PATCH 09/81] Specify general tab size and scss-specific tab size in workspace settings --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 518f23af54d..7fca1b24188 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,10 @@ "**/coverage": true, "**/generated": true, "docs": true + }, + "editor.insertSpaces": true, + "editor.tabSize": 4, + "[scss]": { + "editor.tabSize": 2 } } From a1ee5cb2ff6890c2ab2673bf4d0cf0af271245bc Mon Sep 17 00:00:00 2001 From: Brie Bunge Date: Tue, 12 Sep 2017 19:47:15 -0700 Subject: [PATCH 10/81] Left-aligned time zone offset label --- packages/labs/src/common/classes.ts | 3 ++ .../timezone-input/_timezone-input.scss | 9 +++++- .../timezone-input/timezoneInput.tsx | 29 ++++++++++++------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/labs/src/common/classes.ts b/packages/labs/src/common/classes.ts index cb5cb606d24..dcc5cbcb6b2 100644 --- a/packages/labs/src/common/classes.ts +++ b/packages/labs/src/common/classes.ts @@ -15,3 +15,6 @@ export const SELECT_POPOVER = `${SELECT}-popover`; export const TAG_INPUT = "pt-tag-input"; export const TAG_INPUT_ICON = `${TAG_INPUT}-icon`; export const TIMEZONE_INPUT = "pt-timezone-input"; +export const TIMEZONE_MENU_ITEM = "pt-timezone-menu-item"; +export const TIMEZONE_MENU_ITEM_TEXT = "pt-timezone-menu-item-text"; +export const TIMEZONE_MENU_ITEM_LABEL = "pt-timezone-menu-item-label"; diff --git a/packages/labs/src/components/timezone-input/_timezone-input.scss b/packages/labs/src/components/timezone-input/_timezone-input.scss index 4ca2b9c9ba4..1d39d5addd4 100644 --- a/packages/labs/src/components/timezone-input/_timezone-input.scss +++ b/packages/labs/src/components/timezone-input/_timezone-input.scss @@ -9,4 +9,11 @@ @import "~@blueprintjs/core/src/components/forms/common"; @import "~@blueprintjs/core/src/components/tag/common"; -.pt-timezone-input {} +.pt-timezone-input { + .pt-timezone-menu-item-label { + float: left; + margin-right: 7px; + margin-left: 0; + min-width: 90px; + } +} diff --git a/packages/labs/src/components/timezone-input/timezoneInput.tsx b/packages/labs/src/components/timezone-input/timezoneInput.tsx index 73479ccec0a..0869777f573 100644 --- a/packages/labs/src/components/timezone-input/timezoneInput.tsx +++ b/packages/labs/src/components/timezone-input/timezoneInput.tsx @@ -73,6 +73,7 @@ export class TimezoneInput extends AbstractComponent} onItemSelect={this.handleItemSelect} resetOnSelect={true} + popoverProps={{ popoverClassName: Classes.TIMEZONE_INPUT }} >
@@ -142,6 +148,12 @@ export class TimezoneSelectExample extends BaseExample, + , ], [ this.renderFormatSelect(), @@ -149,6 +161,26 @@ export class TimezoneSelectExample extends BaseExample { + const { value, displayValue, defaultDisplayValue, placeholder, disabled } = targetProps; + const hasValue = value != null; + const targetTextClasses = classNames({ [Classes.TEXT_MUTED]: !hasValue }); + const targetIconClasses = classNames(Classes.ALIGN_RIGHT, { [Classes.TEXT_MUTED]: !hasValue }); + + return ( + + ); + } + private renderFormatSelect() { return (