added more search/display fields and made more robust

master
Malar Kannan 2017-06-20 18:50:54 +05:30
parent 6159f53137
commit 2c62605deb
5 changed files with 210 additions and 93 deletions

10
package-lock.json generated
View File

@ -28,6 +28,11 @@
"version": "https://registry.npmjs.org/@types/jest/-/jest-19.2.4.tgz", "version": "https://registry.npmjs.org/@types/jest/-/jest-19.2.4.tgz",
"integrity": "sha1-VDZRcSU1lit9xhXhjko4H8JodEI=" "integrity": "sha1-VDZRcSU1lit9xhXhjko4H8JodEI="
}, },
"@types/lodash": {
"version": "4.14.66",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.66.tgz",
"integrity": "sha512-LpGSiIy5/utq8AT2bSXGnENnS1kCZJ1m84L1yqKst2UehSZe6VWROmiysYg/lLJR6zu2ooeVoQtkUHToA+mEtQ=="
},
"@types/node": { "@types/node": {
"version": "https://registry.npmjs.org/@types/node/-/node-7.0.31.tgz", "version": "https://registry.npmjs.org/@types/node/-/node-7.0.31.tgz",
"integrity": "sha1-gOpNF1WZsqABScKaEKTrLf9ZLoY=" "integrity": "sha1-gOpNF1WZsqABScKaEKTrLf9ZLoY="
@ -4370,6 +4375,11 @@
"integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=",
"dev": true "dev": true
}, },
"reflexbox": {
"version": "3.0.0-0",
"resolved": "https://registry.npmjs.org/reflexbox/-/reflexbox-3.0.0-0.tgz",
"integrity": "sha512-r+7vdyy+kscKFyk2083MLemiT/x3nwzCSif/UwCCoobmwq+cNmJ+hrL7Bu1tJjFOAYjVbIk/GyL5svTRiD0wzg=="
},
"regenerate": { "regenerate": {
"version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", "version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz",
"integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=", "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=",

View File

@ -7,6 +7,7 @@
"@blueprintjs/table": "^1.16.0", "@blueprintjs/table": "^1.16.0",
"@types/es6-shim": "^0.31.34", "@types/es6-shim": "^0.31.34",
"@types/jest": "^19.2.4", "@types/jest": "^19.2.4",
"@types/lodash": "^4.14.66",
"@types/node": "^7.0.29", "@types/node": "^7.0.29",
"@types/pure-render-decorator": "^0.2.27", "@types/pure-render-decorator": "^0.2.27",
"@types/react": "^15.0.27", "@types/react": "^15.0.27",
@ -18,6 +19,7 @@
"react-addons-css-transition-group": "^15.5.2", "react-addons-css-transition-group": "^15.5.2",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-transition-group": "^1.1.3", "react-transition-group": "^1.1.3",
"reflexbox": "^3.0.0-0",
"semantic-ui-css": "^2.2.10", "semantic-ui-css": "^2.2.10",
"semantic-ui-react": "^0.68.5", "semantic-ui-react": "^0.68.5",
"xml2js": "^0.4.17", "xml2js": "^0.4.17",

View File

@ -26,15 +26,5 @@
You need to enable JavaScript to run this app. You need to enable JavaScript to run this app.
</noscript> </noscript>
<div id="root"></div> <div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body> </body>
</html> </html>

View File

@ -1,13 +1,11 @@
{ {
"short_name": "React App", "short_name": "LexEditor",
"name": "Create React App Sample", "name": "Freespeech LexEditor App",
"icons": [ "icons": [{
{
"src": "favicon.ico", "src": "favicon.ico",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
} }],
],
"start_url": "./index.html", "start_url": "./index.html",
"display": "standalone", "display": "standalone",
"theme_color": "#000000", "theme_color": "#000000",

View File

@ -1,34 +1,38 @@
import * as React from 'react'; import * as React from 'react';
import * as _ from 'lodash';
import { import {
// Button,
// Classes,
// InputGroup,
// Intent,
// Menu,
// MenuItem,
// Popover,
// Position,
// Spinner,
// Switch,
// Tag,
// Tooltip,
FocusStyleManager, FocusStyleManager,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import * as XML from 'xml2js'; import * as XML from 'xml2js';
import * as XPath from 'xml2js-xpath'; const { Flex, Box } = require('reflexbox');
FocusStyleManager.onlyShowFocusOnTabs(); FocusStyleManager.onlyShowFocusOnTabs();
type ReactEvent = React.ChangeEvent<HTMLInputElement>; enum EditorMode {
Empty = 1, Edit, Add
}
interface LexEditorProps { interface LexEditorProps {
fileName: RequestInfo; fileName: RequestInfo;
} }
const searchLensMap = {
'label': 'label[0]',
'unl': 'unl[0]',
'synset': 'lexprops[0].wnsynset[0]',
'guid': 'guid[0]',
'pos': 'pos[0]',
'image': 'image[0]',
'relations': 'relations[0]',
'frame': 'syntacticprops[0].property[0]._',
'morphclass': 'lexprops[0].morphology[0].morph[0]._',
'stats': 'stats[0].property[0]._',
'lang': '$.id',
};
class LexEditor extends React.Component<LexEditorProps, any> { class LexEditor extends React.Component<LexEditorProps, any> {
constructor(props: LexEditorProps) { constructor(props: LexEditorProps) {
super(props); super(props);
this.handleOnSearch = this.handleOnSearch.bind(this);
this.state = { lexData: {}, matchedEntries: [] }; this.state = { lexData: {}, matchedEntries: [] };
} }
@ -39,91 +43,204 @@ class LexEditor extends React.Component<LexEditorProps, any> {
XML.parseString(xmlString, (err, lexData) => { XML.parseString(xmlString, (err, lexData) => {
this.setState({ ...this.state, lexData }); this.setState({ ...this.state, lexData });
}); });
})
.catch((e) => {
console.log('errored :', e);
}); });
// .catch((error) => {
// console.error(error);
// });
} }
public render() { public render() {
return ( return (
<div> <div>
<LexSearch handleOnSearch={this.handleOnSearch} /> <LexSearch handleOnSearch={(e: any) => this.handleOnSearch(e)} />
<LexMatches matchedEntries={this.state.matchedEntries} /> <LexMatches
</div> matchedEntries={this.state.matchedEntries}
); mode={this.state.mode}
} searchText={this.state.searchText}
searchType={this.state.searchType}
private handleOnSearch(event: ReactEvent): void {
let searchText = event.target.value;
// this.setState({ ...this.state, searchText });
let pathQ = './/lang';
let words = XPath.find(this.state.lexData.document, pathQ);
let matchedEntries = words.filter((obj) => {
if (obj.label[0] === searchText) {
return true;
}
return false;
});
this.setState({ ...this.state, matchedEntries });
}
}
interface LexSearchProps {
handleOnSearch: (e: ReactEvent) => void;
}
class LexSearch extends React.Component<LexSearchProps, null> {
public render() {
return (
<div className="pt-input-group" style={{ width: '300px' }}>
<span className="pt-icon pt-icon-search" />
<input
className="pt-input"
type="search"
placeholder="Search input"
dir="auto"
onChange={e => this.props.handleOnSearch(e)}
/> />
</div> </div>
); );
} }
private handleOnSearch(searchEvent: any): void {
let searchText = searchEvent.searchValue;
let searchType = searchLensMap[searchEvent.searchType];
let matchedEntries = _.chain(this.state.lexData)
.get<any>('document.lexicon[0].item')
.flatMap((o: any) => _.chain(o)
.get<any>('entry')
.map((p: any) => _.chain(p)
.get<any>('lang[0]')
.set('guid[0]', o.$.guid)
.value())
.value()
)
.filter((q: any) => _.get<any>(q, searchType, '') === searchText)
.value();
let emptyOrAdd = searchText === '' ? EditorMode.Empty : EditorMode.Add;
let mode = matchedEntries.length > 0 ? EditorMode.Edit : emptyOrAdd;
this.setState({ ...this.state, matchedEntries, mode, searchText, searchType });
}
} }
class LexMatches extends React.Component<any, any> { class LexSearch extends React.Component<any, any> {
searchType: String = 'label';
searchValue: String = '';
public render() { public render() {
return ( return (
<div> <Flex p={1} align="center" className="pt-control-group">
{this.props.matchedEntries.map((mObj: any) => { <div
console.log('Matched ObjectEntry' + mObj); className="pt-select"
return (<LexEntry key={mObj.unl} lexItem={mObj} />); onChange={e => this.handleTypeChange(e)}
>
<select>
{_.keys(searchLensMap).map(k => {
return <option value={k}> {_.capitalize(k)}</option>;
})} })}
</select>
</div>
<div className="pt-input-group" style={{ width: '120px' }}>
<span className="pt-icon pt-icon-search" />
<input
type="text"
className="pt-input"
placeholder="Search input"
dir="auto"
onChange={e => this.handleLookup(e)}
/>
</div>
</Flex>
);
}
private handleLookup(e: any) {
this.searchValue = e.target.value;
this.props.handleOnSearch({ searchType: this.searchType, searchValue: this.searchValue });
}
private handleTypeChange(e: any) {
this.searchType = e.target.value;
this.props.handleOnSearch({ searchType: this.searchType, searchValue: this.searchValue });
}
}
function selectMode(props: any) {
let editEntries = props.matchedEntries.map((mObj: any) => {
let uniqueKey = mObj.guid[0] + '#' + mObj.$.id;
return (<LexEdit key={uniqueKey} lexItem={mObj} />);
});
switch (props.mode) {
case EditorMode.Edit:
return editEntries;
case EditorMode.Add:
let addProps = {};
_.set(addProps, props.searchType, props.searchText);
return <LexEdit lexItem={addProps} />;
default:
return (
<div className="pt-non-ideal-state">
<div className="pt-non-ideal-state-visual pt-non-ideal-state-icon">
<span className="pt-icon pt-icon-folder-open" />
</div>
<h4 className="pt-non-ideal-state-title">Empty</h4>
<div className="pt-non-ideal-state-description">
Type something in the searchbar.
</div>
</div> </div>
); );
} }
} }
class LexEntry extends React.Component<any, any> { function LexMatches(props: any) {
return (
<Flex m={10} align="center" wrap={true}>
{selectMode(props)}
</Flex>
);
}
class LexEdit extends React.Component<any, any> {
public render() {
let li = this.props.lexItem;
let lexFields = _.keys(searchLensMap).map(e => {
return this.getLabelField(e, searchLensMap[e], li);
});
return (
<Box wx={240} mx={10} my={20} className="pt-card pt-elevation-2 pt-interactive">
<label className="pt-label">
GUID {_.get<any>(li, 'guid', '')}
</label>
<label className="pt-label">
Language: {_.get<any>(li, '$.id', '')}
</label>
<Box column={true} m={1}>
{lexFields}
</Box>
</Box>
);
}
private getLabelField(text: string, labelLens: string, li: any) {
return (
<LexField
key={text}
labelText={_.capitalize(text)}
fieldType={text}
fieldText={_.get<any>(li, labelLens, '')}
fieldHandler={(e: any) => this.handleOnChange(e)}
/>
);
}
private handleOnChange(event: any) {
this.setState(event);
}
}
class LexField extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.state = { fieldText: this.props.fieldText };
}
public render() { public render() {
return ( return (
<div className="pt-form-group"> <div className="pt-form-group pt-inline">
<label className="pt-label" htmlFor="example-form-group-input-a"> <Box w={2 / 5}>
UNL <label style={{ textAlign: 'right' }} className="pt-label" htmlFor="example-form-group-input-a">
{this.props.labelText}
</label> </label>
<div className="pt-form-content"> </Box>
<Box w={3 / 5}>
<input <input
id="example-form-group-input-a" id="example-form-group-input-a"
className="pt-input" className="pt-input"
style={{ width: '300px' }} style={{ width: '110px' }}
placeholder="UNL Value" placeholder={this.props.fieldType}
value={this.props.lexItem.unl} value={this.state.fieldText}
type="text" type="text"
dir="auto" dir="auto"
onChange={(e: any) => this.onFieldChange(e)}
/> />
</div> </Box>
</div> </div>
); );
} }
public componentWillReceiveProps(nextProps: any) {
if (this.props.fieldText !== nextProps.fieldText) {
let fieldText = nextProps.fieldText;
this.setState({ ...this.state, fieldText });
}
}
private onFieldChange(e: any) {
let fieldText = e.target.value;
let eventData = {};
eventData[this.props.fieldType] = fieldText;
this.setState({ ...this.state, fieldText });
this.props.fieldHandler(eventData);
}
} }
export default LexEditor; export default LexEditor;