Skip to content

Instantly share code, notes, and snippets.

@basarat
Last active June 6, 2017 04:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save basarat/0fff387f9fd44479e6b0a3c51c473eb8 to your computer and use it in GitHub Desktop.
Save basarat/0fff387f9fd44479e6b0a3c51c473eb8 to your computer and use it in GitHub Desktop.
import { observer } from 'mobx-react';
import * as field from './field';
import * as React from 'react';
import { InputStyles } from '../base/inputs';
import { classes, cssRaw } from 'typestyle';
import { FocusClassNames } from '../utils/dom';
import { Dates } from '../utils/validation';
import { Colors } from '../base/styles';
/**
* Don't break running on node
*/
let Pikaday: any = typeof window === 'undefined' ? {} : require('pikaday');
/**
* From https://github.com/dbushell/Pikaday/blob/160ec7de33a9620550e923dc6d955c392dbd5481/css/pikaday.css
*/
cssRaw(`
/*!
* Pikaday
* Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/
*/
.pika-single {
z-index: 9999;
display: block;
position: relative;
color: #333;
background: #fff;
border: 1px solid #ccc;
border-bottom-color: #bbb;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
/*
clear child float (pika-lendar), using the famous micro clearfix hack
http://nicolasgallagher.com/micro-clearfix-hack/
*/
.pika-single:before,
.pika-single:after {
content: " ";
display: table;
}
.pika-single:after { clear: both }
.pika-single { *zoom: 1 }
.pika-single.is-hidden {
display: none;
}
.pika-single.is-bound {
position: absolute;
box-shadow: 0 5px 15px -5px rgba(0,0,0,.5);
}
.pika-lendar {
float: left;
width: 240px;
margin: 8px;
}
.pika-title {
position: relative;
text-align: center;
}
.pika-label {
display: inline-block;
*display: inline;
position: relative;
z-index: 9999;
overflow: hidden;
margin: 0;
padding: 5px 3px;
font-size: 14px;
line-height: 20px;
font-weight: bold;
background-color: #fff;
}
.pika-title select {
cursor: pointer;
position: absolute;
z-index: 9998;
margin: 0;
left: 0;
top: 5px;
filter: alpha(opacity=0);
opacity: 0;
}
.pika-prev,
.pika-next {
display: block;
cursor: pointer;
position: relative;
outline: none;
border: 0;
padding: 0;
width: 20px;
height: 30px;
/* hide text using text-indent trick, using width value (it's enough) */
text-indent: 20px;
white-space: nowrap;
overflow: hidden;
background-color: transparent;
background-position: center center;
background-repeat: no-repeat;
background-size: 75% 75%;
opacity: .5;
*position: absolute;
*top: 0;
}
.pika-prev:hover,
.pika-next:hover {
opacity: 1;
}
.pika-prev,
.is-rtl .pika-next {
float: left;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');
*left: 0;
}
.pika-next,
.is-rtl .pika-prev {
float: right;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');
*right: 0;
}
.pika-prev.is-disabled,
.pika-next.is-disabled {
cursor: default;
opacity: .2;
}
.pika-select {
display: inline-block;
*display: inline;
}
.pika-table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
border: 0;
}
.pika-table th,
.pika-table td {
width: 14.285714285714286%;
padding: 0;
}
.pika-table th {
color: #999;
font-size: 12px;
line-height: 25px;
font-weight: bold;
text-align: center;
}
.pika-button {
cursor: pointer;
display: block;
box-sizing: border-box;
-moz-box-sizing: border-box;
outline: none;
border: 0;
margin: 0;
width: 100%;
padding: 5px;
color: #666;
font-size: 12px;
line-height: 15px;
text-align: right;
background: #f5f5f5;
}
.pika-week {
font-size: 11px;
color: #999;
}
.is-today .pika-button {
color: #33aaff;
font-weight: bold;
}
.is-selected .pika-button,
.has-event .pika-button {
color: #fff;
font-weight: bold;
background: #33aaff;
box-shadow: inset 0 1px 3px #178fe5;
border-radius: 3px;
}
.has-event .pika-button {
background: #005da9;
box-shadow: inset 0 1px 3px #0076c9;
}
.is-disabled .pika-button,
.is-inrange .pika-button {
background: #D5E9F7;
}
.is-startrange .pika-button {
color: #fff;
background: #6CB31D;
box-shadow: none;
border-radius: 3px;
}
.is-endrange .pika-button {
color: #fff;
background: #33aaff;
box-shadow: none;
border-radius: 3px;
}
.is-disabled .pika-button,
.is-outside-current-month .pika-button {
pointer-events: none;
cursor: default;
color: #999;
opacity: .3;
}
.pika-button:hover,
.pika-row.pick-whole-week:hover .pika-button {
color: #fff;
background: #ff8000;
box-shadow: none;
border-radius: 3px;
}
/* styling for abbr */
.pika-table abbr {
border-bottom: none;
cursor: help;
}
`);
/**
* Override
*/
cssRaw(`
/** Hover */
.pika-button:hover,
.pika-row.pick-whole-week:hover .pika-button {
background: #333;
}
/** Selected */
.is-selected .pika-button,
.has-event .pika-button {
color: #fff;
background: ${Colors.datePickerSelectionColor};
box-shadow: none;
}
`);
@observer
export class DateField extends React.Component<
field.FieldPropsWithoutInput<string>,
{ focused: boolean }
> {
state = {
focused: false
}
random = (Math.random() * 1000).toString();
ctrls: {
input?: HTMLInputElement;
} = {}
picker: any;
componentDidMount() {
this.picker = new Pikaday({
field: this.ctrls.input,
format: 'DD/MM/YYYY',
position: 'top right',
yearRange: 100,
onSelect: () => {
this.props.fieldState.onChange(this.picker.toString());
}
});
}
componentWillUnmount() {
this.picker.destroy();
}
render() {
// Add the types
const InputField = field.Field as { new(): field.Field<string> };
// Add the input
let input = (fieldProps: field.InputProps<string>) => {
const style = this.props.fieldState.hasError
? {
color: InputStyles.errorColor,
borderColor: InputStyles.errorColor
}
: {};
return <input
ref={ref => this.ctrls.input = ref}
id={this.props.id}
disabled={this.props.disabled}
name={this.props.name}
className={classes(
InputStyles.rootClassName,
this.props.className,
this.props.fieldState.hasError && FocusClassNames.errorInput
)}
style={style}
type="text"
value={fieldProps.value}
onChange={this.handleChange}
/>;
}
const finalProps = { input, ...this.props };
return <InputField {...finalProps} />;
}
handleChange = (evt: React.FormEvent<HTMLInputElement>) => {
let value = (evt.target as HTMLInputElement).value;
this.props.fieldState.onChange(value);
const date = Dates.toDate(value);
if (date.valid && this.picker) {
this.picker.setDate(date.date);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment