Skip to content

Commit

Permalink
Merge pull request #100 from github/flow
Browse files Browse the repository at this point in the history
Type check with Flow
  • Loading branch information
mislav authored Feb 22, 2019
2 parents 30f3af8 + 721c7b4 commit d756c05
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 94 deletions.
14 changes: 13 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
{
"extends": [
"plugin:github/browser",
"plugin:github/es6"
"plugin:github/es6",
"plugin:github/flow"
],
"parser": "babel-eslint",
"overrides": [
{
"files": "test/**/*.js",
"rules": {
"flowtype/require-valid-file-annotation": "off",
"github/unescaped-html-literal": "off"
}
},
{
"files": "test/**/*.js",
"excludedFiles": "test/karma.config.js",
"env": {
"mocha": true
},
"globals": {
"assert": true
}
}
]
}
9 changes: 9 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[ignore]

[include]

[libs]

[options]

[lints]
18 changes: 12 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf ./dist ./node_modules",
"lint": "eslint .",
"lint": "github-lint",
"build": "rollup -c",
"prepublishOnly": "npm run pretest",
"pretest": "npm run lint && npm run build",
Expand All @@ -15,11 +15,11 @@
"repository": "github/time-elements",
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-export-default-from": "^7.2.0",
"babel-preset-github": "^2.1.1",
"babel-preset-github": "^2.1.2",
"chai": "^4.2.0",
"eslint": "^5.12.0",
"eslint-plugin-github": "^1.9.0",
"flow-bin": "^0.93.0",
"karma": "^3.1.4",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^2.2.0",
Expand All @@ -30,5 +30,7 @@
"rollup-plugin-babel": "^4.2.0",
"webcomponents.js": "^0.7.23"
},
"eslintIgnore": ["dist/"]
"eslintIgnore": [
"dist/"
]
}
2 changes: 2 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/* @flow strict */

module.exports = require('eslint-plugin-github/prettier.config')
8 changes: 4 additions & 4 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* @flow strict */

import babel from 'rollup-plugin-babel'

