react-ui-basics 0.0.190 → 0.0.192
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DatePicker.css +22 -18
- package/DatePicker.d.ts +4 -0
- package/DatePicker.js +49 -22
- package/DatePicker.js.map +1 -1
- package/DateTools.js +1 -1
- package/DateTools.js.map +1 -1
- package/Table.d.ts +1 -0
- package/Table.js +3 -1
- package/Table.js.map +1 -1
- package/bundle.css +525 -522
- package/package.json +1 -1
- package/src/DatePicker.css +22 -18
- package/src/DatePicker.scss +19 -17
- package/src/DatePicker.tsx +81 -45
- package/src/DateTools.ts +1 -1
- package/src/Table.tsx +5 -1
package/package.json
CHANGED
package/src/DatePicker.css
CHANGED
|
@@ -8,21 +8,25 @@
|
|
|
8
8
|
.DatePicker > .TextField input {
|
|
9
9
|
text-align: center;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
.calendar {
|
|
12
13
|
user-select: none;
|
|
13
14
|
position: absolute;
|
|
14
15
|
top: 33px;
|
|
15
16
|
z-index: 10;
|
|
16
17
|
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
|
18
|
+
border-radius: 4px;
|
|
19
|
+
overflow: hidden;
|
|
17
20
|
transition: 0.2s all ease;
|
|
18
21
|
opacity: 0;
|
|
19
22
|
visibility: hidden;
|
|
20
23
|
}
|
|
21
|
-
.
|
|
24
|
+
.calendar.focused {
|
|
22
25
|
opacity: 1;
|
|
23
26
|
visibility: visible;
|
|
24
27
|
}
|
|
25
|
-
.
|
|
28
|
+
.calendar .monthSelect {
|
|
29
|
+
align-items: center;
|
|
26
30
|
margin-bottom: 0;
|
|
27
31
|
background: white;
|
|
28
32
|
font-size: 12px;
|
|
@@ -31,39 +35,39 @@
|
|
|
31
35
|
padding-left: 16px;
|
|
32
36
|
height: 40px;
|
|
33
37
|
}
|
|
34
|
-
.
|
|
38
|
+
.calendar .monthSelect span {
|
|
35
39
|
margin-right: auto;
|
|
36
40
|
}
|
|
37
|
-
.
|
|
41
|
+
.calendar .monthSelect .Button {
|
|
38
42
|
min-width: 25px;
|
|
39
43
|
min-height: 25px;
|
|
40
44
|
}
|
|
41
|
-
.
|
|
45
|
+
.calendar .monthSelect .Button .material-icons {
|
|
42
46
|
font-size: 16px;
|
|
43
47
|
}
|
|
44
|
-
.
|
|
48
|
+
.calendar .monthSelect .Button.disabled {
|
|
45
49
|
background: transparent;
|
|
46
50
|
}
|
|
47
|
-
.
|
|
51
|
+
.calendar .CalendarMonthView {
|
|
48
52
|
background: white;
|
|
49
53
|
}
|
|
50
|
-
.
|
|
54
|
+
.calendar .CalendarMonthView .headers, .calendar .CalendarMonthView .days {
|
|
51
55
|
margin-bottom: 0;
|
|
52
56
|
}
|
|
53
|
-
.
|
|
57
|
+
.calendar .CalendarMonthView .header {
|
|
54
58
|
font-size: 12px;
|
|
55
59
|
font-weight: bold;
|
|
56
60
|
text-align: center;
|
|
57
61
|
text-transform: uppercase;
|
|
58
62
|
margin: 5px;
|
|
59
63
|
}
|
|
60
|
-
.
|
|
64
|
+
.calendar .CalendarMonthView .day {
|
|
61
65
|
font-size: 10px;
|
|
62
66
|
}
|
|
63
|
-
.
|
|
67
|
+
.calendar .CalendarMonthView .day.notSelectedMonth {
|
|
64
68
|
color: #969696;
|
|
65
69
|
}
|
|
66
|
-
.
|
|
70
|
+
.calendar .CalendarMonthView .day span {
|
|
67
71
|
display: inline-flex;
|
|
68
72
|
justify-content: center;
|
|
69
73
|
align-items: center;
|
|
@@ -74,20 +78,20 @@
|
|
|
74
78
|
transition: 0.2s all ease;
|
|
75
79
|
cursor: pointer;
|
|
76
80
|
}
|
|
77
|
-
.
|
|
81
|
+
.calendar .CalendarMonthView .day span:hover {
|
|
78
82
|
background-color: #e6e6e6;
|
|
79
83
|
}
|
|
80
|
-
.
|
|
84
|
+
.calendar .CalendarMonthView .day span.inactive {
|
|
81
85
|
color: lightgray;
|
|
82
86
|
cursor: unset;
|
|
83
87
|
}
|
|
84
|
-
.
|
|
88
|
+
.calendar .CalendarMonthView .day span.inactive:hover {
|
|
85
89
|
background: unset;
|
|
86
90
|
}
|
|
87
|
-
.
|
|
91
|
+
.calendar .CalendarMonthView .day span.selected {
|
|
88
92
|
color: white;
|
|
89
93
|
background-color: #2196F3;
|
|
90
94
|
}
|
|
91
|
-
.
|
|
95
|
+
.calendar .CalendarMonthView .day span.selected:hover {
|
|
92
96
|
background-color: #51adf6;
|
|
93
97
|
}
|
package/src/DatePicker.scss
CHANGED
|
@@ -11,26 +11,28 @@
|
|
|
11
11
|
text-align: center;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.calendar {
|
|
17
|
+
user-select: none;
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 33px;
|
|
20
|
+
z-index: 10;
|
|
21
|
+
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
|
22
|
+
border-radius: 4px;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
|
|
25
|
+
transition: 0.2s all ease;
|
|
26
|
+
opacity: 0;
|
|
27
|
+
visibility: hidden;
|
|
28
|
+
|
|
29
|
+
&.focused {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
visibility: visible;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
34
|
.monthSelect {
|
|
35
|
+
align-items: center;
|
|
34
36
|
margin-bottom: 0;
|
|
35
37
|
background: white;
|
|
36
38
|
font-size: 12px;
|
package/src/DatePicker.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import ReactDOM from "react-dom";
|
|
2
3
|
import './DatePicker.css'
|
|
3
4
|
import TextField from "./TextField";
|
|
4
5
|
import * as DateTools from "./DateTools";
|
|
5
|
-
import {orNoop, classNames, preventDefault} from "./Tools";
|
|
6
|
+
import {orNoop, classNames, preventDefault, ref} from "./Tools";
|
|
6
7
|
import Button from './Button'
|
|
7
8
|
import CalendarMonthView from "./CalendarMonthView";
|
|
8
9
|
import MaterialIcon from "./MaterialIcon";
|
|
@@ -41,7 +42,8 @@ export interface DatePickerProps {
|
|
|
41
42
|
parser: (input: string) => Date
|
|
42
43
|
onChange: (e) => void
|
|
43
44
|
dayOfWeekToString: (day: number) => string | JSX.Element
|
|
44
|
-
monthToString: (day: number) => string | JSX.Element
|
|
45
|
+
monthToString: (day: number) => string | JSX.Element,
|
|
46
|
+
portal?: Element | DocumentFragment
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
interface DatePickerState {
|
|
@@ -49,10 +51,12 @@ interface DatePickerState {
|
|
|
49
51
|
value: Date,
|
|
50
52
|
selectedMonth: DateExtended,
|
|
51
53
|
text: string,
|
|
54
|
+
popupStyles?: any,
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
class DatePicker extends React.Component<DatePickerProps, DatePickerState> {
|
|
55
58
|
private el: HTMLDivElement;
|
|
59
|
+
private popup: HTMLDivElement;
|
|
56
60
|
private listener: (e) => void;
|
|
57
61
|
|
|
58
62
|
constructor(props: DatePickerProps) {
|
|
@@ -78,7 +82,8 @@ class DatePicker extends React.Component<DatePickerProps, DatePickerState> {
|
|
|
78
82
|
if (this.props.open !== prevProps.open) {
|
|
79
83
|
const open = !!this.props.open
|
|
80
84
|
this.setState({
|
|
81
|
-
focused: open
|
|
85
|
+
focused: open,
|
|
86
|
+
popupStyles: this.getPopupStyles()
|
|
82
87
|
})
|
|
83
88
|
}
|
|
84
89
|
if (!this.state.focused && !!prevState.focused) {
|
|
@@ -86,10 +91,24 @@ class DatePicker extends React.Component<DatePickerProps, DatePickerState> {
|
|
|
86
91
|
}
|
|
87
92
|
};
|
|
88
93
|
|
|
94
|
+
private getPopupStyles = () => {
|
|
95
|
+
const boundingClientRect = this.el.getBoundingClientRect();
|
|
96
|
+
return {
|
|
97
|
+
left: boundingClientRect.left + 'px',
|
|
98
|
+
top: boundingClientRect.bottom + 'px',
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
89
102
|
componentDidMount() {
|
|
90
103
|
document.addEventListener('mousedown', this.listener = (e) => {
|
|
91
|
-
if (this.state.focused && !this.el.contains(e.target))
|
|
92
|
-
this.
|
|
104
|
+
if (this.state.focused && !this.el.contains(e.target)) {
|
|
105
|
+
if (!this.props.portal || !this.popup.contains(e.target)) {
|
|
106
|
+
this.setState({focused: false});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
this.setState({
|
|
111
|
+
popupStyles: this.getPopupStyles()
|
|
93
112
|
})
|
|
94
113
|
};
|
|
95
114
|
|
|
@@ -98,35 +117,80 @@ class DatePicker extends React.Component<DatePickerProps, DatePickerState> {
|
|
|
98
117
|
};
|
|
99
118
|
|
|
100
119
|
shouldComponentUpdate(nextProps, nextState, nextContext) {
|
|
101
|
-
if (!DateTools.equals(this.props.value, nextProps.value))
|
|
120
|
+
if (!DateTools.equals(this.props.value, nextProps.value)) {
|
|
102
121
|
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!DateTools.equals(this.state.value, nextState.value)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
103
127
|
|
|
104
|
-
if (
|
|
128
|
+
if (this.props.open !== nextProps.open) {
|
|
105
129
|
return true;
|
|
130
|
+
}
|
|
106
131
|
|
|
107
|
-
if (this.
|
|
132
|
+
if (this.state.text !== nextState.text) {
|
|
108
133
|
return true;
|
|
134
|
+
}
|
|
109
135
|
|
|
110
|
-
if (this.state.
|
|
136
|
+
if (this.state.focused !== nextState.focused) {
|
|
111
137
|
return true;
|
|
138
|
+
}
|
|
112
139
|
|
|
113
|
-
if (this.state.
|
|
140
|
+
if (this.state.popupStyles !== nextState.popupStyles) {
|
|
114
141
|
return true;
|
|
142
|
+
}
|
|
115
143
|
|
|
116
|
-
if (this.state.selectedMonth !== nextState.selectedMonth)
|
|
144
|
+
if (this.state.selectedMonth !== nextState.selectedMonth) {
|
|
117
145
|
return true;
|
|
146
|
+
}
|
|
118
147
|
|
|
119
|
-
if (!DateTools.equals(this.props.minDate, nextProps.minDate))
|
|
148
|
+
if (!DateTools.equals(this.props.minDate, nextProps.minDate)) {
|
|
120
149
|
return true;
|
|
150
|
+
}
|
|
121
151
|
|
|
122
152
|
return false;
|
|
123
153
|
};
|
|
124
154
|
|
|
125
155
|
render() {
|
|
126
|
-
const {minDate, formatter, parser, dayOfWeekToString, monthToString, renderInput} = this.props;
|
|
127
|
-
const {value, text, focused, selectedMonth} = this.state;
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
const {minDate, formatter, parser, dayOfWeekToString, monthToString, renderInput, portal} = this.props;
|
|
157
|
+
const {value, text, focused, selectedMonth, popupStyles} = this.state;
|
|
158
|
+
|
|
159
|
+
let popup = <div className={classNames('calendar', focused && 'focused', portal && 'portal')}
|
|
160
|
+
ref={ref('popup', this)}
|
|
161
|
+
style={popupStyles}>
|
|
162
|
+
<div className={'row monthSelect'}>
|
|
163
|
+
<span>
|
|
164
|
+
{monthToString(selectedMonth.get(DateTools.TimeUnit.MONTH))}
|
|
165
|
+
{selectedMonth.get(DateTools.TimeUnit.YEAR)}
|
|
166
|
+
</span>
|
|
167
|
+
<Button flat={true} round={true} disabled={minDate && DateTools.dateOf(selectedMonth).isBefore(minDate)} onClick={() => {
|
|
168
|
+
this.setState({selectedMonth: DateTools.dateOf(selectedMonth).subtract(1, DateTools.TimeUnit.MONTH)})
|
|
169
|
+
}}>
|
|
170
|
+
<MaterialIcon icon={'chevron_left'}/>
|
|
171
|
+
</Button>
|
|
172
|
+
<Button flat={true} round={true} onClick={() => {
|
|
173
|
+
this.setState({selectedMonth: DateTools.dateOf(selectedMonth).add(1, DateTools.TimeUnit.MONTH)})
|
|
174
|
+
}}>
|
|
175
|
+
<MaterialIcon icon={'chevron_right'}/>
|
|
176
|
+
</Button>
|
|
177
|
+
</div>
|
|
178
|
+
<CalendarMonthView monthOf={selectedMonth}
|
|
179
|
+
dayOfWeekToString={dayOfWeekToString}
|
|
180
|
+
dayRenderer={(date) => <DayRenderer
|
|
181
|
+
day={DateTools.dateOf(date)}
|
|
182
|
+
selected={value}
|
|
183
|
+
minDate={minDate}
|
|
184
|
+
onClick={value => {
|
|
185
|
+
this.onChange(value)
|
|
186
|
+
this.setState({focused: false, text: formatter(value)});
|
|
187
|
+
}}
|
|
188
|
+
/>}/>
|
|
189
|
+
</div>;
|
|
190
|
+
|
|
191
|
+
popup = portal ? ReactDOM.createPortal(popup, portal) : popup
|
|
192
|
+
|
|
193
|
+
return <div className={`DatePicker`} ref={ref('el', this)}>
|
|
130
194
|
{!!renderInput && renderInput()}
|
|
131
195
|
{!renderInput && <TextField id={'value'} value={text}
|
|
132
196
|
autoComplete="off"
|
|
@@ -150,35 +214,7 @@ class DatePicker extends React.Component<DatePickerProps, DatePickerState> {
|
|
|
150
214
|
}
|
|
151
215
|
}}
|
|
152
216
|
/>}
|
|
153
|
-
|
|
154
|
-
<div className={'row monthSelect'}>
|
|
155
|
-
<span>
|
|
156
|
-
{monthToString(selectedMonth.get(DateTools.TimeUnit.MONTH))}
|
|
157
|
-
{selectedMonth.get(DateTools.TimeUnit.YEAR)}
|
|
158
|
-
</span>
|
|
159
|
-
<Button flat={true} round={true} disabled={minDate && DateTools.dateOf(selectedMonth).isBefore(minDate)} onClick={() => {
|
|
160
|
-
this.setState({selectedMonth: DateTools.dateOf(selectedMonth).subtract(1, DateTools.TimeUnit.MONTH)})
|
|
161
|
-
}}>
|
|
162
|
-
<MaterialIcon icon={'chevron_left'}/>
|
|
163
|
-
</Button>
|
|
164
|
-
<Button flat={true} round={true} onClick={() => {
|
|
165
|
-
this.setState({selectedMonth: DateTools.dateOf(selectedMonth).add(1, DateTools.TimeUnit.MONTH)})
|
|
166
|
-
}}>
|
|
167
|
-
<MaterialIcon icon={'chevron_right'}/>
|
|
168
|
-
</Button>
|
|
169
|
-
</div>
|
|
170
|
-
<CalendarMonthView monthOf={selectedMonth}
|
|
171
|
-
dayOfWeekToString={dayOfWeekToString}
|
|
172
|
-
dayRenderer={(date) => <DayRenderer
|
|
173
|
-
day={DateTools.dateOf(date)}
|
|
174
|
-
selected={value}
|
|
175
|
-
minDate={minDate}
|
|
176
|
-
onClick={value => {
|
|
177
|
-
this.onChange(value)
|
|
178
|
-
this.setState({focused: false, text: formatter(value)});
|
|
179
|
-
}}
|
|
180
|
-
/>}/>
|
|
181
|
-
</div>
|
|
217
|
+
{popup}
|
|
182
218
|
</div>
|
|
183
219
|
};
|
|
184
220
|
|
package/src/DateTools.ts
CHANGED
|
@@ -3,7 +3,7 @@ export const resetTime = (date: Date) => {
|
|
|
3
3
|
return date
|
|
4
4
|
};
|
|
5
5
|
export const timeOf = (date: Date) => date.getTime();
|
|
6
|
-
export const equals = (a: Date, b: Date) => a && b && timeOf(a) === timeOf(b);
|
|
6
|
+
export const equals = (a: Date, b: Date) => (a === b) || (a && b && timeOf(a) === timeOf(b));
|
|
7
7
|
export const isBefore = (a: Date, b: Date) => a && b && timeOf(a) < timeOf(b);
|
|
8
8
|
export const isAfter = (a: Date, b: Date) => a && b && timeOf(a) > timeOf(b);
|
|
9
9
|
export const max = (a: Date, b: Date) => {
|
package/src/Table.tsx
CHANGED
|
@@ -61,6 +61,7 @@ export interface TableColumn<T> {
|
|
|
61
61
|
onClick?: (e: SyntheticEvent, startEditing: () => void, item: T, field: keyof T, row: number, column: number) => void,
|
|
62
62
|
comparator?: Comparator,
|
|
63
63
|
header?: string | ReactElement,
|
|
64
|
+
headerColspan?: number,
|
|
64
65
|
sortable?: boolean,
|
|
65
66
|
displayEditor?: boolean,
|
|
66
67
|
formatter?: Formatter<T, T[keyof T]>,
|
|
@@ -234,8 +235,11 @@ class Table<T> extends Component<TableProps<T>, TableState<T>> {
|
|
|
234
235
|
<tr ref={headers}>
|
|
235
236
|
{columns.filter(it => !!it).map((column, i) => {
|
|
236
237
|
const sortable = column.sortable || column.sortable == null;
|
|
238
|
+
if (column.headerColspan === 0)
|
|
239
|
+
return null
|
|
240
|
+
|
|
237
241
|
return (
|
|
238
|
-
<th key={i} onClick={e => sortable && sort(e, column.field, comparatorGetter(column))}>
|
|
242
|
+
<th key={i} colSpan={column.headerColspan} onClick={e => sortable && sort(e, column.field, comparatorGetter(column))}>
|
|
239
243
|
<div className={classNames('hidden', sortable && 'sortable', sortBy === column.field && 'active')}>
|
|
240
244
|
{column.header}
|
|
241
245
|
{column.header && sortBy === column.field && sortOrder === SORT_ASC && (<MaterialIcon icon={'keyboard_arrow_up'}/>)}
|