diff options
Diffstat (limited to 'src/client/views/collections')
8 files changed, 101 insertions, 81 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 0865058be..1859ebee7 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -577,7 +577,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { return (null); } let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc; - console.log("pw = " + this.panelWidth() + "," + this.panelHeight() + " " + this.contentScaling()); return <DocumentView key={this._document[Id]} Document={this._document} DataDoc={resolvedDataDoc} diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index f1e013edd..194765880 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -25,6 +25,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faExpand } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { KeyCodes } from "../../northstar/utils/KeyCodes"; library.add(faExpand); @@ -66,7 +67,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { @action onKeyDown = (e: KeyboardEvent): void => { - if (this.props.isFocused && this.props.isEditable) { + if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) { document.removeEventListener("keydown", this.onKeyDown); this._isEditing = true; this.props.setIsEditing(true); diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 2e0ec20db..3c3708a30 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -8,7 +8,7 @@ box-sizing: border-box; // position: absolute; width: 100%; - height: calc(100% - 50px); + height: calc(100% - 70px); // overflow: hidden; // overflow-x: scroll; // border: none; @@ -161,7 +161,7 @@ } .rt-tr { - // width: 100%; + width: 100%; min-height: 30px; // height: $MAX_ROW_HEIGHT; } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 31de4e146..7bd0b5965 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -199,13 +199,13 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get schemaTable() { return ( <SchemaTable - Document={this.props.Document} // child doc + Document={this.props.Document} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} // childDocs={this.childDocs} CollectionView={this.props.CollectionView} ContainingCollectionView={this.props.ContainingCollectionView} - fieldKey={this.props.fieldKey} // might just be this. + fieldKey={this.props.fieldKey} renderDepth={this.props.renderDepth} moveDocument={this.props.moveDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} @@ -290,7 +290,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> { console.log("columns"); return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []); } - set columns(columns: SchemaHeaderField[]) { console.log("setting columns"); this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns); } + @computed get childDocs() { + let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + return DocListCast(doc[this.props.fieldKey]); + } + set columns(columns: SchemaHeaderField[]) { this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column<Doc>[] { let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); @@ -300,8 +304,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let focusedCol = this._focusedCell.col; let isEditable = !this._headerIsEditing;// && this.props.isSelected(); - let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = DocListCast(cdoc[this.props.fieldKey]); + // let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // let children = DocListCast(cdoc[this.props.fieldKey]); + let children = this.childDocs; if (children.reduce((found, doc) => found || doc.type === "collection", false)) { columns.push( @@ -425,8 +430,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } tableRemoveDoc = (document: Doc): boolean => { - let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + let children = this.childDocs; if (children.indexOf(document) !== -1) { children.splice(children.indexOf(document), 1); return true; @@ -520,8 +526,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this.changeFocusedCellByDirection(direction); - let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + let children = this.childDocs; const pdoc = FieldValue(children[this._focusedCell.row]); pdoc && this.props.setPreviewDoc(pdoc); } @@ -529,8 +536,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @action changeFocusedCellByDirection = (direction: string): void => { - let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + let children = this.childDocs; switch (direction) { case "tab": if (this._focusedCell.col + 1 === this.columns.length && this._focusedCell.row + 1 === children.length) { @@ -561,7 +569,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @action changeFocusedCellByIndex = (row: number, col: number): void => { let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); this._focusedCell = { row: row, col: col }; this.props.setFocused(this.props.Document); @@ -571,8 +579,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } createRow = () => { - let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + let children = this.childDocs; let newDoc = Docs.Create.TextDocument({ width: 100, height: 30 }); let proto = Doc.GetProto(newDoc); @@ -691,7 +700,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } get documentKeys() { - const docs = DocListCast(this.props.Document[this.props.fieldKey]); + // const docs = DocListCast(this.props.Document[this.props.fieldKey]); + let docs = this.childDocs; let keys: { [key: string]: boolean } = {}; // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be @@ -719,8 +729,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @computed get reactTable() { - let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - let children = DocListCast(cdoc[this.props.fieldKey]); + // let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // let children = DocListCast(cdoc[this.props.fieldKey]); + let children = this.childDocs; let previewWidth = this.previewWidth(); // + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; let hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false); @@ -732,7 +743,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { return <ReactTable style={{ position: "relative", float: "left", width: `calc(100% - ${previewWidth}px` }} - data={children} + data={this.childDocs} page={0} pageSize={children.length} showPagination={false} @@ -766,7 +777,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let csv: string = this.columns.reduce((val, col) => val + col + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; let self = this; - DocListCast(this.props.Document.data).map(doc => { + this.childDocs.map(doc => { csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; }); @@ -785,8 +796,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { getField = (row: number, col?: number) => { // const docs = DocListCast(this.props.Document[this.props.fieldKey]); - let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - const docs = DocListCast(cdoc[this.props.fieldKey]); + // let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + // const docs = DocListCast(cdoc[this.props.fieldKey]); + let docs = this.childDocs; row = row % docs.length; while (row < 0) row += docs.length; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b96b1f8c8..f647da8f0 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -140,12 +140,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { previewScript={undefined}> </CollectionSchemaPreview>; } - getDocHeight(d: Doc) { + getDocHeight(d: Doc, columnScale: number = 1) { let nw = NumCast(d.nativeWidth); let nh = NumCast(d.nativeHeight); if (!BoolCast(d.ignoreAspect) && nw && nh) { let aspect = nw && nh ? nh / nw : 1; - let wid = Math.min(d[WidthSym](), this.columnWidth); + let wid = Math.min(d[WidthSym](), this.columnWidth / columnScale); return wid * aspect; } return d[HeightSym](); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index cf6079196..387e189e7 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -69,62 +69,50 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC children(docs: Doc[]) { let parent = this.props.parent; - this.props.parent._docXfs.length = 0; + parent._docXfs.length = 0; return docs.map((d, i) => { - let layoutDoc = Doc.expandTemplateLayout(d, parent.props.DataDoc); let headings = this.props.headings(); let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx); let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d); - let width = () => (d.nativeWidth && !BoolCast(d.ignoreAspect) ? Math.min(pair.layout[WidthSym](), parent.columnWidth) : parent.columnWidth) / (uniqueHeadings.length + 1); - let height = () => parent.getDocHeight(pair.layout); - if (parent.singleColumn) { - let dxf; - let dref = React.createRef<HTMLDivElement>(); - if (uniqueHeadings.length > 0) { - dxf = () => this.getDocTransform(layoutDoc, dref.current!); - this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height }); - } - else { - //have to add the height of all previous single column sections or the doc decorations will be in the wrong place. - dxf = () => this.getSingleDocTransform(layoutDoc, i, width()); - this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height }); - } - let rowHgtPcnt = height(); - return <div className="collectionStackingView-columnDoc" key={d[Id]} ref={dref} style={{ width: width(), marginTop: i === 0 ? 0 : parent.gridGap, height: `${rowHgtPcnt}` }} > - {this.props.parent.getDisplayDoc(layoutDoc, d, dxf, width)} - </div>; - } else { - let dref = React.createRef<HTMLDivElement>(); - let dxf = () => this.getDocTransform(layoutDoc, dref.current!); - this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height }); - let rowHgtPcnt = height(); - let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap); - let divStyle = parent.singleColumn ? { width: width(), marginTop: i === 0 ? 0 : parent.gridGap, height: `${rowHgtPcnt}` } : { gridRowEnd: `span ${rowSpan}` }; - return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={divStyle} > - {this.props.parent.getDisplayDoc(layoutDoc, d, dxf, width)} - </div>; - } + let width = () => (d.nativeWidth && !BoolCast(d.ignoreAspect) ? Math.min(pair.layout[WidthSym](), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1); + let height = () => parent.getDocHeight(pair.layout, uniqueHeadings.length + 1);// / (d.nativeWidth && !BoolCast(d.ignoreAspect) ? uniqueHeadings.length + 1 : 1); + let dref = React.createRef<HTMLDivElement>(); + // if (uniqueHeadings.length > 0) { + let dxf = () => this.getDocTransform(pair.layout, dref.current!); + this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height }); + // } + // else { + // //have to add the height of all previous single column sections or the doc decorations will be in the wrong place. + // let dxf = () => this.getDocTransform(layoutDoc, i, width()); + // this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height }); + // } + let rowHgtPcnt = height(); + let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap); + let style = parent.singleColumn ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: `${rowHgtPcnt}` } : { gridRowEnd: `span ${rowSpan}` }; + return <div className={`collectionStackingView-${parent.singleColumn ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} > + {this.props.parent.getDisplayDoc(pair.layout, pair.data, dxf, width)} + </div>; + // } else { + // let dref = React.createRef<HTMLDivElement>(); + // let dxf = () => this.getDocTransform(layoutDoc, dref.current!); + // this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height }); + // let rowHgtPcnt = height(); + // let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap); + // let divStyle = parent.singleColumn ? { width: width(), marginTop: i === 0 ? 0 : parent.gridGap, height: `${rowHgtPcnt}` } : { gridRowEnd: `span ${rowSpan}` }; + // return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={divStyle} > + // {this.props.parent.getDisplayDoc(layoutDoc, d, dxf, width)} + // </div>; + // } }); } - getSingleDocTransform(doc: Doc, ind: number, width: number) { - let localY = this.props.parent.filteredChildren.reduce((height, d, i) => - height + (i < ind ? this.props.parent.getDocHeight(Doc.expandTemplateLayout(d, this.props.parent.props.DataDoc)) + this.props.parent.gridGap : 0), this.props.parent.yMargin); - let translate = this.props.parent.props.ScreenToLocalTransform().inverse().transformPoint((this.props.parent.props.PanelWidth() - width) / 2, localY); - return this.offsetTransform(doc, translate[0], translate[1]); - } - - offsetTransform(doc: Doc, translateX: number, translateY: number) { - let outerXf = Utils.GetScreenTransform(this.props.parent._masonryGridRef!); - let offset = this.props.parent.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - return this.props.parent.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.props.parent.columnWidth); - } - getDocTransform(doc: Doc, dref: HTMLDivElement) { let { scale, translateX, translateY } = Utils.GetScreenTransform(dref); let outerXf = Utils.GetScreenTransform(this.props.parent._masonryGridRef!); let offset = this.props.parent.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - return this.props.parent.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.props.parent.columnWidth); + return this.props.parent.props.ScreenToLocalTransform(). + translate(offset[0], offset[1] - (this.props.parent.chromeCollapsed ? 0 : 100)). + scale(NumCast(doc.width, 1) / this.props.parent.columnWidth); } getValue = (value: string): any => { @@ -279,8 +267,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`} style={{ padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`, - margin: "auto 5px", - width: singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`, + margin: "auto", + width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`, height: 'max-content', position: "relative", gridGap: style.gridGap, diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 044d5336a..9c751c4df 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -31,7 +31,7 @@ let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); export class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> { @observable private _viewSpecsOpen: boolean = false; @observable private _dateWithinValue: string = ""; - @observable private _dateValue: Date = new Date(); + @observable private _dateValue: Date | string = ""; @observable private _keyRestrictions: [JSX.Element, string][] = []; @observable private _collapsed: boolean = false; @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); } @@ -124,10 +124,24 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro let monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0; let weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0; let dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7; - let lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset); - let upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1); - let dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; - let fullScript = `return ${dateRestrictionScript} && ${keyRestrictionScript}`; + let dateRestrictionScript = ""; + if (this._dateValue instanceof Date) { + let lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset); + let upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1); + dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; + } + else { + let createdDate = new Date(this._dateValue); + if (!isNaN(createdDate.getTime())) { + let lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset); + let upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1); + dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; + } + } + let fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ? + `return ${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} ${keyRestrictionScript}` : + `return ${keyRestrictionScript} ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` : + "return true"; let compiled = CompileScript(fullScript, { params: { doc: Doc.name } }); if (compiled.compiled) { this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled); @@ -221,7 +235,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro </select> <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight" id={this._datePickerElGuid} - value={this._dateValue.toLocaleDateString()} + value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue} + onChange={(e) => runInAction(() => this._dateValue = e.target.value)} onPointerDown={this.openDatePicker} placeholder="Value" /> </div> diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx index f292557b5..9c3c9c07c 100644 --- a/src/client/views/collections/KeyRestrictionRow.tsx +++ b/src/client/views/collections/KeyRestrictionRow.tsx @@ -18,12 +18,17 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr if (this._key && this._value) { let parsedValue: string | number = `"${this._value}"`; let parsed = parseInt(this._value); + let type = "string"; if (!isNaN(parsed)) { parsedValue = parsed; + type = "number"; } - let scriptText = `(doc.${this._key} ${this._contains ? "===" : "!=="} ${parsedValue})`; + let scriptText = `${this._contains ? "" : "!"}((doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))`; this.props.script(scriptText); } + else { + this.props.script(""); + } return ( <div className="collectionViewBaseChrome-viewSpecsMenu-row"> <input className="collectionViewBaseChrome-viewSpecsMenu-rowLeft" |
