Compare commits

...

4 Commits

Author SHA1 Message Date
Greg Shuflin
eb46a87c8e General improvements for non-Sai conlangs 2021-09-12 17:27:09 -07:00
Greg Shuflin
010552a4fc Move over App to .tsx 2021-09-12 02:22:50 -07:00
Greg Shuflin
3b2083fa27 Fix indenting 2021-09-12 02:07:44 -07:00
Greg Shuflin
44156694a2 More types 2021-09-12 02:07:29 -07:00
6 changed files with 298 additions and 260 deletions

View File

@ -20,6 +20,7 @@ module.exports = {
"@typescript-eslint" "@typescript-eslint"
], ],
rules: { rules: {
"arrow-parens": ["error", "always"] "arrow-parens": ["error", "always"],
"indent": ["error", 4]
}, },
}; };

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import App from "./src/App.jsx"; import App from "./src/App.tsx";
const root = document.getElementById("root"); const root = document.getElementById("root");
ReactDOM.render(<App />, root); ReactDOM.render(<App />, root);

View File

@ -10,8 +10,9 @@
"start": "parcel index.html", "start": "parcel index.html",
"build": "parcel build index.html --no-source-maps", "build": "parcel build index.html --no-source-maps",
"prebuild": "yarn run typecheck", "prebuild": "yarn run typecheck",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit --jsx preserve",
"lint": "eslint src/*" "lint": "eslint src/*",
"lint-fix": "eslint src/* --fix"
}, },
"devDependencies": { "devDependencies": {
"@parcel/transformer-image": "2.0.0-rc.0", "@parcel/transformer-image": "2.0.0-rc.0",

View File

@ -1,180 +0,0 @@
import React, {Component} from 'react';
import './App.scss';
import {declineSaimiar} from './saimiar_morphology.ts';
const backendUrl = 'https://kucinakobackend.ichigo.everydayimshuflin.com';
function makeRequest(queryString, jsonHandler) {
const effectiveUrl = `${backendUrl}/${queryString}`;
fetch(`${effectiveUrl}`)
.then((resp) => resp.json())
.then((json) => {
jsonHandler(json);
});
}
function renderConlangName(name) {
if (name === 'saimiar') {
return 'Saimiar';
}
if (name === 'elesu') {
return 'Elésu';
}
if (name === 'juteyuji') {
return 'Juteyuji';
}
if (name === 'tukvaysi') {
return 'Tukvaysi';
}
}
const Entry = (props) => {
const {conlang} = props;
if (conlang === 'saimiar') {
return <SaiEntry entry={ props.entry } />;
}
return <div>Unknown entry type for { conlang }</div>;
};
const SaiEntry = (props) => {
const {entry} = props;
const synCategory = entry.syn_category;
const isNominal = synCategory === 'nominal';
return (
<div className="searchResult" key={ entry.id }>
<b>{ entry.sai }</b> - { entry.eng }
<br />
<span className="synclass">
<i>{ entry.syn_category }</i>
{ entry.morph_type ? `\t\t${entry.morph_type}` : null }
<br/>
{ isNominal ? formatMorphology(entry) : null }
</span>
</div>
);
};
function formatMorphology(entry) {
const decl = declineSaimiar(entry);
if (!decl) {
return '';
}
return (<span style={ {fontSize: 'medium', color: '#6a3131'} } >
Abs: <i>{decl.abs}</i>, Erg: <i>{decl.erg}</i>,
Adp: <i>{decl.adp}</i>,
All: <i>{decl.all}</i>,
Loc: <i>{decl.loc}</i>,
Ell: <i>{decl.ell}</i>,
Inst: <i>{decl.inst}</i>,
Rel: <i>{decl.rel}</i>
</span>);
}
const Results = (props) => {
const content = () => {
const {conlang} = props;
const num = props.searchResults.length;
const renderedName = renderConlangName(conlang);
const searchType = (props.direction === 'toConlang') ? `English -> ${renderedName}` : `${renderedName} -> English`;
const header = (
<div className="searchResultHeader" key="header">
Searched for <b>{ props.searchTerm }</b>, { searchType }, found { num } result(s)
</div>);
const entries = props.searchResults.map(
(entry, _idx) => <Entry entry={ entry } key= { entry.id } conlang={ conlang } />,
);
return [header].concat(entries);
};
const results = props.searchResults;
return (
<div className="results">
{ results ? content() : 'No search' }
</div>
);
};
class App extends Component {
constructor(props) {
super(props);
this.input = React.createRef();
this.handleLangChange = this.handleLangChange.bind(this);
this.searchEng = this.searchEng.bind(this);
this.searchSaimiar = this.searchSaimiar.bind(this);
this.state = {
searchResults: null,
conlang: 'saimiar',
direction: null,
searchTerm: null,
};
}
searchSaimiar(_evt) {
const searchTerm = this.input.current.value;
const request = `saimiar?sai=like.*${searchTerm}*`;
if (searchTerm === '') {
this.setState({searchResults: null, searchTerm: null, direction: null});
} else {
makeRequest(request, (json) => {
this.setState({searchResults: json, searchTerm, direction: 'toEnglish'});
});
}
}
searchEng(_evt) {
const searchTerm = this.input.current.value;
const request = `saimiar?eng=like.*${searchTerm}*`;
if (searchTerm === '') {
this.setState({searchResults: null, searchTerm: null});
} else {
makeRequest(request, (json) => {
this.setState({searchResults: json, searchTerm, direction: 'toConlang'});
});
}
}
handleLangChange(evt) {
const conlang = evt.target.value;
this.setState({conlang});
}
render() {
return (
<main>
<div className="container">
<div className="search">
<h1>Kucinako</h1>
<div className="textInput">
<input className="textInput" type="text" ref={ this.input } />
</div>
<br/>
<select ref={ this.langSelection } onChange={ this.handleLangChange } defaultValue="saimiar">
<option value="saimiar">Saimiar</option>
<option value="elesu">Elesu</option>
<option value="tukvaysi">Tukvaysi</option>
<option value="juteyuji">Juteyuji</option>
</select>
<button onClick={ this.searchSaimiar } className="searchButton">Saimiar</button>
<button onClick={ this.searchEng } className="searchButton">English</button>
</div>
<Results
searchResults={ this.state.searchResults }
searchTerm= { this.state.searchTerm }
conlang={ this.state.conlang }
direction={ this.state.direction }
/>
</div>
</main>
);
}
}
export default App;

205
src/App.tsx Normal file
View File

@ -0,0 +1,205 @@
import React, {Component} from 'react';
import './App.scss';
import {declineSaimiar} from './saimiar_morphology';
const backendUrl = 'https://kucinakobackend.ichigo.everydayimshuflin.com';
enum Conlang {
Saimiar = 'sai',
Elesu = 'ele',
Tukvaysi = 'tuk',
Juteyuji = 'jut',
}
const renderConlang = (conlang: Conlang): string => {
if (conlang === Conlang.Saimiar) {
return 'Saimiar';
}
if (conlang === Conlang.Elesu) {
return 'Elésu';
}
if (conlang === Conlang.Juteyuji) {
return 'Juteyuji';
}
if (conlang === Conlang.Tukvaysi) {
return 'Tukvaysi';
}
};
function makeRequest(queryString, jsonHandler) {
const effectiveUrl = `${backendUrl}/${queryString}`;
fetch(`${effectiveUrl}`)
.then((resp) => resp.json())
.then((json) => {
jsonHandler(json);
});
}
const Entry = (props) => {
const {conlang} = props;
if (conlang === Conlang.Saimiar) {
return <SaiEntry entry={ props.entry } />;
}
return <div>Conlang { conlang } not yet supported</div>;
};
const SaiEntry = (props) => {
const {entry} = props;
const synCategory = entry.syn_category;
const isNominal = synCategory === 'nominal';
return (
<div className="searchResult" key={ entry.id }>
<b>{ entry.sai }</b> - { entry.eng }
<br />
<span className="synclass">
<i>{ entry.syn_category }</i>
{ entry.morph_type ? `\t\t${entry.morph_type}` : null }
<br/>
{ isNominal ? formatMorphology(entry) : null }
</span>
</div>
);
};
function formatMorphology(entry) {
const decl = declineSaimiar(entry);
if (!decl) {
return '';
}
return (<span style={ {fontSize: 'medium', color: '#6a3131'} } >
Abs: <i>{decl.abs}</i>, Erg: <i>{decl.erg}</i>,
Adp: <i>{decl.adp}</i>,
All: <i>{decl.all}</i>,
Loc: <i>{decl.loc}</i>,
Ell: <i>{decl.ell}</i>,
Inst: <i>{decl.inst}</i>,
Rel: <i>{decl.rel}</i>
</span>);
}
const Results = (props) => {
const content = () => {
const {conlang} = props;
const num = props.searchResults.length;
console.log(`Conlang is: ${conlang}`);
const renderedName = renderConlang(conlang);
const searchType = (props.direction === 'toConlang') ? `English -> ${renderedName}` : `${renderedName} -> English`;
const header = (
<div className="searchResultHeader" key="header">
Searched for <b>{ props.searchTerm }</b>, { searchType }, found { num } result(s)
</div>);
const entries = props.searchResults.map(
(entry, _idx) => <Entry entry={ entry } key= { entry.id } conlang={ conlang } />,
);
return [header].concat(entries);
};
const results = props.searchResults;
return (
<div className="results">
{ results ? content() : 'No search' }
</div>
);
};
interface App {
[x: string]: any;
}
class App extends Component {
constructor(props) {
super(props);
this.input = React.createRef();
this.handleLangChange = this.handleLangChange.bind(this);
this.searchEng = this.searchEng.bind(this);
this.searchSaimiar = this.searchSaimiar.bind(this);
this.searchConlang = this.searchConlang.bind(this);
this.state = {
searchResults: null,
conlang: Conlang.Saimiar,
direction: null,
searchTerm: null,
};
}
searchConlang(_evt) {
const searchTerm = this.input.current.value;
const {conlang} = this.state;
if (conlang === Conlang.Saimiar) {
this.searchSaimiar(searchTerm);
} else {
console.error(`Conlang ${conlang} not supported`);
}
}
searchSaimiar(searchTerm: string) {
const request = `saimiar?sai=like.*${searchTerm}*`;
if (searchTerm === '') {
this.setState({searchResults: null, searchTerm: null, direction: null});
} else {
makeRequest(request, (json) => {
this.setState({searchResults: json, searchTerm, direction: 'toEnglish'});
});
}
}
searchEng(_evt) {
const searchTerm = this.input.current.value;
const request = `saimiar?eng=like.*${searchTerm}*`;
if (searchTerm === '') {
this.setState({searchResults: null, searchTerm: null});
} else {
makeRequest(request, (json) => {
this.setState({searchResults: json, searchTerm, direction: 'toConlang'});
});
}
}
handleLangChange(evt) {
const conlang: Conlang = evt.target.value as Conlang;
console.log('Conlang in handlelangchange', conlang);
this.setState({conlang});
}
render() {
const conlangs = [Conlang.Saimiar, Conlang.Elesu, Conlang.Tukvaysi, Conlang.Juteyuji];
const langSelectDropdown = (
<select ref={ this.langSelection } onChange={ this.handleLangChange } defaultValue={Conlang.Saimiar}>
{conlangs.map((conlang) => <option value={conlang} key={conlang}>{renderConlang(conlang)}</option>)}
</select>
);
return (
<main>
<div className="container">
<div className="search">
<h1>Kucinako</h1>
<div className="textInput">
<input className="textInput" type="text" ref={ this.input } />
</div>
<br/>
{ langSelectDropdown }
<button onClick={ this.searchConlang } className="searchButton">{renderConlang(this.state.conlang)}</button>
<button onClick={ this.searchEng } className="searchButton">English</button>
</div>
<Results
searchResults={ this.state.searchResults }
searchTerm= { this.state.searchTerm }
conlang={ this.state.conlang }
direction={ this.state.direction }
/>
</div>
</main>
);
}
}
export default App;

View File

@ -1,99 +1,110 @@
const rootEndingPair = (str) => ({root: str.slice(0, -1), ending: str.slice(-1)}); const rootEndingPair = (str) => ({root: str.slice(0, -1), ending: str.slice(-1)});
function declineSaimiar(entry) { type SaimiarDeclension = {
const split = entry.sai.split(' '); abs: string;
const sai = split.at(-1); erg: string;
const morph = entry.morph_type; adp: string;
all: string;
loc: string;
ell: string;
inst: string;
rel: string;
};
if (morph === '-V') { function declineSaimiar(entry): SaimiarDeclension {
return vowelDeclension(sai); const split = entry.sai.split(' ');
} const sai = split.at(-1);
const morph = entry.morph_type;
if (morph === '-a/i') { if (morph === '-V') {
return aiDeclension(sai); return vowelDeclension(sai);
} }
if (morph === 'e-') { if (morph === '-a/i') {
return initalDeclension(sai); return aiDeclension(sai);
} }
if (morph === '-C') { if (morph === 'e-') {
return consonantDeclension(sai); return initalDeclension(sai);
} }
console.warn(`Can't decline entry '${entry.sai}'`); if (morph === '-C') {
console.log(entry); return consonantDeclension(sai);
return null; }
console.warn(`Can't decline entry '${entry.sai}'`);
console.log(entry);
return null;
} }
function vowelDeclension(sai: string) { function vowelDeclension(sai: string): SaimiarDeclension {
const {root, ending} = rootEndingPair(sai); const {root, ending} = rootEndingPair(sai);
const adpEnding = ending === 'u' ? 'ys' : `${ending}s`; const adpEnding = ending === 'u' ? 'ys' : `${ending}s`;
return { return {
abs: `${root}${ending}`, abs: `${root}${ending}`,
erg: `${root}${ending}na`, erg: `${root}${ending}na`,
adp: `${root}${adpEnding}`, adp: `${root}${adpEnding}`,
all: `so${root}${adpEnding}`, all: `so${root}${adpEnding}`,
loc: `${root}${ending}xa`, loc: `${root}${ending}xa`,
ell: `tlê${root}${adpEnding}`, ell: `tlê${root}${adpEnding}`,
inst: `${root}${ending}ŕa`, inst: `${root}${ending}ŕa`,
rel: `${root}${ending}źi`, rel: `${root}${ending}źi`,
}; };
} }
function aiDeclension(sai: string) { function aiDeclension(sai: string): SaimiarDeclension {
const {root, ending} = rootEndingPair(sai); const {root, ending} = rootEndingPair(sai);
return { return {
abs: `${root}${ending}`, abs: `${root}${ending}`,
erg: `${root}iad`, erg: `${root}iad`,
adp: `${root}i`, adp: `${root}i`,
all: `so${root}i`, all: `so${root}i`,
loc: `${root}iath`, loc: `${root}iath`,
ell: `tlê${root}i`, ell: `tlê${root}i`,
inst: `${root}iar`, inst: `${root}iar`,
rel: `${root}iai`, rel: `${root}iai`,
}; };
} }
function consonantDeclension(sai: string) { function consonantDeclension(sai: string): SaimiarDeclension {
const split = rootEndingPair(sai); const split = rootEndingPair(sai);
const root = split.ending === 'ø' ? split.root : sai; const root = split.ending === 'ø' ? split.root : sai;
const absFinal = split.ending === 'ø' ? 'ø' : ''; const absFinal = split.ending === 'ø' ? 'ø' : '';
return { return {
abs: `${root}${absFinal}`, abs: `${root}${absFinal}`,
erg: `${root}ad`, erg: `${root}ad`,
adp: `${root}e`, adp: `${root}e`,
all: `so${root}i`, all: `so${root}i`,
loc: `${root}ak`, loc: `${root}ak`,
ell: `tlê${root}i`, ell: `tlê${root}i`,
inst: `${root}ar`, inst: `${root}ar`,
rel: `${root}ai`, rel: `${root}ai`,
}; };
} }
const vowels = ['a', 'e', 'ê', 'i', 'o', 'ô', 'u', 'y']; const vowels = ['a', 'e', 'ê', 'i', 'o', 'ô', 'u', 'y'];
function initalDeclension(sai: string) { function initalDeclension(sai: string): SaimiarDeclension {
const initial = sai.slice(0, 1); const initial = sai.slice(0, 1);
const root = sai.slice(1); const root = sai.slice(1);
const finalRootSound = root.slice(-1); const finalRootSound = root.slice(-1);
const finalVowel = vowels.includes(finalRootSound); const finalVowel = vowels.includes(finalRootSound);
const instEnding = finalVowel ? 'ŕø' : 'ar'; const instEnding = finalVowel ? 'ŕø' : 'ar';
const relEnding = finalVowel ? 'źi' : 'ai'; const relEnding = finalVowel ? 'źi' : 'ai';
return { return {
abs: `${initial}${root}`, abs: `${initial}${root}`,
erg: `da${root}`, erg: `da${root}`,
adp: `i${root}`, adp: `i${root}`,
all: `so${root}`, all: `so${root}`,
loc: `xa${root}`, loc: `xa${root}`,
ell: `tlê${root}`, ell: `tlê${root}`,
inst: `i${root}${instEnding}`, inst: `i${root}${instEnding}`,
rel: `${initial}${root}${relEnding}`, rel: `${initial}${root}${relEnding}`,
}; };
} }
export {declineSaimiar}; export {declineSaimiar, SaimiarDeclension};