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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-ui-basics",
3
- "version": "0.0.190",
3
+ "version": "0.0.191",
4
4
  "description": "",
5
5
  "module": "index.js",
6
6
  "_main": "index.cjs.js",
@@ -8,21 +8,25 @@
8
8
  .DatePicker > .TextField input {
9
9
  text-align: center;
10
10
  }
11
- .DatePicker .calendar {
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
- .DatePicker .calendar.focused {
24
+ .calendar.focused {
22
25
  opacity: 1;
23
26
  visibility: visible;
24
27
  }
25
- .DatePicker .monthSelect {
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
- .DatePicker .monthSelect span {
38
+ .calendar .monthSelect span {
35
39
  margin-right: auto;
36
40
  }
37
- .DatePicker .monthSelect .Button {
41
+ .calendar .monthSelect .Button {
38
42
  min-width: 25px;
39
43
  min-height: 25px;
40
44
  }
41
- .DatePicker .monthSelect .Button .material-icons {
45
+ .calendar .monthSelect .Button .material-icons {
42
46
  font-size: 16px;
43
47
  }
44
- .DatePicker .monthSelect .Button.disabled {
48
+ .calendar .monthSelect .Button.disabled {
45
49
  background: transparent;
46
50
  }
47
- .DatePicker .CalendarMonthView {
51
+ .calendar .CalendarMonthView {
48
52
  background: white;
49
53
  }
50
- .DatePicker .CalendarMonthView .headers, .DatePicker .CalendarMonthView .days {
54
+ .calendar .CalendarMonthView .headers, .calendar .CalendarMonthView .days {
51
55
  margin-bottom: 0;
52
56
  }
53
- .DatePicker .CalendarMonthView .header {
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
- .DatePicker .CalendarMonthView .day {
64
+ .calendar .CalendarMonthView .day {
61
65
  font-size: 10px;
62
66
  }
63
- .DatePicker .CalendarMonthView .day.notSelectedMonth {
67
+ .calendar .CalendarMonthView .day.notSelectedMonth {
64
68
  color: #969696;
65
69
  }
66
- .DatePicker .CalendarMonthView .day span {
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
- .DatePicker .CalendarMonthView .day span:hover {
81
+ .calendar .CalendarMonthView .day span:hover {
78
82
  background-color: #e6e6e6;
79
83
  }
80
- .DatePicker .CalendarMonthView .day span.inactive {
84
+ .calendar .CalendarMonthView .day span.inactive {
81
85
  color: lightgray;
82
86
  cursor: unset;
83
87
  }
84
- .DatePicker .CalendarMonthView .day span.inactive:hover {
88
+ .calendar .CalendarMonthView .day span.inactive:hover {
85
89
  background: unset;
86
90
  }
87
- .DatePicker .CalendarMonthView .day span.selected {
91
+ .calendar .CalendarMonthView .day span.selected {
88
92
  color: white;
89
93
  background-color: #2196F3;
90
94
  }
91
- .DatePicker .CalendarMonthView .day span.selected:hover {
95
+ .calendar .CalendarMonthView .day span.selected:hover {
92
96
  background-color: #51adf6;
93
97
  }
@@ -11,26 +11,28 @@
11
11
  text-align: center;
12
12
  }
13
13
  }
14
-
15
- .calendar {
16
- user-select: none;
17
- position: absolute;
18
- top: 33px;
19
- z-index: 10;
20
- 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);
21
-
22
- transition: 0.2s all ease;
23
- opacity: 0;
24
- visibility: hidden;
25
-
26
- &.focused {
27
- opacity: 1;
28
- visibility: visible;
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;
@@ -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.setState({focused: false});
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
- return <div className={`DatePicker`} ref={el => this.el = el}>
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
+ &nbsp;{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
- <div className={`calendar ${focused && 'focused'}`}>
154
- <div className={'row monthSelect'}>
155
- <span>
156
- {monthToString(selectedMonth.get(DateTools.TimeUnit.MONTH))}
157
- &nbsp;{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'}/>)}