Adding complete model and detail page

master
Arthur VALIN 2 years ago
parent 6d8561da75
commit f1a4952a2a

Binary file not shown.

@ -1,7 +1,13 @@
{
"ExpandedNodes": [
""
"",
"\\assets",
"\\components",
"\\model",
"\\navigation",
"\\pages",
"\\redux"
],
"SelectedNode": "\\C:\\Users\\Siph9\\Source\\Repos\\LEARNIHON",
"SelectedNode": "\\components\\KanjiListCell.tsx",
"PreviewInSolutionExplorer": false
}

Binary file not shown.

@ -23,7 +23,7 @@ export const animatedStyles = {
{
translateY: animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -Dimensions.get('window').height / 1.9]
outputRange: [0, -Dimensions.get('window').height / 2]
}),
},
],

@ -0,0 +1,67 @@
import React from 'react';
import { FlatList, StyleSheet, Text, useColorScheme, View } from 'react-native';
interface detailExamplesProps {
data: { japanese: string, english: string }[]
}
const DetailExamples = (props: detailExamplesProps) => {
const detailExamplesStyle = useColorScheme() == 'light' ? detailExamplesStyle_light : detailExamplesStyle_dark;
return (
<View style={detailExamplesStyle.container}>
<FlatList data={props.data} keyExtractor={item => item.japanese + item.english}
renderItem={
({ item }) =>
<View style={detailExamplesStyle.cellContainer} onStartShouldSetResponder={() => true}>
<Text style={detailExamplesStyle.textJapanese}>{item.japanese}</Text>
<Text style={detailExamplesStyle.textEnglish}>{item.english}</Text>
</View>
}>
</FlatList>
</View>
);
};
const detailExamplesStyle_light = StyleSheet.create({
container: {
width: '100%',
paddingBottom: 50,
},
cellContainer: {
flex: 1,
flexDirection: "row"
},
textJapanese: {
width: "50%"
},
textEnglish: {
textAlign: "right",
width: "50%"
}
})
const detailExamplesStyle_dark = StyleSheet.create({
container: {
width: '100%',
paddingBottom: 50,
},
cellContainer: {
flex: 1,
flexDirection: "row"
},
textJapanese: {
width: "50%",
color: "white"
},
textEnglish: {
textAlign: "right",
width: "50%",
color: "white"
}
})
export default DetailExamples;

@ -0,0 +1,73 @@
import React from 'react';
import { StyleSheet, Text, useColorScheme, View } from 'react-native';
import { SvgXml } from 'react-native-svg';
import { useDispatch, useSelector } from 'react-redux';
interface detailRadicalProps {
character: string,
icon: string
}
const DetailRadical = (props: detailRadicalProps) => {
const detailRadicalStyle = useColorScheme() == 'light' ? detailRadicalStyle_light : detailRadicalStyle_dark;
return (
<View style={detailRadicalStyle.container}>
<Text style={detailRadicalStyle.radicalText}>{props.character}</Text>
<SvgXml
xml={props.icon
.replace(/fill="#[0-9a-f]{6}"/g, `fill=${detailRadicalStyle.svg.color}`)}
width="30"
height="30"
opacity={0.5}
style={detailRadicalStyle.radicalIcon}
/>
</View>
);
};
const detailRadicalStyle_light = StyleSheet.create({
container: {
height: 30,
width: 30,
},
svg: {
color: "#FF5C5C"
},
radicalIcon: {
position: "absolute"
},
radicalText: {
fontWeight: "bold",
textAlign: "center",
width: 30,
height: 30,
fontSize: 25
},
})
const detailRadicalStyle_dark = StyleSheet.create({
container: {
height: 30,
width: 30,
},
svg: {
color: "#FF5C5C"
},
radicalIcon: {
position: "absolute"
},
radicalText: {
fontWeight: "bold",
textAlign: "center",
width: 30,
height: 30,
fontSize: 25,
color: "white"
},
})
export default DetailRadical;

