added more search/display fields and made more robust
parent
6159f53137
commit
2c62605deb
|
|
@ -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=",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class LexMatches extends React.Component<any, any> {
|
private handleOnSearch(searchEvent: any): void {
|
||||||
public render() {
|
let searchText = searchEvent.searchValue;
|
||||||
return (
|
let searchType = searchLensMap[searchEvent.searchType];
|
||||||
<div>
|
let matchedEntries = _.chain(this.state.lexData)
|
||||||
{this.props.matchedEntries.map((mObj: any) => {
|
.get<any>('document.lexicon[0].item')
|
||||||
console.log('Matched ObjectEntry' + mObj);
|
.flatMap((o: any) => _.chain(o)
|
||||||
return (<LexEntry key={mObj.unl} lexItem={mObj} />);
|
.get<any>('entry')
|
||||||
})}
|
.map((p: any) => _.chain(p)
|
||||||
</div>
|
.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 LexEntry extends React.Component<any, any> {
|
class LexSearch extends React.Component<any, any> {
|
||||||
|
searchType: String = 'label';
|
||||||
|
searchValue: String = '';
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<div className="pt-form-group">
|
<Flex p={1} align="center" className="pt-control-group">
|
||||||
<label className="pt-label" htmlFor="example-form-group-input-a">
|
<div
|
||||||
UNL
|
className="pt-select"
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
<div className="pt-form-content">
|
<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() {
|
||||||
|
return (
|
||||||
|
<div className="pt-form-group pt-inline">
|
||||||
|
<Box w={2 / 5}>
|
||||||
|
<label style={{ textAlign: 'right' }} className="pt-label" htmlFor="example-form-group-input-a">
|
||||||
|
{this.props.labelText}
|
||||||
|
</label>
|
||||||
|
</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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue