フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-

7c3b3366947123ba6772698b09edf4e2?s=47 subroh_0508
August 24, 2019

フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-

Kotlin Fest 2019 - Hall C 16:45〜
「フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-」のセッション資料です

7c3b3366947123ba6772698b09edf4e2?s=128

subroh_0508

August 24, 2019
Tweet

Transcript

  1. 2.

    "CPVU.F w ʹ͜͠Γ͞ͿΖʙ ຊ໊ࡔ্੖৴  w ౦ژ౎ɾҏ౾େౡग़਎ w גࣜձࣾ#FBS5BJM w

    "OESPJEΤϯδχΞ ,PUMJO+BWB ˑ w 8FCΤϯδχΞ 3BJMT3FBDU  w झຯͰ,PUMJO+4
  2. 7.

    ,PUMJO+4ͷ֓ཁ +4ͰͷϑϩϯτΤϯυ։ൃʹඞཁͳϞϊ w +4ίʔυɾελΠϧγʔτ $44 ɾ)5.-ϑΝΠϧ w ύοέʔδ؅ཧπʔϧ OQN 

    w Ϟδϡʔϧόϯυϥʔ 8FCQBDL  w ෳ਺ͷ+4ϞδϡʔϧΛͭʹ·ͱΊɺ)5.-Ͱѻ͍΍͍͢ܗʹ͢Δ w Ϟδϡʔϧؒͷґଘؔ܎ղܾɺNJOJGZɺίʔυɾϦιʔεͷม׵ ྫ54ˠ+4ɺ4BTTˠ$44
  3. 8.

    ,PUMJO+4ͷ֓ཁ w +4ίʔυʹม׵Մೳͳ,PUMJOඪ४ϥΠϒϥϦ w ,PUMJOˠ+4΁ͷม׵Λ࣮ߦ͢Δ(SBEMFϓϥάΠϯ w OQN8FCQBDLͷίϚϯυΛ࣮ߦ͢Δ(SBEMFϓϥάΠϯ ʴIUNMDTTͷ%4-ϥΠϒϥϦɺ֤छ+4 ϥούʔ ϥΠϒϥϦ

    ,PUMJO+4͕ఏڙ͢ΔϞϊ ,PUMJO͔Β+4΁ͷม׵ɺOQNʹΑΔόʔδϣϯ؅ཧɺ8FCQBDLʹΑΔόϯυϧ શͯΛ,PUMJOͷੈքͰ׬݁ͤ͞ɺϑϩϯτΤϯυ։ൃΛ࣮ݱ͢Δʂ
  4. 9.

    ,PUMJO+4ͷྺ࢙ ,PUMJOϦϦʔε w ,PUMJO+4ਖ਼ࣜϦϦʔεɻʮLPUMJOKTʯͰ,PUMJOˠ+4ม׵͕Ͱ͖ΔΑ͏ʹ LPUMJOGSPOUFOEQMVHJOϦϦʔε w (SBEMF͔ΒͷOQNɺ8FCQBDLͷίϚϯυ࣮ߦΛαϙʔτ͢ΔϓϥάΠϯ ,PUMJOϦϦʔε w ,PUMJO.11ొ৔ɻ+7.΍/BUJWFͷϞδϡʔϧͱϩδοΫΛڞ௨Խ

    ,PUMJOϦϦʔε w LPUMJOKTˠDPNKFUCSBJOTLPUMJOKTʹվ໊ɺLPUMJOGSPOUFOEQMVHJOͷҰ෦ػೳ͕౷߹ w ʮLPUMJOGSPOUFOEQMVHJO͸ࠓޙഇࢭ༧ఆʯͱࠂ஌ ,PUMJOϦϦʔε w 8FCQBDLͷઃఆ߲໨௥Ճɺόάमਖ਼
  5. 10.

    ,PUMJOϦϦʔε w ,PUMJO+4ਖ਼ࣜϦϦʔεɻʮLPUMJOKTʯͰ,PUMJOˠ+4ม׵͕Ͱ͖ΔΑ͏ʹ LPUMJOGSPOUFOEQMVHJOϦϦʔε w (SBEMF͔ΒͷOQNɺ8FCQBDLͷίϚϯυ࣮ߦΛαϙʔτ͢ΔϓϥάΠϯ ,PUMJOϦϦʔε w ,PUMJO.11ొ৔ɻ+7.΍/BUJWFͷϞδϡʔϧͱϩδοΫΛڞ௨Խ ,PUMJOϦϦʔε

    w LPUMJOKTˠDPNKFUCSBJOTLPUMJOKTʹվ໊ɺLPUMJOGSPOUFOEQMVHJOͷҰ෦ػೳ͕౷߹ w ʮLPUMJOGSPOUFOEQMVHJO͸ࠓޙഇࢭ༧ఆʯͱࠂ஌ ,PUMJOϦϦʔε w 8FCQBDLͷઃఆ߲໨௥Ճɺόάमਖ਼ ,PUMJO+4ͷྺ࢙ ,PUMJO.11ͷҰ෦ͱͯ͠ ֤छϓϥάΠϯ͕·ͱ·ΔྲྀΕ
  6. 11.

    ,PUMJOϦϦʔε w ,PUMJO+4ਖ਼ࣜϦϦʔεɻʮLPUMJOKTʯͰ,PUMJOˠ+4ม׵͕Ͱ͖ΔΑ͏ʹ LPUMJOGSPOUFOEQMVHJOϦϦʔε w (SBEMF͔ΒͷOQNɺ8FCQBDLͷίϚϯυ࣮ߦΛαϙʔτ͢ΔϓϥάΠϯ ,PUMJOϦϦʔε w ,PUMJO.11ొ৔ɻ+7.΍/BUJWFͷϞδϡʔϧͱϩδοΫΛڞ௨Խ ,PUMJOϦϦʔε

    w LPUMJOKTˠDPNKFUCSBJOTLPUMJOKTʹվ໊ɺLPUMJOGSPOUFOEQMVHJOͷҰ෦ػೳ͕౷߹ w ʮLPUMJOGSPOUFOEQMVHJO͸ࠓޙഇࢭ༧ఆʯͱࠂ஌ ,PUMJOϦϦʔε w 8FCQBDLͷઃఆ߲໨௥Ճɺόάमਖ਼ ,PUMJO+4ͷྺ࢙ ͜ͷ࣌ظͷ৘ใ͕݁ߏଟ͍ ࠓޙݹ͘ͳΔͷͰ஫ҙʂ
  7. 13.

    ࣄྫ  ϙʔτϑΥϦΦαΠτ (JUIVCTVCSPIQPSUGPMJP ࢖༻ϥΠϒϥϦɾ"1* w ,PUMJO+4 W  w

    3FBDUKT w .BUFSJBM6* ˠ'JSFCBTF)PTUJOHͰϗεςΟϯά ɹ-JOLTVCSPINF w (PPHMF.BQ"1* w SFBDUTXJQFBCMFWJFXT
  8. 15.
  9. 16.

    ࣄྫ  খௗ͞Μͷࣗशࣨ (JUIVCTVCSPIPUPOBTIJLPUMJOEFW ࢓༷ϥΠϒϥϦ w ,PUMJO+4 W  w

    3FBDUKT w .BUFSJBM6* ˠϧʔςΟϯάϥΠϒϥϦΛ࢖ͬͨ41" ɹ খௗ͞Μ w 3FBDI3PVUFS w LPUMJOQMBZHSPVOE
  10. 22.

     ,PUMJO+4 OQN w ϞδϡʔϧͷΠϯετʔϧɾϓϩδΣΫτੜ੒ɾ࣮ߦ # インストール $ npm install

    -g create-react-kotlin-app # プロジェクト生成 $ create-react-kotlin-app my-app # 実行 $ cd my-app/ $ npm start
  11. 26.

    ,PUMJO+4ͷϥούʔϞδϡʔϧ͕ ґଘؔ܎ʹ௥ՃࡁΈʂ "devDependencies": { "@jetbrains/gen-idea-libs": "^1.0.11", "@jetbrains/kotlin-extensions": "^1.0.1-pre.58", "@jetbrains/kotlin-react": "^16.5.2-pre.58",

    "@jetbrains/kotlin-react-dom": "^16.5.2-pre.58", "@jetbrains/kotlin-webpack-plugin": "^2.0.3", "@jetbrains/ts2kt-automator": "^1.0.12", "kotlin": "^1.3.0", "kotlinx-html": “^0.6.11", … }
  12. 29.

    package index import … fun main(args: Array<String>) { requireAll(require.context("src", true,

    js("/\\.css$/"))) render(document.getElementById("root")) { child(App::class) { } } }
  13. 30.

    package index import … fun main(args: Array<String>) { requireAll(require.context("src", true,

    js("/\\.css$/"))) render(document.getElementById("root")) { child(App::class) { } } } TSDҎԼͷDTTΛҰׅಡΈࠐΈ
  14. 31.

    package index import … fun main(args: Array<String>) { requireAll(require.context("src", true,

    js("/\\.css$/"))) render(document.getElementById("root")) { child(App::class) { } } } "QQίϯϙʔωϯτΛSFOEFS
  15. 32.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } }
  16. 33.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } 3$PNQPOFOUΛܧঝ$PNQPOFOUఆٛ
  17. 34.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } SFOEFSϝιου಺ʹIUNMΛ%4-Ͱهड़
  18. 35.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } EJWDMBTT/BNFl"QQIFBEFSzʜEJW
  19. 36.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } I8FMDPNFUP3FBDUXJUI,PUMJOI
  20. 37.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } ճస͢Δϩΰͷදࣔίϯϙʔωϯτ
  21. 38.

    package app import … class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } ܦա࣌ؒදࣔͷίϯϙʔωϯτ
  22. 39.

    interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(props) { override fun TickerState.init(props: TickerProps) { secondsElapsed = props.startFrom } var timerID: Int? = null override fun componentDidMount() { timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000) } override fun componentWillUnmount() { window.clearInterval(timerID!!) } override fun RBuilder.render() { +"This app has been running for ${state.secondsElapsed} seconds." } }
  23. 40.

    interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(props) { override fun TickerState.init(props: TickerProps) { secondsElapsed = props.startFrom } var timerID: Int? = null override fun componentDidMount() { timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000) } override fun componentWillUnmount() { window.clearInterval(timerID!!) } override fun RBuilder.render() { +"This app has been running for ${state.secondsElapsed} seconds." } } 31SPQTΛܧঝͨ͠5JDLFS1SPQT ˠ਌ίϯϙʔωϯτ͔Β ɹड͚औΔ஋ͷఆٛ
  24. 41.

    interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(props) { override fun TickerState.init(props: TickerProps) { secondsElapsed = props.startFrom } var timerID: Int? = null override fun componentDidMount() { timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000) } override fun componentWillUnmount() { window.clearInterval(timerID!!) } override fun RBuilder.render() { +"This app has been running for ${state.secondsElapsed} seconds." } } 34UBUFΛܧঝͨ͠5JDLFS4UBUF ˠ5JDLFSίϯϙʔωϯτͷঢ়ଶ
  25. 42.

    interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(props) { override fun TickerState.init(props: TickerProps) { secondsElapsed = props.startFrom } var timerID: Int? = null override fun componentDidMount() { timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000) } override fun componentWillUnmount() { window.clearInterval(timerID!!) } override fun RBuilder.render() { +"This app has been running for ${state.secondsElapsed} seconds." } } JOJUؔ਺Ͱܦա࣌ؒͷॳظԽ
  26. 43.

    interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(props) { override fun TickerState.init(props: TickerProps) { secondsElapsed = props.startFrom } var timerID: Int? = null override fun componentDidMount() { timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000) } override fun componentWillUnmount() { window.clearInterval(timerID!!) } override fun RBuilder.render() { +"This app has been running for ${state.secondsElapsed} seconds." } } ඵ͝ͱʹTUBUFͷ஋Λߋ৽ ˞TFU4UBUFϝιουͰ஋ߋ৽ SFOEFSϝιουͷݺͼग़͠
  27. 44.

    interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(props) { override fun TickerState.init(props: TickerProps) { secondsElapsed = props.startFrom } var timerID: Int? = null override fun componentDidMount() { timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000) } override fun componentWillUnmount() { window.clearInterval(timerID!!) } override fun RBuilder.render() { +"This app has been running for ${state.secondsElapsed} seconds." } } ܦա࣌ؒͷදࣔ
  28. 45.

    ίʔυൺֱ class App extends React.Component { render() { return (

    <div className='App-header'> <Logo/> <h2>Welcome to React with Kotlin</h2> <p className='App-intro'> To get started, edit <code>app/App.kt</code> and save to reload. </p> <p className='App-ticker'> <Ticker startFrom={ 0 }/> </p> </div> ); } } class App : RComponent<RProps, RState>() { override fun RBuilder.render() { div("App-header") { child(Logo::class) { } h2 { +"Welcome to React with Kotlin" } } p("App-intro") { +"To get started, edit " code { +"app/App.kt" } +" and save to reload." } p("App-ticker") { child(Ticker:class) { attrs.startFrom = 0 } } } } +4 ,PUMJO
  29. 46.

    w +4ϥΠϒϥϦΛ௥Ճ͍ͨ͠ʂ w npm install hoge --save w ௥Ճͨ͠+4ϥΠϒϥϦΛ,PUMJOͰѻ͍͍ͨʂ w

    ޙड़ w 8FCQBDLͷઃఆΛΧελϚΠζ͍ͨ͠ʂ w npm run eject ͰઃఆϑΝΠϧΛग़ྗՄ  ,PUMJO+4 OQN
  30. 50.

    (SBEMF1MVHJOͷࢦఆ SFQPTJUPSJFTͷࢦఆ  ,PUMJO+4 (SBEMF plugins { id 'org.jetbrains.kotlin.js' version

    '1.3.50' } group 'subroh0508.net' version '1.0-SNAPSHOT' repositories { jcenter() mavenCentral() maven { url "http://dl.bintray.com/kotlin/kotlin-js-wrappers" } maven { url 'https://plugins.gradle.org/m2/' } }
  31. 51.

    8FCQBDLͷઃఆ  ,PUMJO+4 (SBEMF kotlin { target { browser {

    runTask { devServer = new KotlinWebpackConfig.DevServer( true, false, true, true, false, 8080, null, ["$projectDir/src/main/resources".toString()] ) archiveFileName = "kotlin-react-sample.js" } } }
  32. 52.

    8FCQBDLͷઃఆ  ,PUMJO+4 (SBEMF kotlin { target { browser {

    runTask { devServer = new KotlinWebpackConfig.DevServer( true, false, true, true, false, 8080, null, ["$projectDir/src/main/resources".toString()] ) archiveFileName = "kotlin-react-sample.js" } } } CSPXTFSϒϩοΫ಺Ͱ 8FCQBDLͷઃఆΛهड़
  33. 53.

    kotlin { target { browser { runTask { devServer =

    new KotlinWebpackConfig.DevServer( true, false, true, true, false, 8080, null, ["$projectDir/src/main/resources".toString()] ) archiveFileName = "kotlin-react-sample.js" } } } 8FCQBDLͷઃఆ  ,PUMJO+4 (SBEMF QPSUɾQSPYZɾDPOUFOU#BTF౳Λࢦఆ ˠϦιʔεσΟϨΫτϦͷύεΛ DPOUFOU#BTFʹࢦఆ
  34. 54.

    kotlin { target { browser { runTask { devServer =

    new KotlinWebpackConfig.DevServer( true, false, true, true, false, 8080, null, ["$projectDir/src/main/resources".toString()] ) archiveFileName = "kotlin-react-sample.js" } } } 8FCQBDLͷઃఆ  ,PUMJO+4 (SBEMF BSDIJWF'JMF/BNF ˠόϯυϧޙͷϑΝΠϧ໊
  35. 55.

    ϥΠϒϥϦͷґଘؔ܎  ,PUMJO+4 (SBEMF sourceSets { main { dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib-js" implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12" implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41" implementation npm("core-js", "~3.1.4") implementation npm("react", "16.9.0") implementation npm("react-dom", "16.9.0") implementation npm("styled-components", "3.4.10") implementation npm("inline-style-prefixer", "5.0.4") } } }
  36. 56.

    sourceSets { main { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-js" implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12"

    implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41" implementation npm("core-js", "~3.1.4") implementation npm("react", "16.9.0") implementation npm("react-dom", "16.9.0") implementation npm("styled-components", "3.4.10") implementation npm("inline-style-prefixer", "5.0.4") } } } ϥΠϒϥϦͷґଘؔ܎  ,PUMJO+4 (SBEMF ,PUMJO+4ͷඪ४ϥΠϒϥϦ
  37. 57.

    ϥΠϒϥϦͷґଘؔ܎ sourceSets { main { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-js" implementation

    "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12" implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41" implementation npm("core-js", "~3.1.4") implementation npm("react", "16.9.0") implementation npm("react-dom", "16.9.0") implementation npm("styled-components", "3.4.10") implementation npm("inline-style-prefixer", "5.0.4") } } }  ,PUMJO+4 (SBEMF LPUMJOYIUNMIUNMͷ%4-ϥΠϒϥϦ LPUMJOSFBDU3FBDUͷϥούʔϥΠϒϥϦ
  38. 58.

    sourceSets { main { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-js" implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12"

    implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41" implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41" implementation npm("core-js", "~3.1.4") implementation npm("react", "16.9.0") implementation npm("react-dom", "16.9.0") implementation npm("styled-components", "3.4.10") implementation npm("inline-style-prefixer", "5.0.4") } } } ϥΠϒϥϦͷґଘؔ܎  ,PUMJO+4 (SBEMF +4ͷϥΠϒϥϦ͸OQN  Ͱࢦఆ
  39. 59.

    ,PUMJO+4ͷίʔσΟϯά  ,PUMJO+4 (SBEMF // main.kt fun main() { render(document.getElementById("root"))

    { child(App::class) { } } } // App.kt class App : RComponent<RProps, RState>() { override fun RBuilder.render() { div { +"Hello, World!" } } }
  40. 60.

    JOEFYIUNMͷ࡞੒  ,PUMJO+4 (SBEMF <!DOCTYPE html> <html lang="en"> <head> <meta

    charset="UTF-8"> <title>Title</title> </head> <body> <div id="root"></div> <script src="kotlin-react-sample.js"></script> </body> </html> ˡIUNMϑΝΠϧΛ SFTPVSDFTҎԼʹ഑ஔ
  41. 62.

    ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ interface AppState : RState { var count: Int

    } class App : RComponent<RProps, AppState>() { override fun AppState.init() { count = 0 } private fun onClickedButton() { setState { count += 1 } } override fun RBuilder.render() { div { +"count: ${state.count}" } button { attrs.onClickFunction = { onClickedButton() } +"click!" } } }
  42. 63.

    interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { override fun AppState.init() { count = 0 } private fun onClickedButton() { setState { count += 1 } } override fun RBuilder.render() { div { +"count: ${state.count}" } button { attrs.onClickFunction = { onClickedButton() } +"click!" } } } TUBUFͷܕఆٛ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  43. 64.

    interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { override fun AppState.init() { count = 0 } private fun onClickedButton() { setState { count += 1 } } override fun RBuilder.render() { div { +"count: ${state.count}" } button { attrs.onClickFunction = { onClickedButton() } +"click!" } } } TUBUFͷॳظԽ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  44. 65.

    interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { override fun AppState.init() { count = 0 } private fun onClickedButton() { setState { count += 1 } } override fun RBuilder.render() { div { +"count: ${state.count}" } button { attrs.onClickFunction = { onClickedButton() } +"click!" } } } ϘλϯΫϦοΫ࣌ͷίʔϧόοΫؔ਺ ˠTFU4UBUFϝιουͰΧ΢ϯτΛ૿΍͢ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  45. 66.

    interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { override fun AppState.init() { count = 0 } private fun onClickedButton() { setState { count += 1 } } override fun RBuilder.render() { div { +"count: ${state.count}" } button { attrs.onClickFunction = { onClickedButton() } +"click!" } } } Χ΢ϯτͷදࣔͱίʔϧόοΫͷࢦఆ ˠBUUSTPO$MJDL'VODUJPO\^ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  46. 67.

    ελΠϧΛ͚͍ͭͨ DTTJO,PUMJO override fun RBuilder.render() { div { +"count: ${state.count}"

    } styledButton { css { width = 200.px height = 100.px backgroundColor = Color.aqua } attrs.onClickFunction = { onClickedButton() } +"click!" } }
  47. 68.

    ελΠϧΛ͚͍ͭͨ DTTJO,PUMJO override fun RBuilder.render() { div { +"count: ${state.count}"

    } styledButton { css { width = 200.px height = 100.px backgroundColor = Color.aqua } attrs.onClickFunction = { onClickedButton() } +"click!" } } TUZMFE#VUUPOʹ͢Δͱ DTT IBOEMFS3VMF4FU ϝιου͕ੜ͑Δ
  48. 69.

    ελΠϧΛ͚͍ͭͨ DTTJO,PUMJO override fun RBuilder.render() { div { +"count: ${state.count}"

    } styledButton { css { width = 200.px height = 100.px backgroundColor = Color.aqua } attrs.onClickFunction = { onClickedButton() } +"click!" } } DTTϒϩοΫͷதͰελΠϧΛهड़ ˞TUZMFEDPNQPOFOUͷػೳ
  49. 73.

    +4੡ϥΠϒϥϦͷಋೖ ґଘؔ܎௥Ճ ྫSFBDUTXJQFBCMFWJFXT  w OQNͷ৔߹ npm install react-swipeable-views --save

    w (SBEMFͷ৔߹CVJMEHSBEMFʹ௥ه sourceSets { main { dependencies { ... implementation npm("react-swipeable-views") ... } } }
  50. 75.

    +4੡ϥΠϒϥϦͷಋೖ !+T.PEVMFΞϊςʔγϣϯ w +4ϥΠϒϥϦΛ,PUMJOͷੈքʹΠϯϙʔτ͢ΔΞϊςʔγϣϯ @JsModule("react-swipeable-views") private external val reactSwipeableViewModule: dynamic

    fun test() { reactSwipeableViewModule.default.onChangeIndex = { ... } } +4ϥΠϒϥϦΛΠϯϙʔτ PO$IBOHF*OEFYʹ ϥϜμࣜΛ୅ೖ Ξϊςʔγϣϯ͚ͭͩʂ +4੡ϥΠϒϥϦΛ,PUMJOͷੈքͰ ҰԠ͸ ѻ͑Δʂ
  51. 77.

    +4੡ϥΠϒϥϦͷಋೖ EZOBNJDܕ w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ // { foo: 'foo',

    bar: { hoge: 111 } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin
  52. 78.

    +4੡ϥΠϒϥϦͷಋೖ EZOBNJDܕ w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ // { foo: 'foo',

    bar: { hoge: 111 } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin +4ͷΦϒδΣΫτΛੜ੒
  53. 79.

    +4੡ϥΠϒϥϦͷಋೖ EZOBNJDܕ w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ // { foo: 'foo',

    bar: { hoge: 111 } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin ଘࡏ͢ΔΩʔͷ஋Λऔͬͯ͘Δ
  54. 80.

    +4੡ϥΠϒϥϦͷಋೖ EZOBNJDܕ w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ // { foo: 'foo',

    bar: { hoge: 111 } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin ଘࡏ͠ͳ͍Ωʔͷ஋Λ +4ʹ͍ۙจ๏Ͱ୅ೖͰ͖ͯ͠·͏
  55. 81.

    +4੡ϥΠϒϥϦͷಋೖ EZOBNJDܕ w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ // { foo: 'foo',

    bar: { hoge: 111 } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }") print(jsObject.foo) // => 'foo' print(jsObject.bar.hoge) // => 111 jsObject.aaa.bbb = "add from kotlin" // => Error! ͜͏ͳͬͯཉ͍͠
  56. 82.

    FYUFSOBMम০ࢠ w ,PUMJOͷܕఆٛͱ+4ͷΦϒδΣΫτΛϚοϐϯά͢Δम০ࢠ external interface TestObject { var foo: String

    var bar: TestChildObject } external interface TestChildObject { var hoge: String } val jsObject: TestObject = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject.foo) // => 'foo' print(jsObject.bar.hoge) // => 111 5ZQF4DSJQUͷܕఆٛϑΝΠϧͱ ໾ׂ͸ಉ͡ +4੡ϥΠϒϥϦͷಋೖ
  57. 83.

    +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸ w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ +4੡ϥΠϒϥϦͷಋೖ @JsModule("react-swipeable-views") private external val

    reactSwipeableViewModule: dynamic external interface ReactSwipeableProps : RProps { var onChangeIndex: (Int, Int, ChangeReason) -> Unit } val RBuilder.swipeableViews: RClass<ReactSwipeableProps> get() = reactSwipeableViewModule.default as RClass<ReactSwipeableProps>
  58. 84.

    +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸ w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ +4੡ϥΠϒϥϦͷಋೖ @JsModule("react-swipeable-views") private external val

    reactSwipeableViewModule: dynamic external interface ReactSwipeableProps : RProps { var onChangeIndex: (Int, Int, ChangeReason) -> Unit } val RBuilder.swipeableViews: RClass<ReactSwipeableProps> get() = reactSwipeableViewModule.default as RClass<ReactSwipeableProps> FYUFSOBMJOUFSGBDFΛఆٛ ˠϓϩύςΟʮPO$IBOHF*OEFYʯ͕ ɹଘࡏ͢Δ͜ͱΛڭ͑ࠐ·ͤΔ
  59. 85.

    +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸ w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ +4੡ϥΠϒϥϦͷಋೖ @JsModule("react-swipeable-views") private external val

    reactSwipeableViewModule: dynamic external interface ReactSwipeableProps : RProps { var onChangeIndex: (Int, Int, ChangeReason) -> Unit } val RBuilder.swipeableViews: RClass<ReactSwipeableProps> get() = reactSwipeableViewModule.default as RClass<ReactSwipeableProps> Πϯϙʔτͨ͠ϞδϡʔϧΛ3$MBTT31SPQTʹΩϟετ
  60. 86.

    +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸ w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ +4੡ϥΠϒϥϦͷಋೖ fun ReactSwipeableProps.onChangeIndex(block: (Int) ->

    Unit) { onChangeIndex = { index: Int, _, _ -> block(index) } } // RComponentクラス内 override fun RBuilder.render() { swipeableViews { attrs.onChangeIndex { i -> setState { tabNumber = i } } } }
  61. 87.

    +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸ w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ +4੡ϥΠϒϥϦͷಋೖ fun ReactSwipeableProps.onChangeIndex(block: (Int) ->

    Unit) { onChangeIndex = { index: Int, _, _ -> block(index) } } // RComponentクラス内 override fun RBuilder.render() { swipeableViews { attrs.onChangeIndex { i -> setState { tabNumber = i } } } } 1SPQTͷ֦ுؔ਺ͱͯ͠ ηολʔΛఆٛ
  62. 88.

    +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸ w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ +4੡ϥΠϒϥϦͷಋೖ fun ReactSwipeableProps.onChangeIndex(block: (Int) ->

    Unit) { onChangeIndex = { index: Int, _, _ -> block(index) } } // RComponentクラス内 override fun RBuilder.render() { swipeableViews { attrs.onChangeIndex { i -> setState { tabNumber = i } } } } BUUSTϓϩύςΟ͔Β1SPQT͕ੜ͑Δ ˠ֦ுؔ਺ݺͼग़͠
  63. 91.

    Ͳ͏ͯ͠΋ॻ͖ํ͕෼͔Βͳ͍ʂ w KT  ϝιουҾ਺ʹੜͷ+4ίʔυΛจࣈྻͰ౉͢͜ͱ͕Ͱ͖Δ w +4ΛͱΓ͋͑ͣίϐϖˠগͣͭ͠,PUMJOʹม׵ +4੡ϥΠϒϥϦͷಋೖ inline fun

    Window.initMap() = js("""new window.google.maps.Map( document.getElementById('googleMap'), { center: { lat: 34.740864, lng: 139.391152 }, zoom: 11.3, mapTypeId: 'roadmap' } )""") ྫ(PPHMF.BQ"1*ͷॳظԽίʔυ
  64. 93.

    ·ͱΊ ,PUMJO+4ͷ͜Ε͔Β w CBDLFOEͱGSPOUFOE͕ͭͷ,PUMJOϓϩδΣΫτʹ·ͱ·Δʂ w EBUBDMBTTͷڞ༗ɺ(SBEMFͰͷϏϧυɺ%4-ͰͷIUNMDTTهड़ ຊ൪ಋೖ΁ͷน w ѹ౗తͳࣄྫɾ৘ใෆ଍ʂɹϥΠϒϥϦ΋·ͩ·ͩগͳ͍ʂ w

    ؆୯ͳϞϊͰ΋࡞ͬͯެ։ˠ஌ݟͷ஝ੵɺ,PUMJO+4ͷલਐͷҰॿ ,PUMJOͰϑϩϯτΤϯυͷ։ൃ͕Ͱ͖Δͷ͸ָ͍͠ʂ ୔ࢁͷਓ͕৮ͬͯ͘ΕΔͱخ͍͠☺
  65. 95.

    ίʔυϦʔσΟϯά LPUMJOSFBDU ʮ)FMMP 8PSMEʯΛදࣔ͢ΔαϯϓϧΞϓϦ class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div { +"Hello, World!" } } } 3$PNQPOFOUʁ 3#VJMEFSʁ SFOEFSϝιου͸ ͳ֦ͥுؔ਺ʁ
  66. 97.

    ίʔυϦʔσΟϯά LPUMJOSFBDU @ReactDsl open class RBuilder { val childList =

    mutableListOf<Any>() fun child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = emptyList()) = node(C::class, props, children) fun RProps.children() { childList.addAll(Children.toArray(children)) } } 3#VJMEFSΫϥε
  67. 98.

    ίʔυϦʔσΟϯά LPUMJOSFBDU 3#VJMEFSΫϥε w %0.ཁૉΛ֨ೲ͢ΔDIJME-JTUϓϩύςΟΛ࣋ͭ @ReactDsl open class RBuilder {

    val childList = mutableListOf<Any>() fun child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = emptyList()) = node(C::class, props, children) fun RProps.children() { childList.addAll(Children.toArray(children)) } } @ReactDsl open class RBuilder { val childList = mutableListOf<Any>()
  68. 99.

    ίʔυϦʔσΟϯά LPUMJOSFBDU 3#VJMEFSΫϥε w %0.ཁૉΛ֨ೲ͢ΔDIJME-JTUϓϩύςΟΛ࣋ͭ w DIJMEϝιουΛ࢖ͬͯཁૉΛ௥ՃͰ͖Δ @ReactDsl open class

    RBuilder { val childList = mutableListOf<Any>() fun child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = emptyList()) = node(C::class, props, children) fun RProps.children() { childList.addAll(Children.toArray(children)) } } fun child(element: ReactElement): ReactElement { childList.add(element) return element }
  69. 100.

    @ReactDsl open class RBuilder { val childList = mutableListOf<Any>() fun

    child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = emptyList()) = node(C::class, props, children) fun RProps.children() { childList.addAll(Children.toArray(children)) } } ίʔυϦʔσΟϯά LPUMJOSFBDU 3#VJMEFSΫϥε w %0.ཁૉΛ֨ೲ͢ΔDIJME-JTUϓϩύςΟΛ࣋ͭ w DIJMEϝιουɺଞʹͭ w 3$PNQPOFOUΛ௥Ճ w QSPQTͱࢠཁૉΛݸผͷҾ਺Ͱ௥ՃFUD fun child(type: Any, props: P, children: List<Any>): ReactElement fun child(type: Any, props: P, handler: RHandler<P>): ReactElement fun child(klazz: KClass<C>, handler: RHandler<P>): ReactElement inline fun child(noinline handler: RHandler<P>): ReactElement
  70. 102.

    ίʔυϦʔσΟϯά LPUMJOSFBDU EJWϝιου w 3#VJMEFSͷ֦ுؔ਺ͱͯ͠ఆٛ w Ҿ਺CMPDLͷܕ͸3%0.#VJMEFS%*7 6OJU w CMPDLͷதͰEJW΍ͦͷଞ)UNM5BHͷϝιουΛ࠶ؼతʹهड़Ͱ͖Δʂ

    w )UNM5BH༻ϝιου͸શ֦ͯுؔ਺ˠ3#VJMEFS͕ͪ͝Ό͔ͭͳ͍ʂ inline fun RBuilder.div( classes: String? = null, block: RDOMBuilder<DIV>.() -> Unit ): ReactElement = tag(block) { DIV(attributesMapOf("class", classes), it) } 3%0.#VJMEFS ˠ3#VJMEFSͷࢠΫϥε EJW\ TQBO\ B\^ ^ ^
  71. 103.

    ίʔυϦʔσΟϯά LPUMJOSFBDU UBHϝιου w )UNM5BH༻ϝιου͔Β࣮ࡍʹݺͼग़͞ΕΔϝιου w 3#VJMEFSDIJME  ϝιουΛ࣮ߦ w

    ,PUMJO+4Ͱಠࣗ)5.-λάΛѻ͍͍ͨʂˠUBHϝιουͰΑ͠ͳʹ inline fun <T : Tag> RBuilder.tag( block: RDOMBuilder<T>.() -> Unit, noinline factory: (TagConsumer<Unit>) -> T ): ReactElement = child(RDOMBuilder(factory).apply { block() }.create())
  72. 106.

    ίʔυϦʔσΟϯά LPUMJOSFBDU 3#VJMEFSΫϥε w 4USJOHVOBSZ1MVT ϝιου w ԋࢉࢠΦʔόʔϩʔυ ˠจࣈྻͷ୯߲ԋࢉ IPHF

    Λ্ॻ͖ w 3#VJMEFSΫϥεͷ಺෦Ͱએݴ ɹˠUIJT͕3#VJMEFSͷ࣌ͷΈ༗ޮ ɹɹར༻ൣғΛݶఆ͠ɺ෭࡞༻Λ཈͑Δʂ @ReactDsl open class RBuilder { val childList = mutableListOf<Any>() fun child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = emptyList()) = node(C::class, props, children) fun RProps.children() { childList.addAll(Children.toArray(children)) } } operator fun String.unaryPlus() { childList.add(this) }