@ -26,13 +26,13 @@ const KanjiCard = (props: KanjiProps) => {
const fetchData = async () => {
await fetch(`https://kanjialive-api.p.rapidapi.com/api/public/kanji/${props.kanji}`, options)
.then(async response => {
.then(async response => {
const data = await response.json()
setData(data);
const xml = await (await fetch(data.kanji.video.poster)).text();
setImgXml(xml);
})
.catch(err => console.log(err));
.catch(err => console.log(err));
}
useEffect(() => {
@ -44,7 +44,7 @@ const KanjiCard = (props: KanjiProps) => {
return (
<View style={kanjiCardStyle.container}>
<View style={kanjiCardStyle.container}>
<Text style={kanjiCardStyle.text}> {loading ? <Text>Loading...</Text> : <Text>{res.kanji.onyomi.katakana}</Text>}</Text>
{!loading && (
@ -54,11 +54,11 @@ const KanjiCard = (props: KanjiProps) => {
width="200"
height="200"
/>
)}
<Text style={kanjiCardStyle.text}> {loading ? <Text/> : <Text>{res.kanji.meaning.english}</Text>}</Text>
<KanjiAnswerField/>
<Button title="OK" color="#FF5C5C" />
</View>
)}
<Text style={kanjiCardStyle.text}> {loading ? <Text /> : <Text>{res.kanji.meaning.english}</Text>}</Text>
<KanjiAnswerField />
<Button title="OK" color="#FF5C5C" />
</View>
);
};

@ -34,7 +34,7 @@ const KanjiList = () => {
renderSectionHeader={({ section }) => (
<Text style={kanjiListStyle.sectionHeader}>{section.title}</Text>
)}
keyExtractor={item => `basicListEntry-${item}`}
keyExtractor={item => `basicListEntry-${item.character}`}
>
</SectionList>
</View>

@ -14,7 +14,7 @@ const KanjiListCell = (props: kanjiListCellProps) => {
const navigator = useNavigation();
return (
<TouchableOpacity onPress={() => navigator.push("Detail", {"kanji": props.kanji})} style={cellStyle.item}>
<TouchableOpacity onPress={() => navigator.navigate("Detail", {"kanji": props.kanji})} style={cellStyle.item}>
<Text style={cellStyle.kanji}>{props.kanji.character}</Text>
<Text style={cellStyle.text}>{props.kanji.meaning}</Text>
</TouchableOpacity>

@ -32,7 +32,7 @@ const KanjiPlaygroundList = (props: kanjiPlaygroundListProps) => {
</TouchableOpacity>
)
}
keyExtractor={item => `basicListEntry-${item}`}>
keyExtractor={item => `basicListEntry-${item.character}`}>
</FlatList>
</View>
);

@ -2,11 +2,34 @@ export class Kanji {
private _character: string;
private _meaning: string;
private _image: string;
private _animation: string;
private _strokes: number;
private _onyomi: string;
private _kunyomi: string;
private _radical: {
character: string,
position: string
}
private _examples: {
japanese: string,
english: string
}[]
constructor(character: string, meaning: string, image: string) {
constructor(character: string, meaning: string, image: string,
animation: string, strokes: number, onyomi: string,
kunyomi: string,
radical: { character: string, position: string },
examples: { japanese: string, english: string }[]
) {
this._character = character;
this._meaning = meaning;
this._image = image;
this._animation = animation;
this._strokes = strokes;
this._onyomi = onyomi;
this._kunyomi = kunyomi;
this._radical = radical;
this._examples = examples;
}
get character(): string {
@ -21,11 +44,41 @@ export class Kanji {
return this._image;
}
get animation(): string {
return this._animation;
}
get strokes(): number {
return this._strokes;
}
get onyomi(): string {
return this._onyomi;
}
get kunyomi(): string {
return this._kunyomi;
}
get radical(): { character: string, position: string } {
return this._radical;
}
get examples(): { japanese: string, english: string }[] {
return this._examples;
}
toObject() {
return {
character: this._character,
meaning: this._meaning,
image: this._image
image: this._image,
animation: this._animation,
strokes: this._strokes,
onyomi: this._onyomi,
kunyomi: this.kunyomi,
radical: this._radical,
examples: this._examples
};
}
@ -34,14 +87,31 @@ export class Kanji {
export class KanjiMapper {
static ApiJsonToKanji(json: any): Kanji {
console.log(typeof json)
return new Kanji(json.kanji.character, json.kanji.meaning.english, json.kanji.video.poster);
var radical: { character: string, position: string } = {
character: json.radical.character,
position: json.radical.position.icon
};
var examples: { japanese: string, english: string }[] = [];
json.examples.forEach(
(entry) => {
examples.push({
japanese: entry.japanese,
english: entry.meaning.english
})
}
)
return new Kanji(json.kanji.character, json.kanji.meaning.english, json.kanji.video.poster,
json.kanji.video.mp4, json.kanji.strokes.count, json.kanji.onyomi.katakana, json.kanji.kunyomi.hiragana,
radical, examples);
}
// @ts-ignore
static SerializedObjectToKanji(obj): Kanji | null {
if (!obj) return null;
return new Kanji(obj.character, obj.meaning, obj.image);
return new Kanji(obj.character, obj.meaning, obj.image, obj.animation, obj.strokes, obj.onyomi, obj.kunyomi, obj.radical, obj.examples);
}
}

@ -9,10 +9,10 @@ export default function KanjiStackNavigator() {
const Stack = createStackNavigator();
return (
<Stack.Navigator initialRouteName="List"
<Stack.Navigator initialRouteName="KanjiList"
screenOptions={stackOptions}
>
<Stack.Screen name="List" component={List} />
<Stack.Screen name="KanjiList" component={List} />
<Stack.Screen name="Detail" component={Detail}/>
</Stack.Navigator>
)
@ -20,6 +20,6 @@ export default function KanjiStackNavigator() {
const stackOptions: StackNavigationOptions = {
headerShown: false,
headerShown: false,
presentation: "modal"
};
};

101
package-lock.json generated

@ -26,6 +26,7 @@
"react-native-feather": "^1.1.2",
"react-native-gesture-handler": "^2.8.0",
"react-native-svg": "13.4.0",
"react-native-video": "^5.2.1",
"react-native-web": "~0.18.9",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
@ -36,6 +37,7 @@
"@types/react": "~18.0.14",
"@types/react-native": "~0.70.6",
"@types/react-native-canvas": "^0.1.9",
"@types/react-native-video": "^5.0.14",
"typescript": "^4.6.3"
}
},
@ -4706,6 +4708,16 @@
"@types/react-native": "*"
}
},
"node_modules/@types/react-native-video": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/@types/react-native-video/-/react-native-video-5.0.14.tgz",
"integrity": "sha512-KdcyY4HY/Q1l6f5qQA337BNVN+GsdZy836j9CXbWHZ008VVNzSlnJypJQPsnUgI0EPBw/uG/lyJk6cg9Jj1syg==",
"dev": true,
"dependencies": {
"@types/react": "*",
"@types/react-native": "*"
}
},
"node_modules/@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -7706,6 +7718,16 @@
"node": ">= 0.6"
}
},
"node_modules/deprecated-react-native-prop-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz",
"integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==",
"dependencies": {
"@react-native/normalize-color": "*",
"invariant": "*",
"prop-types": "*"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@ -7871,6 +7893,11 @@
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
"license": "ISC"
},
"node_modules/eme-encryption-scheme-polyfill": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.1.1.tgz",
"integrity": "sha512-njD17wcUrbqCj0ArpLu5zWXtaiupHb/2fIUQGdInf83GlI+Q6mmqaPGLdrke4savKAu15J/z1Tg/ivDgl14g0g=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -11745,6 +11772,11 @@
"integrity": "sha512-VHdsIWwXNO1l+fqwNdYZ/dDGnaN60RLuOIgMnwL+2kE3woPvxpOpeusjfaMZbTFaQFwGnTTzFbVHqQrDqf1FnQ==",
"license": "MIT"
},
"node_modules/keymirror": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
"integrity": "sha512-vIkZAFWoDijgQT/Nvl2AHCMmnegN2ehgTPYuyy2hWQkQSntI0S7ESYqdLkoSe1HyEBFHHkCgSIvVdSEiWwKvCg=="
},
"node_modules/keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@ -14969,6 +15001,17 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/react-native-video": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-5.2.1.tgz",
"integrity": "sha512-aJlr9MeTuQ0LpZ4n+EC9RvhoKeiPbLtI2Rxy8u7zo/wzGevbRpWHSBj9xZ5YDBXnAVXzuqyNIkGhdw7bfdIBZw==",
"dependencies": {
"deprecated-react-native-prop-types": "^2.2.0",
"keymirror": "^0.1.1",
"prop-types": "^15.7.2",
"shaka-player": "^2.5.9"
}
},
"node_modules/react-native-web": {
"version": "0.18.11",
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.11.tgz",
@ -15905,6 +15948,15 @@
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
"license": "ISC"
},
"node_modules/shaka-player": {
"version": "2.5.23",
"resolved": "https://registry.npmjs.org/shaka-player/-/shaka-player-2.5.23.tgz",
"integrity": "sha512-3MC9k0OXJGw8AZ4n/ZNCZS2yDxx+3as5KgH6Tx4Q5TRboTBBCu6dYPI5vp1DxKeyU12MBN1Zcbs7AKzXv2EnCg==",
"deprecated": "Shaka Player < v3.2 is no longer supported.",
"dependencies": {
"eme-encryption-scheme-polyfill": "^2.0.1"
}
},
"node_modules/shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@ -22192,6 +22244,16 @@
"@types/react-native": "*"
}
},
"@types/react-native-video": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/@types/react-native-video/-/react-native-video-5.0.14.tgz",
"integrity": "sha512-KdcyY4HY/Q1l6f5qQA337BNVN+GsdZy836j9CXbWHZ008VVNzSlnJypJQPsnUgI0EPBw/uG/lyJk6cg9Jj1syg==",
"dev": true,
"requires": {
"@types/react": "*",
"@types/react-native": "*"
}
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -24272,6 +24334,16 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
},
"deprecated-react-native-prop-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz",
"integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==",
"requires": {
"@react-native/normalize-color": "*",
"invariant": "*",
"prop-types": "*"
}
},
"destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@ -24393,6 +24465,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
},
"eme-encryption-scheme-polyfill": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.1.1.tgz",
"integrity": "sha512-njD17wcUrbqCj0ArpLu5zWXtaiupHb/2fIUQGdInf83GlI+Q6mmqaPGLdrke4savKAu15J/z1Tg/ivDgl14g0g=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -27196,6 +27273,11 @@
"resolved": "https://registry.npmjs.org/keychain/-/keychain-1.3.0.tgz",
"integrity": "sha512-VHdsIWwXNO1l+fqwNdYZ/dDGnaN60RLuOIgMnwL+2kE3woPvxpOpeusjfaMZbTFaQFwGnTTzFbVHqQrDqf1FnQ=="
},
"keymirror": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
"integrity": "sha512-vIkZAFWoDijgQT/Nvl2AHCMmnegN2ehgTPYuyy2hWQkQSntI0S7ESYqdLkoSe1HyEBFHHkCgSIvVdSEiWwKvCg=="
},
"keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@ -29423,6 +29505,17 @@
}
}
},
"react-native-video": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-5.2.1.tgz",
"integrity": "sha512-aJlr9MeTuQ0LpZ4n+EC9RvhoKeiPbLtI2Rxy8u7zo/wzGevbRpWHSBj9xZ5YDBXnAVXzuqyNIkGhdw7bfdIBZw==",
"requires": {
"deprecated-react-native-prop-types": "^2.2.0",
"keymirror": "^0.1.1",
"prop-types": "^15.7.2",
"shaka-player": "^2.5.9"
}
},
"react-native-web": {
"version": "0.18.11",
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.18.11.tgz",
@ -30068,6 +30161,14 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"shaka-player": {
"version": "2.5.23",
"resolved": "https://registry.npmjs.org/shaka-player/-/shaka-player-2.5.23.tgz",
"integrity": "sha512-3MC9k0OXJGw8AZ4n/ZNCZS2yDxx+3as5KgH6Tx4Q5TRboTBBCu6dYPI5vp1DxKeyU12MBN1Zcbs7AKzXv2EnCg==",
"requires": {
"eme-encryption-scheme-polyfill": "^2.0.1"
}
},
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",

@ -27,6 +27,7 @@
"react-native-feather": "^1.1.2",
"react-native-gesture-handler": "^2.8.0",
"react-native-svg": "13.4.0",
"react-native-video": "^5.2.1",
"react-native-web": "~0.18.9",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
@ -37,6 +38,7 @@
"@types/react": "~18.0.14",
"@types/react-native": "~0.70.6",
"@types/react-native-canvas": "^0.1.9",
"@types/react-native-video": "^5.0.14",
"typescript": "^4.6.3"
},
"private": true

