react-ui-basics 0.0.190 → 0.0.191
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 +46 -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 +769 -766
- package/package.json +1 -1
- package/src/DatePicker.css +22 -18
- package/src/DatePicker.scss +19 -17
- package/src/DatePicker.tsx +77 -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,76 @@ 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
|
+
}
|
|
103
123
|
|
|
104
|
-
if (!DateTools.equals(this.state.value, nextState.value))
|
|
124
|
+
if (!DateTools.equals(this.state.value, nextState.value)) {
|
|
105
125
|
return true;
|
|
126
|
+
}
|
|
106
127
|
|
|
107
|
-
if (this.props.open !== nextProps.open)
|
|
128
|
+
if (this.props.open !== nextProps.open) {
|
|
108
129
|
return true;
|
|
130
|
+
}
|
|
109
131
|
|
|
110
|
-
if (this.state.text !== nextState.text)
|
|
132
|
+
if (this.state.text !== nextState.text) {
|
|
111
133
|
return true;
|
|
134
|
+
}
|
|
112
135
|
|
|
113
|
-
if (this.state.focused !== nextState.focused)
|
|
136
|
+
if (this.state.focused !== nextState.focused) {
|
|
114
137
|
return true;
|
|
138
|
+
}
|
|
115
139
|
|
|
116
|
-
if (this.state.selectedMonth !== nextState.selectedMonth)
|
|
140
|
+
if (this.state.selectedMonth !== nextState.selectedMonth) {
|
|
117
141
|
return true;
|
|
142
|
+
}
|
|
118
143
|
|
|
119
|
-
if (!DateTools.equals(this.props.minDate, nextProps.minDate))
|
|
144
|
+
if (!DateTools.equals(this.props.minDate, nextProps.minDate)) {
|
|
120
145
|
return true;
|
|
146
|
+
}
|
|
121
147
|
|
|
122
148
|
return false;
|
|
123
149
|
};
|
|
124
150
|
|
|
125
151
|
render() {
|
|
126
|
-
const {minDate, formatter, parser, dayOfWeekToString, monthToString, renderInput} = this.props;
|
|
127
|
-
const {value, text, focused, selectedMonth} = this.state;
|
|
128
|
-
|
|
129
|
-
|
|
152
|
+
const {minDate, formatter, parser, dayOfWeekToString, monthToString, renderInput, portal} = this.props;
|
|
153
|
+
const {value, text, focused, selectedMonth, popupStyles } = this.state;
|
|
154
|
+
|
|
155
|
+
let popup = <div className={classNames('calendar', focused && 'focused', portal && 'portal')}
|
|
156
|
+
ref={ref('popup', this)}
|
|
157
|
+
style={popupStyles}>
|
|
158
|
+
<div className={'row monthSelect'}>
|
|
159
|
+
<span>
|
|
160
|
+
{monthToString(selectedMonth.get(DateTools.TimeUnit.MONTH))}
|
|
161
|
+
{selectedMonth.get(DateTools.TimeUnit.YEAR)}
|
|
162
|
+
</span>
|
|
163
|
+
<Button flat={true} round={true} disabled={minDate && DateTools.dateOf(selectedMonth).isBefore(minDate)} onClick={() => {
|
|
164
|
+
this.setState({selectedMonth: DateTools.dateOf(selectedMonth).subtract(1, DateTools.TimeUnit.MONTH)})
|
|
165
|
+
}}>
|
|
166
|
+
<MaterialIcon icon={'chevron_left'}/>
|
|
167
|
+
</Button>
|
|
168
|
+
<Button flat={true} round={true} onClick={() => {
|
|
169
|
+
this.setState({selectedMonth: DateTools.dateOf(selectedMonth).add(1, DateTools.TimeUnit.MONTH)})
|
|
170
|
+
}}>
|
|
171
|
+
<MaterialIcon icon={'chevron_right'}/>
|
|
172
|
+
</Button>
|
|
173
|
+
</div>
|
|
174
|
+
<CalendarMonthView monthOf={selectedMonth}
|
|
175
|
+
dayOfWeekToString={dayOfWeekToString}
|
|
176
|
+
dayRenderer={(date) => <DayRenderer
|
|
177
|
+
day={DateTools.dateOf(date)}
|
|
178
|
+
selected={value}
|
|
179
|
+
minDate={minDate}
|
|
180
|
+
onClick={value => {
|
|
181
|
+
this.onChange(value)
|
|
182
|
+
this.setState({focused: false, text: formatter(value)});
|
|
183
|
+
}}
|
|
184
|
+
/>}/>
|
|
185
|
+
</div>;
|
|
186
|
+
|
|
187
|
+
popup = portal ? ReactDOM.createPortal(popup, portal) : popup
|
|
188
|
+
|
|
189
|
+
return <div className={`DatePicker`} ref={ref('el', this)}>
|
|
130
190
|
{!!renderInput && renderInput()}
|
|
131
191
|
{!renderInput && <TextField id={'value'} value={text}
|
|
132
192
|
autoComplete="off"
|
|
@@ -150,35 +210,7 @@ class DatePicker extends React.Component<DatePickerProps, DatePickerState> {
|
|
|
150
210
|
}
|
|
151
211
|
}}
|
|
152
212
|
/>}
|
|
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>
|
|
213
|
+
{popup}
|
|
182
214
|
</div>
|
|
183
215
|
};
|
|
184
216
|
|
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'}/>)}
|