const pkg = require('./package.json')
Expand All @@ -7,8 +9,7 @@ export default [
input: 'src/index.js',
plugins: [
babel({
presets: ['github'],
plugins: ['@babel/plugin-proposal-export-default-from']
presets: ['github']
})
],
output: {
Expand All @@ -21,8 +22,7 @@ export default [
input: 'src/index.js',
plugins: [
babel({
presets: ['github'],
plugins: ['@babel/plugin-proposal-export-default-from']
presets: ['github']
})
],
output: {
Expand Down
21 changes: 13 additions & 8 deletions src/extended-time-element.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
/* @flow strict */

import {makeFormatter} from './utils'

export default class ExtendedTimeElement extends HTMLElement {
_date: ?Date

static get observedAttributes() {
return ['datetime', 'day', 'format', 'hour', 'minute', 'month', 'second', 'title', 'weekday', 'year']
}

// Internal: Refresh the time element's formatted date when an attribute changes.
//
// Returns nothing.
attributeChangedCallback(attrName, oldValue, newValue) {
attributeChangedCallback(attrName: string, oldValue: string, newValue: string) {
if (attrName === 'datetime') {
const millis = Date.parse(newValue)
this._date = isNaN(millis) ? null : new Date(millis)
Expand All @@ -30,26 +34,27 @@ export default class ExtendedTimeElement extends HTMLElement {
// value takes precedence over this custom format.
//
// Returns a formatted time String.
getFormattedTitle() {
if (!this._date) {
return
}
getFormattedTitle(): ?string {
const date = this._date
if (!date) return

const formatter = titleFormatter()
if (formatter) {
return formatter.format(this._date)
return formatter.format(date)
} else {
try {
return this._date.toLocaleString()
return date.toLocaleString()
} catch (e) {
if (e instanceof RangeError) {
return this._date.toString()
return date.toString()
} else {
throw e
}
}
}
}

getFormattedDate(): ?string {}
}

const titleFormatter = makeFormatter({
Expand Down
12 changes: 8 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export LocalTimeElement from './local-time-element'
export RelativeTimeElement from './relative-time-element'
export TimeAgoElement from './time-ago-element'
export TimeUntilElement from './time-until-element'
/* @flow strict */

import LocalTimeElement from './local-time-element'
import RelativeTimeElement from './relative-time-element'
import TimeAgoElement from './time-ago-element'
import TimeUntilElement from './time-until-element'

export {LocalTimeElement, RelativeTimeElement, TimeAgoElement, TimeUntilElement}
48 changes: 23 additions & 25 deletions src/local-time-element.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* @flow strict */

import {strftime, makeFormatter, isDayFirst} from './utils'
import ExtendedTimeElement from './extended-time-element'

const formatters = new WeakMap()

export default class LocalTimeElement extends ExtendedTimeElement {
attributeChangedCallback(attrName, oldValue, newValue) {
attributeChangedCallback(attrName: string, oldValue: string, newValue: string) {
if (attrName === 'hour' || attrName === 'minute' || attrName === 'second' || attrName === 'time-zone-name') {
formatters.delete(this)
}
Expand All @@ -27,13 +29,12 @@ export default class LocalTimeElement extends ExtendedTimeElement {
// second - "numeric", "2-digit"
//
// Returns a formatted time String.
getFormattedDate() {
if (!this._date) {
return
}
getFormattedDate(): ?string {
const d = this._date
if (!d) return

const date = formatDate(this) || ''
const time = formatTime(this) || ''
const date = formatDate(this, d) || ''
const time = formatTime(this, d) || ''
return `${date} ${time}`.trim()
}
}
Expand All @@ -48,7 +49,7 @@ export default class LocalTimeElement extends ExtendedTimeElement {
// el - The local-time element to format.
//
// Returns a date String or null if no date formats are provided.
function formatDate(el) {
function formatDate(el: Element, date: Date) {
// map attribute values to strftime
const props = {
weekday: {
Expand Down Expand Up @@ -80,7 +81,7 @@ function formatDate(el) {
format = format.replace(/(\s,)|(,\s$)/, '')

// squeeze spaces from final string
return strftime(el._date, format)
return strftime(date, format)
.replace(/\s+/, ' ')
.trim()
}
Expand All @@ -91,21 +92,18 @@ function formatDate(el) {
// el - The local-time element to format.
//
// Returns a time String or null if no time formats are provided.
function formatTime(el) {
// retrieve format settings from attributes
const options = {
hour: el.getAttribute('hour'),
minute: el.getAttribute('minute'),
second: el.getAttribute('second'),
timeZoneName: el.getAttribute('time-zone-name')
}
function formatTime(el: Element, date: Date) {
const options: Intl$DateTimeFormatOptions = {}

// Remove unset format attributes.
for (const opt in options) {
if (!options[opt]) {
delete options[opt]
}
}
// retrieve format settings from attributes
const hour = el.getAttribute('hour')
if (hour === 'numeric' || hour === '2-digit') options.hour = hour
const minute = el.getAttribute('minute')
if (minute === 'numeric' || minute === '2-digit') options.minute = minute
const second = el.getAttribute('second')
if (second === 'numeric' || second === '2-digit') options.second = second
const tz = el.getAttribute('time-zone-name')
if (tz === 'short' || tz === 'long') options.timeZoneName = tz

// No time format attributes provided.
if (Object.keys(options).length === 0) {
Expand All @@ -121,11 +119,11 @@ function formatTime(el) {
const formatter = factory()
if (formatter) {
// locale-aware formatting of 24 or 12 hour times
return formatter.format(el._date)
return formatter.format(date)
} else {
// fall back to strftime for non-Intl browsers
const timef = options.second ? '%H:%M:%S' : '%H:%M'
return strftime(el._date, timef)
return strftime(date, timef)
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/relative-time-element.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* @flow strict */

import RelativeTime from './relative-time'
import ExtendedTimeElement from './extended-time-element'

Expand Down Expand Up @@ -45,7 +47,7 @@ function updateNowElements() {
let time, i, len
for (i = 0, len = nowElements.length; i < len; i++) {
time = nowElements[i]
time.textContent = time.getFormattedDate()
time.textContent = time.getFormattedDate() || ''
}
}

Expand Down
10 changes: 7 additions & 3 deletions src/relative-time.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/* @flow strict */

import {strftime, makeFormatter, isDayFirst, isThisYear, isYearSeparator} from './utils'

export default class RelativeTime {
constructor(date) {
date: Date

constructor(date: Date) {
this.date = date
}

Expand Down Expand Up @@ -50,7 +54,7 @@ export default class RelativeTime {
return this.timeAgoFromMs(ms)
}

timeAgoFromMs(ms) {
timeAgoFromMs(ms: number) {
const sec = Math.round(ms / 1000)
const min = Math.round(sec / 60)
const hr = Math.round(min / 60)
Expand Down Expand Up @@ -112,7 +116,7 @@ export default class RelativeTime {
return this.timeUntilFromMs(ms)
}

timeUntilFromMs(ms) {
timeUntilFromMs(ms: number) {
const sec = Math.round(ms / 1000)
const min = Math.round(sec / 60)
const hr = Math.round(min / 60)
Expand Down
15 changes: 8 additions & 7 deletions src/time-ago-element.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/* @flow strict */

import RelativeTime from './relative-time'
import RelativeTimeElement from './relative-time-element'

export default class TimeAgoElement extends RelativeTimeElement {
getFormattedDate() {
if (this._date) {
const format = this.getAttribute('format')
if (format === 'micro') {
return new RelativeTime(this._date).microTimeAgo()
} else {
return new RelativeTime(this._date).timeAgo()
}
const format = this.getAttribute('format')
if (!this._date) return
if (format === 'micro') {
return new RelativeTime(this._date).microTimeAgo()
} else {
return new RelativeTime(this._date).timeAgo()
}
}
}
Expand Down
15 changes: 8 additions & 7 deletions src/time-until-element.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/* @flow strict */

import RelativeTime from './relative-time'
import RelativeTimeElement from './relative-time-element'

export default class TimeUntilElement extends RelativeTimeElement {
getFormattedDate() {
if (this._date) {
const format = this.getAttribute('format')
if (format === 'micro') {
return new RelativeTime(this._date).microTimeUntil()
} else {
return new RelativeTime(this._date).timeUntil()
}
const format = this.getAttribute('format')
if (!this._date) return
if (format === 'micro') {
return new RelativeTime(this._date).microTimeUntil()
} else {
return new RelativeTime(this._date).timeUntil()
}
}
}
Expand Down
Loading

0 comments on commit d756c05

Please sign in to comment.