@ -1,28 +1,287 @@
import React from 'react';
import { Text, View, StyleSheet, useColorScheme } from 'react-native';
import React, { useEffect, useState } from 'react';
import { Text, View, StyleSheet, useColorScheme, FlatList, ScrollView } from 'react-native';
import KanjiListCell from '../components/KanjiListCell';
import { Kanji } from '../model/kanji';
import { Kanji, KanjiMapper } from '../model/kanji';
import Video from 'react-native-video';
import { SvgXml } from 'react-native-svg';
import KanjiPlaygroundList from '../components/KanjiPlaygroundList';
import DetailExamples from '../components/DetailExamples';
import DetailRadical from '../components/DetailRadical';
const Detail = ({route}) => {
const kanji = route.params.kanji;
const kanji_temp = route.params.kanji;
const detailStyle = useColorScheme() == 'light' ? detailStyle_light : detailStyle_dark;
const [imgXml, setImgXml] = useState('<svg></svg>');
const [iconXml, setIconXml] = useState('<svg></svg>');
const fetchXml = async () => {
const xml = await (await fetch(kanji.image)).text();
const iconxml = await (await fetch(kanji.radical.position)).text();
setImgXml(xml);
setIconXml(iconxml);
}
useEffect(() => {
fetchXml();
}, []);
const kanji = KanjiMapper.ApiJsonToKanji({
"kanji": {
"character": "所",
"meaning": {
"english": "place"
},
"strokes": {
"count": 8,
"timings": [
0,
1.465,
2.598,
3.465,
4.332,
5.332,
6.265,
7.198,
8.533333
],
"images": [
"https://media.kanjialive.com/kanji_strokes/(ba)sho_1.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_2.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_3.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_4.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_5.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_6.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_7.svg",
"https://media.kanjialive.com/kanji_strokes/(ba)sho_8.svg"
]
},
"onyomi": {
"romaji": "sho",
"katakana": "ショ"
},
"kunyomi": {
"romaji": "tokoro",
"hiragana": "ところ"
},
"video": {
"poster": "https://media.kanjialive.com/kanji_strokes/(ba)sho_8.svg",
"mp4": "https://media.kanjialive.com/kanji_animations/kanji_mp4/(ba)sho_00.mp4",
"webm": "https://media.kanjialive.com/kanji_animations/kanji_webm/(ba)sho_00.webm"
}
},
"radical": {
"character": "⼧",
"strokes": 4,
"image": "https://media.kanjialive.com/radical_character/todare.svg",
"position": {
"hiragana": "たれ",
"romaji": "tare",
"icon": "https://media.kanjialive.com/rad_positions/tare.svg"
},
"name": {
"hiragana": "とだれ",
"romaji": "todare"
},
"meaning": {
"english": "door"
},
"animation": [
"https://media.kanjialive.com/rad_frames/todare0.svg",
"https://media.kanjialive.com/rad_frames/todare1.svg",
"https://media.kanjialive.com/rad_frames/todare2.svg"
]
},
"references": {
"grade": 3,
"kodansha": "568",
"classic_nelson": "1821"
},
"examples": [
{
"japanese": "場所(ばしょ)",
"meaning": {
"english": "place"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_a.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_a.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_a.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_a.mp3"
}
},
{
"japanese": "住所(じゅうしょ)",
"meaning": {
"english": "address"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_b.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_b.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_b.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_b.mp3"
}
},
{
"japanese": "近所(きんじょ)",
"meaning": {
"english": "neighborhood"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_c.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_c.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_c.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_c.mp3"
}
},
{
"japanese": "役所(やくしょ)",
"meaning": {
"english": "public office"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_d.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_d.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_d.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_d.mp3"
}
},
{
"japanese": "名所(めいしょ)",
"meaning": {
"english": "famous place"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_e.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_e.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_e.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_e.mp3"
}
},
{
"japanese": "研究所(けんきゅうじょ)",
"meaning": {
"english": "research institute"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_f.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_f.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_f.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_f.mp3"
}
},
{
"japanese": "停留所(ていりゅうじょ)",
"meaning": {
"english": "bus stop"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_g.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_g.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_g.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_g.mp3"
}
},
{
"japanese": "所有する(しょゆうする)",
"meaning": {
"english": "possess"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_h.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_h.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_h.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_h.mp3"
}
},
{
"japanese": "所(ところ)",
"meaning": {
"english": "place"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_i.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_i.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_i.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_i.mp3"
}
},
{
"japanese": "台所(だいどころ)",
"meaning": {
"english": "kitchen"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_j.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_j.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_j.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_j.mp3"
}
},
{
"japanese": "居所(いどころ)",
"meaning": {
"english": "whereabouts"
},
"audio": {
"opus": "https://media.kanjialive.com/examples_audio/audio-opus/(ba)sho_06_k.opus",
"aac": "https://media.kanjialive.com/examples_audio/audio-aac/(ba)sho_06_k.aac",
"ogg": "https://media.kanjialive.com/examples_audio/audio-ogg/(ba)sho_06_k.ogg",
"mp3": "https://media.kanjialive.com/examples_audio/audio-mp3/(ba)sho_06_k.mp3"
}
}
]
})
return (
<View style={detailStyle.container}>
<KanjiListCell kanji={kanji}/>
<Text style={detailStyle.text}>{kanji.onyomi}</Text>
<Text style={detailStyle.text}>{kanji.kunyomi}</Text>
<SvgXml
xml={imgXml
.replace(/fill="#[0-9a-f]{6}"/g, `fill=${detailStyle.svg.color}`)}
width="100"
height="100" />
<Text style={detailStyle.tinyText}>{kanji.strokes + " strokes"}</Text>
<Text style={detailStyle.meaningText}>{kanji.meaning}</Text>
<Text style={detailStyle.title}>Radical</Text>
<DetailRadical character={kanji.radical.character} icon={iconXml}/>
<Text style={detailStyle.title}>Examples</Text>
<DetailExamples data={kanji.examples} />
</View>
);
};
const detailStyle_light = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: "#e4e4e4"
backgroundColor: "#e4e4e4",
},
svg: {
color: "black"
},
title: {
fontWeight: "bold",
fontSize: 20,
},
text: {
color: "black"
},
tinyText: {
fontSize: 10,
color: "black"
},
meaningText: {
fontSize: 50,
color: "#FF5C5C",
fontWeight: "900",
}
})
@ -32,8 +291,29 @@ const detailStyle_dark = StyleSheet.create({
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#3c3c3c',
backgroundColor: "#1c1c1c",
},
svg: {
color: "white"
},
title: {
fontWeight: "bold",
fontSize: 20,
color: "white"
},
text: {
color: "white"
},
tinyText: {
fontSize: 10,
color: "white"
},
meaningText: {
fontSize: 50,
color: "#FF5C5C",
fontWeight: "900",
}
});
export default Detail;
Loading…
Cancel
Save