@@ -15,6 +15,7 @@ import React, { Component } from 'react';
1515import styles from 'components/BrowserCell/BrowserCell.scss' ;
1616import { unselectable } from 'stylesheets/base.scss' ;
1717import Tooltip from '../Tooltip/PopperTooltip.react' ;
18+ import * as ColumnPreferences from 'lib/ColumnPreferences' ;
1819
1920export default class BrowserCell extends Component {
2021 constructor ( ) {
@@ -23,13 +24,161 @@ export default class BrowserCell extends Component {
2324 this . cellRef = React . createRef ( ) ;
2425 this . copyableValue = undefined ;
2526 this . state = {
26- showTooltip : false
27+ showTooltip : false ,
28+ content : null ,
29+ classes : [ ]
30+ } ;
31+ }
32+
33+ async renderCellContent ( ) {
34+ let content = this . props . value ;
35+ let isNewRow = this . props . row < 0 ;
36+ this . copyableValue = content ;
37+ let classes = [ styles . cell , unselectable ] ;
38+ if ( this . props . hidden ) {
39+ content = this . props . value !== undefined || ! isNewRow ? '(hidden)' : this . props . isRequired ? '(required)' : '(undefined)' ;
40+ classes . push ( styles . empty ) ;
41+ } else if ( this . props . value === undefined ) {
42+ if ( this . props . type === 'ACL' ) {
43+ this . copyableValue = content = 'Public Read + Write' ;
44+ } else {
45+ this . copyableValue = content = '(undefined)' ;
46+ classes . push ( styles . empty ) ;
47+ }
48+ content = isNewRow && this . props . isRequired && this . props . value === undefined ? '(required)' : content ;
49+ } else if ( this . props . value === null ) {
50+ this . copyableValue = content = '(null)' ;
51+ classes . push ( styles . empty ) ;
52+ } else if ( this . props . value === '' ) {
53+ content = < span > </ span > ;
54+ classes . push ( styles . empty ) ;
55+ } else if ( this . props . type === 'Pointer' ) {
56+ const defaultPointerKey = await ColumnPreferences . getPointerDefaultKey ( this . props . appId , this . props . value . className ) ;
57+ let dataValue = this . props . value . id ;
58+ if ( defaultPointerKey !== 'objectId' ) {
59+ dataValue = this . props . value . get ( defaultPointerKey ) ;
60+ if ( dataValue && typeof dataValue === 'object' ) {
61+ if ( dataValue instanceof Date ) {
62+ dataValue = dataValue . toLocaleString ( ) ;
63+ }
64+ else {
65+ if ( ! this . props . value . id ) {
66+ dataValue = this . props . value . id ;
67+ } else {
68+ dataValue = '(undefined)' ;
69+ }
70+ }
71+ }
72+ if ( ! dataValue ) {
73+ if ( this . props . value . id ) {
74+ dataValue = this . props . value . id ;
75+ } else {
76+ dataValue = '(undefined)' ;
77+ }
78+ }
79+ }
80+
81+ if ( this . props . value && this . props . value . __type ) {
82+ const object = new Parse . Object ( this . props . value . className ) ;
83+ object . id = this . props . value . objectId ;
84+ this . props . value = object ;
85+ }
86+
87+ content = this . props . onPointerClick ? (
88+ < Pill value = { dataValue } onClick = { this . props . onPointerClick . bind ( undefined , this . props . value ) } followClick = { true } />
89+ ) : (
90+ dataValue
91+ ) ;
92+
93+ this . copyableValue = this . props . value . id ;
94+ }
95+ else if ( this . props . type === 'Array' ) {
96+ if ( this . props . value [ 0 ] && typeof this . props . value [ 0 ] === 'object' && this . props . value [ 0 ] . __type === 'Pointer' ) {
97+ const array = [ ] ;
98+ this . props . value . map ( ( v , i ) => {
99+ if ( typeof v !== 'object' || v . __type !== 'Pointer' ) {
100+ throw new Error ( 'Invalid type found in pointer array' ) ;
101+ }
102+ const object = new Parse . Object ( v . className ) ;
103+ object . id = v . objectId ;
104+ array . push (
105+ < Pill key = { i } value = { v . objectId } onClick = { this . props . onPointerClick . bind ( undefined , object ) } followClick = { true } />
106+ ) ;
107+ } ) ;
108+ this . copyableValue = content = < ul >
109+ { array . map ( a => < li > { a } </ li > ) }
110+ </ ul >
111+ if ( array . length > 1 ) {
112+ classes . push ( styles . hasMore ) ;
113+ }
114+ }
115+ else {
116+ this . copyableValue = content = JSON . stringify ( this . props . value ) ;
117+ }
118+ }
119+ else if ( this . props . type === 'Date' ) {
120+ if ( typeof value === 'object' && this . props . value . __type ) {
121+ this . props . value = new Date ( this . props . value . iso ) ;
122+ } else if ( typeof value === 'string' ) {
123+ this . props . value = new Date ( this . props . value ) ;
124+ }
125+ this . copyableValue = content = dateStringUTC ( this . props . value ) ;
126+ } else if ( this . props . type === 'Boolean' ) {
127+ this . copyableValue = content = this . props . value ? 'True' : 'False' ;
128+ } else if ( this . props . type === 'Object' || this . props . type === 'Bytes' ) {
129+ this . copyableValue = content = JSON . stringify ( this . props . value ) ;
130+ } else if ( this . props . type === 'File' ) {
131+ const fileName = this . props . value . url ( ) ? getFileName ( this . props . value ) : 'Uploading\u2026' ;
132+ content = < Pill value = { fileName } fileDownloadLink = { this . props . value . url ( ) } /> ;
133+ this . copyableValue = fileName ;
134+ } else if ( this . props . type === 'ACL' ) {
135+ let pieces = [ ] ;
136+ let json = this . props . value . toJSON ( ) ;
137+ if ( Object . prototype . hasOwnProperty . call ( json , '*' ) ) {
138+ if ( json [ '*' ] . read && json [ '*' ] . write ) {
139+ pieces . push ( 'Public Read + Write' ) ;
140+ } else if ( json [ '*' ] . read ) {
141+ pieces . push ( 'Public Read' ) ;
142+ } else if ( json [ '*' ] . write ) {
143+ pieces . push ( 'Public Write' ) ;
144+ }
145+ }
146+ for ( let role in json ) {
147+ if ( role !== '*' ) {
148+ pieces . push ( role ) ;
149+ }
150+ }
151+ if ( pieces . length === 0 ) {
152+ pieces . push ( 'Master Key Only' ) ;
153+ }
154+ this . copyableValue = content = pieces . join ( ', ' ) ;
155+ } else if ( this . props . type === 'GeoPoint' ) {
156+ this . copyableValue = content = `(${ this . props . value . latitude } , ${ this . props . value . longitude } )` ;
157+ } else if ( this . props . type === 'Polygon' ) {
158+ this . copyableValue = content = this . props . value . coordinates . map ( coord => `(${ coord } )` )
159+ } else if ( this . props . type === 'Relation' ) {
160+ content = this . props . setRelation ? (
161+ < div style = { { textAlign : 'center' } } >
162+ < Pill onClick = { ( ) => this . props . setRelation ( this . props . value ) } value = 'View relation' followClick = { true } />
163+ </ div >
164+ ) : (
165+ 'Relation'
166+ ) ;
167+ this . copyableValue = undefined ;
27168 }
28169 this . onContextMenu = this . onContextMenu . bind ( this ) ;
29170
171+ if ( this . props . markRequiredField && this . props . isRequired && ! this . props . value ) {
172+ classes . push ( styles . required ) ;
173+ }
174+
175+ this . setState ( { ...this . state , content, classes } )
30176 }
31177
32- componentDidUpdate ( prevProps ) {
178+ async componentDidUpdate ( prevProps ) {
179+ if ( this . props . value !== prevProps . value ) {
180+ await this . renderCellContent ( ) ;
181+ }
33182 if ( this . props . current ) {
34183 const node = this . cellRef . current ;
35184 const { setRelation } = this . props ;
@@ -58,7 +207,7 @@ export default class BrowserCell extends Component {
58207 }
59208
60209 shouldComponentUpdate ( nextProps , nextState ) {
61- if ( nextState . showTooltip !== this . state . showTooltip ) {
210+ if ( nextState . showTooltip !== this . state . showTooltip || nextState . content !== this . state . content ) {
62211 return true ;
63212 }
64213 const shallowVerifyProps = [ ...new Set ( Object . keys ( this . props ) . concat ( Object . keys ( nextProps ) ) ) ]
@@ -225,139 +374,27 @@ export default class BrowserCell extends Component {
225374 } ) ) ) ;
226375 }
227376
377+ componentDidMount ( ) {
378+ this . renderCellContent ( ) ;
379+ }
380+
228381 //#endregion
229382
230383 render ( ) {
231- let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, setRelation, onPointerClick, onPointerCmdClick, row, col, field, onEditSelectedRow, readonly, isRequired, markRequiredFieldRow } = this . props ;
232- let content = value ;
384+ let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, onPointerCmdClick, row, col, field, onEditSelectedRow, readonly, isRequired, markRequiredFieldRow } = this . props ;
233385 let isNewRow = row < 0 ;
234- this . copyableValue = content ;
235- let classes = [ styles . cell , unselectable ] ;
236- if ( hidden ) {
237- content = value !== undefined || ! isNewRow ? '(hidden)' : isRequired ? '(required)' : '(undefined)' ;
238- classes . push ( styles . empty ) ;
239- } else if ( value === undefined ) {
240- if ( type === 'ACL' ) {
241- this . copyableValue = content = 'Public Read + Write' ;
242- } else {
243- this . copyableValue = content = '(undefined)' ;
244- classes . push ( styles . empty ) ;
245- }
246- content = isNewRow && isRequired && value === undefined ? '(required)' : content ;
247- } else if ( value === null ) {
248- this . copyableValue = content = '(null)' ;
249- classes . push ( styles . empty ) ;
250- } else if ( value === '' ) {
251- content = < span > </ span > ;
252- classes . push ( styles . empty ) ;
253- } else if ( type === 'Pointer' ) {
254- if ( value && value . __type ) {
255- const object = new Parse . Object ( value . className ) ;
256- object . id = value . objectId ;
257- value = object ;
258- }
259- content = onPointerClick ? (
260- < Pill
261- value = { value . id }
262- onClick = { onPointerClick . bind ( undefined , value ) }
263- followClick = { true }
264- />
265- ) : (
266- value . id
267- ) ;
268- this . copyableValue = value . id ;
269- }
270- else if ( type === 'Array' ) {
271- if ( value [ 0 ] && typeof value [ 0 ] === 'object' && value [ 0 ] . __type === 'Pointer' ) {
272- const array = [ ] ;
273- value . map ( ( v , i ) => {
274- if ( typeof v !== 'object' || v . __type !== 'Pointer' ) {
275- throw new Error ( 'Invalid type found in pointer array' ) ;
276- }
277- const object = new Parse . Object ( v . className ) ;
278- object . id = v . objectId ;
279- array . push (
280- < Pill
281- key = { v . objectId }
282- value = { v . objectId }
283- onClick = { onPointerClick . bind ( undefined , object ) }
284- followClick = { true }
285- />
286- ) ;
287- } ) ;
288- content = < ul className = { styles . hasMore } >
289- { array . map ( a => < li > { a } </ li > ) }
290- </ ul >
291- this . copyableValue = JSON . stringify ( value ) ;
292- if ( array . length > 1 ) {
293- classes . push ( styles . removePadding ) ;
294- }
295- }
296- else {
297- this . copyableValue = content = JSON . stringify ( value ) ;
298- }
299- }
300- else if ( type === 'Date' ) {
301- if ( typeof value === 'object' && value . __type ) {
302- value = new Date ( value . iso ) ;
303- } else if ( typeof value === 'string' ) {
304- value = new Date ( value ) ;
305- }
306- this . copyableValue = content = dateStringUTC ( value ) ;
307- } else if ( type === 'Boolean' ) {
308- this . copyableValue = content = value ? 'True' : 'False' ;
309- } else if ( type === 'Object' || type === 'Bytes' ) {
310- this . copyableValue = content = JSON . stringify ( value ) ;
311- } else if ( type === 'File' ) {
312- const fileName = value . url ( ) ? getFileName ( value ) : 'Uploading\u2026' ;
313- content = < Pill value = { fileName } fileDownloadLink = { value . url ( ) } /> ;
314- this . copyableValue = fileName ;
315- } else if ( type === 'ACL' ) {
316- let pieces = [ ] ;
317- let json = value . toJSON ( ) ;
318- if ( Object . prototype . hasOwnProperty . call ( json , '*' ) ) {
319- if ( json [ '*' ] . read && json [ '*' ] . write ) {
320- pieces . push ( 'Public Read + Write' ) ;
321- } else if ( json [ '*' ] . read ) {
322- pieces . push ( 'Public Read' ) ;
323- } else if ( json [ '*' ] . write ) {
324- pieces . push ( 'Public Write' ) ;
325- }
326- }
327- for ( let role in json ) {
328- if ( role !== '*' ) {
329- pieces . push ( role ) ;
330- }
331- }
332- if ( pieces . length === 0 ) {
333- pieces . push ( 'Master Key Only' ) ;
334- }
335- this . copyableValue = content = pieces . join ( ', ' ) ;
336- } else if ( type === 'GeoPoint' ) {
337- this . copyableValue = content = `(${ value . latitude } , ${ value . longitude } )` ;
338- } else if ( type === 'Polygon' ) {
339- this . copyableValue = content = value . coordinates . map ( coord => `(${ coord } )` )
340- } else if ( type === 'Relation' ) {
341- content = setRelation ? (
342- < div style = { { textAlign : 'center' } } >
343- < Pill onClick = { ( ) => setRelation ( value ) } value = 'View relation' followClick = { true } />
344- </ div >
345- ) : (
346- 'Relation'
347- ) ;
348- this . copyableValue = undefined ;
349- }
350386
351- if ( current ) {
387+ let classes = [ ...this . state . classes ] ;
388+
389+ if ( current ) {
352390 classes . push ( styles . current ) ;
353391 }
354-
355392 if ( markRequiredFieldRow === row && isRequired && ! value ) {
356393 classes . push ( styles . required ) ;
357394 }
358395
359396 return readonly ? (
360- < Tooltip placement = 'bottom' tooltip = 'Read only (CTRL+C to copy)' visible = { this . state . showTooltip } >
397+ < Tooltip placement = 'bottom' tooltip = 'Read only (CTRL+C to copy)' visible = { this . state . showTooltip } >
361398 < span
362399 ref = { this . cellRef }
363400 className = { classes . join ( ' ' ) }
@@ -382,7 +419,7 @@ export default class BrowserCell extends Component {
382419 } }
383420 onContextMenu = { this . onContextMenu }
384421 >
385- { isNewRow ? '(auto)' : content }
422+ { row < 0 || isNewRow ? '(auto)' : this . state . content }
386423 </ span >
387424 </ Tooltip >
388425 ) : (
@@ -413,13 +450,11 @@ export default class BrowserCell extends Component {
413450 if ( [ 'ACL' , 'Boolean' , 'File' ] . includes ( type ) ) {
414451 e . preventDefault ( ) ;
415452 }
416- onEditChange ( true ) ;
417- }
418- } }
419- onContextMenu = { this . onContextMenu }
420- >
421- { content }
422- </ span >
453+ } } }
454+ onContextMenu = { this . onContextMenu . bind ( this ) }
455+ >
456+ { this . state . content }
457+ </ span >
423458 ) ;
424459 }
425460}
0 commit comments