├── _config.yml ├── vuekt-plugin ├── settings.gradle ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── org.musyozoku.vuekt.properties │ │ └── kotlin │ │ └── org │ │ └── musyozoku │ │ └── vuekt │ │ └── js2vue │ │ ├── VuePluginExtension.kt │ │ ├── VuePlugin.kt │ │ ├── Js2VueConfigTask.kt │ │ └── Js2VueTask.kt └── build.gradle ├── .gitignore ├── release └── org │ └── musyozoku │ ├── vuekt │ ├── maven-metadata.xml.md5 │ ├── 0.1.0 │ │ ├── vuekt-0.1.0.jar.md5 │ │ ├── vuekt-0.1.0.pom.md5 │ │ ├── vuekt-0.1.0.jar.sha1 │ │ ├── vuekt-0.1.0.pom.sha1 │ │ ├── vuekt-0.1.0.jar │ │ └── vuekt-0.1.0.pom │ ├── maven-metadata.xml.sha1 │ └── maven-metadata.xml │ ├── vuekt-js2vue │ ├── maven-metadata.xml.md5 │ ├── maven-metadata.xml.sha1 │ ├── 0.1.0 │ │ ├── vuekt-js2vue-0.1.0.jar.md5 │ │ ├── vuekt-js2vue-0.1.0.pom.md5 │ │ ├── vuekt-js2vue-0.1.0.jar.sha1 │ │ ├── vuekt-js2vue-0.1.0.pom.sha1 │ │ ├── vuekt-js2vue-0.1.0.jar │ │ └── vuekt-js2vue-0.1.0.pom │ └── maven-metadata.xml │ └── vuekt-plugin │ ├── maven-metadata.xml.md5 │ ├── maven-metadata.xml.sha1 │ ├── 0.1.0 │ ├── vuekt-plugin-0.1.0.jar.md5 │ ├── vuekt-plugin-0.1.0.pom.md5 │ ├── vuekt-plugin-0.1.0.jar.sha1 │ ├── vuekt-plugin-0.1.0.pom.sha1 │ ├── vuekt-plugin-0.1.0.jar │ └── vuekt-plugin-0.1.0.pom │ └── maven-metadata.xml ├── bin ├── publish_plugin.sh ├── webpack.sh └── archive.sh ├── single-file ├── webpack.config.d │ ├── 01_js2vue.js │ ├── 62_vue-loader-plugin.js │ ├── 22_output.js │ ├── 21_entry.js │ ├── 42_resolve.js │ ├── 00_README.txt │ ├── 41_module.js │ └── 61_html-plugin.js ├── src │ └── main │ │ └── kotlin │ │ └── dummy.kt ├── greeting │ └── main │ │ └── greeting.kt ├── webContent │ └── greeting.html ├── greeting-component │ └── main │ │ └── GreetingComponent.kt └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── guide ├── webpack.config.d │ ├── output.js │ ├── 00_README.txt │ ├── resolve.js │ ├── entry.js │ └── module.js ├── computed │ └── main │ │ ├── lodash │ │ └── Lodash.kt │ │ └── Computed.kt ├── instance.html ├── src │ └── main │ │ └── kotlin │ │ └── dummy.kt ├── filters.html ├── computed.html ├── filters │ └── main │ │ └── Filters.kt ├── syntax │ └── main │ │ └── Syntax.kt ├── index.html ├── build.gradle ├── events.html ├── syntax.html ├── forms │ └── main │ │ └── Forms.kt ├── list.html ├── class-and-style.html ├── instance │ └── main │ │ └── Instance.kt ├── events_ │ └── main │ │ └── Events.kt ├── class-and-style │ └── main │ │ └── ClassAndStyle.kt ├── forms.html ├── index │ └── main │ │ └── Index.kt ├── components.html ├── list │ └── main │ │ └── List.kt └── components │ └── main │ └── Components.kt ├── .idea ├── vcs.xml ├── .gitignore └── gradle.xml ├── vuekt ├── TODO.md ├── package.json ├── src │ └── main │ │ └── kotlin │ │ └── org │ │ └── musyozoku │ │ └── vuekt │ │ ├── plugin.kt │ │ ├── util.kt │ │ ├── json.kt │ │ ├── array.kt │ │ ├── vnode.kt │ │ ├── vue.kt │ │ └── options.kt └── build.gradle ├── settings.gradle ├── config.gradle ├── vuekt-js2vue ├── build.gradle └── src │ └── main │ └── kotlin │ └── org │ └── musyozoku │ └── vuekt │ └── js2vue │ └── ComponentVue.kt ├── gradlew.bat ├── gradlew ├── README_ja.md ├── README.md └── LICENSE /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-leap-day -------------------------------------------------------------------------------- /vuekt-plugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'vuekt-plugin' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /local.properties 3 | .DS_Store 4 | *.iml 5 | node_modules -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/maven-metadata.xml.md5: -------------------------------------------------------------------------------- 1 | 48f7357adecbf41c68400fd82cfec5bd -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.jar.md5: -------------------------------------------------------------------------------- 1 | d08c494cf3bd9fde6241fcbbc269422a -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.pom.md5: -------------------------------------------------------------------------------- 1 | d752a82f2add1b4a504531c482784c35 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/maven-metadata.xml.md5: -------------------------------------------------------------------------------- 1 | a21297fd7d14509f474f88c8b600792b -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/maven-metadata.xml.md5: -------------------------------------------------------------------------------- 1 | 3964d50f7449005ab18c80dc5e0cbed4 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/maven-metadata.xml.sha1: -------------------------------------------------------------------------------- 1 | d16328108bf7694c6b37947c962cbd237b1649f6 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/maven-metadata.xml.sha1: -------------------------------------------------------------------------------- 1 | 7b0c0707f8c52d37c775c872fe3f012e33e3ff46 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/maven-metadata.xml.sha1: -------------------------------------------------------------------------------- 1 | df488c1a3e98760c780e84b955cc10d263723502 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.jar.sha1: -------------------------------------------------------------------------------- 1 | 000c5900fab5b5f98dc327814826de83f32bad1e -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.pom.sha1: -------------------------------------------------------------------------------- 1 | a075bc00477f63af895ebe814f72c1af75df3b71 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.jar.md5: -------------------------------------------------------------------------------- 1 | cc7fb015d9a12b0638296485dc191fab -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.pom.md5: -------------------------------------------------------------------------------- 1 | 32f1e45bee9512e14d46739a8b15c938 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.jar.md5: -------------------------------------------------------------------------------- 1 | c7d9fc9c2a82b7f95a89bbd662fb5f7b -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.pom.md5: -------------------------------------------------------------------------------- 1 | 08d83c91e3376033ecb47e4224075a83 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.jar.sha1: -------------------------------------------------------------------------------- 1 | 63dba51d657994a11c0f310fd69635f4d36bb376 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.pom.sha1: -------------------------------------------------------------------------------- 1 | d00973f2262ba18b352defc2ca6ab5778e67e186 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.jar.sha1: -------------------------------------------------------------------------------- 1 | cf99ca7c4f926d476fcc7166dcbfd2b1a27b25f9 -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.pom.sha1: -------------------------------------------------------------------------------- 1 | 582966abec6f746ba8179548f91369625a3d9b8b -------------------------------------------------------------------------------- /bin/publish_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./gradlew --project-dir=vuekt-plugin publishToMavenLocal 4 | 5 | # EOF -------------------------------------------------------------------------------- /single-file/webpack.config.d/01_js2vue.js: -------------------------------------------------------------------------------- 1 | // Disable `fs` module on web target. 2 | config.node = { fs: 'empty' } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nosix/vue-kotlin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /vuekt-plugin/src/main/resources/META-INF/gradle-plugins/org.musyozoku.vuekt.properties: -------------------------------------------------------------------------------- 1 | implementation-class=org.musyozoku.vuekt.js2vue.VuePlugin -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nosix/vue-kotlin/HEAD/release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.jar -------------------------------------------------------------------------------- /guide/webpack.config.d/output.js: -------------------------------------------------------------------------------- 1 | // For reference from browser console 2 | Object.assign(config.output, { 3 | library: 'main', 4 | libraryTarget: 'var' 5 | }) 6 | -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nosix/vue-kotlin/HEAD/release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.jar -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nosix/vue-kotlin/HEAD/release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.jar -------------------------------------------------------------------------------- /single-file/webpack.config.d/62_vue-loader-plugin.js: -------------------------------------------------------------------------------- 1 | // noinspection JSAnnotator 2 | const { VueLoaderPlugin } = require('vue-loader') 3 | config.plugins.push(new VueLoaderPlugin()) 4 | -------------------------------------------------------------------------------- /single-file/webpack.config.d/22_output.js: -------------------------------------------------------------------------------- 1 | // For reference from browser console (not required) 2 | Object.assign(config.output, { 3 | library: 'main', 4 | libraryTarget: 'var' 5 | }) 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /guide/webpack.config.d/00_README.txt: -------------------------------------------------------------------------------- 1 | // The following variables can be referred to in the script. 2 | // 3 | // webpack: 4 | // webpack module 5 | // 6 | // config: 7 | // webpack config (see https://webpack.js.org/configuration/) 8 | // 9 | -------------------------------------------------------------------------------- /single-file/webpack.config.d/21_entry.js: -------------------------------------------------------------------------------- 1 | // Multiple entries example 2 | config.entry = { 3 | 'greeting': 'greeting', 4 | 'vue': 'vue', 5 | 'vuekt': 'vuekt' 6 | } 7 | 8 | // Note: 'vendor': ['vue', 'vuekt'] style is not supported by kotlin-frontend-plugin. -------------------------------------------------------------------------------- /guide/webpack.config.d/resolve.js: -------------------------------------------------------------------------------- 1 | // https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only 2 | Object.assign(config.resolve, { 3 | extensions: ['.js', '.css', '.vue'], 4 | alias: { 5 | 'vue': 'vue/dist/vue.js' 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /vuekt/TODO.md: -------------------------------------------------------------------------------- 1 | - Improvement `this` notation 2 | - Currently `thisAs` function 3 | - To be function with receiver (as hope) 4 | - Support VueClass annotation that is collection of annotations 5 | - `@JsModule(vue.MODULE)` 6 | - `@JsNonModule` 7 | - `@JsName(vue.CLASS)` -------------------------------------------------------------------------------- /guide/computed/main/lodash/Lodash.kt: -------------------------------------------------------------------------------- 1 | @file:JsNonModule 2 | @file:JsModule("lodash") 3 | package lodash 4 | 5 | external fun debounce(func: () -> Unit, wait: Int? = definedExternally, options: Any? = definedExternally): Function 6 | 7 | external fun capitalize(string: String): String -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 28 13:31:18 JST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /bin/webpack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CMD=`basename $0` 4 | 5 | if [ $# -ne 1 ]; then 6 | echo "$CMD guide|single-file" 1>&2 7 | exit 1 8 | fi 9 | 10 | if [ ! `which node` ]; then 11 | echo "Please set PATH to node command." 1>&2 12 | exit 1 13 | fi 14 | 15 | cd $1/build 16 | 17 | node node_modules/webpack/bin/webpack.js --config webpack.config.js 18 | 19 | # EOF -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.musyozoku 4 | vuekt 5 | 6 | 0.1.0 7 | 8 | 0.1.0 9 | 10 | 20170922051110 11 | 12 | 13 | -------------------------------------------------------------------------------- /guide/webpack.config.d/entry.js: -------------------------------------------------------------------------------- 1 | config.entry = { 2 | 'index': 'index', 3 | 'instance': 'instance', 4 | 'syntax': 'syntax', 5 | 'computed': 'computed', 6 | 'class-and-style': 'class-and-style', 7 | 'list': 'list', 8 | 'events_': 'events_', 9 | 'forms': 'forms', 10 | 'components': 'components', 11 | 'filters': 'filters', 12 | 'vendor': ['vue', 'vuekt'] 13 | } 14 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | *.xml 4 | 5 | !/codeStyleSettings.xml 6 | !/copyright/*.xml 7 | !/fileColors.xml 8 | !/encodings.xml 9 | !/gradle.xml 10 | !/runConfigurations/*.xml 11 | 12 | !/inspectionProfiles/*.xml 13 | /inspectionProfiles/profiles_settings.xml 14 | 15 | !/scopes/*.xml 16 | /scopes/scope_settings.xml 17 | 18 | !/templateLanguages.xml 19 | !/vcs.xml -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.musyozoku 4 | vuekt-js2vue 5 | 6 | 0.1.0 7 | 8 | 0.1.0 9 | 10 | 20170922051112 11 | 12 | 13 | -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.musyozoku 4 | vuekt-plugin 5 | 6 | 0.1.0 7 | 8 | 0.1.0 9 | 10 | 20170922051105 11 | 12 | 13 | -------------------------------------------------------------------------------- /single-file/webpack.config.d/42_resolve.js: -------------------------------------------------------------------------------- 1 | // https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only 2 | Object.assign(config.resolve, { 3 | extensions: ['.js'], 4 | alias: { 5 | 'vue$': 'vue/dist/vue.common.js' 6 | } 7 | }) 8 | 9 | config.resolveLoader = { 10 | // required because `module not found` occurs on some loaders 11 | modules: [require('path').join(__dirname, 'node_modules')] 12 | } -------------------------------------------------------------------------------- /vuekt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Vue.kt", 3 | "version": "2.4.4", 4 | "description": "Vue.kt that is Vue.js in Kotlin", 5 | "author": "nosix", 6 | "homepage": "https://github.com/nosix/vue-kotlin", 7 | "license": "Apache-2.0", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://git@github.com/nosix/vue-kotlin.git" 11 | }, 12 | "devDependencies": { 13 | "vue": "2.4.4", 14 | "ts2kt": "0.0.14" 15 | } 16 | } -------------------------------------------------------------------------------- /vuekt-plugin/src/main/kotlin/org/musyozoku/vuekt/js2vue/VuePluginExtension.kt: -------------------------------------------------------------------------------- 1 | package org.musyozoku.vuekt.js2vue 2 | 3 | open class VuePluginExtension { 4 | 5 | /** 6 | * Files to match this pattern translate to vue file. 7 | */ 8 | var targetPattern = """.*-component\.js""" 9 | 10 | /** 11 | * Name of the file generated as `webpack.config.d/$configFile` 12 | */ 13 | var configFile = "01_${Js2VueConfigTask.CONFIG_FILE_SUFFIX}" 14 | } -------------------------------------------------------------------------------- /guide/instance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Vue Instance / Vueインスタンス 6 | 7 | 8 | 9 | 10 | 11 | 12 |
{{ a }}
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /guide/webpack.config.d/module.js: -------------------------------------------------------------------------------- 1 | Object.assign(config.module, { 2 | rules: [ 3 | { 4 | test: /\.vue$/, 5 | loader: 'vue-loader', 6 | options: { 7 | esModule: false // required for Kotlin/JS 8 | } 9 | }, 10 | { 11 | test: /\.css$/, 12 | use: [ 13 | 'vue-style-loader', 14 | 'css-loader' 15 | ] 16 | } 17 | ], 18 | }) 19 | -------------------------------------------------------------------------------- /bin/archive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CMD=`basename $0` 4 | 5 | if [ $# -ne 1 ]; then 6 | echo "$CMD version" 1>&2 7 | exit 1 8 | fi 9 | 10 | version_option=-Pversion=$1 11 | 12 | ./gradlew $version_option clean 13 | 14 | ./gradlew $version_option --project-dir=vuekt-plugin publishToMavenLocal uploadArchives 15 | 16 | ./gradlew $version_option vuekt:publishToMavenLocal vuekt:uploadArchives 17 | 18 | ./gradlew $version_option vuekt-js2vue:publishToMavenLocal vuekt-js2vue:uploadArchives 19 | 20 | # EOF -------------------------------------------------------------------------------- /guide/src/main/kotlin/dummy.kt: -------------------------------------------------------------------------------- 1 | // This is dummy file. 2 | // 3 | // This project creates modules in sub-projects to generate multiple entry points. 4 | // Therefore, we do not make `guide` module. 5 | // However, `guide` module is necessary. 6 | // 7 | // If this file does not exist, the following error occurs. 8 | // 9 | // > File '/guide/build/classes/main/guide_main.js' specified for property 'sourceFile' does not exist. 10 | // 11 | // `compileKotlin2Js.kotlinOptions.outputFile` is used for module name. 12 | // -------------------------------------------------------------------------------- /single-file/src/main/kotlin/dummy.kt: -------------------------------------------------------------------------------- 1 | // This is dummy file. 2 | // 3 | // This project creates modules in sub-projects to generate multiple entry points. 4 | // Therefore, we do not make `guide` module. 5 | // However, `guide` module is necessary. 6 | // 7 | // If this file does not exist, the following error occurs. 8 | // 9 | // > File '/guide/build/classes/main/guide_main.js' specified for property 'sourceFile' does not exist. 10 | // 11 | // `compileKotlin2Js.kotlinOptions.outputFile` is used for module name. 12 | // -------------------------------------------------------------------------------- /single-file/webpack.config.d/00_README.txt: -------------------------------------------------------------------------------- 1 | // Scripts in this directory is used in `webpack.config.js`. 2 | // 3 | // The following variables can be referred to in scripts. 4 | // 5 | // webpack: 6 | // webpack module 7 | // 8 | // config: 9 | // webpack config (see https://webpack.js.org/configuration/) 10 | // 11 | // Caution: the following settings are preset. 12 | // context 13 | // entry 14 | // output 15 | // path, filename, chunkFilename, publicPath 16 | // resolve 17 | // modules 18 | // 19 | -------------------------------------------------------------------------------- /single-file/webpack.config.d/41_module.js: -------------------------------------------------------------------------------- 1 | Object.assign(config.module, { 2 | rules: [ 3 | { 4 | test: /\.vue$/, 5 | loader: 'vue-loader', 6 | options: { 7 | esModule: false // required for Kotlin/JS 8 | } 9 | }, 10 | { 11 | test: /\.html$/, 12 | loader: 'html-loader' 13 | }, 14 | { 15 | test: /\.css$/, 16 | use: [ 17 | 'vue-style-loader', 18 | 'css-loader' 19 | ] 20 | } 21 | ], 22 | }) 23 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'vue-kotlin' 2 | 3 | include 'vuekt' 4 | include 'vuekt-js2vue' 5 | include 'vuekt-plugin' 6 | 7 | include 'guide' 8 | include 'guide:index' 9 | include 'guide:instance' 10 | include 'guide:syntax' 11 | include 'guide:computed' 12 | include 'guide:class-and-style' 13 | include 'guide:list' 14 | include 'guide:events_' 15 | include 'guide:forms' 16 | include 'guide:components' 17 | include 'guide:filters' 18 | 19 | include 'single-file' 20 | include 'single-file:greeting' 21 | include 'single-file:greeting-component' -------------------------------------------------------------------------------- /single-file/greeting/main/greeting.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | 3 | @JsModule("greeting-component.vue") 4 | external val GreetingComponent: Component = definedExternally 5 | 6 | @JsModule("vue") 7 | @JsName("Vue") 8 | external class AppVue(options: ComponentOptions) : Vue { 9 | var message: String 10 | } 11 | 12 | val vm = AppVue(ComponentOptions { 13 | el = ElementConfig("#app") 14 | data = Data(json { 15 | message = "Vue & Kotlin" 16 | }) 17 | components = json { 18 | this["greeting"] = ComponentConfig(GreetingComponent) 19 | } 20 | }) -------------------------------------------------------------------------------- /single-file/webpack.config.d/61_html-plugin.js: -------------------------------------------------------------------------------- 1 | // Process HTML when bundling (not required) 2 | var HtmlWebpackPlugin = require('html-webpack-plugin') 3 | var fs = require('fs') 4 | 5 | for (var entry in config.entry) { 6 | var file = entry + '.html' 7 | var path = '../webContent/' + file 8 | if (fs.existsSync(path)) { 9 | config.plugins.push(new HtmlWebpackPlugin({ 10 | inject: false, // if true, all bundles will be set to script 11 | filename: file, 12 | template: '!!html-webpack-plugin/lib/loader.js!' + path, 13 | })) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /single-file/webContent/greeting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Single File Components / 単一ファイルコンポーネント 6 | 7 | 8 | 9 | 11 | 12 |
13 | {{ message }} 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /guide/filters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filters / フィルター 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | {{ message }} -> {{ message | capitalize }}
14 | ->
15 | {{ message | filterA('arg1', arg2) }}
16 |
17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/plugin.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | package org.musyozoku.vuekt 4 | 5 | typealias PluginFunction = (Vue: Any /* typeof Vue */, options: T?) -> Unit 6 | 7 | external interface PluginObject : JsonOf { 8 | var install: PluginFunction 9 | } 10 | 11 | /** 12 | * `PluginObject | PluginFunction` 13 | */ 14 | external interface PluginConfig 15 | 16 | inline fun PluginConfig(value: PluginObject): PluginConfig = value.asDynamic() 17 | inline fun PluginConfig(value: PluginFunction): PluginConfig = value.asDynamic() 18 | 19 | inline fun PluginConfig.toObject(): PluginObject = this.asDynamic() 20 | inline fun PluginConfig.toFunction(): PluginFunction = this.asDynamic() 21 | -------------------------------------------------------------------------------- /vuekt-plugin/src/main/kotlin/org/musyozoku/vuekt/js2vue/VuePlugin.kt: -------------------------------------------------------------------------------- 1 | package org.musyozoku.vuekt.js2vue 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | class VuePlugin : Plugin { 7 | 8 | override fun apply(project: Project) { 9 | project.extensions.create("vue", VuePluginExtension::class.java) 10 | 11 | project.tasks.create("js2vue-config", Js2VueConfigTask::class.java) { 12 | project.tasks.getByPath("bundle").dependsOn(it) 13 | project.tasks.getByPath("run").dependsOn(it) 14 | } 15 | 16 | project.tasks.create("js2vue", Js2VueTask::class.java) { 17 | it.dependsOn(project.tasks.getByPath("compileKotlin2Js")) 18 | project.tasks.getByPath("bundle").dependsOn(it) 19 | project.tasks.getByPath("run").dependsOn(it) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | group_id = 'org.musyozoku' 3 | vuekt_version = '1.0-SNAPSHOT' 4 | vuekt_js2vue_version = '1.0-SNAPSHOT' 5 | vuekt_plugin_version = '1.0-SNAPSHOT' 6 | 7 | kotlin_version = '1.3.21' 8 | gradle_node_version = '1.2.0' 9 | kotlin_frontend_version = '0.0.45' 10 | node_version = '11.10.0' 11 | vue_version = '2.6.7' 12 | aza_kotlin_css_version = '1.01' 13 | 14 | licence_name = 'The Apache Software License, Version 2.0' 15 | licence_url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 16 | 17 | releaseTest = project.hasProperty('use_remote') 18 | 19 | def version = project.getProperty('version') 20 | if (version != 'unspecified') { 21 | vuekt_version = version 22 | vuekt_js2vue_version = version 23 | vuekt_plugin_version = version 24 | } 25 | } 26 | 27 | // Note: 28 | // `gradle.properties` can not be used. 29 | // `vuekt-plugin` project and `vue-kotlin` project depend on each other and can not be built. -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt/0.1.0/vuekt-0.1.0.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.musyozoku 6 | vuekt 7 | 0.1.0 8 | 2017 9 | 10 | 11 | The Apache Software License, Version 2.0 12 | http://www.apache.org/licenses/LICENSE-2.0.txt 13 | repo 14 | 15 | 16 | 17 | 18 | org.jetbrains.kotlin 19 | kotlin-stdlib-js 20 | 1.1.4-3 21 | compile 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/util.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | package org.musyozoku.vuekt 4 | 5 | /** 6 | * JavaScript native `this` 7 | */ 8 | external val `this`: dynamic 9 | 10 | /** 11 | * Typed JavaScript native `this` 12 | */ 13 | inline fun thisAs(): T = `this` 14 | 15 | /** 16 | * Type of `void 0` 17 | */ 18 | external interface Void 19 | 20 | /** 21 | * Constant of `void 0` 22 | */ 23 | val void: Void = js("void 0") 24 | 25 | /** 26 | * `{ [propertyName: String]: T }` 27 | */ 28 | external interface JsonOf 29 | 30 | inline operator fun JsonOf.get(propertyName: String): T = this.asDynamic()[propertyName] 31 | inline operator fun JsonOf.set(propertyName: String, value: T) { 32 | this.asDynamic()[propertyName] = value 33 | } 34 | 35 | /** 36 | * `T | Array` 37 | */ 38 | external interface OneOrMany 39 | 40 | inline fun OneOrMany(value: T): OneOrMany = value.asDynamic() 41 | inline fun OneOrMany(value: Array): OneOrMany = value.asDynamic() 42 | -------------------------------------------------------------------------------- /guide/computed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Computed Properties and Watchers / 算出プロパティとウォッチャ 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Original message: "{{ message }}"

14 |

Computed reversed message: "{{ reversedMessage }}"

15 |
16 | 17 | 18 | 19 | 20 |
{{ fullName }}
21 | 22 | 23 | 24 | 25 |
26 |

27 | Ask a yes/no question: 28 | 29 |

30 |

{{ answer }}

31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /single-file/greeting-component/main/GreetingComponent.kt: -------------------------------------------------------------------------------- 1 | import azadev.kotlin.css.dimens.em 2 | import azadev.kotlin.css.fontSize 3 | import azadev.kotlin.css.textAlign 4 | import org.musyozoku.vuekt.Data 5 | import org.musyozoku.vuekt.Vue 6 | import org.musyozoku.vuekt.js2vue.ComponentOptionsBuilder 7 | import org.musyozoku.vuekt.js2vue.ComponentVue 8 | import org.musyozoku.vuekt.js2vue.StyleBuilder 9 | import org.musyozoku.vuekt.js2vue.translate 10 | import org.musyozoku.vuekt.json 11 | 12 | external class GreetingComponent : Vue { 13 | var greeting: String 14 | } 15 | 16 | class GreetingComponentVue : ComponentVue { 17 | 18 | override val template: String = """

{{ greeting }} World!

""" 19 | 20 | override val style: StyleBuilder = { 21 | p { 22 | fontSize = 2.em 23 | textAlign = center 24 | } 25 | } 26 | 27 | override val script: ComponentOptionsBuilder = { 28 | data = Data { 29 | json { 30 | greeting = "Hello" 31 | } 32 | } 33 | } 34 | } 35 | 36 | @Suppress("unused") 37 | val options = translate(GreetingComponentVue()) -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/json.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | package org.musyozoku.vuekt 4 | 5 | // Type is not `Json` because we do not want to have `get` and `set` functions. 6 | inline fun json(): T = js("({})") 7 | 8 | fun json(init: T.() -> Unit): T = json().apply(init) 9 | 10 | fun ComponentOptions(init: ComponentOptions.() -> Unit): ComponentOptions = json(init) 11 | fun FunctionalComponentOptions(init: FunctionalComponentOptions.() -> Unit): FunctionalComponentOptions = json(init) 12 | fun RenderContext(init: RenderContext.() -> Unit): RenderContext = json(init) 13 | fun PropOptions(init: PropOptions.() -> Unit): PropOptions = json(init) 14 | fun ComputedOptions(init: ComputedOptions.() -> Unit): ComputedOptions = json(init) 15 | fun WatchOptions(init: WatchOptions.() -> Unit): WatchOptions = json(init) 16 | fun DirectiveOptions(init: DirectiveOptions.() -> Unit): DirectiveOptions = json(init) 17 | fun WatchHandlerOptions(init: WatchHandlerOptions.() -> Unit): WatchHandlerOptions = json(init) 18 | fun ModelOptions(init: ModelOptions.() -> Unit): ModelOptions = json(init) 19 | -------------------------------------------------------------------------------- /guide/filters/main/Filters.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | 3 | // new Vue({ 4 | // // ... 5 | // filters: { 6 | // capitalize: function (value) { 7 | // if (!value) return '' 8 | // value = value.toString() 9 | // return value.charAt(0).toUpperCase() + value.slice(1) 10 | // } 11 | // } 12 | // }) 13 | 14 | @JsModule(vue.MODULE) 15 | @JsNonModule 16 | @JsName(vue.CLASS) 17 | external class ExampleVue(options: ComponentOptions) : Vue { 18 | var message: String 19 | var rawId: Int 20 | var arg2: String 21 | } 22 | 23 | val vm = ExampleVue(ComponentOptions { 24 | el = ElementConfig("#example") 25 | data = Data(json = json { 26 | message = "hello" 27 | rawId = 1 28 | arg2 = ".txt" 29 | }) 30 | filters = json { 31 | this["capitalize"] = { 32 | it: String -> 33 | it.capitalize() 34 | } 35 | this["formatId"] = { 36 | it: Int, digits: Int -> 37 | it.asDynamic().toFixed(digits) // FIXME: JavaScript の関数を実行 38 | } 39 | this["filterA"] = { 40 | it: String, arg1: String, arg2: String -> 41 | "$arg1$it$arg2" 42 | } 43 | } 44 | }) -------------------------------------------------------------------------------- /guide/syntax/main/Syntax.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnsafeCastFromDynamic") 2 | 3 | import org.musyozoku.vuekt.* 4 | 5 | @JsModule(vue.MODULE) 6 | @JsNonModule 7 | @JsName(vue.CLASS) 8 | external class ExampleVue(options: ComponentOptions) : Vue { 9 | var msg: String 10 | var rawHtml: String 11 | var dynamicId: String 12 | var isButtonDisabled: Boolean 13 | var number: Int 14 | var ok: Boolean 15 | var message: String 16 | var id: String 17 | var url: String 18 | } 19 | 20 | fun main(args: Array) { 21 | val vm = ExampleVue(ComponentOptions { 22 | el = ElementConfig("#example") 23 | data = Data(json = json { 24 | msg = "Hi" 25 | rawHtml = """""" 26 | dynamicId = "id-0101" 27 | isButtonDisabled = true 28 | number = 1 29 | ok = true 30 | message = "hello" 31 | id = "1" 32 | url = "https://jp.vuejs.org/" 33 | }) 34 | methods = json { 35 | this["doSomething"] = { 36 | val self = thisAs() 37 | self.message = "do something" 38 | } 39 | } 40 | }) 41 | 42 | vm.msg = "Oh!" 43 | } -------------------------------------------------------------------------------- /vuekt-plugin/src/main/kotlin/org/musyozoku/vuekt/js2vue/Js2VueConfigTask.kt: -------------------------------------------------------------------------------- 1 | package org.musyozoku.vuekt.js2vue 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.tasks.TaskAction 5 | import java.io.File 6 | 7 | open class Js2VueConfigTask : DefaultTask() { 8 | 9 | companion object { 10 | val CONFIG_FILE_SUFFIX: String = "js2vue.js" 11 | } 12 | 13 | private val extension: VuePluginExtension = project.extensions.getByType(VuePluginExtension::class.java) 14 | 15 | @Suppress("MemberVisibilityCanPrivate") 16 | val webpackConfigDir: File 17 | get() = project.projectDir.resolve("webpack.config.d") 18 | 19 | @Suppress("MemberVisibilityCanPrivate") 20 | val configFile: File 21 | get() = webpackConfigDir.resolve(extension.configFile) 22 | 23 | @Suppress("unused") 24 | @TaskAction 25 | fun generate() { 26 | webpackConfigDir.exists() || webpackConfigDir.mkdir() 27 | val configFiles = webpackConfigDir.listFiles { _, name -> name.endsWith(CONFIG_FILE_SUFFIX) } 28 | if (configFiles.isEmpty()) { 29 | configFile.writeText(""" 30 | // Disable `fs` module on web target. 31 | config.node = { fs: 'empty' } 32 | """.trimIndent()) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-js2vue/0.1.0/vuekt-js2vue-0.1.0.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.musyozoku 6 | vuekt-js2vue 7 | 0.1.0 8 | 2017 9 | 10 | 11 | The Apache Software License, Version 2.0 12 | http://www.apache.org/licenses/LICENSE-2.0.txt 13 | repo 14 | 15 | 16 | 17 | 18 | org.jetbrains.kotlin 19 | kotlin-stdlib-js 20 | 1.1.4-3 21 | compile 22 | 23 | 24 | org.musyozoku 25 | vuekt 26 | 0.1.0 27 | compile 28 | 29 | 30 | xyz.nulldev 31 | aza-kotlin-css-js 32 | 1.01 33 | compile 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vuekt-js2vue/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin2js' 2 | apply plugin: 'maven-publish' 3 | apply plugin: 'maven' 4 | 5 | group group_id 6 | version vuekt_js2vue_version 7 | 8 | dependencies { 9 | compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" 10 | compile "org.musyozoku:vuekt:$vuekt_version" 11 | compile "xyz.nulldev:aza-kotlin-css-js:$aza_kotlin_css_version" 12 | } 13 | 14 | compileKotlin2Js { 15 | kotlinOptions { 16 | outputFile = "$buildDir/js/${project.name}.js" 17 | moduleKind = "umd" 18 | sourceMap = true 19 | } 20 | } 21 | 22 | // Skip tasks 23 | compileJava.enabled = false 24 | processResources.enabled = false 25 | 26 | // Task dependencies 27 | def publishVuektTask = tasks.getByPath(':vuekt:publishToMavenLocal') 28 | 29 | build.dependsOn(publishVuektTask) 30 | build.mustRunAfter(publishVuektTask) 31 | 32 | // for publishToMavenLocal task 33 | publishing { 34 | publications { 35 | mavenJava(MavenPublication) { 36 | from components.java 37 | } 38 | } 39 | } 40 | 41 | // for uploadArchives task (distribute with GitHub) 42 | uploadArchives { 43 | repositories { 44 | mavenDeployer { 45 | repository(url: "file:$releaseDir") 46 | pom.project { 47 | inceptionYear '2017' 48 | packaging 'jar' 49 | licenses { 50 | license { 51 | name licence_name 52 | url licence_url 53 | distribution 'repo' 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /guide/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Introduction / はじめに 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | {{ message }} 14 |
15 | 16 |
17 | 18 | Hover your mouse over me for a few seconds 19 | to see my dynamically bound title! 20 | 21 |
22 | 23 | 24 | 25 | 26 |
27 |

Now you see me

28 |
29 | 30 |
31 |
    32 |
  1. 33 | {{ todo.text }} 34 |
  2. 35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 |

{{ message }}

43 | 44 |
45 | 46 |
47 |

{{ message }}

48 | 49 |
50 | 51 | 52 | 53 | 54 |
55 |
    56 | 57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /vuekt/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin2js' 2 | apply plugin: 'com.moowork.node' 3 | apply plugin: 'maven-publish' 4 | apply plugin: 'maven' 5 | 6 | group group_id 7 | version vuekt_version 8 | 9 | // Skip tasks 10 | compileJava.enabled = false 11 | processResources.enabled = false 12 | 13 | dependencies { 14 | compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | 21 | task ts2kt(type: NodeTask) { 22 | String nodeDir = "${project.projectDir}/node_modules" 23 | script = file("$nodeDir/ts2kt/ts2kt.js") 24 | 25 | String destDir = 'src/main/kotlin/org/vuejs' 26 | file(destDir).mkdir() 27 | 28 | args = ['-d', destDir] + file("$nodeDir/vue/types").listFiles().collect { it.absolutePath } 29 | } 30 | 31 | compileKotlin2Js { 32 | kotlinOptions { 33 | outputFile = "$buildDir/js/${project.name}.js" 34 | moduleKind = "umd" 35 | sourceMap = true 36 | } 37 | } 38 | 39 | // for publishToMavenLocal task 40 | publishing { 41 | publications { 42 | mavenJava(MavenPublication) { 43 | from components.java 44 | } 45 | } 46 | } 47 | 48 | // for uploadArchives task (distribute with GitHub) 49 | uploadArchives { 50 | repositories { 51 | mavenDeployer { 52 | repository(url: "file:$releaseDir") 53 | pom.project { 54 | inceptionYear '2017' 55 | packaging 'jar' 56 | licenses { 57 | license { 58 | name licence_name 59 | url licence_url 60 | distribution 'repo' 61 | } 62 | } 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /release/org/musyozoku/vuekt-plugin/0.1.0/vuekt-plugin-0.1.0.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.musyozoku 6 | vuekt-plugin 7 | 0.1.0 8 | 2017 9 | 10 | 11 | The Apache Software License, Version 2.0 12 | http://www.apache.org/licenses/LICENSE-2.0.txt 13 | repo 14 | 15 | 16 | 17 | 18 | org.jetbrains.kotlin 19 | kotlin-stdlib 20 | 1.1.4-3 21 | compile 22 | 23 | 24 | org.jetbrains.kotlin 25 | kotlin-gradle-plugin 26 | 1.1.4-3 27 | compile 28 | 29 | 30 | kotlin-compiler-embeddable 31 | * 32 | 33 | 34 | 35 | 36 | org.jetbrains.kotlin 37 | kotlin-frontend-plugin 38 | 0.0.21 39 | compile 40 | 41 | 42 | kotlin-compiler-embeddable 43 | * 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /vuekt-plugin/src/main/kotlin/org/musyozoku/vuekt/js2vue/Js2VueTask.kt: -------------------------------------------------------------------------------- 1 | package org.musyozoku.vuekt.js2vue 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.tasks.TaskAction 5 | import org.jetbrains.kotlin.gradle.frontend.util.kotlinOutput 6 | import org.jetbrains.kotlin.gradle.frontend.util.nodePath 7 | import org.jetbrains.kotlin.gradle.frontend.util.startWithRedirectOnFail 8 | import java.io.File 9 | 10 | open class Js2VueTask : DefaultTask() { 11 | 12 | private val extension: VuePluginExtension = project.extensions.getByType(VuePluginExtension::class.java) 13 | 14 | @Suppress("MemberVisibilityCanPrivate") 15 | val nodeCommand: File 16 | get() = nodePath(project, "node").first() 17 | 18 | @Suppress("MemberVisibilityCanPrivate") 19 | val nodeModulesDir: File 20 | get() = project.buildDir.resolve("node_modules") 21 | 22 | @Suppress("MemberVisibilityCanPrivate") 23 | val workingDirs: List 24 | get() = project.subprojects.map { kotlinOutput(it).parentFile!! } 25 | 26 | @Suppress("unused") 27 | @TaskAction 28 | fun translate() { 29 | val regExp = extension.targetPattern.toRegex() 30 | 31 | workingDirs.forEach { workingDir -> 32 | project.logger.debug(workingDir.absolutePath) 33 | 34 | workingDir.listFiles { _, name -> 35 | name.matches(regExp) 36 | }.forEach { script -> 37 | project.logger.debug(script.absolutePath) 38 | 39 | ProcessBuilder(nodeCommand.absolutePath, script.absolutePath) 40 | .directory(workingDir) 41 | .apply { environment().put("NODE_PATH", nodeModulesDir.absolutePath) } 42 | .startWithRedirectOnFail(project, "node .js") 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /vuekt-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | 3 | apply from: '../config.gradle' 4 | 5 | ext { 6 | releaseDir = "$projectDir/../release" 7 | } 8 | 9 | repositories { 10 | jcenter() 11 | } 12 | 13 | dependencies { 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | } 16 | } 17 | 18 | apply plugin: 'groovy' 19 | apply plugin: 'kotlin' 20 | apply plugin: 'maven-publish' 21 | apply plugin: 'maven' 22 | 23 | group group_id 24 | version vuekt_plugin_version 25 | 26 | repositories { 27 | jcenter() 28 | maven { 29 | // kotlin-frontend-plugin 30 | url "https://dl.bintray.com/kotlin/kotlin-eap" 31 | } 32 | } 33 | 34 | dependencies { 35 | compile gradleApi() 36 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 37 | compile("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") { 38 | exclude module: "kotlin-compiler-embeddable" 39 | } 40 | compile("org.jetbrains.kotlin:kotlin-frontend-plugin:$kotlin_frontend_version") { 41 | exclude module: "kotlin-compiler-embeddable" 42 | } 43 | } 44 | 45 | // for publishToMavenLocal task 46 | publishing { 47 | publications { 48 | mavenJava(MavenPublication) { 49 | from components.java 50 | } 51 | } 52 | } 53 | 54 | // for uploadArchives task (distribute with GitHub) 55 | uploadArchives { 56 | repositories { 57 | mavenDeployer { 58 | repository(url: "file:$releaseDir") 59 | pom.project { 60 | inceptionYear '2017' 61 | packaging 'jar' 62 | licenses { 63 | license { 64 | name licence_name 65 | url licence_url 66 | distribution 'repo' 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /guide/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin2js' 2 | apply plugin: 'org.jetbrains.kotlin.frontend' 3 | 4 | // Skip tasks 5 | classes.enabled = false 6 | jar.enabled = false 7 | 8 | dependencies { 9 | compile project(':guide:index') 10 | compile project(':guide:instance') 11 | compile project(':guide:syntax') 12 | compile project(':guide:computed') 13 | compile project(':guide:class-and-style') 14 | compile project(':guide:list') 15 | compile project(':guide:events_') 16 | compile project(':guide:forms') 17 | compile project(':guide:components') 18 | compile project(':guide:filters') 19 | } 20 | 21 | compileKotlin2Js { 22 | kotlinOptions { 23 | sourceMap = true 24 | } 25 | } 26 | 27 | compileTestKotlin2Js { 28 | kotlinOptions { 29 | sourceMap = true 30 | } 31 | } 32 | 33 | kotlinFrontend { 34 | downloadNodeJsVersion = node_version 35 | sourceMaps = true 36 | 37 | npm { 38 | dependency('vue', vue_version) 39 | dependency('axios', '0.12.0') 40 | 41 | devDependency('webpack', '4.29.5') 42 | } 43 | 44 | webpackBundle { 45 | publicPath = "/" 46 | port = 8088 47 | stats = "errors-only" 48 | } 49 | } 50 | 51 | subprojects { 52 | 53 | apply plugin: 'kotlin2js' 54 | 55 | // Skip tasks 56 | classes.enabled = false 57 | jar.enabled = false 58 | 59 | // Kotlin/JS library dependencies 60 | dependencies { 61 | compile "org.musyozoku:vuekt:$vuekt_version" 62 | } 63 | 64 | sourceSets { 65 | main.kotlin.srcDirs += "main" 66 | test.kotlin.srcDirs += "test" 67 | } 68 | 69 | compileKotlin2Js { 70 | kotlinOptions { 71 | moduleKind = "commonjs" 72 | sourceMap = true 73 | } 74 | } 75 | } 76 | 77 | // Task dependencies 78 | def publishVuektTask = tasks.getByPath(':vuekt:publishToMavenLocal') 79 | 80 | packages.dependsOn(publishVuektTask) 81 | packages.mustRunAfter(publishVuektTask) -------------------------------------------------------------------------------- /guide/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Event Handling / イベントハンドリング 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

The button above has been clicked {{ counter }} times.

15 |
16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 33 |
34 | 35 | 36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /guide/syntax.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Template Syntax / テンプレート構文 6 | 14 | 15 | 16 | 17 |
18 | 19 | 20 |

Text

21 | Message: {{ msg }}
22 | This will never change: {{ msg }}
23 | 24 | 25 | 26 |

Raw HTML

27 | {{ rawHtml }}
28 |

29 | 30 | 31 | 32 |

Attributes

33 |
Dynamic ID

34 |
35 | 36 | 37 | 38 |

Using JavaScript Expressions

39 | {{ number + 1 }}
40 | {{ ok ? 'YES' : 'NO' }}
41 | {{ message.split('').reverse().join('') }}
42 |
List ID

43 | 44 | 45 | 46 |

Modifiers

47 |
48 | 49 | 50 | 51 |

Shorthands

52 | Vue.js
53 | do something 54 |
55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /vuekt-js2vue/src/main/kotlin/org/musyozoku/vuekt/js2vue/ComponentVue.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package org.musyozoku.vuekt.js2vue 4 | 5 | import azadev.kotlin.css.Stylesheet 6 | import org.musyozoku.vuekt.ComponentOptions 7 | import org.musyozoku.vuekt.Vue 8 | 9 | private external fun require(moduleName: String): dynamic 10 | 11 | typealias StyleBuilder = Stylesheet.() -> Unit 12 | 13 | typealias ComponentOptionsBuilder = ComponentOptions.() -> Unit 14 | 15 | interface ComponentVue { 16 | 17 | /** 18 | * Base name of files. 19 | * 20 | * Default: if class name is `FooBarComponentVue`, then base name is `foo-bar-component`. 21 | */ 22 | val name: String get() = this::class.js.name 23 | .replace("^Vue|Vue$".toRegex(), "").replace("([A-Z])".toRegex(), "-$1").toLowerCase().trim('-') 24 | 25 | /** 26 | * Output vue file name. 27 | * 28 | * Default: `${name}.vue` 29 | */ 30 | val outputFile: String get() = "$name.vue" 31 | 32 | /** 33 | * Input script name 34 | * 35 | * Default: `${name}.js` 36 | */ 37 | val inputFile: String get() = "$name.js" 38 | 39 | /** 40 | * Vue template. 41 | */ 42 | val template: String 43 | 44 | /** 45 | * If style is scoped, then this value is true. 46 | * 47 | * Default: false 48 | */ 49 | val scopedStyle: Boolean get() = false 50 | 51 | /** 52 | * Vue style. 53 | */ 54 | val style: StyleBuilder 55 | 56 | /** 57 | * Vue script. 58 | */ 59 | val script: ComponentOptionsBuilder 60 | } 61 | 62 | fun translate(vue: ComponentVue): ComponentOptions { 63 | val fs = require("fs") 64 | 65 | if (fs.writeFileSync != undefined) { 66 | 67 | fs.writeFileSync(vue.outputFile, """ 68 | | 71 | | 72 | | 73 | |${Stylesheet(vue.style).render()} 74 | | 75 | | 76 | | 79 | """.trimMargin()) 80 | } 81 | 82 | return ComponentOptions(vue.script) 83 | } 84 | -------------------------------------------------------------------------------- /guide/forms/main/Forms.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | 3 | @JsModule(vue.MODULE) 4 | @JsNonModule 5 | @JsName(vue.CLASS) 6 | external class Example1Vue(options: ComponentOptions) : Vue { 7 | var message: String 8 | var checked: Boolean 9 | var checkedNames: Array 10 | var picked: String 11 | var selected: String 12 | var multiSelected: Array 13 | var options: Array 14 | } 15 | 16 | class OptionItem(val text: String, val value: String) 17 | 18 | // new Vue({ 19 | // el: '...', 20 | // data: { 21 | // ... 22 | // checkedNames: [] 23 | // ... 24 | // selected: 'A', 25 | // options: [ 26 | // { text: 'One', value: 'A' }, 27 | // { text: 'Two', value: 'B' }, 28 | // { text: 'Three', value: 'C' } 29 | // ] 30 | // } 31 | // }) 32 | 33 | val example1 = Example1Vue(ComponentOptions { 34 | el = ElementConfig("#example-1") 35 | data = Data(json = json { 36 | message = "" 37 | checked = false 38 | checkedNames = emptyArray() 39 | picked = "" 40 | selected = "A" 41 | multiSelected = emptyArray() 42 | options = arrayOf( 43 | OptionItem("One", "A"), 44 | OptionItem("Two", "B"), 45 | OptionItem("Three", "C") 46 | ) 47 | }) 48 | }) 49 | 50 | @JsModule(vue.MODULE) 51 | @JsNonModule 52 | @JsName(vue.CLASS) 53 | external class Example2Vue(options: ComponentOptions) : Vue { 54 | var toggle: String 55 | var a: String 56 | var b: String 57 | var pick: String 58 | var selected: String 59 | } 60 | 61 | val example2 = Example2Vue(ComponentOptions { 62 | el = ElementConfig("#example-2") 63 | data = Data(json = json { 64 | toggle = "" 65 | a = "a is selected" 66 | b = "b is selected" 67 | pick = "" 68 | selected = "" 69 | }) 70 | }) 71 | 72 | @JsModule(vue.MODULE) 73 | @JsNonModule 74 | @JsName(vue.CLASS) 75 | external class Example3Vue(options: ComponentOptions) : Vue { 76 | var msg: String 77 | var age: Int 78 | } 79 | 80 | val example3 = Example3Vue(ComponentOptions { 81 | el = ElementConfig("#example-3") 82 | data = Data(json = json { 83 | msg = "" 84 | age = 0 85 | }) 86 | }) -------------------------------------------------------------------------------- /guide/list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | List Rendering / リストレンダリング 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
  • {{ item.message }}
  • 14 |
15 | 16 | 20 |
    21 |
  • 22 | {{ parentMessage }} - {{ index }} - {{ item.message }} 23 |
  • 24 |
25 | 26 | 27 | 28 | 29 |
    30 |
  • 31 | {{ index }}. {{ key }} : {{ value }} 32 |
  • 33 |
34 | 35 | 36 | 37 | 38 |
{{ userProfile }}
39 | 40 | 41 | 42 | 43 |
44 |
    45 |
  1. {{ n }}
  2. 46 |
47 |
    48 |
  1. {{ n }}
  2. 49 |
50 |
51 | 52 | 53 | 54 | 55 |
56 | 59 |
    60 |
  • 65 |
66 |
67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /single-file/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin2js' 2 | apply plugin: 'org.jetbrains.kotlin.frontend' 3 | apply plugin: 'org.musyozoku.vuekt' 4 | 5 | ext { 6 | enableSourceMap = false 7 | } 8 | 9 | // Skip tasks 10 | classes.enabled = false 11 | jar.enabled = false 12 | 13 | dependencies { 14 | compile project(':single-file:greeting') 15 | compile project(':single-file:greeting-component') 16 | } 17 | 18 | compileKotlin2Js { 19 | kotlinOptions { 20 | sourceMap = enableSourceMap 21 | } 22 | } 23 | 24 | compileTestKotlin2Js { 25 | kotlinOptions { 26 | sourceMap = enableSourceMap 27 | } 28 | } 29 | 30 | kotlinFrontend { 31 | downloadNodeJsVersion = node_version 32 | sourceMaps = enableSourceMap 33 | 34 | npm { 35 | dependency('vue', vue_version) 36 | 37 | devDependency('webpack', '4.29.5') 38 | devDependency('webpack-dev-server', '3.2.0') 39 | devDependency('vue-loader', '15.6.4') 40 | devDependency('vue-template-compiler', vue_version) 41 | devDependency('css-loader', '2.1.0') 42 | devDependency('html-webpack-plugin', '3.2.0') 43 | } 44 | 45 | webpackBundle { 46 | contentPath = file("$projectDir/webContent") 47 | publicPath = "/" 48 | port = 8088 49 | stats = "errors-only" 50 | } 51 | } 52 | 53 | subprojects { 54 | 55 | apply plugin: 'kotlin2js' 56 | 57 | // Skip tasks 58 | classes.enabled = false 59 | jar.enabled = false 60 | 61 | // Kotlin/JS library dependencies 62 | dependencies { 63 | compile "org.musyozoku:vuekt:$vuekt_version" 64 | compile "org.musyozoku:vuekt-js2vue:$vuekt_js2vue_version" 65 | compile "xyz.nulldev:aza-kotlin-css-js:$aza_kotlin_css_version" 66 | } 67 | 68 | sourceSets { 69 | main.kotlin.srcDirs += "main" 70 | test.kotlin.srcDirs += "test" 71 | } 72 | 73 | compileKotlin2Js { 74 | kotlinOptions { 75 | moduleKind = "commonjs" 76 | sourceMap = enableSourceMap 77 | } 78 | } 79 | } 80 | 81 | // Task dependencies 82 | def publishVuektTask = tasks.getByPath(':vuekt:publishToMavenLocal') 83 | def publishVuektJs2VueTask = tasks.getByPath(':vuekt-js2vue:publishToMavenLocal') 84 | 85 | packages.dependsOn(publishVuektTask) 86 | packages.dependsOn(publishVuektJs2VueTask) 87 | packages.mustRunAfter(publishVuektTask) 88 | packages.mustRunAfter(publishVuektJs2VueTask) -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/array.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | // See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 4 | 5 | package org.musyozoku.vuekt 6 | 7 | /** 8 | * The push() method adds one or more elements to the end of an array and returns the new length of the array. 9 | */ 10 | inline fun Array.push(vararg element: T): Int 11 | = this.asDynamic().push.apply(this, element) 12 | 13 | /** 14 | * The pop() method removes the last element from an array and returns that element. 15 | * This method changes the length of the array. 16 | */ 17 | inline fun Array.pop(): T? 18 | = this.asDynamic().pop() 19 | 20 | /** 21 | * The shift() method removes the first element from an array and returns that element. 22 | * This method changes the length of the array. 23 | */ 24 | inline fun Array.shift(): T? 25 | = this.asDynamic().shift() 26 | 27 | /** 28 | * The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array. 29 | */ 30 | inline fun Array.unshift(vararg element: T): Int 31 | = this.asDynamic().unshift.apply(this, element) 32 | 33 | /** 34 | * The splice() method changes the contents of an array by removing existing elements. 35 | */ 36 | inline fun Array.splice(index: Int): Array 37 | = this.asDynamic().splice.apply(this, arrayOf(index)) 38 | 39 | /** 40 | * The splice() method changes the contents of an array by removing existing elements. 41 | */ 42 | inline fun Array.splice(index: Int, howMany: Int): Array 43 | = this.asDynamic().splice.apply(this, arrayOf(index, howMany)) 44 | 45 | /** 46 | * The splice() method changes the contents of an array by removing existing elements and adding new elements. 47 | */ 48 | inline fun Array.splice(index: Int, howMany: Int, vararg element: T): Array 49 | = this.asDynamic().splice.apply(this, arrayOf(index, howMany) + element) 50 | 51 | /** 52 | * The sort() method sorts the elements of an array in place and returns the array. 53 | * The sort is not necessarily stable. 54 | * The default sort order is according to string Unicode code points. 55 | */ 56 | inline fun Array.sort(): Array 57 | = this.asDynamic().sort() 58 | 59 | /** 60 | * The sort() method sorts the elements of an array in place and returns the array. 61 | * The sort is not necessarily stable. 62 | */ 63 | inline fun Array.sort(noinline compareFunction: (a: T, b: T) -> Int): Array 64 | = this.asDynamic().sort(compareFunction) 65 | 66 | /** 67 | * The reverse() method reverses an array in place. 68 | * The first array element becomes the last, and the last array element becomes the first. 69 | */ 70 | inline fun Array.reverse(): Array 71 | = this.asDynamic().reverse() 72 | -------------------------------------------------------------------------------- /guide/class-and-style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Class and Style Bindings / クラスとスタイルのバインディング 6 | 20 | 21 | 22 | 23 |

Binding HTML Classes

24 | 25 | 26 | 27 | 28 |

Object Syntax

29 |
30 |
{ active: isActive }
31 |
{ active: isActive, 'text-danger': hasError }
33 |
classObject
34 |
35 | 36 | 42 | 43 | 44 | 45 | 46 |

Array Syntax

47 |
48 |
[activeClass, errorClass]
49 |
[isActive ? activeClass : '', errorClass]
50 |
[{ active: isActive }, errorClass]
51 |
52 | 53 | 59 | 60 | 61 | 62 | 63 |

With Components

64 |
65 | baz boo 66 | { active: isActive } 67 |
68 | 69 |

Binding Inline Styles

70 | 71 | 72 | 73 | 74 |

Object Syntax

75 |
76 |
77 | { color: activeColor, fontSize: fontSize + 'px' }
78 |
79 |
[baseStyles, overridingStyles]
80 |
81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /guide/instance/main/Instance.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | import kotlin.browser.document 3 | 4 | // var data = { a: 1 } 5 | // var vm = new Vue({ 6 | // el: '#example', 7 | // data: data 8 | // }) 9 | // 10 | // // These reference the same object! 11 | // // これらは同じオブジェクトを参照します! 12 | // vm.$data === data // -> true 13 | // 14 | // // Setting the property on the instance also affects the original data 15 | // // プロパティへの代入は、元のデータにも反映されます 16 | // vm.a = 2 17 | // data.a // => 2 18 | // 19 | // // ... and vice-versa 20 | // // ... そして、その逆もまたしかりです 21 | // data.a = 3 22 | // vm.a // => 3 23 | // 24 | // vm.$el === document.getElementById('example') // -> true 25 | // 26 | // // $watch is an instance method 27 | // // $watch はインスタンスメソッドです 28 | // vm.$watch('a', function (newVal, oldVal) { 29 | // // This callback will be called when `vm.a` changes 30 | // // このコールバックは `vm.a` の値が変わる時に呼ばれます 31 | // }) 32 | 33 | @JsModule(vue.MODULE) 34 | @JsNonModule 35 | @JsName(vue.CLASS) 36 | external class ExampleVue(options: ComponentOptions?) : Vue { 37 | var a: Int 38 | } 39 | 40 | fun main(args: Array) { 41 | val data = json { 42 | a = 1 43 | } 44 | val vm = ExampleVue(ComponentOptions { 45 | el = ElementConfig("#example") 46 | this.data = Data(data) 47 | 48 | // https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks 49 | // https://jp.vuejs.org/v2/guide/instance.html#インスタンスライフサイクルフック 50 | 51 | beforeCreate = { 52 | val self = thisAs() 53 | println("beforeCreate: ${self.a}") 54 | } 55 | created = { 56 | val self = thisAs() 57 | println("created: ${self.a}") 58 | } 59 | beforeMount = { 60 | val self = thisAs() 61 | println("beforeMount: ${self.a}") 62 | } 63 | mounted = { 64 | val self = thisAs() 65 | println("mounted: ${self.a}") 66 | } 67 | beforeUpdate = { 68 | val self = thisAs() 69 | println("beforeUpdate: ${self.a}") 70 | } 71 | updated = { 72 | val self = thisAs() 73 | println("updated: ${self.a}") 74 | } 75 | beforeDestroy = { 76 | val self = thisAs() 77 | println("beforeDestroy: ${self.a}") 78 | } 79 | destroyed = { 80 | val self = thisAs() 81 | println("destroyed: ${self.a}") 82 | } 83 | }) 84 | 85 | println("vm.data: ${vm.`$data` === data}") // => true 86 | 87 | vm.a = 2 88 | println("data.a: ${data.a}") // => 2 89 | 90 | data.a = 3 91 | println("vm.a: ${vm.a}") // => 3 92 | 93 | println("vm.el: ${vm.`$el` === document.getElementById("example")}") // => true 94 | 95 | vm.`$watch`("a", { 96 | newVal: Int, oldVal: Int -> 97 | println("vm.watch: $oldVal -> $newVal") 98 | thisAs().`$destroy`() 99 | }) 100 | 101 | vm.a = 4 // 'watch' function is called. 102 | } -------------------------------------------------------------------------------- /guide/events_/main/Events.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | import org.w3c.dom.Element 3 | import org.w3c.dom.events.Event 4 | 5 | // var example1 = new Vue({ 6 | // el: '#example-1', 7 | // data: { 8 | // counter: 0 9 | // } 10 | // }) 11 | 12 | @JsModule(vue.MODULE) 13 | @JsNonModule 14 | @JsName(vue.CLASS) 15 | external class Example1Vue(options: ComponentOptions) : Vue { 16 | var counter: Int 17 | } 18 | 19 | val example1 = Example1Vue(ComponentOptions { 20 | el = ElementConfig("#example-1") 21 | data = Data(json = json { 22 | counter = 0 23 | }) 24 | }) 25 | 26 | // var example2 = new Vue({ 27 | // el: '#example-2', 28 | // data: { 29 | // name: 'Vue.js' 30 | // }, 31 | // // define methods under the `methods` object 32 | // // `methods` オブジェクトの下にメソッドを定義する 33 | // methods: { 34 | // greet: function (event) { 35 | // // `this` inside methods points to the Vue instance 36 | // // メソッド内の `this` は、 Vue インスタンスを参照します 37 | // alert('Hello ' + this.name + '!') 38 | // // `event` is the native DOM event 39 | // // `event` は、ネイティブ DOM イベントです 40 | // if (event) { 41 | // alert(event.target.tagName) 42 | // } 43 | // } 44 | // } 45 | // }) 46 | //// JavaScript からメソッドを呼び出すこともできます 47 | //example2.greet() // => 'Hello Vue.js!' 48 | 49 | @JsModule(vue.MODULE) 50 | @JsNonModule 51 | @JsName(vue.CLASS) 52 | external class Example2Vue(options: ComponentOptions) : Vue { 53 | var name: String 54 | } 55 | 56 | external fun alert(message: String) 57 | 58 | val example2 = Example2Vue(ComponentOptions { 59 | el = ElementConfig("#example-2") 60 | data = Data(json = json { 61 | name = "Vue.js" 62 | }) 63 | methods = json { 64 | this["greet"] = { event: Event? -> 65 | val self = thisAs() 66 | alert("Hello ${self.name}!") 67 | event?.let { 68 | alert((it.target as Element).tagName) 69 | } 70 | } 71 | } 72 | }) 73 | 74 | // new Vue({ 75 | // el: '#example-3', 76 | // methods: { 77 | // say: function (message) { 78 | // alert(message) 79 | // } 80 | // } 81 | // warn: function (message, event) { 82 | // // now we have access to the native event 83 | // // ネイティブイベントを参照しています 84 | // if (event) event.preventDefault() 85 | // alert(message) 86 | // } 87 | // }) 88 | 89 | val example3 = Vue(ComponentOptions { 90 | el = ElementConfig("#example-3") 91 | methods = json { 92 | this["say"] = { message: String -> 93 | alert(message) 94 | } 95 | this["warn"] = { message: String, event: Event? -> 96 | event?.preventDefault() 97 | alert(message) 98 | } 99 | } 100 | }) 101 | 102 | // // enable v-on:keyup.f1 103 | // Vue.config.keyCodes.f1 = 112 104 | 105 | val example4 = Vue(ComponentOptions { 106 | el = ElementConfig("#example-4") 107 | methods = json { 108 | this["submit"] = { 109 | alert("submit") 110 | } 111 | } 112 | }) 113 | 114 | fun main(args: Array) { 115 | Vue.config.keyCodes["f1"] = 112 116 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 63 | 64 | -------------------------------------------------------------------------------- /guide/class-and-style/main/ClassAndStyle.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | import kotlin.js.Json 3 | 4 | // data: { 5 | // isActive: true, 6 | // hasError: false 7 | // } 8 | 9 | // data: { 10 | // isActive: true, 11 | // error: null 12 | // }, 13 | // computed: { 14 | // classObject: function () { 15 | // return { 16 | // active: this.isActive && !this.error, 17 | // 'text-danger': this.error && this.error.type === 'fatal', 18 | // } 19 | // } 20 | // } 21 | 22 | @JsModule(vue.MODULE) 23 | @JsNonModule 24 | @JsName(vue.CLASS) 25 | external class Example1Vue(options: ComponentOptions) : Vue { 26 | var isActive: Boolean 27 | var hasError: Boolean 28 | var error: dynamic // FIXME 29 | } 30 | 31 | val example1 = Example1Vue(ComponentOptions { 32 | el = ElementConfig("#example1") 33 | data = Data(json = json { 34 | isActive = true 35 | hasError = false 36 | error = null 37 | }) 38 | computed = json { 39 | this["classObject"] = ComputedConfig { 40 | val self = thisAs() 41 | json { 42 | this["active"] = self.isActive && self.error == null 43 | this["text-danger"] = self.error != null && self.error.type == "fatal" 44 | } 45 | } 46 | } 47 | }) 48 | 49 | // data: { 50 | // activeClass: 'active', 51 | // errorClass: 'text-danger' 52 | // } 53 | 54 | @JsModule(vue.MODULE) 55 | @JsNonModule 56 | @JsName(vue.CLASS) 57 | external class Example2Vue(options: ComponentOptions) : Vue { 58 | var isActive: Boolean 59 | var activeClass: String 60 | var errorClass: String 61 | } 62 | 63 | val example2 = Example2Vue(ComponentOptions { 64 | el = ElementConfig("#example2") 65 | data = Data(json = json { 66 | isActive = false 67 | activeClass = "active" 68 | errorClass = "text-danger" 69 | }) 70 | }) 71 | 72 | // Vue.component('my-component', { 73 | // template: '

Hi

' 74 | // }) 75 | 76 | external class MyComponent : Vue 77 | 78 | val myComponent = Vue.component("my-component", Component(ComponentOptions { 79 | template = """

Hi, my component

""" 80 | })) 81 | 82 | @JsModule(vue.MODULE) 83 | @JsNonModule 84 | @JsName(vue.CLASS) 85 | external class Example3Vue(options: ComponentOptions) : Vue { 86 | var isActive: Boolean 87 | } 88 | 89 | val example3 = Example3Vue(ComponentOptions { 90 | el = ElementConfig("#example3") 91 | data = Data(json = json { 92 | isActive = true 93 | }) 94 | }) 95 | 96 | // data: { 97 | // activeColor: 'red', 98 | // fontSize: 30 99 | // } 100 | 101 | // data: { 102 | // styleObject: { 103 | // color: 'red', 104 | // fontSize: '13px' 105 | // } 106 | // } 107 | 108 | @JsModule(vue.MODULE) 109 | @JsNonModule 110 | @JsName(vue.CLASS) 111 | external class Example4Vue(options: ComponentOptions) : Vue { 112 | var activeColor: String 113 | var fontSize: Int 114 | var styleObject: Styles 115 | var baseStyles: Styles 116 | var overridingStyles: Styles 117 | } 118 | 119 | external interface Styles { 120 | var color: String 121 | var fontSize: String 122 | } 123 | 124 | val example4 = Example4Vue(ComponentOptions { 125 | el = ElementConfig("#example4") 126 | data = Data(json = json { 127 | activeColor = "red" 128 | fontSize = 30 129 | styleObject = json { 130 | color = "red" 131 | fontSize = "13px" 132 | } 133 | baseStyles = json { 134 | color = "#777777" 135 | } 136 | overridingStyles = json { 137 | fontSize = "25px" 138 | } 139 | }) 140 | }) -------------------------------------------------------------------------------- /guide/forms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Form Input Bindings / フォーム入力バインディング 6 | 7 | 8 | 9 |

Basic Usage

10 | 11 |
12 | 13 | 14 |

Text

15 | 16 |

Message is: {{ message }}

17 | 18 | 19 | 20 |

Multiline text

21 | Multiline message is: 22 |

{{ message }}

23 | 24 | 25 | 26 | 27 |

Checkbox

28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | Checked names: {{ checkedNames }} 39 | 40 | 41 | 42 |

Radio

43 | 44 | 45 |
46 | 47 | 48 |
49 | Picked: {{ picked }} 50 | 51 | 52 | 53 |

Select

54 | 60 | Selected: {{ selected }} 61 |
62 | 67 |
68 | Selected: {{ multiSelected }} 69 |
70 | 75 | Selected: {{ selected }} 76 |
77 | 78 |

Value Bindings

79 | 80 |
81 | 82 | 83 |

Checkbox

84 | 89 | Selected: {{ toggle }} 90 | 91 | 92 | 93 |

Radio

94 | 95 | 96 |
97 | Picked: {{ pick }} 98 | 99 | 100 | 101 |

Select Options

102 | 107 |
108 | Selected: {{ selected }} 109 |
110 | 111 |

Modifiers

112 | 113 | 114 | 115 | 116 |
117 | 118 | 119 | 120 |
121 |
msg: '{{ msg }}'
122 | age: {{ age }} 123 |
124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/vnode.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | package org.musyozoku.vuekt 4 | 5 | import org.w3c.dom.Node 6 | import kotlin.js.Json 7 | 8 | external interface VNode { 9 | var tag: String? 10 | var data: VNodeData? 11 | var children: Array? 12 | var text: String? 13 | var elm: Node? 14 | var ns: String? 15 | var context: Vue? 16 | var key: Key? 17 | var componentOptions: VNodeComponentOptions? 18 | var componentInstance: Vue? 19 | var parent: VNode? 20 | var raw: Boolean? 21 | var isStatic: Boolean? 22 | var isRootInsert: Boolean 23 | var isComment: Boolean 24 | } 25 | 26 | external interface VNodeData { 27 | var key: Key? 28 | var slot: String? 29 | var scopedSlots: ScopedSlotMap? 30 | var ref: String? 31 | var tag: String? 32 | var staticClass: String? 33 | var `class`: Any? 34 | var staticStyle: Json? // { [key: String]: Any } 35 | var style: OneOrMany? // Array | Object 36 | var props: Json? // { [key: String]: Any } 37 | var attrs: Json? // { [key: String]: Any } 38 | var domProps: Json? // { [key: String]: Any } 39 | var hook: JsonOf>? // { [key: String]: Function } 40 | var on: JsonOf>>? // { [key: String]: Function | Array } 41 | var nativeOn: JsonOf>>? // { [key: String]: Function | Array } 42 | var transition: Json? 43 | var show: Boolean? 44 | var inlineTemplate: InlineTemplate? 45 | var directives: Array? 46 | var keepAlive: Boolean? 47 | } 48 | 49 | external interface InlineTemplate { 50 | var render: Function 51 | var staticRenderFns: Array> 52 | } 53 | 54 | external interface VNodeDirective { 55 | val name: String 56 | val value: Any 57 | val oldValue: Any 58 | val expression: Any 59 | val arg: String 60 | val modifiers: JsonOf // { [key: String]: Boolean } 61 | } 62 | 63 | external interface VNodeComponentOptions { 64 | var Ctor: Any // typeof Vue 65 | var propsData: Json? 66 | var listeners: Json? 67 | var children: VNodeChildren? 68 | var tag: String? 69 | } 70 | 71 | /** 72 | * `String | Number` 73 | */ 74 | external interface Key 75 | 76 | inline fun Key(name: String): Key = name.asDynamic() 77 | inline fun Key(index: Int): Key = index.asDynamic() 78 | 79 | inline fun Key.toName(): String = this.asDynamic() 80 | inline fun Key.toIndex(): Int = this.asDynamic() 81 | 82 | /** 83 | * `{ [propertyName: String]: ScopedSlot }` 84 | */ 85 | external interface ScopedSlotMap : JsonOf 86 | 87 | /** 88 | * `(props: Any) -> VNodeChildrenArrayContents | String` 89 | */ 90 | external interface ScopedSlot 91 | 92 | inline fun ScopedSlot(func: (props: Any) -> VNodeChildrenArrayContents): ScopedSlot = func.asDynamic() 93 | inline fun ScopedSlot(value: String): ScopedSlot = value.asDynamic() // TODO change parameter name 94 | 95 | inline fun ScopedSlot.toFunction(): (props: Any) -> VNodeChildrenArrayContents = this.asDynamic() 96 | inline fun ScopedSlot.toString(): String = this.asDynamic() // TODO change method name 97 | 98 | /** 99 | * `VNodeChildrenArrayContents | [ScopedSlot] | String` 100 | */ 101 | external interface VNodeChildren 102 | 103 | inline fun VNodeChildren(contents: VNodeChildrenArrayContents): VNodeChildren = contents.asDynamic() 104 | inline fun VNodeChildren(scopedSlot: Array): VNodeChildren = scopedSlot.asDynamic() 105 | inline fun VNodeChildren(value: String): VNodeChildren = value.asDynamic() // TODO change parameter name 106 | 107 | inline fun VNodeChildren.toContents(): VNodeChildrenArrayContents = this.asDynamic() 108 | inline fun VNodeChildren.toScopedSlot(): Array = this.asDynamic() 109 | inline fun VNodeChildren.toString(): String = this.asDynamic() // TODO change method name 110 | 111 | /** 112 | * `{ [x: Number]: VNode | String | VNodeChildren }` 113 | */ 114 | external interface VNodeChildrenArrayContents 115 | 116 | inline operator fun VNodeChildrenArrayContents.get(x: Int): Contents = this.asDynamic()[x] 117 | inline operator fun VNodeChildrenArrayContents.set(x: Int, value: Contents) { 118 | this.asDynamic()[x] = value 119 | } 120 | 121 | /** 122 | * `VNode | String | VNodeChildren` 123 | */ 124 | external interface Contents 125 | 126 | inline fun Contents(node: VNode): Contents = node.asDynamic() 127 | inline fun Contents(value: String): Contents = value.asDynamic() // TODO change parameter name 128 | inline fun Contents(children: VNodeChildren): Contents = children.asDynamic() 129 | 130 | inline fun Contents.toVNode(): VNode = this.asDynamic() 131 | inline fun Contents.toString(): String = this.asDynamic() // TODO change method name 132 | inline fun Contents.toVNodeChildren(): VNodeChildren = this.asDynamic() 133 | -------------------------------------------------------------------------------- /guide/index/main/Index.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | import kotlin.js.Date 3 | 4 | @JsModule(vue.MODULE) 5 | @JsNonModule 6 | @JsName(vue.CLASS) 7 | external class AppVue(options: ComponentOptions) : Vue { 8 | var message: String 9 | } 10 | 11 | // https://vuejs.org/v2/guide/#Declarative-Rendering 12 | // 13 | // var app = new Vue({ 14 | // el: '#app', 15 | // data: { 16 | // message: 'Hello Vue!' 17 | // } 18 | // }) 19 | 20 | val app = AppVue(ComponentOptions { 21 | el = ElementConfig("#app") 22 | data = Data(json = json { 23 | message = "Hello Vue!" 24 | }) 25 | }) 26 | 27 | // Try: input `index.app.message = 'Hi'` on browser console. 28 | 29 | // var app2 = new Vue({ 30 | // el: '#app-2', 31 | // data: { 32 | // message: 'You loaded this page on ' + new Date() 33 | // } 34 | // }) 35 | 36 | val app2 = AppVue(ComponentOptions { 37 | el = ElementConfig("#app-2") 38 | data = Data(json = json { 39 | message = "You loaded this page on ${Date()}" 40 | }) 41 | }) 42 | 43 | // Try: input `index.app2.message = 'some new message'` on browser console. 44 | 45 | // var app3 = new Vue({ 46 | // el: '#app-3', 47 | // data: { 48 | // seen: true 49 | // } 50 | // }) 51 | 52 | @JsModule(vue.MODULE) 53 | @JsNonModule 54 | @JsName(vue.CLASS) 55 | external class App3Vue(options: ComponentOptions) : Vue { 56 | var seen: Boolean 57 | } 58 | 59 | val app3 = App3Vue(ComponentOptions { 60 | el = ElementConfig("#app-3") 61 | data = Data(json = json { 62 | seen = true 63 | }) 64 | }) 65 | 66 | // Try: input `index.app3.seen = false` on browser console. 67 | 68 | // var app4 = new Vue({ 69 | // el: '#app-4', 70 | // data: { 71 | // todos: [ 72 | // { text: 'Learn JavaScript' }, 73 | // { text: 'Learn Vue' }, 74 | // { text: 'Build something awesome' } 75 | // ] 76 | // } 77 | // }) 78 | 79 | @JsModule(vue.MODULE) 80 | @JsNonModule 81 | @JsName(vue.CLASS) 82 | external class App4Vue(options: ComponentOptions) : Vue { 83 | var todos: Array 84 | } 85 | 86 | class Text(var text: String) 87 | 88 | val app4 = App4Vue(ComponentOptions { 89 | el = ElementConfig("#app-4") 90 | data = Data(json = json { 91 | todos = arrayOf( 92 | Text("Learn JavaScript"), 93 | Text("Learn Vue"), 94 | Text("Build something awesome") 95 | ) 96 | }) 97 | }) 98 | 99 | // Try: input `index.app4.todos.push({ text: 'New item' })` on browser console. 100 | 101 | // var app5 = new Vue({ 102 | // el: '#app-5', 103 | // data: { 104 | // message: 'Hello Vue.js!' 105 | // }, 106 | // methods: { 107 | // reverseMessage: function () { 108 | // this.message = this.message.split('').reverse().join('') 109 | // } 110 | // } 111 | // }) 112 | 113 | val app5 = AppVue(ComponentOptions { 114 | el = ElementConfig("#app-5") 115 | data = Data(json = json { 116 | message = "Hello Vue.js!" 117 | }) 118 | methods = json { 119 | this["reverseMessage"] = { 120 | val self = thisAs() 121 | self.message = self.message.split("").reversed().joinToString("") 122 | } 123 | } 124 | }) 125 | 126 | // var app6 = new Vue({ 127 | // el: '#app-6', 128 | // data: { 129 | // message: 'Hello Vue!' 130 | // } 131 | // }) 132 | 133 | val app6 = AppVue(ComponentOptions { 134 | el = ElementConfig("#app-6") 135 | data = Data(json = json { 136 | message = "Hello Vue!" 137 | }) 138 | }) 139 | 140 | // Vue.component('todo-item', { 141 | // props: ['todo'], 142 | // template: '
  • {{ todo.text }}
  • ' 143 | // }) 144 | // 145 | // var app7 = new Vue({ 146 | // el: '#app-7', 147 | // data: { 148 | // groceryList: [ 149 | // { id: 0, text: 'Vegetables' }, 150 | // { id: 1, text: 'Cheese' }, 151 | // { id: 2, text: 'Whatever else humans are supposed to eat' } 152 | // ] 153 | // } 154 | // }) 155 | 156 | @JsModule(vue.MODULE) 157 | @JsNonModule 158 | @JsName(vue.CLASS) 159 | external class App7Vue(options: ComponentOptions) : Vue { 160 | var groceryList: Array 161 | } 162 | 163 | class Item(var id: Int, var text: String) 164 | 165 | val TodoItem = Vue.component("todo-item", Component(ComponentOptions { 166 | props = Props(arrayOf("todo")) 167 | template = "
  • {{ todo.text }}
  • " 168 | })) 169 | 170 | val app7 = App7Vue(ComponentOptions { 171 | el = ElementConfig("#app-7") 172 | data = Data(json = json { 173 | groceryList = arrayOf( 174 | Item(0, "Vegetables"), 175 | Item(1, "Cheese"), 176 | Item(2, "Whatever else humans are supposed to eat") 177 | ) 178 | }) 179 | }) -------------------------------------------------------------------------------- /guide/components.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Components / コンポーネント 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 |
    15 | 16 | 17 | 18 | 19 |
    20 | 21 | 22 | 23 |
    24 | 25 | 26 | 27 | 28 |
    29 | 30 | 31 |
    32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
    42 |

    {{ total }}

    43 | 44 | 45 |
    46 | 47 | 48 | 49 | 50 |
    51 | 52 | 53 | 54 | 55 |

    Total: ${{ total }}

    56 |
    57 | 58 | 59 | 60 | 61 |
    62 | 63 | 64 | {{ foo }} 65 |
    66 | 67 | 68 | 69 | 70 |
    71 |

    I'm the parent title

    72 | 73 |

    This is some original content

    74 |

    This is some more original content

    75 |
    76 |
    77 | 78 | 79 | 80 | 81 |
    82 | 83 |

    Here might be a page title

    84 |

    A paragraph for the main content.

    85 |

    And another one.

    86 |

    Here's some contact info

    87 |
    88 |
    89 | 90 | 91 | 92 | 93 |
    94 | 95 | 96 | 99 | 100 |
    101 | 102 | 103 | 104 | 105 |
    106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
    114 | 115 | 116 | 117 | 118 |
    119 |
    120 | 121 |
    122 |
    123 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/vue.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | // See also: Vue 2.x [https://vuejs.org/v2/api/] 4 | 5 | package org.musyozoku.vuekt 6 | 7 | import org.w3c.dom.HTMLElement 8 | import kotlin.js.* 9 | 10 | object vue { 11 | const val MODULE = "vue" 12 | const val CLASS = "Vue" 13 | } 14 | 15 | /** 16 | * `??? -> VNode` 17 | */ 18 | typealias CreateElement = Function 19 | 20 | /** 21 | * # Example 22 | * 23 | * ``` 24 | * @JsModule(vue.MODULE) 25 | * @JsNonModule 26 | * @JsName(vue.CLASS) 27 | * external class MyVue(options: ComponentOptions) : Vue { 28 | * var message: String 29 | * } 30 | * 31 | * MyVue(ComponentOptions { 32 | * ... 33 | * }) 34 | * ``` 35 | * 36 | * # Example Component 37 | * 38 | * ``` 39 | * external class MyComponent : Vue 40 | * 41 | * Vue.component("my-component", Component(ComponentOptions { 42 | * ... 43 | * }) 44 | * ``` 45 | */ 46 | @JsModule(vue.MODULE) 47 | @JsNonModule 48 | @JsName(vue.CLASS) 49 | external open class Vue(options: ComponentOptions? = definedExternally) { 50 | 51 | companion object { 52 | // Global Config 53 | val config: VueConfig 54 | // Global API 55 | fun extend(options: Any /* ComponentOptions | FunctionalComponentOptions */): Any // typeof Vue 56 | 57 | fun nextTick(callback: () -> Unit, context: Array? = definedExternally) 58 | fun nextTick(): Promise 59 | fun set(target: Any, key: String, value: T): T 60 | fun set(target: Array, key: Int, value: T): T 61 | fun delete(target: Json, key: String) 62 | fun delete(target: Array, key: Int) 63 | fun directive(id: String, definition: DirectiveConfig? = definedExternally): DirectiveOptions 64 | fun filter(id: String, definition: Function? = definedExternally): Function 65 | fun component(id: String, definition: Component? = definedExternally): Any // typeof Vue 66 | fun component(id: String, definition: AsyncComponent? = definedExternally): Any // typeof Vue 67 | fun use(plugin: PluginConfig, options: T?) 68 | fun mixin(mixin: Any /* typeof Vue | ComponentOptions */) 69 | fun compile(template: String) 70 | val version: String 71 | } 72 | 73 | // Instance Properties 74 | var `$data`: Vue 75 | val `$el`: HTMLElement 76 | val `$options`: ComponentOptions 77 | val `$parent`: Vue 78 | val `$root`: Vue 79 | val `$children`: Array 80 | val `$refs`: JsonOf // { [key: String]: Vue | Element | Array | Array } 81 | val `$slots`: JsonOf?> // { [key: String]: Array } 82 | val `$scopedSlots`: ScopedSlotMap 83 | val `$isServer`: Boolean 84 | val `$ssrContext`: Any 85 | val `$props`: Any 86 | val `$vnode`: VNode 87 | val `$attrs`: Any // { [key: String]: String } | void 88 | val `$listeners`: Any // { [key: String]: Function | Array } | void 89 | 90 | var `$createElement`: CreateElement 91 | 92 | // Instance Methods / Data 93 | fun `$watch`( 94 | exp: String, 95 | callback: WatchHandler, 96 | options: WatchOptions? = definedExternally): () -> Unit 97 | 98 | fun `$watch`( 99 | fn: () -> T, 100 | callback: WatchHandler, 101 | options: WatchOptions? = definedExternally): () -> Unit 102 | 103 | fun `$set`(target: Any, key: String, value: T): T 104 | fun `$set`(target: Array, key: Int, value: T): T 105 | fun `$delete`(target: Json, key: String) 106 | fun `$delete`(target: Array, key: Int) 107 | 108 | // Instance Methods / Event 109 | fun `$on`(event: String, callback: Function): Vue // -> this 110 | 111 | fun `$on`(event: Array, callback: Function): Vue // -> this 112 | fun `$once`(event: String, callback: Function): Vue // -> this 113 | fun `$off`(event: String? = definedExternally, callback: Function? = definedExternally): Vue // -> this 114 | fun `$off`(event: Array? = definedExternally, callback: Function? = definedExternally): Vue // -> this 115 | fun `$emit`(event: String, vararg args: Any): Vue // -> this 116 | 117 | // Instance Methods / Lifecycle 118 | fun `$mount`(elementOrSelector: Any? /* Element | String */ = definedExternally, 119 | hydrating: Boolean? = definedExternally): Vue // -> this 120 | fun `$forceUpdate`() 121 | fun `$destroy`() 122 | fun `$nextTick`(callback: () -> Unit) // V.() -> Unit 123 | fun `$nextTick`(): Promise 124 | } 125 | 126 | external interface VueConfig { 127 | 128 | val silent: Boolean 129 | val optionMergeStrategies: JsonOf?> // { [key: String]: Function } 130 | val devtools: Boolean 131 | val productionTip: Boolean 132 | val performance: Boolean 133 | val errorHandler: (err: Error, vm: Vue, info: String) -> Unit 134 | val warnHandler: (msg: String, vm: Vue, trace: String) -> Unit 135 | val ignoredElements: Array 136 | val keyCodes: JsonOf // { [key: String]: Number } 137 | } 138 | 139 | external interface CompileResult { 140 | fun render(createElement: Any /* typeof Vue.prototype.$createElement */): VNode 141 | var staticRenderFns: Array<() -> VNode> 142 | } 143 | 144 | /** 145 | * `Vue | Element | Array | Array` 146 | */ 147 | external interface Ref 148 | 149 | inline fun Ref(vm: Vue): Ref = vm.asDynamic() 150 | inline fun Ref(element: HTMLElement): Ref = element.asDynamic() 151 | inline fun Ref(vms: Array): Ref = vms.asDynamic() 152 | inline fun Ref(elements: Array): Ref = elements.asDynamic() 153 | 154 | inline fun Ref.toVue(): Vue = this.asDynamic() 155 | inline fun Ref.toHTMLElement(): HTMLElement = this.asDynamic() 156 | inline fun Ref.toVueList(): Array = this.asDynamic() 157 | inline fun Ref.toHTMLElementList(): Array = this.asDynamic() 158 | -------------------------------------------------------------------------------- /guide/computed/main/Computed.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | 3 | @JsNonModule 4 | @JsModule("axios") 5 | external val axios: dynamic 6 | 7 | @JsModule(vue.MODULE) 8 | @JsNonModule 9 | @JsName(vue.CLASS) 10 | external class ExampleVue(options: ComponentOptions) : Vue { 11 | var message: String 12 | val reversedMessage: String 13 | } 14 | 15 | @JsModule(vue.MODULE) 16 | @JsNonModule 17 | @JsName(vue.CLASS) 18 | external class DemoVue(options: ComponentOptions) : Vue { 19 | var firstName: String 20 | var lastName: String 21 | var fullName: String 22 | } 23 | 24 | @JsModule(vue.MODULE) 25 | @JsNonModule 26 | @JsName(vue.CLASS) 27 | external class WatchExampleVue(options: ComponentOptions) : Vue { 28 | var question: String 29 | var answer: String 30 | fun getAnswer() 31 | } 32 | 33 | fun main(args: Array) { 34 | 35 | // var vm = new Vue({ 36 | // el: '#example', 37 | // data: { 38 | // message: 'Hello' 39 | // }, 40 | // computed: { 41 | // // a computed getter 42 | // // 算出 getter 関数 43 | // reversedMessage: function () { 44 | // // `this` points to the vm instance 45 | // // `this` は vm インスタンスを指します 46 | // return this.message.split('').reverse().join('') 47 | // } 48 | // } 49 | // }) 50 | 51 | val example = ExampleVue(ComponentOptions { 52 | el = ElementConfig("#example") 53 | data = Data(json = json { 54 | message = "Hello" 55 | }) 56 | computed = json { 57 | this["reversedMessage"] = ComputedConfig { 58 | val self = thisAs() 59 | self.message.split("").reversed().joinToString("") 60 | } 61 | } 62 | }) 63 | 64 | // console.log(vm.reversedMessage) // => 'olleH' 65 | // vm.message = 'Goodbye' 66 | // console.log(vm.reversedMessage) // => 'eybdooG' 67 | 68 | println(example.reversedMessage) // => 'olleH' 69 | example.message = "Goodbye" 70 | println(example.reversedMessage) // => 'eybdooG' 71 | 72 | // var vm = new Vue({ 73 | // el: '#demo', 74 | // data: { 75 | // firstName: 'Foo', 76 | // lastName: 'Bar' 77 | // }, 78 | // computed: { 79 | // fullName: { 80 | // // getter 81 | // get: function () { 82 | // return this.firstName + ' ' + this.lastName 83 | // }, 84 | // // setter 85 | // set: function (newValue) { 86 | // var names = newValue.split(' ') 87 | // this.firstName = names[0] 88 | // this.lastName = names[names.length - 1] 89 | // } 90 | // } 91 | // } 92 | // }) 93 | 94 | val demo = DemoVue(ComponentOptions { 95 | el = ElementConfig("#demo") 96 | data = Data(json = json { 97 | firstName = "Foo" 98 | lastName = "Bar" 99 | }) 100 | computed = json { 101 | this["fullName"] = ComputedConfig(ComputedOptions { 102 | get = { 103 | val self = thisAs() 104 | "${self.firstName} ${self.lastName}" 105 | } 106 | set = { newValue -> 107 | val (firstName, lastName) = newValue.split(" ") 108 | val self = thisAs() 109 | self.firstName = firstName 110 | self.lastName = lastName 111 | } 112 | }) 113 | } 114 | }) 115 | 116 | println(demo.fullName) // => 'Foo Bar' 117 | demo.fullName = "John Doe" 118 | println(demo.fullName) // => 'John Doe' 119 | println(demo.firstName) // => 'John' 120 | println(demo.lastName) // => 'Doe' 121 | 122 | // var watchExampleVM = new Vue({ 123 | // el: '#watch-example', 124 | // data: { 125 | // question: '', 126 | // answer: 'I cannot give you an answer until you ask a question!' 127 | // }, 128 | // watch: { 129 | // // whenever question changes, this function will run 130 | // question: function (newQuestion) { 131 | // this.answer = 'Waiting for you to stop typing...' 132 | // this.getAnswer() 133 | // } 134 | // }, 135 | // methods: { 136 | // // _.debounce is a function provided by lodash to limit how 137 | // // often a particularly expensive operation can be run. 138 | // // In this case, we want to limit how often we access 139 | // // yesno.wtf/api, waiting until the user has completely 140 | // // finished typing before making the ajax request. To learn 141 | // // more about the _.debounce function (and its cousin 142 | // // _.throttle), visit: https://lodash.com/docs#debounce 143 | // getAnswer: _.debounce( 144 | // function () { 145 | // if (this.question.indexOf('?') === -1) { 146 | // this.answer = 'Questions usually contain a question mark. ;-)' 147 | // return 148 | // } 149 | // this.answer = 'Thinking...' 150 | // var vm = this 151 | // axios.get('https://yesno.wtf/api') 152 | // .then(function (response) { 153 | // vm.answer = _.capitalize(response.data.answer) 154 | // }) 155 | // .catch(function (error) { 156 | // vm.answer = 'Error! Could not reach the API. ' + error 157 | // }) 158 | // }, 159 | // // This is the number of milliseconds we wait for the 160 | // // user to stop typing. 161 | // 500 162 | // ) 163 | // } 164 | // }) 165 | 166 | WatchExampleVue(ComponentOptions { 167 | el = ElementConfig("#watch-example") 168 | data = Data(json = json { 169 | question = "" 170 | answer = "I cannot give you an answer until you ask a question!" 171 | }) 172 | watch = json { 173 | this["question"] = Watcher { _, _ -> 174 | val self = thisAs() 175 | self.answer = "Waiting for you to stop typing..." 176 | self.getAnswer() 177 | } 178 | } 179 | methods = json { 180 | this["getAnswer"] = lodash.debounce( 181 | fun() { 182 | val self = thisAs() 183 | if (self.question.indexOf("?") == -1) { 184 | self.answer = "Questions usually contain a question mark. ;-)" 185 | } else { 186 | self.answer = "Thinking..." 187 | axios.get("https://yesno.wtf/api") 188 | .then(fun(response: dynamic) { 189 | self.answer = lodash.capitalize(response.data.answer as String) 190 | }) 191 | .catch(fun(error: String) { 192 | self.answer = "Error! Could not reach the API. " + error 193 | }) 194 | } 195 | }, 196 | 500 197 | ) 198 | } 199 | }) 200 | } -------------------------------------------------------------------------------- /guide/list/main/List.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | 3 | @JsModule(vue.MODULE) 4 | @JsNonModule 5 | @JsName(vue.CLASS) 6 | external class ExampleVue(options: ComponentOptions) : Vue { 7 | var parentMessage: String 8 | var items: Array 9 | var `object`: Person 10 | } 11 | 12 | class Item(val message: String) 13 | 14 | // var example1 = new Vue({ 15 | // el: '#example-1', 16 | // data: { 17 | // items: [ 18 | // { message: 'Foo' }, 19 | // { message: 'Bar' } 20 | // ] 21 | // } 22 | // }) 23 | 24 | val example1 = ExampleVue(ComponentOptions { 25 | el = ElementConfig("#example-1") 26 | data = Data(json = json { 27 | items = arrayOf(Item("Foo"), Item("Bar")) 28 | }) 29 | }) 30 | 31 | // var example2 = new Vue({ 32 | // el: '#example-2', 33 | // data: { 34 | // parentMessage: 'Parent', 35 | // items: [ 36 | // { message: 'Foo' }, 37 | // { message: 'Bar' } 38 | // ] 39 | // } 40 | // }) 41 | 42 | val example2 = ExampleVue(ComponentOptions { 43 | el = ElementConfig("#example-2") 44 | data = Data(json = json { 45 | parentMessage = "Parent" 46 | items = arrayOf(Item("Foo"), Item("Bar")) 47 | }) 48 | }) 49 | 50 | // new Vue({ 51 | // el: '#v-for-object', 52 | // data: { 53 | // object: { 54 | // firstName: 'John', 55 | // lastName: 'Doe', 56 | // age: 30 57 | // } 58 | // } 59 | // }) 60 | 61 | class Person( 62 | val firstName: String, 63 | val lastName: String, 64 | val age: Int) 65 | 66 | val repeatObjectVue = ExampleVue(ComponentOptions { 67 | el = ElementConfig("#repeat-object") 68 | data = Data(json = json { 69 | `object` = Person("John", "Doe", 30) 70 | }) 71 | }) 72 | 73 | // https://vuejs.org/v2/guide/list.html#Mutation-Methods 74 | // https://jp.vuejs.org/v2/guide/list.html#変更メソッド 75 | 76 | fun push() { 77 | println(example1.items.push(Item("Baz"), Item("Boo"))) 78 | } 79 | 80 | fun pop() { 81 | println(example1.items.pop()?.message) 82 | } 83 | 84 | fun shift() { 85 | println(example1.items.shift()?.message) 86 | } 87 | 88 | fun unshift() { 89 | println(example1.items.unshift(Item("FooBar"), Item("FooBoo"))) 90 | } 91 | 92 | fun splice1() { 93 | println(example1.items.splice(1)) 94 | } 95 | 96 | fun splice2() { 97 | println(example1.items.splice(1, 1)) 98 | } 99 | 100 | fun splice3() { 101 | println(example1.items.splice(1, 3, Item("BarBaz"), Item("BarBoo"))) 102 | } 103 | 104 | fun sort() { 105 | println(arrayOf("Foo", "Bar", "Boo").sort()) 106 | } 107 | 108 | fun sortWithFunction() { 109 | println(example1.items.sort { a, b -> if (a.message == b.message) 0 else if (a.message > b.message) 1 else -1 }) 110 | } 111 | 112 | fun reverse() { 113 | println(example1.items.reverse()) 114 | } 115 | 116 | fun replace() { 117 | example1.items = example1.items.filter { it.message.contains("Foo") }.toTypedArray() 118 | } 119 | 120 | // Try following code on browser console: 121 | // m = main 122 | // m.push() 123 | // m.pop() 124 | // m.shift() 125 | // m.unshift() 126 | // m.sort() 127 | // m.sortWithFunction() 128 | // m.reverse() 129 | // m.splice3() 130 | // m.splice2() 131 | // m.splice1() 132 | // m.replace() 133 | 134 | @JsModule(vue.MODULE) 135 | @JsNonModule 136 | @JsName(vue.CLASS) 137 | external class Example3Vue(options: ComponentOptions) : Vue { 138 | var userProfile: UserProfile 139 | } 140 | 141 | class UserProfile(var name: String) 142 | 143 | // var vm = new Vue({ 144 | // data: { 145 | // userProfile: { 146 | // name: 'Anika' 147 | // } 148 | // } 149 | // }) 150 | 151 | val example3 = Example3Vue(ComponentOptions { 152 | el = ElementConfig("#example3") 153 | data = Data(json = json { 154 | userProfile = UserProfile("Anika") 155 | }) 156 | }) 157 | 158 | fun resetUserProfile() { 159 | example3.userProfile = UserProfile("Anika") 160 | } 161 | 162 | fun setAge1() { 163 | Vue.set(example3.userProfile, "age", 27) 164 | } 165 | 166 | fun setAge2() { 167 | example3.`$set`(example3.userProfile, "age", 27) 168 | } 169 | 170 | // Try following code on browser console: 171 | // m = main 172 | // m.setAge1() 173 | // m.resetUserProfile() 174 | // m.setAge2() 175 | // m.resetUserProfile() 176 | 177 | @JsModule(vue.MODULE) 178 | @JsNonModule 179 | @JsName(vue.CLASS) 180 | external class Example4Vue(options: ComponentOptions) : Vue { 181 | var numbers: Array 182 | } 183 | 184 | // data: { 185 | // numbers: [ 1, 2, 3, 4, 5 ] 186 | // }, 187 | // computed: { 188 | // evenNumbers: function () { 189 | // return this.numbers.filter(function (number) { 190 | // return number % 2 === 0 191 | // }) 192 | // } 193 | // } 194 | // methods: { 195 | // even: function (numbers) { 196 | // return numbers.filter(function (number) { 197 | // return number % 2 === 0 198 | // }) 199 | // } 200 | // } 201 | 202 | val example4 = Example4Vue(ComponentOptions { 203 | el = ElementConfig("#example4") 204 | data = Data(json = json { 205 | numbers = arrayOf(1, 2, 3, 4, 5) 206 | }) 207 | computed = json { 208 | this["evenNumbers"] = ComputedConfig { 209 | val self = thisAs() 210 | self.numbers.filter { it % 2 == 0 }.toTypedArray() 211 | } 212 | } 213 | methods = json { 214 | this["even"] = { numbers: Array -> 215 | numbers.filter { it % 2 == 0 }.toTypedArray() 216 | } 217 | } 218 | }) 219 | 220 | // Vue.component('todo-item', { 221 | // template: '\ 222 | //
  • \ 223 | // {{ title }}\ 224 | // \ 225 | //
  • \ 226 | // ', 227 | // props: ['title'] 228 | // }) 229 | // new Vue({ 230 | // el: '#todo-list-example', 231 | // data: { 232 | // newTodoText: '', 233 | // todos: [ 234 | // { 235 | // id: 1, 236 | // title: 'Do the dishes', 237 | // }, 238 | // { 239 | // id: 2, 240 | // title: 'Take out the trash', 241 | // }, 242 | // { 243 | // id: 3, 244 | // title: 'Mow the lawn' 245 | // } 246 | // ], 247 | // nextTodoId: 4 248 | // }, 249 | // methods: { 250 | // addNewTodo: function () { 251 | // this.todos.push({ 252 | // id: this.nextTodoId++, 253 | // title: this.newTodoText 254 | // }) 255 | // this.newTodoText = '' 256 | // } 257 | // } 258 | // }) 259 | 260 | external class TodoItemComponent : Vue 261 | 262 | val todoItem = Vue.component("todo-item", Component(ComponentOptions { 263 | template = """ 264 |
  • 265 | {{ title }} 266 | 267 |
  • 268 | """ 269 | props = Props(arrayOf("title")) 270 | })) 271 | 272 | class TodoItem(val id: Int, val title: String) 273 | 274 | @JsModule(vue.MODULE) 275 | @JsNonModule 276 | @JsName(vue.CLASS) 277 | external class TodoListVue(options: ComponentOptions) : Vue { 278 | var newTodoText: String 279 | var todos: Array 280 | var nextTodoId: Int 281 | } 282 | 283 | val todoListExample = TodoListVue(ComponentOptions { 284 | el = ElementConfig("#todo-list-example") 285 | data = Data(json = json { 286 | newTodoText = "" 287 | todos = arrayOf( 288 | TodoItem(1, "Do the dishes"), 289 | TodoItem(2, "Take out the trash"), 290 | TodoItem(3, "Mow the lawn") 291 | ) 292 | nextTodoId = 4 293 | }) 294 | methods = json { 295 | this["addNewTodo"] = { 296 | val self = thisAs() 297 | self.todos.push(TodoItem(self.nextTodoId++, self.newTodoText)) 298 | self.newTodoText = "" 299 | } 300 | } 301 | }) 302 | -------------------------------------------------------------------------------- /README_ja.md: -------------------------------------------------------------------------------- 1 | [English](./README.md) | 日本語/Japanese 2 | 3 | # vue-kotlin 4 | Kotlin での Vue.js の使用を支援するライブラリとツール 5 | 6 | - vuekt 7 | - Kotlin/JS のための Vue.js の型定義 8 | - vuekt-js2vue 9 | - vue ファイルの代わりとなる Kotlin ファイルを作るためのライブラリ 10 | - CSS には Kotlin DSL を使用 11 | - vuekt-plugin 12 | - vuekt を使った開発を支援する Gradle プラグイン 13 | - vuekt-js2vue を使って書かれたコードから vue ファイルを生成 14 | - kotlin-frontend-plugin を拡張 15 | 16 | ## 使用方法 17 | 18 | ### Gradle の設定 19 | 20 | ```groovy 21 | buildscript { 22 | repositories { 23 | jcenter() 24 | maven { 25 | // kotlin-frontend-plugin (vuekt-plugin depends) 26 | url "https://dl.bintray.com/kotlin/kotlin-eap" 27 | } 28 | maven { 29 | // vuekt-plugin 30 | url "https://nosix.github.io/vue-kotlin/release" 31 | } 32 | } 33 | 34 | dependencies { 35 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 36 | classpath "org.jetbrains.kotlin:kotlin-frontend-plugin:$kotlin_frontend_version" 37 | classpath "org.musyozoku:vuekt-plugin:$vuekt_plugin_version" 38 | } 39 | } 40 | 41 | repositories { 42 | jcenter() 43 | maven { 44 | // vuekt, vuekt-js2vue 45 | url "https://nosix.github.io/vue-kotlin/release" 46 | } 47 | } 48 | 49 | apply plugin: 'kotlin2js' 50 | apply plugin: 'org.jetbrains.kotlin.frontend' // kotlin-frontend-plugin 51 | apply plugin: 'org.musyozoku.vuekt' // vuekt-plugin 52 | 53 | dependencies { 54 | // sub projects 55 | compile project(':greeting') // vue コンポーネントを使うアプリケーション 56 | compile project(':greeting-component') // 単一ファイル vue コンポーネント 57 | } 58 | 59 | kotlinFrontend { 60 | downloadNodeJsVersion = node_version 61 | 62 | npm { 63 | dependency('vue', vue_version) 64 | 65 | devDependency('vue-loader', '*') 66 | devDependency('vue-template-compiler', vue_version) 67 | devDependency('css-loader', '*') 68 | devDependency('html-webpack-plugin', '*') 69 | } 70 | 71 | webpackBundle { 72 | contentPath = file("$projectDir/webContent") 73 | publicPath = "/" 74 | port = 8088 75 | } 76 | } 77 | 78 | subprojects { 79 | 80 | apply plugin: 'kotlin2js' 81 | 82 | dependencies { 83 | compile "org.musyozoku:vuekt:$vuekt_version" 84 | compile "org.musyozoku:vuekt-js2vue:$vuekt_js2vue_version" 85 | } 86 | 87 | sourceSets { 88 | main.kotlin.srcDirs += "main" 89 | test.kotlin.srcDirs += "test" 90 | } 91 | 92 | compileKotlin2Js { 93 | kotlinOptions { 94 | moduleKind = "commonjs" // webpack は CommonJS を使用 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | 必要に応じて行う vuekt-plugin の設定: 101 | 102 | ```groovy 103 | vue { 104 | targetPattern = ".*-component\\.js" 105 | configFile = "01_js2vue.js" 106 | } 107 | ``` 108 | 109 | - targetPattern 110 | - vuekt-plugin は ComponentVue から生成された JavaScript ファイルを targetPattern によって探します 111 | - (検索された) JavaScript ファイルは vue ファイルを生成します 112 | - configFile 113 | - vuekt-plugin は webpack.config.d ディレクトリに追加の webpack config を生成します 114 | - この config は bundle するときに必要とされます 115 | 116 | ### プロジェクト構造 117 | 118 | 例: 119 | 120 | ``` 121 | project_root 122 | greeting 123 | main 124 | [アプリケーション: Kotlin] 125 | greeting-component 126 | main 127 | [コンポーネント: Kotlin] 128 | src 129 | main 130 | kotlin 131 | dummy.kt (*1) 132 | webContent 133 | [ウェブリソース: HTML, etc.] 134 | webpack.config.d 135 | [webpack config: JavaScript] 136 | ``` 137 | 138 | webpack config の設定が必要です。 139 | GitHub リポジトリの `single-file` プロジェクトを参照してください。 140 | 141 | (*1): webpack config にて複数の entry を持たせる際に必要となります。 142 | kotlin-frontend-plugin は単一の entry を前提としています。 143 | 前提としては、サブプロジェクトを作りません。 144 | 145 | ### アプリケーション 146 | 147 | 例: 148 | 149 | ```kotlin 150 | // GreetingComponent = require('greeting-component.vue') 151 | @JsModule("greeting-component.vue") 152 | external val GreetingComponent: Component = definedExternally 153 | 154 | @JsModule("vue") // node_modules でのモジュール名 155 | @JsName("Vue") // `AppVue` は JavaScript コードでは `Vue` に変換される 156 | external class AppVue(options: ComponentOptions) : Vue { 157 | var message: String 158 | } 159 | 160 | val vm = AppVue(ComponentOptions { 161 | el = ElementConfig("#app") // ElementConfig は union 型 162 | data = Data(json { // json 関数は JSON インスタンスを生成 163 | message = "Vue & Kotlin" // AppVue のプロパティにアクセス可能 164 | }) 165 | components = json { 166 | this["greeting"] = ComponentConfig(GreetingComponent) 167 | } 168 | }) 169 | ``` 170 | 171 | ### コンポーネント 172 | 173 | 例: 174 | 175 | ```kotlin 176 | external class GreetingComponent : Vue { 177 | var greeting: String 178 | } 179 | 180 | // クラス名はファイル名として使用される (オーバーライド可能) 181 | class GreetingComponentVue : ComponentVue { 182 | 183 | // vue テンプレート (備考: IntelliJ では、inject language を使用して編集するとシンタックスハイライトされる) 184 | override val template: String = """

    {{ greeting }} World!

    """ 185 | 186 | // CSS (scopedStyle プロパティを true でオーバーライドすると scoped になる) 187 | override val style: StyleBuilder = { 188 | p { 189 | fontSize = 2.em 190 | textAlign = center 191 | } 192 | } 193 | 194 | override val script: ComponentOptionsBuilder = { 195 | data = Data { 196 | json { 197 | greeting = "Hello" 198 | } 199 | } 200 | } 201 | } 202 | 203 | @Suppress("unused") 204 | val options = translate(GreetingComponentVue()) // 必須、bundle するときに使用 205 | ``` 206 | 207 | CSS library: [null-dev/Aza-Kotlin-CSS-JS](https://github.com/null-dev/Aza-Kotlin-CSS-JS) 208 | (MIT License) 209 | 210 | 2つのファイルが生成される: 211 | 212 | - greeting-component.js (kotlin2js が生成) 213 | - greeting-component.vue (js2vue が生成) 214 | 215 | 生成された vue ファイル (greeting-component.vue): 216 | 217 | ```html 218 | 221 | 222 | 225 | 226 | 229 | ``` 230 | 231 | コンポーネントの `options` は vue ファイルで使用されます。 232 | 233 | ### データフロー 234 | 235 | 236 | 237 | - `greeting-component.js` と `greeting-component.vue` は `GreetingComponentVue` から名付けられます 238 | - 前置/後置の `Vue` は削除されます 239 | - キャメルケースはケバブケースに変換されます 240 | - ComponentVue::name プロパティをオーバーライドすることで名前を変更できます 241 | - デフォルト: `this::class.js.name.replace("^Vue|Vue$".toRegex(), "").replace("([A-Z])".toRegex(), "-$1").toLowerCase().trim('-')` 242 | - vuekt-js2vue は `*-component.js` を探します 243 | - build.gradle の targetPattern を設定することで検索パターンを変更できます 244 | 245 | ## 試行方法 246 | 247 | 1. このリポジトリを clone します 248 | 249 | 1. ローカルリポジトリにプラグインを配信します 250 | 251 | ``` 252 | $ ./gradlew --project-dir=vuekt-plugin publishToMavenLocal 253 | ``` 254 | 255 | 製品ビルドを試す (縮小化は未対応) 256 | 257 | 1. バンドルファイルを生成します 258 | 259 | ``` 260 | $ ./gradlew bundle 261 | ``` 262 | 263 | 1. 以下のファイルをブラウザで開きます 264 | 265 | - `guide/index.html` 266 | - `guide/instance.html` 267 | - `guide/syntax.html` 268 | - `guide/computed.html` 269 | - `guide/class-and-style.html` 270 | - `guide/list.html` 271 | - `guide/events_.html` 272 | - `guide/forms.html` 273 | - `guide/components.html` 274 | - `guide/filters.html` 275 | - `single-file/build/bundle/greeting.html` 276 | 277 | 開発ビルドを試す 278 | 279 | 1. webpack-dev-server を起動します 280 | (`-t` は [continuous build](https://docs.gradle.org/current/userguide/continuous_build.html) のためのオプション) 281 | 282 | ``` 283 | $ ./gradlew -t single-file:run 284 | ``` 285 | 286 | 1. ブラウザで `http://localhost:8088/` を開きます 287 | 288 | HMR (Hot Module Replacement) が有効です 289 | (`greeting/main/greeting.kt` と `greeting-component/main/GreetingComponent.kt` を編集します) 290 | 291 | 1. webpack-dev-server を停止します 292 | 293 | ``` 294 | $ ./gradlew single-file:stop 295 | ``` 296 | 297 | ## トラブルシューティング 298 | 299 | 1. Execution failed for task webpack-bundle: `node webpack.js failed` 300 | 301 | 以下のスクリプトを試してください: 302 | ``` 303 | $ bin/webpack.sh 304 | ``` 305 | 306 | 1. `vuekt` と `vuekt-js2vue` を変更しても `guide` と `single-file` プロジェクトが更新されない 307 | 308 | kotlin-frontend-plugin が node_modules を更新しません 309 | 310 | 以下のコマンドを試してください: 311 | ``` 312 | $ ./gradlew clean 313 | ``` 314 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | [日本語/Japanese](./README_ja.md) 2 | 3 | # vue-kotlin 4 | Libraries and tools supporting the use of Vue.js in Kotlin. 5 | 6 | - vuekt 7 | - Type definition of Vue.js for Kotlin/JS 8 | - vuekt-js2vue 9 | - A library for creating Kotlin files that replace vue files 10 | - Use Kotlin DSL for CSS 11 | - vuekt-plugin 12 | - Gradle plugin to support development using vuekt 13 | - Generate vue files from code written with vuekt-js2vue 14 | - Extend kotlin-frontend-plugin 15 | 16 | ## Usage 17 | 18 | ### Gradle setting 19 | 20 | ```groovy 21 | buildscript { 22 | repositories { 23 | jcenter() 24 | maven { 25 | // kotlin-frontend-plugin (vuekt-plugin depends) 26 | url "https://dl.bintray.com/kotlin/kotlin-eap" 27 | } 28 | maven { 29 | // vuekt-plugin 30 | url "https://nosix.github.io/vue-kotlin/release" 31 | } 32 | } 33 | 34 | dependencies { 35 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 36 | classpath "org.jetbrains.kotlin:kotlin-frontend-plugin:$kotlin_frontend_version" 37 | classpath "org.musyozoku:vuekt-plugin:$vuekt_plugin_version" 38 | } 39 | } 40 | 41 | repositories { 42 | jcenter() 43 | maven { 44 | // vuekt, vuekt-js2vue 45 | url "https://nosix.github.io/vue-kotlin/release" 46 | } 47 | } 48 | 49 | apply plugin: 'kotlin2js' 50 | apply plugin: 'org.jetbrains.kotlin.frontend' // kotlin-frontend-plugin 51 | apply plugin: 'org.musyozoku.vuekt' // vuekt-plugin 52 | 53 | dependencies { 54 | // sub projects 55 | compile project(':greeting') // application that use vue component 56 | compile project(':greeting-component') // single file vue component 57 | } 58 | 59 | kotlinFrontend { 60 | downloadNodeJsVersion = node_version 61 | 62 | npm { 63 | dependency('vue', vue_version) 64 | 65 | devDependency('vue-loader', '*') 66 | devDependency('vue-template-compiler', vue_version) 67 | devDependency('css-loader', '*') 68 | devDependency('html-webpack-plugin', '*') 69 | } 70 | 71 | webpackBundle { 72 | contentPath = file("$projectDir/webContent") 73 | publicPath = "/" 74 | port = 8088 75 | } 76 | } 77 | 78 | subprojects { 79 | 80 | apply plugin: 'kotlin2js' 81 | 82 | dependencies { 83 | compile "org.musyozoku:vuekt:$vuekt_version" 84 | compile "org.musyozoku:vuekt-js2vue:$vuekt_js2vue_version" 85 | } 86 | 87 | sourceSets { 88 | main.kotlin.srcDirs += "main" 89 | test.kotlin.srcDirs += "test" 90 | } 91 | 92 | compileKotlin2Js { 93 | kotlinOptions { 94 | moduleKind = "commonjs" // webpack use CommonJS 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | optional vuekt-plugin settings: 101 | 102 | ```groovy 103 | vue { 104 | targetPattern = ".*-component\\.js" 105 | configFile = "01_js2vue.js" 106 | } 107 | ``` 108 | 109 | - targetPattern 110 | - vuekt-plugin searches targetPattern for JavaScript files generated from ComponentVue 111 | - JavaScript files generate vue files 112 | - configFile 113 | - vuekt-plugin generate additional webpack config in webpack.config.d directory 114 | - This config is required when bundling 115 | 116 | ### Project structure 117 | 118 | For example: 119 | 120 | ``` 121 | project_root 122 | greeting 123 | main 124 | [application: Kotlin] 125 | greeting-component 126 | main 127 | [component: Kotlin] 128 | src 129 | main 130 | kotlin 131 | dummy.kt (*1) 132 | webContent 133 | [web resources: HTML, etc.] 134 | webpack.config.d 135 | [webpack config: JavaScript] 136 | ``` 137 | 138 | You need to set webpack config. 139 | See `single-file` project in GitHub repository. 140 | 141 | (*1): It is necessary if you have multiple entries in webpack config. 142 | kotlin-frontend-plugin assumes one entry. 143 | By default it does not create sub projects. 144 | 145 | ### Application 146 | 147 | For example: 148 | 149 | ```kotlin 150 | // GreetingComponent = require('greeting-component.vue') 151 | @JsModule("greeting-component.vue") 152 | external val GreetingComponent: Component = definedExternally 153 | 154 | @JsModule("vue") // module name in node_modules 155 | @JsName("Vue") // `AppVue` is converted to the name `Vue` in JavaScript code 156 | external class AppVue(options: ComponentOptions) : Vue { 157 | var message: String 158 | } 159 | 160 | val vm = AppVue(ComponentOptions { 161 | el = ElementConfig("#app") // ElementConfig is union type 162 | data = Data(json { // json function create JSON instance 163 | message = "Vue & Kotlin" // accessible property of AppVue 164 | }) 165 | components = json { 166 | this["greeting"] = ComponentConfig(GreetingComponent) 167 | } 168 | }) 169 | ``` 170 | 171 | ### Component 172 | 173 | For example: 174 | 175 | ```kotlin 176 | external class GreetingComponent : Vue { 177 | var greeting: String 178 | } 179 | 180 | // class name is used for file name (can be overridden) 181 | class GreetingComponentVue : ComponentVue { 182 | 183 | // vue template (Note: in IntelliJ, editing with inject language will result in syntax highlighting) 184 | override val template: String = """

    {{ greeting }} World!

    """ 185 | 186 | // CSS (it becomes scoped if scopedStyle property is overridden with true) 187 | override val style: StyleBuilder = { 188 | p { 189 | fontSize = 2.em 190 | textAlign = center 191 | } 192 | } 193 | 194 | override val script: ComponentOptionsBuilder = { 195 | data = Data { 196 | json { 197 | greeting = "Hello" 198 | } 199 | } 200 | } 201 | } 202 | 203 | @Suppress("unused") 204 | val options = translate(GreetingComponentVue()) // required, used by bundling 205 | ``` 206 | 207 | CSS library: [null-dev/Aza-Kotlin-CSS-JS](https://github.com/null-dev/Aza-Kotlin-CSS-JS) 208 | (MIT License) 209 | 210 | Two files are generated: 211 | 212 | - greeting-component.js (kotlin2js generated) 213 | - greeting-component.vue (js2vue generated) 214 | 215 | Generated vue file (greeting-component.vue): 216 | 217 | ```html 218 | 221 | 222 | 225 | 226 | 229 | ``` 230 | 231 | `options` in the component is used in the vue file. 232 | 233 | ### Data flow 234 | 235 | 236 | 237 | - `greeting-component.js` and `greeting-component.vue` are named from `GreetingComponentVue` 238 | - `Vue` of prefix/suffix is removed 239 | - Camel case is converted to kebab case 240 | - You can change the name by overriding ComponentVue::name property 241 | - default is `this::class.js.name.replace("^Vue|Vue$".toRegex(), "").replace("([A-Z])".toRegex(), "-$1").toLowerCase().trim('-')` 242 | - vuekt-js2vue finds `*-component.js` 243 | - You can change the pattern by setting targetPattern in build.gradle 244 | 245 | ## Trial 246 | 247 | 1. Clone this repository. 248 | 249 | 1. Publish plugin to local repository. 250 | 251 | ``` 252 | $ ./gradlew --project-dir=vuekt-plugin publishToMavenLocal 253 | ``` 254 | 255 | Try production build (minify is still) 256 | 257 | 1. Generate bundle files. 258 | 259 | ``` 260 | $ ./gradlew bundle 261 | ``` 262 | 263 | 1. Open the following in a browser. 264 | 265 | - `guide/index.html` 266 | - `guide/instance.html` 267 | - `guide/syntax.html` 268 | - `guide/computed.html` 269 | - `guide/class-and-style.html` 270 | - `guide/list.html` 271 | - `guide/events_.html` 272 | - `guide/forms.html` 273 | - `guide/components.html` 274 | - `guide/filters.html` 275 | - `single-file/build/bundle/greeting.html` 276 | 277 | Try development build 278 | 279 | 1. Run webpack-dev-server. 280 | (`-t` is option for [continuous build](https://docs.gradle.org/current/userguide/continuous_build.html)) 281 | 282 | ``` 283 | $ ./gradlew -t single-file:run 284 | ``` 285 | 286 | 1. Open `http://localhost:8088/` on the browser. 287 | 288 | HMR (Hot Module Replacement) is enabled. 289 | (Edit `greeting/main/greeting.kt` and/or `greeting-component/main/GreetingComponent.kt`) 290 | 291 | 1. Stop webpack-dev-server. 292 | 293 | ``` 294 | $ ./gradlew single-file:stop 295 | ``` 296 | 297 | ## Troubleshooting 298 | 299 | 1. Execution failed for task webpack-bundle: `node webpack.js failed` 300 | 301 | Please try the following script: 302 | ``` 303 | $ bin/webpack.sh 304 | ``` 305 | 306 | 1. Changing `vuekt` and/or `vuekt-js2vue` don't update `guide` and `single-file` project. 307 | 308 | kotlin-frontend-plugin does not update node_modules. 309 | 310 | Please try the following command: 311 | ``` 312 | $ ./gradlew clean 313 | ``` 314 | -------------------------------------------------------------------------------- /vuekt/src/main/kotlin/org/musyozoku/vuekt/options.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused", "UnsafeCastFromDynamic", "NOTHING_TO_INLINE") 2 | 3 | package org.musyozoku.vuekt 4 | 5 | import org.w3c.dom.HTMLElement 6 | import kotlin.js.Json 7 | import kotlin.js.Promise 8 | 9 | /** 10 | * `new (varargs args: Any): Any` 11 | */ 12 | typealias Constructor = Function 13 | 14 | object js { 15 | val String: Constructor = js("String") 16 | val Number: Constructor = js("Number") 17 | val Boolean: Constructor = js("Boolean") 18 | val Function: Constructor> = js("Function") 19 | val Object: Constructor = js("Object") 20 | val Array: Constructor> = js("Array") 21 | val Symbol: Constructor = js("Symbol") // TODO specify type parameter 22 | } 23 | 24 | /** 25 | * `typeof Vue | ComponentOptions | FunctionalComponentOptions` 26 | */ 27 | external interface Component 28 | 29 | inline fun Component(typeOfVue: Any): Component = typeOfVue.asDynamic() // TODO change Any 30 | inline fun Component(options: ComponentOptions): Component = options.asDynamic() 31 | inline fun Component(functionalOptions: FunctionalComponentOptions): Component = functionalOptions.asDynamic() 32 | 33 | inline fun Component.toTypeOfVue(): Any = this.asDynamic() // TODO change Any 34 | inline fun Component.toComponentOptions(): ComponentOptions = this.asDynamic() 35 | inline fun Component.toFunctionalComponentOptions(): FunctionalComponentOptions = this.asDynamic() 36 | 37 | typealias AsyncComponent = (resolve: (component: Component) -> Unit, reject: (reason: Any?) -> Unit) -> AsyncComponentResult 38 | 39 | /** 40 | * `Promise | Component | void` 41 | */ 42 | external interface AsyncComponentResult 43 | 44 | inline fun AsyncComponentResult(promise: Promise): AsyncComponentResult = promise.asDynamic() 45 | inline fun AsyncComponentResult(component: Component): AsyncComponentResult = component.asDynamic() 46 | inline fun AsyncComponentResult(void: Void): AsyncComponentResult = void.asDynamic() 47 | 48 | external interface ComponentOptions { 49 | // Data 50 | var data: Data? // Object | V.() -> Object 51 | var props: Props? 52 | var propsData: Json? 53 | var computed: JsonOf>? // { [key: String]: V.() -> Any | ComputedOptions } 54 | var methods: JsonOf?>? // { [key: String]: V.(args: Array) -> Any } 55 | var watch: JsonOf? // { [key: String]: String | WatchHandler | ({ handler: WatchHandler } & WatchOptions) } 56 | // DOM 57 | var el: ElementConfig? 58 | var template: String? 59 | var render: RenderFunction? // V.(createElement: CreateElement) -> VNode 60 | var renderError: RenderErrorFunction? 61 | var staticRenderFns: Array? 62 | // Lifecycle Hooks 63 | var beforeCreate: LifecycleHookFunction? 64 | var created: LifecycleHookFunction? 65 | var beforeMount: LifecycleHookFunction? 66 | var mounted: LifecycleHookFunction? 67 | var beforeUpdate: LifecycleHookFunction? 68 | var updated: LifecycleHookFunction? 69 | var activated: LifecycleHookFunction? 70 | var deactivated: LifecycleHookFunction? 71 | var beforeDestroy: LifecycleHookFunction? 72 | var destroyed: LifecycleHookFunction? 73 | // Assets 74 | var directives: JsonOf? // { [key: String]: DirectiveOptions | DirectiveFunction } 75 | var components: JsonOf? // { [key: String]: Component | AsyncComponent } 76 | var transitions: JsonOf? // { [key: String]: Object } 77 | var filters: JsonOf?>? // { [key: String]: Function } 78 | // Composition 79 | var provide: Data? // Object | () -> Object 80 | var inject: Any? // Array | { [key: String]: String | Symbol } 81 | var parent: Vue? 82 | var mixins: Array? // Array 83 | var extends: Any? // ComponentOptions | typeof Vue 84 | // Misc 85 | var model: ModelOptions? 86 | var name: String? 87 | var delimiters: Delimiter? 88 | var comments: Boolean? 89 | var inheritAttrs: Boolean? 90 | } 91 | 92 | external interface FunctionalComponentOptions { 93 | var name: String? 94 | var props: Props? 95 | var functional: Boolean 96 | var render: (createElement: CreateElement, context: RenderContext) -> VNode? // VNode | Unit 97 | } 98 | 99 | external interface RenderContext { 100 | var props: Any 101 | var children: Array 102 | var slots: () -> Any 103 | var data: VNodeData 104 | var parent: Vue 105 | var injections: Any 106 | } 107 | 108 | external interface PropOptions { 109 | var type: TypeConfig? 110 | var required: Boolean? 111 | var default: T? 112 | var validator: ((value: T) -> Boolean)? 113 | } 114 | 115 | external interface ComputedOptions { 116 | var get: (() -> T)? // V.() -> Any 117 | var set: ((value: T) -> Unit)? // V.(value: Any) -> Unit 118 | var cache: Boolean? 119 | } 120 | 121 | /** 122 | * `V.(value: T, oldValue: T) -> Unit` 123 | */ 124 | typealias WatchHandler = (value: T, oldValue: T) -> Unit 125 | 126 | external interface WatchOptions { 127 | var deep: Boolean? 128 | var immediate: Boolean? 129 | } 130 | 131 | typealias DirectiveFunction = (el: HTMLElement, binding: VNodeDirective, vnode: VNode, oldVnode: VNode) -> Unit 132 | 133 | external interface DirectiveOptions { 134 | var bind: DirectiveFunction? 135 | var inserted: DirectiveFunction? 136 | var update: DirectiveFunction? 137 | var componentUpdated: DirectiveFunction? 138 | var unbind: DirectiveFunction? 139 | } 140 | 141 | /** 142 | * `T | () -> T` 143 | */ 144 | external interface Data 145 | 146 | inline fun Data(json: T): Data = json.asDynamic() 147 | inline fun Data(factory: () -> T): Data = factory.asDynamic() 148 | 149 | inline fun Data.toObject(): T = this.asDynamic() 150 | inline fun Data.toFactory(): () -> T = this.asDynamic() 151 | 152 | /** 153 | * `Array | { [propertyName: String]: PropOptions | Constructor | Array }` 154 | */ 155 | external interface Props 156 | 157 | inline fun Props(propNames: Array): Props = propNames.asDynamic() 158 | inline fun Props(propConfig: JsonOf): Props = propConfig.asDynamic() 159 | 160 | inline fun Props.toNames(): Array = this.asDynamic() 161 | inline fun Props.toConfig(): JsonOf = this.asDynamic() 162 | 163 | /** 164 | * `PropOptions | Constructor | Array` 165 | */ 166 | external interface PropConfig 167 | 168 | inline fun PropConfig(options: PropOptions): PropConfig = options.asDynamic() 169 | inline fun PropConfig(constructor: Constructor): PropConfig = constructor.asDynamic() 170 | inline fun PropConfig(constructors: Array>): PropConfig = constructors.asDynamic() 171 | 172 | inline fun PropConfig.toOptions(): PropOptions = this.asDynamic() 173 | inline fun PropConfig.toConstructor(): Constructor = this.asDynamic() 174 | inline fun PropConfig.toConstructorList(): Array> = this.asDynamic() 175 | 176 | /** 177 | * `ComputedOptions | () -> T` 178 | */ 179 | external interface ComputedConfig 180 | 181 | inline fun ComputedConfig(factory: () -> T): ComputedConfig = factory.asDynamic() 182 | inline fun ComputedConfig(options: ComputedOptions): ComputedConfig = options.asDynamic() 183 | 184 | inline fun ComputedConfig.toFactory(): () -> T = this.asDynamic() 185 | inline fun ComputedConfig.toOptions(): ComputedOptions = this.asDynamic() 186 | 187 | /** 188 | * `String | WatchHandler | ({ handler: WatchHandler } & WatchOptions)` 189 | */ 190 | external interface Watcher 191 | 192 | inline fun Watcher(methodName: String): Watcher = methodName.asDynamic() 193 | inline fun Watcher(handler: WatchHandler): Watcher = handler.asDynamic() 194 | inline fun Watcher(options: WatchHandlerOptions): Watcher = options.asDynamic() 195 | 196 | inline fun Watcher.toMethodName(): String = this.asDynamic() 197 | inline fun Watcher.toHandler(): WatchHandler = this.asDynamic() 198 | inline fun Watcher.toOptions(): WatchHandlerOptions = this.asDynamic() 199 | 200 | /** 201 | * `{ handler: WatchHandler } & WatchOptions` 202 | */ 203 | external interface WatchHandlerOptions : WatchOptions { 204 | var handler: WatchHandler 205 | } 206 | 207 | /** 208 | * `String | HTMLElement` 209 | */ 210 | external interface ElementConfig 211 | 212 | inline fun ElementConfig(selector: String): ElementConfig = selector.asDynamic() 213 | inline fun ElementConfig(element: HTMLElement): ElementConfig = element.asDynamic() 214 | 215 | inline fun ElementConfig.toSelector(): String = this.asDynamic() 216 | inline fun ElementConfig.toElement(): HTMLElement = this.asDynamic() 217 | 218 | /** 219 | * `(createElement: CreateElement) -> VNode` 220 | */ 221 | typealias RenderFunction = (createElement: CreateElement) -> VNode 222 | 223 | /** 224 | * `(createElement: CreateElement, error: Error) -> VNode` 225 | */ 226 | typealias RenderErrorFunction = (createElement: CreateElement, error: Error) -> VNode 227 | 228 | /** 229 | * `V.() -> Unit` 230 | */ 231 | typealias LifecycleHookFunction = () -> Unit 232 | 233 | /** 234 | * `DirectiveOptions | DirectiveFunction` 235 | */ 236 | external interface DirectiveConfig 237 | 238 | inline fun DirectiveConfig(options: DirectiveOptions): DirectiveConfig = options.asDynamic() 239 | inline fun DirectiveConfig(function: DirectiveFunction): DirectiveConfig = function.asDynamic() 240 | 241 | inline fun DirectiveConfig.toOptions(): DirectiveOptions = this.asDynamic() 242 | inline fun DirectiveConfig.toFunction(): DirectiveFunction = this.asDynamic() 243 | 244 | /** 245 | * `Component | AsyncComponent` 246 | */ 247 | external interface ComponentConfig 248 | 249 | inline fun ComponentConfig(component: Component): ComponentConfig = component.asDynamic() 250 | inline fun ComponentConfig(asyncComponent: AsyncComponent): ComponentConfig = asyncComponent.asDynamic() 251 | 252 | inline fun ComponentConfig.toComponent(): Component = this.asDynamic() 253 | inline fun ComponentConfig.toAsyncComponent(): AsyncComponent = this.asDynamic() 254 | 255 | external interface ModelOptions { 256 | var prop: String? 257 | var event: String? 258 | } 259 | 260 | /** 261 | * `[String, String]` 262 | */ 263 | typealias Delimiter = Array 264 | 265 | inline fun Delimiter(begin: String, end: String) = arrayOf(begin, end) 266 | 267 | /** 268 | * `Constructor | Array | null` 269 | */ 270 | external interface TypeConfig 271 | 272 | inline fun TypeConfig(constructor: Constructor): TypeConfig = constructor.asDynamic() 273 | inline fun TypeConfig(constructors: Array>): TypeConfig<*> = constructors.asDynamic() 274 | 275 | inline fun TypeConfig.toConstructor(): Constructor = this.asDynamic() 276 | inline fun TypeConfig<*>.toConstructorList(): Array> = this.asDynamic() 277 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /guide/components/main/Components.kt: -------------------------------------------------------------------------------- 1 | import org.musyozoku.vuekt.* 2 | import org.w3c.dom.HTMLInputElement 3 | import org.w3c.dom.events.Event 4 | import kotlin.browser.window 5 | 6 | // var Child = { 7 | // template: '
    A custom component!
    ' 8 | // } 9 | 10 | external class MyComponent : Vue 11 | 12 | val Child = ComponentOptions { 13 | template = "
    A custom component!
    " 14 | } 15 | 16 | // new Vue({ 17 | // // ... 18 | // components: { 19 | // // will only be available in parent's template 20 | // // は親テンプレートでのみ有効になります 21 | // 'my-component': Child 22 | // } 23 | // }) 24 | 25 | val example = Vue(ComponentOptions { 26 | el = ElementConfig("#example") 27 | components = json { 28 | this["my-component"] = ComponentConfig(Component(Child)) 29 | } 30 | }) 31 | 32 | // Vue.component('simple-counter', { 33 | // template: '', 34 | // // data は技術的には関数なので、Vue は警告を出しません。 35 | // // しかし、各コンポーネントのインスタンスは 36 | // // 同じオブジェクトの参照を返します。 37 | // data: function () { 38 | // return { 39 | // counter: 0 40 | // } 41 | // } 42 | // }) 43 | // 44 | // new Vue({ 45 | // el: '#example-2' 46 | // }) 47 | 48 | external class SimpleCounterComponent : Vue { 49 | var counter: Int 50 | } 51 | 52 | val simpleCounter = Vue.component("simple-counter", Component(ComponentOptions { 53 | template = """""" 54 | data = Data { 55 | json { 56 | counter = 0 57 | } 58 | } 59 | })) 60 | 61 | val example2 = Vue(ComponentOptions { 62 | el = ElementConfig("#example-2") 63 | }) 64 | 65 | // Vue.component('child', { 66 | // props: ['myMessage'], 67 | // template: '{{ myMessage }}' 68 | // }) 69 | 70 | external class ChildComponent : Vue 71 | 72 | val child = Vue.component("child", Component(ComponentOptions { 73 | props = Props(arrayOf("myMessage")) 74 | template = "{{ myMessage }}" 75 | })) 76 | 77 | @JsModule(vue.MODULE) 78 | @JsNonModule 79 | @JsName(vue.CLASS) 80 | external class Example1Vue(options: ComponentOptions) : Vue { 81 | var parentMsg: String 82 | } 83 | 84 | val example3 = Example1Vue(ComponentOptions { 85 | el = ElementConfig("#example-3") 86 | data = Data(json = json { 87 | parentMsg = "" 88 | }) 89 | }) 90 | 91 | // Vue.component('example', { 92 | // props: { 93 | // // 基本な型チェック (`null` はどんな型でも受け付ける) 94 | // propA: Number, 95 | // // 複数の受け入れ可能な型 96 | // propB: [String, Number], 97 | // // 必須な文字列 98 | // propC: { 99 | // type: String, 100 | // required: true 101 | // }, 102 | // // デフォルト値 103 | // propD: { 104 | // type: Number, 105 | // default: 100 106 | // }, 107 | // // オブジェクトと配列のデフォルトはファクトリ関数から返すようにしています 108 | // propE: { 109 | // type: Object, 110 | // default: function () { 111 | // return { message: 'hello' } 112 | // } 113 | // }, 114 | // // カスタムバリデータ関数 115 | // propF: { 116 | // validator: function (value) { 117 | // return value > 10 118 | // } 119 | // } 120 | // } 121 | // }) 122 | 123 | external class ExampleComponent : Vue 124 | 125 | val exampleComponent = Vue.component("example", Component(ComponentOptions { 126 | props = Props(propConfig = json { 127 | this["propA"] = PropConfig(js.Number) 128 | this["propB"] = PropConfig(constructors = arrayOf(js.String, js.Number)) 129 | this["propC"] = PropConfig(PropOptions { 130 | type = TypeConfig(js.String) 131 | required = true 132 | }) 133 | this["propD"] = PropConfig(PropOptions { 134 | type = TypeConfig(js.Number) 135 | default = 100 136 | }) 137 | this["propE"] = PropConfig(PropOptions { 138 | type = TypeConfig(js.Object) 139 | default = { 140 | json> { 141 | this["message"] = "hello" 142 | } 143 | } 144 | }) 145 | this["propF"] = PropConfig(PropOptions { 146 | validator = { value: Int -> 147 | value > 10 148 | } 149 | }) 150 | }) 151 | })) 152 | 153 | // Vue.component('button-counter', { 154 | // template: '', 155 | // data: function () { 156 | // return { 157 | // counter: 0 158 | // } 159 | // }, 160 | // methods: { 161 | // incrementCounter: function () { 162 | // this.counter += 1 163 | // this.$emit('increment') 164 | // } 165 | // }, 166 | // }) 167 | 168 | external class ButtonCounterComponent : Vue { 169 | var counter: Int 170 | } 171 | 172 | val buttonCounter = Vue.component("button-counter", Component(ComponentOptions { 173 | template = """""" 174 | data = Data { 175 | json { 176 | counter = 0 177 | } 178 | } 179 | methods = json { 180 | this["incrementCounter"] = { 181 | val self = thisAs() 182 | self.counter++ 183 | self.`$emit`("increment") 184 | } 185 | } 186 | })) 187 | 188 | // new Vue({ 189 | // el: '#counter-event-example', 190 | // data: { 191 | // total: 0 192 | // }, 193 | // methods: { 194 | // incrementTotal: function () { 195 | // this.total += 1 196 | // } 197 | // } 198 | // }) 199 | 200 | @JsModule(vue.MODULE) 201 | @JsNonModule 202 | @JsName(vue.CLASS) 203 | external class CounterEventExampleVue(options: ComponentOptions) : Vue { 204 | var total: Int 205 | } 206 | 207 | val counterEventExample = CounterEventExampleVue(ComponentOptions { 208 | el = ElementConfig("#counter-event-example") 209 | data = Data(json = json { 210 | total = 0 211 | }) 212 | methods = json { 213 | this["incrementTotal"] = { 214 | val self = thisAs() 215 | self.total++ 216 | } 217 | } 218 | }) 219 | 220 | // https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events 221 | // https://jp.vuejs.org/v2/guide/components.html#カスタムイベントを使用したフォーム入力コンポーネント 222 | 223 | external class CurrencyInputComponent : Vue { 224 | val value: Int 225 | val label: String 226 | fun formatValue(): String 227 | } 228 | 229 | val currencyInput = Vue.component("currency-input", Component(ComponentOptions { 230 | template = """ 231 |
    232 | 233 | ${'$'} 234 | 240 |
    241 | """.trimIndent() 242 | props = Props(propConfig = json { 243 | this["value"] = PropConfig(PropOptions { 244 | type = TypeConfig(js.Number) 245 | default = 0 246 | }) 247 | this["label"] = PropConfig(PropOptions { 248 | type = TypeConfig(js.String) 249 | default = "" 250 | }) 251 | }) 252 | mounted = { 253 | val self = thisAs() 254 | self.formatValue() 255 | } 256 | methods = json { 257 | this["updateValue"] = { 258 | value: String -> 259 | val self = thisAs() 260 | val trimmedValue = value.trim() 261 | val formattedValue = trimmedValue.substring(0, 262 | if (value.indexOf('.') == -1) value.length else value.indexOf('.') + 3 ) 263 | if (formattedValue != value) { 264 | self.`$refs`["input"]?.let { 265 | val element = it.toHTMLElement() 266 | element.nodeValue = formattedValue 267 | } 268 | } 269 | self.`$emit`("input", formattedValue.toIntOrNull() ?: "") 270 | } 271 | this["formatValue"] = { 272 | val self = thisAs() 273 | self.`$refs`["input"]?.let { 274 | val element = it.toHTMLElement() 275 | element.nodeValue = self.value.toString() 276 | } 277 | } 278 | set("selectAll") { 279 | event: Event -> 280 | window.setTimeout({ 281 | val target = event.target as? HTMLInputElement 282 | target?.select() 283 | }, 0) 284 | } 285 | } 286 | })) 287 | 288 | @JsModule(vue.MODULE) 289 | @JsNonModule 290 | @JsName(vue.CLASS) 291 | external class AppVue(options: ComponentOptions) : Vue { 292 | var price: Int 293 | var shipping: Int 294 | var handling: Int 295 | var discount: Int 296 | } 297 | 298 | val app = AppVue(ComponentOptions { 299 | el = ElementConfig("#app") 300 | data = Data(json = json { 301 | price = 0 302 | shipping = 0 303 | handling = 0 304 | discount = 0 305 | }) 306 | computed = json { 307 | this["total"] = ComputedConfig { 308 | val self = thisAs() 309 | (self.price * 100 + self.shipping * 100 + self.handling * 100 - self.discount * 100) / 100 310 | } 311 | } 312 | }) 313 | 314 | // Vue.component('my-checkbox', { 315 | // model: { 316 | // prop: 'checked', 317 | // event: 'change' 318 | // }, 319 | // props: { 320 | // checked: Boolean, 321 | // // これによって、 `value` プロパティを別の目的で利用することを許可します。 322 | // value: String 323 | // }, 324 | // // ... 325 | // }) 326 | 327 | external class MyCheckboxComponent : Vue 328 | 329 | val MyCheckbox = Vue.component("my-checkbox", Component(ComponentOptions { 330 | model = ModelOptions { 331 | prop = "checked" 332 | event = "change" 333 | } 334 | props = Props(propConfig = json { 335 | this["checked"] = PropConfig(js.Boolean) 336 | this["value"] = PropConfig(js.String) 337 | }) 338 | methods = json { 339 | this["update"] = { 340 | event: Event -> 341 | val self = thisAs() 342 | val target = event.target as? HTMLInputElement 343 | target?.let { 344 | println(it.checked) 345 | self.`$emit`("change", it.checked) 346 | } 347 | } 348 | } 349 | template = """ 350 |
    351 | 352 | {{ value }} 353 |
    354 | """.trimIndent() 355 | })) 356 | 357 | @JsModule(vue.MODULE) 358 | @JsNonModule 359 | @JsName(vue.CLASS) 360 | external class Example4Vue(options: ComponentOptions) : Vue { 361 | var foo: Boolean 362 | } 363 | 364 | val example4 = Example4Vue(ComponentOptions { 365 | el = ElementConfig("#example-4") 366 | data = Data(json = json { 367 | foo = true 368 | }) 369 | }) 370 | 371 | // https://vuejs.org/v2/guide/components.html#Single-Slot 372 | // https://jp.vuejs.org/v2/guide/components.html#単一スロット 373 | 374 | val example5 = Vue(ComponentOptions { 375 | el = ElementConfig("#example-5") 376 | components = json { 377 | this["my-component"] = ComponentConfig(Component(ComponentOptions { 378 | template = """ 379 |
    380 |

    I'm the child title

    381 | 382 | This will only be displayed if there is no content 383 | to be distributed. 384 | 385 |
    386 | """ 387 | })) 388 | } 389 | }) 390 | 391 | // https://vuejs.org/v2/guide/components.html#Named-Slots 392 | // https://jp.vuejs.org/v2/guide/components.html#名前付きスロット 393 | 394 | external class AppLayoutComponent : Vue 395 | 396 | val example6 = Vue(ComponentOptions { 397 | el = ElementConfig("#example-6") 398 | components = json { 399 | this["app-layout"] = ComponentConfig(Component(ComponentOptions { 400 | template = """ 401 |
    402 |
    403 | 404 |
    405 |
    406 | 407 |
    408 |
    409 | 410 |
    411 |
    412 | """ 413 | })) 414 | } 415 | }) 416 | 417 | // https://vuejs.org/v2/guide/components.html#Scoped-Slots 418 | // https://jp.vuejs.org/v2/guide/components.html#スコープ付きスロット 419 | 420 | @JsModule(vue.MODULE) 421 | @JsNonModule 422 | @JsName(vue.CLASS) 423 | external class Example7Vue(options: ComponentOptions) : Vue { 424 | var items: Array 425 | } 426 | 427 | class AwesomeItem(val text: String) 428 | 429 | external class MyAwesomeListComponent : Vue 430 | 431 | val example7 = Example7Vue(ComponentOptions { 432 | el = ElementConfig("#example-7") 433 | data = Data(json = json { 434 | items = arrayOf(AwesomeItem("foo"), AwesomeItem("bar")) 435 | }) 436 | components = json { 437 | this["my-awesome-list"] = ComponentConfig(Component(ComponentOptions { 438 | template = """ 439 |
      440 | 443 | 444 | 445 |
    446 | """ 447 | props = Props(arrayOf("items")) 448 | })) 449 | } 450 | }) 451 | 452 | // https://vuejs.org/v2/guide/components.html#Dynamic-Components 453 | // https://jp.vuejs.org/v2/guide/components.html#動的コンポーネント 454 | 455 | @JsModule(vue.MODULE) 456 | @JsNonModule 457 | @JsName(vue.CLASS) 458 | external class Example8Vue(options: ComponentOptions) : Vue { 459 | var currentView: String 460 | } 461 | 462 | external class ViewComponent : Vue 463 | 464 | val example8 = Example8Vue(ComponentOptions { 465 | el = ElementConfig("#example-8") 466 | data = Data(json = json { 467 | currentView = "home" 468 | }) 469 | components = json { 470 | this["home"] = ComponentConfig(Component(ComponentOptions { 471 | template = "
    Home View
    " 472 | })) 473 | this["posts"] = ComponentConfig(Component(ComponentOptions { 474 | template = "
    Posts View
    " 475 | })) 476 | this["archive"] = ComponentConfig(Component(ComponentOptions { 477 | template = "
    Archive View
    " 478 | })) 479 | } 480 | }) 481 | 482 | // https://vuejs.org/v2/guide/components.html#Child-Component-Refs 483 | // https://jp.vuejs.org/v2/guide/components.html#子コンポーネントの参照 484 | 485 | val example9Parent = Vue(ComponentOptions { 486 | el = ElementConfig("#example-9") 487 | components = json { 488 | this["user-profile"] = ComponentConfig(Component(ComponentOptions { 489 | template = "
    User Profile
    " 490 | })) 491 | } 492 | }) 493 | 494 | val example9Child = example9Parent.`$refs`["profile"] 495 | 496 | // Try following code on browser console: 497 | // main.example9Child 498 | 499 | // Vue.component('async-example', function (resolve, reject) { 500 | // setTimeout(function () { 501 | // // Pass the component definition to the resolve callback 502 | // resolve({ 503 | // template: '
    I am async!
    ' 504 | // }) 505 | // }, 1000) 506 | // }) 507 | 508 | val asyncExample = Vue.component("async-example") { 509 | resolve, _ -> 510 | window.setTimeout({ 511 | resolve(Component(ComponentOptions { 512 | template = "
    I am async!
    " 513 | })) 514 | }, 1000) 515 | AsyncComponentResult(void) 516 | } 517 | 518 | val example10 = Vue(ComponentOptions { 519 | el = ElementConfig("#example-10") 520 | }) --------------------------------------------------------------------------------