├── docs ├── .nojekyll ├── remote.js ├── _coverpage.md ├── index.html └── README.md ├── .babelrc ├── .gitignore ├── src ├── utils │ ├── assign.js │ ├── compiler.js │ ├── require.js │ ├── parser.js │ └── transform.js ├── index.umd.js ├── index.js ├── index.cjs.js ├── components │ ├── editor.js │ ├── playground.js │ └── preview.js └── style │ └── vuep.css ├── remote.js ├── .travis.yml ├── .eslintrc ├── __test__ ├── mock.js ├── editor.spec.js ├── require.spec.js ├── playground.spec.js ├── preview.spec.js ├── parser.spec.js └── compiler.spec.js ├── LICENSE ├── package.json ├── README.md ├── test.html └── dist ├── vuep.min.css ├── vuep.common.js └── vuep.css /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-3"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | .idea 4 | *log* 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /src/utils/assign.js: -------------------------------------------------------------------------------- 1 | import assign from 'simple-assign' 2 | 3 | export default Object.assign || assign 4 | -------------------------------------------------------------------------------- /remote.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'remote', 3 | render () { 4 | return
from remote
5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/remote.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'remote', 3 | render () { 4 | return
from remote
5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: stable 4 | after_success: 5 | - yarn coveralls 6 | - yarn test:ci 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "vue", 3 | "globals": { 4 | "test": true, 5 | "expect": true 6 | }, 7 | "env": { 8 | "mocha": true 9 | } 10 | } -------------------------------------------------------------------------------- /src/index.umd.js: -------------------------------------------------------------------------------- 1 | export * from './' 2 | import 'codemirror/addon/mode/overlay' 3 | import 'codemirror/addon/mode/simple' 4 | import 'codemirror/mode/css/css' 5 | import 'codemirror/mode/htmlmixed/htmlmixed' 6 | import 'codemirror/mode/javascript/javascript' 7 | import 'codemirror/mode/vue/vue' 8 | import 'codemirror/mode/xml/xml' 9 | import 'codemirror/mode/jsx/jsx' 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Vuep from './components/playground' 2 | 3 | Vuep.config = function (opts) { 4 | Vuep.props.options.default = () => opts 5 | } 6 | 7 | function install (Vue, opts) { 8 | Vuep.config(opts) 9 | Vue.component(Vuep.name, Vuep) 10 | } 11 | 12 | Vuep.install = install 13 | 14 | if (typeof Vue !== 'undefined') { 15 | Vue.use(install) // eslint-disable-line 16 | } 17 | 18 | export default Vuep 19 | -------------------------------------------------------------------------------- /__test__/mock.js: -------------------------------------------------------------------------------- 1 | export default function () { 2 | // mock createTextRange function 3 | // https://discuss.codemirror.net/t/working-in-jsdom-or-node-js-natively/138/2 4 | document.body.createTextRange = function () { 5 | return { 6 | setEnd: () => {}, 7 | setStart: () => {}, 8 | getBoundingClientRect: () => ({ right: 0 }), 9 | getClientRects: () => ({ right: 0 }) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/index.cjs.js: -------------------------------------------------------------------------------- 1 | import Vuep from './' 2 | 3 | if (typeof require !== 'undefined') { 4 | require('codemirror/addon/mode/overlay') 5 | require('codemirror/addon/mode/simple') 6 | require('codemirror/mode/css/css') 7 | require('codemirror/mode/htmlmixed/htmlmixed') 8 | require('codemirror/mode/javascript/javascript') 9 | require('codemirror/mode/vue/vue') 10 | require('codemirror/mode/xml/xml') 11 | require('codemirror/mode/jsx/jsx') 12 | } 13 | 14 | export default Vuep 15 | -------------------------------------------------------------------------------- /src/utils/compiler.js: -------------------------------------------------------------------------------- 1 | import evalJS from './transform' 2 | 3 | export default function ({ template, script = 'module.exports={}', styles }, scope = {}) { 4 | try { 5 | if (script === 'module.exports={}' && !template) throw Error('no data') 6 | const result = evalJS(script, scope) 7 | if (template) { 8 | result.template = template 9 | } 10 | return { 11 | result: result, 12 | styles: styles && styles.join(' ') 13 | } 14 | } catch (error) { 15 | return { error } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | # Vuep 0.7 3 | 4 | [![Build Status](https://travis-ci.org/QingWei-Li/vuep.svg?branch=master)](https://travis-ci.org/QingWei-Li/vuep) 5 | [![Coverage Status](https://coveralls.io/repos/github/QingWei-Li/vuep/badge.svg?branch=master)](https://coveralls.io/github/QingWei-Li/vuep?branch=master) 6 | [![npm](https://img.shields.io/npm/v/vuep.svg)](https://www.npmjs.com/package/vuep) 7 | 8 | 9 | > A component for rendering Vue components with live editor and preview. 10 | 11 | 12 | [GitHub](https://github.com/QingWei-Li/vuep/) 13 | [Get Started](#demo) 14 | -------------------------------------------------------------------------------- /__test__/editor.spec.js: -------------------------------------------------------------------------------- 1 | import mock from './mock' 2 | import Vue from 'vue' 3 | import Editor from '../src/components/editor' 4 | 5 | const Ctor = Vue.extend(Editor) 6 | 7 | describe('editor', () => { 8 | mock() 9 | 10 | it('init editor', () => { 11 | const vm = new Ctor().$mount() 12 | expect(vm.$el.querySelector('.CodeMirror')).toBeDefined() 13 | }) 14 | 15 | it('options', () => { 16 | const vm = new Ctor({ 17 | propsData: { 18 | options: { 19 | theme: 'neo' 20 | } 21 | } 22 | }).$mount() 23 | 24 | expect(vm.currentOptions).toEqual({ 25 | lineNumbers: true, 26 | mode: 'text/x-vue', 27 | theme: 'neo', 28 | tabSize: 2 29 | }) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/utils/require.js: -------------------------------------------------------------------------------- 1 | import evalJS from './transform.js' 2 | 3 | const JSMODULE_REG = /\.((js)|(jsx))$/ 4 | 5 | export default function require (url) { 6 | if (JSMODULE_REG.test(url)) { 7 | return getAndCache(url) 8 | } 9 | } 10 | 11 | // modify from docsify: https://github.com/QingWei-Li/docsify/blob/master/src/core/fetch/ajax.js 12 | 13 | const cache = {} 14 | 15 | /** 16 | * Simple ajax get 17 | * @param {string} url 18 | * @return { then(resolve, reject), abort } 19 | */ 20 | function getAndCache (url) { 21 | const xhr = new XMLHttpRequest() // eslint-disable-line 22 | 23 | if (cache[url]) { 24 | return cache[url] 25 | } 26 | 27 | xhr.open('GET', url, false) 28 | xhr.send() 29 | const script = xhr.responseText 30 | cache[url] = evalJS(script) 31 | return cache[url] 32 | } 33 | -------------------------------------------------------------------------------- /__test__/require.spec.js: -------------------------------------------------------------------------------- 1 | import require from '../src/utils/require.js' 2 | 3 | let count = 0 4 | beforeAll(() => { 5 | global.XMLHttpRequest = () => { 6 | return { 7 | open (url) { 8 | count++ 9 | }, 10 | send () { 11 | }, 12 | responseText: 'module.exports="from remote"' 13 | } 14 | } 15 | }) 16 | 17 | test('require works', () => { 18 | global.XMLHttpRequest = () => { 19 | return { 20 | open (url) { 21 | count++ 22 | }, 23 | send () { 24 | }, 25 | responseText: 'module.exports="from remote"' 26 | } 27 | } 28 | expect(require('test.js')).toEqual('from remote') 29 | }) 30 | 31 | test('require cache works', () => { 32 | require('test.js') 33 | require('test.js') 34 | require('test.js') 35 | require('test.js') 36 | expect(count).toEqual(1) 37 | }) 38 | -------------------------------------------------------------------------------- /src/utils/parser.js: -------------------------------------------------------------------------------- 1 | export default function (input) { 2 | const html = document.createElement('div') 3 | const content = html.innerHTML = input.trim() 4 | 5 | try { 6 | const template = html.querySelector('template') 7 | const script = html.querySelector('script') 8 | const styles = Array.prototype.slice.call(html.querySelectorAll('style')).map(n => n.innerHTML) 9 | 10 | if (!template && !script && !styles.length) { 11 | return { 12 | content, 13 | script: content 14 | } 15 | } 16 | 17 | return { 18 | content: /<\/script>$/g.test(content) ? content : (content + '\n'), 19 | template: template ? template.innerHTML : '', 20 | script: script ? script.innerHTML : '', 21 | styles: styles 22 | } 23 | } catch (error) { 24 | /* istanbul ignore next */ 25 | return { error } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/editor.js: -------------------------------------------------------------------------------- 1 | import CodeMirror from 'codemirror' 2 | import assign from '../utils/assign' 3 | 4 | const DEFAULT_OPTIONS = { 5 | lineNumbers: true, 6 | mode: 'text/x-vue', 7 | theme: 'material', 8 | tabSize: 2 9 | } 10 | 11 | export default { 12 | name: 'VueCodeMirror', 13 | 14 | props: ['value', 'options'], 15 | 16 | render (h) { 17 | return h('div', null, [ 18 | h('textarea', { ref: 'textarea' }, this.value) 19 | ]) 20 | }, 21 | 22 | mounted () { 23 | this.currentOptions = assign({}, DEFAULT_OPTIONS, this.options) 24 | this.editor = CodeMirror.fromTextArea(this.$refs.textarea, this.currentOptions) 25 | this.editor.on('change', this.handleChange) 26 | }, 27 | 28 | watch: { 29 | value (val) { 30 | val !== this.editor.getValue() && this.editor.setValue(val) 31 | } 32 | }, 33 | 34 | methods: { 35 | handleChange () { 36 | /* istanbul ignore next */ 37 | this.$emit('change', this.editor.getValue()) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2017 cinwell.li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__test__/playground.spec.js: -------------------------------------------------------------------------------- 1 | import mock from './mock' 2 | import Vue from 'vue' 3 | import Playground from '../src/components/playground' 4 | 5 | const Ctor = Vue.extend(Playground) 6 | 7 | describe('playground', () => { 8 | mock() 9 | 10 | it('element is not found', () => { 11 | expect(() => new Ctor({ 12 | propsData: { 13 | template: '#app' 14 | } 15 | })).toThrowError('#app is not found') 16 | }) 17 | 18 | it('work', () => { 19 | const vm = new Ctor({ 20 | propsData: { 21 | template: `module.exports = { template: '
123
' }` 22 | } 23 | }).$mount() 24 | 25 | vm.$nextTick(_ => { 26 | expect(vm.preview).toEqual({ 27 | template: '
123
' 28 | }) 29 | expect(vm.content).toEqual(`module.exports = { template: '
123
' }`) 30 | }) 31 | }) 32 | 33 | it('styles', () => { 34 | const vm = new Ctor({ 35 | propsData: { 36 | template: `` 37 | } 38 | }).$mount() 39 | 40 | vm.$nextTick(_ => { 41 | expect(vm.preview).toEqual({}) 42 | expect(vm.styles).toEqual('.main {}') 43 | }) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vuep - A component for rendering Vue components with live editor and preview. 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /__test__/preview.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Preview from '../src/components/preview' 3 | 4 | const Ctor = Vue.extend(Preview) 5 | 6 | test('scoped style', () => { 7 | const vm = new Ctor({ 8 | propsData: { 9 | styles: '.main { color: red } #id { width: 10px } p { height: 20 } .wrapper input { background: url("");}' 10 | } 11 | }).$mount() 12 | const _uid = vm._uid 13 | const scoped = '.vuep-scoped-' + _uid 14 | const fixture = `${scoped} .main { color: red } ${scoped} #id { width: 10px } ${scoped} p { height: 20 } ${scoped} .wrapper input { background: url("");}` 15 | 16 | expect(vm.scopedStyle).toEqual(fixture) 17 | }) 18 | 19 | describe('append new vm', () => { 20 | const vm = new Ctor({ 21 | propsData: { 22 | value: { 23 | template: '
hello, {{ name }}
', 24 | data: { 25 | name: 'cinwell' 26 | } 27 | } 28 | } 29 | }).$mount() 30 | 31 | it('created', () => { 32 | expect(vm.codeVM).toBeDefined() 33 | expect(vm.codeVM.$el.textContent).toEqual('hello, cinwell') 34 | }) 35 | 36 | it('updated', () => { 37 | vm.value = { 38 | template: '
{{ name }}
', 39 | data: { 40 | name: 'cinwell' 41 | } 42 | } 43 | 44 | vm.$nextTick(_ => { 45 | expect(vm.codeVM.$el.textContent).toEqual('cinwell') 46 | }) 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /src/utils/transform.js: -------------------------------------------------------------------------------- 1 | import require from './require.js' 2 | window.require = require 3 | 4 | export default function evalJS (script, scope = {}) { 5 | // https://www.npmjs.com/package/babel-standalone 6 | /* istanbul ignore next */ 7 | 8 | if (typeof Babel !== 'undefined') { 9 | const plugins = [] 10 | 11 | // Register jsx plugin 12 | if (window['babel-plugin-transform-vue-jsx']) { 13 | if (!Babel.availablePlugins['transform-vue-jsx']) { // eslint-disable-line 14 | Babel.registerPlugin('transform-vue-jsx', window['babel-plugin-transform-vue-jsx']) // eslint-disable-line 15 | } 16 | plugins.push('transform-vue-jsx') 17 | } 18 | 19 | script = Babel.transform(script, { // eslint-disable-line 20 | presets: [['es2015', { 'loose': true }], 'stage-2'], 21 | plugins, 22 | comments: false 23 | }).code 24 | } 25 | 26 | var scopeDecl = '' 27 | for (var variable in scope) { 28 | if (scope.hasOwnProperty(variable)) { 29 | scopeDecl += 'var ' + variable + ' = __vuep[\'' + variable + '\'];' 30 | } 31 | } 32 | 33 | script = `(function(exports){var module={};module.exports=exports;${scopeDecl};${script};return module.exports.__esModule?module.exports.default:module.exports;})({})` 34 | const result = new Function('__vuep', 'return ' + script)(scope) || {} // eslint-disable-line 35 | return result 36 | } 37 | -------------------------------------------------------------------------------- /__test__/parser.spec.js: -------------------------------------------------------------------------------- 1 | import parser from '../src/utils/parser' 2 | 3 | test('script tag', () => { 4 | const fixture = ` 5 | 6 | 9 | 12 | ` 13 | const result = parser(fixture) 14 | 15 | expect(result.template).toContain('
123
') 16 | expect(result.script).toContain('module.exports = {}') 17 | expect(Array.isArray(result.styles)).toBeTruthy() 18 | expect(result.styles.length).toEqual(1) 19 | expect(result.styles[0]).toContain('div { color: red }') 20 | }) 21 | 22 | test('multiple style', () => { 23 | const fixture = ` 24 | 25 | 26 | 27 | ` 28 | const result = parser(fixture) 29 | 30 | expect(Array.isArray(result.styles)).toBeTruthy() 31 | expect(result.styles.length).toEqual(3) 32 | expect(result.styles[0]).toContain('div { color: red }') 33 | expect(result.styles[1]).toContain('div { color: green }') 34 | expect(result.styles[2]).toContain('div { color: blue }') 35 | }) 36 | 37 | test('only JavaScript', () => { 38 | const fixture = 'module.exports = {}' 39 | const result = parser(fixture) 40 | 41 | expect(result.template).toBeUndefined() 42 | expect(result.script).toContain('module.exports = {}') 43 | expect(result.styles).toBeUndefined() 44 | }) 45 | -------------------------------------------------------------------------------- /src/style/vuep.css: -------------------------------------------------------------------------------- 1 | @import "codemirror/lib/codemirror.css"; 2 | @import "codemirror/theme/material.css"; 3 | 4 | .vuep { 5 | display: flex; 6 | font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; 7 | height: 400px; 8 | 9 | ::-webkit-scrollbar-track { 10 | border-radius: 10px; 11 | background-color: #F5F5F5; 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | width: 8px; 16 | height: 8px; 17 | background-color: #F5F5F5; 18 | } 19 | 20 | ::-webkit-scrollbar-thumb { 21 | border-radius: 8px; 22 | background-color: #bbb; 23 | transition: all 0.5s; 24 | } 25 | 26 | ::-webkit-scrollbar-thumb:hover { 27 | border-radius: 8px; 28 | background-color: #777; 29 | } 30 | } 31 | 32 | .vuep-editor, .vuep-preview, .vuep-error { 33 | border-radius: 2px; 34 | height: inherit; 35 | margin-right: 10px; 36 | overflow: auto; 37 | width: 50%; 38 | 39 | &:last-child { 40 | margin-right: 0; 41 | } 42 | 43 | .CodeMirror { 44 | height: inherit; 45 | } 46 | } 47 | 48 | .vuep-editor { 49 | line-height: 1.2em; 50 | } 51 | 52 | .vuep-error { 53 | color: #f66; 54 | } 55 | 56 | .vuep-preview, .vuep-error { 57 | border: 1px solid #eee; 58 | box-sizing: border-box; 59 | padding: 25px 35px; 60 | } 61 | 62 | @media (max-width: 600px) { 63 | .vuep { 64 | display: block; 65 | height: auto; 66 | } 67 | 68 | .vuep-editor, .vuep-preview, .vuep-error { 69 | margin: 0 0 15px 0; 70 | height: 400px; 71 | width: 100%; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /__test__/compiler.spec.js: -------------------------------------------------------------------------------- 1 | import compiler from '../src/utils/compiler' 2 | 3 | test('No data', () => { 4 | const compiled = compiler({}) 5 | 6 | expect(compiled.error.name).toEqual('Error') 7 | expect(compiled.error.message).toEqual('no data') 8 | }) 9 | 10 | test('only template', () => { 11 | const compiled = compiler({ 12 | template: '
' 13 | }) 14 | 15 | expect(compiled.result).toEqual({ 16 | template: '
' 17 | }) 18 | }) 19 | 20 | test('only empty script', () => { 21 | const compiled = compiler({ 22 | script: 'module.exports = {}' 23 | }) 24 | 25 | expect(compiled.result).toEqual({}) 26 | }) 27 | 28 | test('only script', () => { 29 | const compiled = compiler({ 30 | script: `module.exports = {template : '
'}` 31 | }) 32 | 33 | expect(compiled.result).toEqual({ 34 | template: '
' 35 | }) 36 | }) 37 | 38 | test('script and tempalte', () => { 39 | const compiled = compiler({ 40 | template: '

cinwell

', 41 | script: 'module.exports = { b: {} }' 42 | }) 43 | 44 | expect(compiled.result).toEqual({ 45 | b: {}, 46 | template: '

cinwell

' 47 | }) 48 | }) 49 | 50 | test('multiple style tag', () => { 51 | const compiled = compiler({ 52 | styles: [ 53 | '.a { color: #ccc }', 54 | '.b { height: 100px; width: 200px }' 55 | ], 56 | template: '
' 57 | }) 58 | 59 | expect(compiled.styles).toEqual('.a { color: #ccc } .b { height: 100px; width: 200px }') 60 | }) 61 | 62 | test('no style', () => { 63 | const compiled = compiler({ 64 | template: '
' 65 | }) 66 | 67 | expect(compiled.styles).toBeUndefined() 68 | }) 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuep", 3 | "version": "0.8.1", 4 | "description": "A component for rendering Vue components with live editor and preview.", 5 | "main": "dist/vuep.common.js", 6 | "unpkg": "dist/vuep.js", 7 | "scripts": { 8 | "test": "eslint src && jest", 9 | "test:ci": "npm test && cat ./coverage/lcov.info | coveralls", 10 | "build": "node build/build.js && npm run build:css", 11 | "build:css": "node build/build-css.js", 12 | "dev": "rollup -wc build/watch.js" 13 | }, 14 | "jest": { 15 | "browser": true, 16 | "collectCoverage": true, 17 | "transform": { 18 | "^.+\\.js$": "babel-jest" 19 | } 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/QingWei-Li/vuep.git" 24 | }, 25 | "keywords": [ 26 | "vue", 27 | "playground", 28 | "preview", 29 | "editable", 30 | "component", 31 | "vuejs" 32 | ], 33 | "author": "qingwei-li (https://github.com/QingWei-Li)", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/QingWei-Li/vuep/issues" 37 | }, 38 | "homepage": "https://qingwei-li.github.io/vuep/", 39 | "devDependencies": { 40 | "babel-jest": "^18.0.0", 41 | "babel-preset-es2015": "^6.18.0", 42 | "babel-preset-stage-3": "^6.22.0", 43 | "codemirror": "^5.22.0", 44 | "cssnano": "^3.9.1", 45 | "eslint": "^3.12.2", 46 | "eslint-config-vue": "^2.0.1", 47 | "eslint-plugin-vue": "^1.0.0", 48 | "jest": "^18.0.0", 49 | "postcss": "^5.2.7", 50 | "postcss-salad": "^1.0.6", 51 | "rollup": "^0.38.0", 52 | "rollup-plugin-buble": "^0.15.0", 53 | "rollup-plugin-commonjs": "^6.0.1", 54 | "rollup-plugin-node-resolve": "^2.0.0", 55 | "rollup-plugin-uglify": "^1.0.1", 56 | "rollup-watch": "^3.2.2", 57 | "vue": "^2.1.7" 58 | }, 59 | "peerDependencies": { 60 | "codemirror": "^5.22.0" 61 | }, 62 | "dependencies": { 63 | "simple-assign": "^0.1.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/components/playground.js: -------------------------------------------------------------------------------- 1 | import Editor from './editor' 2 | import Preview from './preview' 3 | import parser from '../utils/parser' 4 | import compiler from '../utils/compiler' 5 | 6 | export default { 7 | name: 'Vuep', 8 | 9 | props: { 10 | template: String, 11 | options: {}, 12 | keepData: Boolean, 13 | value: String, 14 | scope: Object, 15 | iframe: Boolean 16 | }, 17 | 18 | data () { 19 | return { 20 | content: '', 21 | preview: '', 22 | styles: '', 23 | error: '' 24 | } 25 | }, 26 | 27 | render (h) { 28 | let win 29 | 30 | /* istanbul ignore next */ 31 | if (this.error) { 32 | win = h('div', { 33 | class: 'vuep-error' 34 | }, [this.error]) 35 | } else { 36 | win = h(Preview, { 37 | class: 'vuep-preview', 38 | props: { 39 | value: this.preview, 40 | styles: this.styles, 41 | keepData: this.keepData, 42 | iframe: this.iframe 43 | }, 44 | on: { 45 | error: this.handleError 46 | } 47 | }) 48 | } 49 | 50 | return h('div', { class: 'vuep' }, [ 51 | h(Editor, { 52 | class: 'vuep-editor', 53 | props: { 54 | value: this.content, 55 | options: this.options 56 | }, 57 | on: { 58 | change: [this.executeCode, val => this.$emit('input', val)] 59 | } 60 | }), 61 | win 62 | ]) 63 | }, 64 | 65 | watch: { 66 | value: { 67 | immediate: true, 68 | handler (val) { 69 | val && this.executeCode(val) 70 | } 71 | } 72 | }, 73 | 74 | created () { 75 | /* istanbul ignore next */ 76 | if (this.$isServer) return 77 | let content = this.template 78 | 79 | if (/^[\.#]/.test(this.template)) { 80 | const html = document.querySelector(this.template) 81 | if (!html) throw Error(`${this.template} is not found`) 82 | 83 | /* istanbul ignore next */ 84 | content = html.innerHTML 85 | } 86 | 87 | if (content) { 88 | this.executeCode(content) 89 | this.$emit('input', content) 90 | } 91 | }, 92 | 93 | methods: { 94 | handleError (err) { 95 | /* istanbul ignore next */ 96 | this.error = err 97 | }, 98 | 99 | executeCode (code) { 100 | this.error = '' 101 | const result = parser(code) 102 | 103 | /* istanbul ignore next */ 104 | if (result.error) { 105 | this.error = result.error.message 106 | return 107 | } 108 | 109 | const compiledCode = compiler(result, this.scope) 110 | 111 | /* istanbul ignore next */ 112 | if (compiledCode.error) { 113 | this.error = compiledCode.error.message 114 | return 115 | } 116 | 117 | this.content = result.content 118 | this.preview = compiledCode.result 119 | if (compiledCode.styles) this.styles = compiledCode.styles 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/components/preview.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.common' 2 | import assign from '../utils/assign' // eslint-disable-line 3 | 4 | export default { 5 | name: 'preview', 6 | 7 | props: ['value', 'styles', 'keepData', 'iframe'], 8 | 9 | render (h) { 10 | this.className = 'vuep-scoped-' + this._uid 11 | 12 | return h(this.iframe ? 'iframe' : 'div', { 13 | class: this.className 14 | }, [ 15 | this.scopedStyle ? h('style', null, this.scopedStyle) : '' 16 | ]) 17 | }, 18 | 19 | computed: { 20 | scopedStyle () { 21 | return this.styles 22 | ? insertScope(this.styles, `.${this.className}`) 23 | : '' 24 | } 25 | }, 26 | 27 | mounted () { 28 | this.$watch('value', this.renderCode, { immediate: true }) 29 | if (this.iframe) { 30 | this.$el.addEventListener('load', this.renderCode) 31 | } 32 | }, 33 | beforeDestroy () { 34 | if (this.iframe) { 35 | this.$el.removeEventListener('load', this.renderCode) 36 | } 37 | }, 38 | methods: { 39 | renderCode () { 40 | // Firefox needs the iframe to be loaded 41 | if (this.iframe && this.$el.contentDocument.readyState !== 'complete') { 42 | return 43 | } 44 | 45 | const val = this.value 46 | const lastData = this.keepData && this.codeVM && assign({}, this.codeVM.$data) 47 | const container = this.iframe ? this.$el.contentDocument.body : this.$el 48 | 49 | if (this.codeVM) { 50 | this.codeVM.$destroy() 51 | container.removeChild(this.codeVM.$el) 52 | } 53 | 54 | this.codeEl = document.createElement('div') 55 | container.appendChild(this.codeEl) 56 | 57 | if (this.iframe) { 58 | const head = this.$el.contentDocument.head 59 | if (this.styleEl) { 60 | head.removeChild(this.styleEl) 61 | for (const key in this.styleNodes) { 62 | head.removeChild(this.styleNodes[key]) 63 | } 64 | } 65 | this.styleEl = document.createElement('style') 66 | this.styleEl.appendChild(document.createTextNode(this.styles)) 67 | this.styleNodes = [] 68 | const documentStyles = getDocumentStyle() 69 | for (const key in documentStyles) { 70 | this.styleNodes[key] = documentStyles[key].cloneNode(true) 71 | head.appendChild(this.styleNodes[key]) 72 | } 73 | head.appendChild(this.styleEl) 74 | } 75 | 76 | try { 77 | const parent = this 78 | this.codeVM = new Vue({ parent, ...val }).$mount(this.codeEl) 79 | 80 | if (lastData) { 81 | for (const key in lastData) { 82 | this.codeVM[key] = lastData[key] 83 | } 84 | } 85 | } catch (e) { 86 | /* istanbul ignore next */ 87 | this.$emit('error', e) 88 | } 89 | } 90 | } 91 | } 92 | 93 | function insertScope (style, scope) { 94 | const regex = /(^|\})\s*([^{]+)/g 95 | return style.trim().replace(regex, (m, g1, g2) => { 96 | return g1 ? `${g1} ${scope} ${g2}` : `${scope} ${g2}` 97 | }) 98 | } 99 | 100 | function getDocumentStyle () { 101 | const links = document.querySelectorAll('link[rel="stylesheet"]') 102 | const styles = document.querySelectorAll('style') 103 | return Array.from(links).concat(Array.from(styles)) 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vuep (vue playground) 2 | 3 | [![Build Status](https://travis-ci.org/QingWei-Li/vuep.svg?branch=master)](https://travis-ci.org/QingWei-Li/vuep) 4 | [![Coverage Status](https://coveralls.io/repos/github/QingWei-Li/vuep/badge.svg?branch=master)](https://coveralls.io/github/QingWei-Li/vuep?branch=master) 5 | [![npm](https://img.shields.io/npm/v/vuep.svg)](https://www.npmjs.com/package/vuep) 6 | 7 | 8 | > 🎡 A component for rendering Vue components with live editor and preview. 9 | 10 | 11 | 12 | ![image](https://cloud.githubusercontent.com/assets/7565692/21482443/093e4970-cbaf-11e6-89f0-eae73fc49741.png) 13 | 14 | ## Links 15 | 16 | - Docs: https://cinwell.com/vuep/ 17 | - An online playgound: https://vuep.netlify.com 18 | 19 | 20 | 21 | ## Installation 22 | 23 | ### Yarn 24 | ```bash 25 | yarn add vuep codemirror 26 | # npm i vuep codemirror -S 27 | ``` 28 | 29 | ### HTML tag 30 | 31 | ```html 32 | 33 | 34 | 35 | 36 | 37 | 38 | ``` 39 | 40 | ## Quick start 41 | 42 | **Need the full (compiler-included) build of Vue** 43 | 44 | webpack config 45 | ```javascript 46 | { 47 | alias: { 48 | 'vue$': 'vue/dist/vue.common' 49 | } 50 | } 51 | ``` 52 | 53 | ```javascript 54 | import Vue from 'vue' 55 | import Vuep from 'vuep' 56 | import 'vuep/dist/vuep.css' 57 | 58 | Vue.use(Vuep /*, { codemirror options } */) 59 | // or Vue.component('Vuep', Vuep) 60 | 61 | new Vue({ 62 | el: '#app', 63 | 64 | created: function () { 65 | this.code = ` 66 | 69 | 70 | 77 | ` 78 | } 79 | }) 80 | ``` 81 | 82 | 83 | ### Usage A 84 | ```html 85 |
86 | 87 |
88 | ``` 89 | 90 | 91 | ### Usage B 92 | you can write in HTML file or even a markdown file. 93 | 94 | ```html 95 |
96 | 97 |
98 | 99 | 111 | 112 | ``` 113 | 114 | 115 | ### Scope 116 | 117 | You can customize scope by passing an object to the scope property. 118 | 119 | This object can contain component available in main scope to include them into Vuep. 120 | 121 | - **features.js**: Component to showcase into Vuep 122 | ```javascript 123 | export default { 124 | props: { 125 | features: Array 126 | }, 127 | template: `
128 |

Features

129 | 132 |
` 133 | } 134 | ``` 135 | 136 | - **app.js**: Application that needs to showcase Features component through Vuep 137 | ```javascript 138 | import Vue from 'vue' 139 | 140 | import Features from 'features' // Import component 141 | 142 | new Vue({ 143 | el: '#app', 144 | data: function () { 145 | return { 146 | scope: { Features }, // Set the scope of vuep 147 | value: ` 148 | 153 | 154 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 82 | 127 | 183 | 239 | -------------------------------------------------------------------------------- /dist/vuep.min.css: -------------------------------------------------------------------------------- 1 | .CodeMirror{font-family:monospace;height:300px;color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:a 1.06s steps(1) infinite;animation:a 1.06s steps(1) infinite;background-color:#7e7}@-webkit-keyframes a{50%{background-color:transparent}}@keyframes a{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:5;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:none}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:""}span.CodeMirror-selectedtext{background:none}.cm-s-material.CodeMirror{background-color:#263238;color:#e9eded}.cm-s-material .CodeMirror-gutters{background:#263238;color:#537f7e;border:none}.cm-s-material .CodeMirror-guttermarker,.cm-s-material .CodeMirror-guttermarker-subtle,.cm-s-material .CodeMirror-linenumber{color:#537f7e}.cm-s-material .CodeMirror-cursor{border-left:1px solid #f8f8f0}.cm-s-material div.CodeMirror-selected{background:hsla(0,0%,100%,.15)}.cm-s-material.CodeMirror-focused div.CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-material .CodeMirror-line::selection,.cm-s-material .CodeMirror-line>span::selection,.cm-s-material .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-material .CodeMirror-line::-moz-selection,.cm-s-material .CodeMirror-line>span::-moz-selection,.cm-s-material .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-material .CodeMirror-activeline-background{background:transparent}.cm-s-material .cm-keyword{color:#c792ea}.cm-s-material .cm-operator{color:#e9eded}.cm-s-material .cm-variable-2{color:#80cbc4}.cm-s-material .cm-variable-3{color:#82b1ff}.cm-s-material .cm-builtin{color:#decb6b}.cm-s-material .cm-atom,.cm-s-material .cm-number{color:#f77669}.cm-s-material .cm-def{color:#e9eded}.cm-s-material .cm-string{color:#c3e88d}.cm-s-material .cm-string-2{color:#80cbc4}.cm-s-material .cm-comment{color:#546e7a}.cm-s-material .cm-variable{color:#82b1ff}.cm-s-material .cm-meta,.cm-s-material .cm-tag{color:#80cbc4}.cm-s-material .cm-attribute{color:#ffcb6b}.cm-s-material .cm-property{color:#80cbae}.cm-s-material .cm-qualifier,.cm-s-material .cm-variable-3{color:#decb6b}.cm-s-material .cm-tag{color:#ff5370}.cm-s-material .cm-error{color:#fff;background-color:#ec5f67}.cm-s-material .CodeMirror-matchingbracket{text-decoration:underline;color:#fff!important}.vuep{display:-webkit-box;display:-ms-flexbox;display:flex;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;height:400px}.vuep ::-webkit-scrollbar-track{border-radius:10px;background-color:#f5f5f5}.vuep ::-webkit-scrollbar{width:8px;height:8px;background-color:#f5f5f5}.vuep ::-webkit-scrollbar-thumb{border-radius:8px;background-color:#bbb;-webkit-transition:all .5s;transition:all .5s}.vuep ::-webkit-scrollbar-thumb:hover{border-radius:8px;background-color:#777}.vuep-editor,.vuep-error,.vuep-preview{border-radius:2px;height:inherit;margin-right:10px;overflow:auto;width:50%}.vuep-editor .CodeMirror,.vuep-error .CodeMirror,.vuep-preview .CodeMirror{height:inherit}.vuep-editor:last-child,.vuep-error:last-child,.vuep-preview:last-child{margin-right:0}.vuep-editor{line-height:1.2em}.vuep-error{color:#f66}.vuep-error,.vuep-preview{border:1px solid #eee;box-sizing:border-box;padding:25px 35px}@media (max-width:600px){.vuep{display:block;height:auto}.vuep-editor,.vuep-error,.vuep-preview{margin:0 0 15px;height:400px;width:100%}} -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | 4 | 39 | 40 | 41 |
42 | 43 | # Usage 44 | 45 | ## CommonJS 46 | **Need the full (compiler-included) build of Vue** 47 | 48 | webpack config 49 | 50 | ```javascript 51 | { 52 | alias: { 53 | 'vue$': 'vue/dist/vue.common' 54 | } 55 | } 56 | ``` 57 | 58 | ```javascript 59 | import Vue from 'vue' 60 | import Vuep from 'vuep' 61 | import 'vuep/dist/vuep.css' 62 | 63 | Vue.use(Vuep /*, { codemirror options } */) 64 | // or Vue.component('Vuep', Vuep) 65 | 66 | new Vue({ 67 | el: '#app', 68 | 69 | created: function () { 70 | this.code = ` 71 | 74 | 75 | 82 | ` 83 | } 84 | }) 85 | ``` 86 | 87 | template 88 | ```html 89 |
90 | 91 |
92 | ``` 93 | 94 | ## UMD 95 | 96 | index.html 97 | ```html 98 | 99 | 100 | 101 | 102 | 103 | 104 | ``` 105 | 106 | template 107 | ```html 108 | 109 | 110 | 122 | 123 | ``` 124 | 125 | The default is supported in docsify. such as https://qingwei-li.github.io/vuep/README.md 126 | 127 | # Options 128 | 129 | https://codemirror.net/index.html 130 | 131 | ## Global config 132 | ```javascript 133 | Vue.use(Vuep /*, { codemirror config }*/) 134 | ``` 135 | 136 | ## Props 137 | 138 | ```html 139 | 140 | ``` 141 | 142 | ### Default config 143 | ```json 144 | { 145 | "lineNumbers": true, 146 | "mode": "text/x-vue", 147 | "theme": "material", 148 | "tabSize": 2 149 | } 150 | ``` 151 | 152 | ### example 153 | 154 | ```html 155 | 156 | 157 | 166 | ``` 167 | 168 | 169 | 170 | 179 | 180 |
181 | 182 | ## v-model 183 | 184 | ```html 185 | 188 | 189 | 202 | ``` 203 | 204 | ## JavaScript scope 205 | 206 | You can customize JavaScript scope by setting an object to the scope property. 207 | 208 | This object can contain component from your app scope to include them into Vuep scope. 209 | 210 | - **features.js**: Component to showcase into Vuep 211 | 212 | ```javascript 213 | export default { 214 | props: { 215 | features: Array 216 | }, 217 | template: `
218 |

Features

219 | 222 |
` 223 | } 224 | ``` 225 | 226 | - **app.js**: Application that needs to showcase Features component through Vuep 227 | 228 | ```javascript 229 | import Vue from 'vue' 230 | 231 | import Features from 'features' // Import component 232 | 233 | new Vue({ 234 | el: '#app', 235 | data: function () { 236 | return { 237 | scope: { Features }, // Set the scope of vuep 238 | value: ` 239 | 244 | 245 | 310 | 311 | 312 | ## Can I use ES6? 313 | 314 | I know you will worry that some browsers do not support ES6, but we have [babel-standalone](https://www.npmjs.com/package/babel-standalone). 315 | 316 | > babel-standalone is a standalone build of Babel for use in non-Node.js environments, including browsers. 317 | 318 | How to use: 319 | 320 | In your HTML file 321 | 322 | ```html 323 | 324 | ``` 325 | 326 | Done. Now you are free to use ES6, Vuep will compile them to ES5 through the babel. 327 | 328 | 329 | 330 | 331 | 349 | 350 | 351 | ## Can I use JSX? 352 | 353 | Sure. 354 | 355 | ```html 356 | 357 | 358 | ``` 359 | 360 | 361 | 362 | 376 | 377 | ## Can I use `require`? 378 | 379 | Sure. if you use ES6 syntax you can even use `import`. 380 | 381 | vuep implement a nodejs like synchrounous require interface in browser, so you can directly require a js file. 382 | 383 | ```jsx 384 | /* remote.js */ 385 | export default { 386 | name: 'remote', 387 | render () { 388 | return
from remote
389 | } 390 | } 391 | ``` 392 | 393 | 394 | 395 | 408 | 409 | 410 | # Warning 411 | 412 | If you use `script(type="text/x-template)"`, The script tag must be at the end, for example 413 | 414 | ```html 415 | 419 | 420 | ``` 421 | 422 | These will be parsed incorrectly 423 | ```html 424 | 426 | 427 | 428 | 429 | ``` 430 | 431 | ```html 432 | 435 | 436 | 437 | ``` 438 | -------------------------------------------------------------------------------- /dist/vuep.common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 4 | 5 | var CodeMirror = _interopDefault(require('codemirror')); 6 | var Vue$1 = _interopDefault(require('vue/dist/vue.common')); 7 | 8 | var index = function (target) { 9 | var arguments$1 = arguments; 10 | 11 | for (var i = 1; i < arguments.length; i++) { 12 | var source = arguments$1[i]; 13 | for (var key in source) { 14 | if (Object.prototype.hasOwnProperty.call(source, key)) { 15 | target[key] = source[key]; 16 | } 17 | } 18 | } 19 | return target; 20 | }; 21 | 22 | var assign = Object.assign || index; 23 | 24 | var DEFAULT_OPTIONS = { 25 | lineNumbers: true, 26 | mode: 'text/x-vue', 27 | theme: 'material', 28 | tabSize: 2 29 | }; 30 | 31 | var Editor = { 32 | name: 'VueCodeMirror', 33 | 34 | props: ['value', 'options'], 35 | 36 | render: function render (h) { 37 | return h('div', null, [ 38 | h('textarea', { ref: 'textarea' }, this.value) 39 | ]) 40 | }, 41 | 42 | mounted: function mounted () { 43 | this.currentOptions = assign({}, DEFAULT_OPTIONS, this.options); 44 | this.editor = CodeMirror.fromTextArea(this.$refs.textarea, this.currentOptions); 45 | this.editor.on('change', this.handleChange); 46 | }, 47 | 48 | watch: { 49 | value: function value (val) { 50 | val !== this.editor.getValue() && this.editor.setValue(val); 51 | } 52 | }, 53 | 54 | methods: { 55 | handleChange: function handleChange () { 56 | /* istanbul ignore next */ 57 | this.$emit('change', this.editor.getValue()); 58 | } 59 | } 60 | }; 61 | 62 | var Preview = { 63 | name: 'preview', 64 | 65 | props: ['value', 'styles', 'keepData', 'iframe'], 66 | 67 | render: function render (h) { 68 | this.className = 'vuep-scoped-' + this._uid; 69 | 70 | return h(this.iframe ? 'iframe' : 'div', { 71 | class: this.className 72 | }, [ 73 | this.scopedStyle ? h('style', null, this.scopedStyle) : '' 74 | ]) 75 | }, 76 | 77 | computed: { 78 | scopedStyle: function scopedStyle () { 79 | return this.styles 80 | ? insertScope(this.styles, ("." + (this.className))) 81 | : '' 82 | } 83 | }, 84 | 85 | mounted: function mounted () { 86 | this.$watch('value', this.renderCode, { immediate: true }); 87 | if (this.iframe) { 88 | this.$el.addEventListener('load', this.renderCode); 89 | } 90 | }, 91 | beforeDestroy: function beforeDestroy () { 92 | if (this.iframe) { 93 | this.$el.removeEventListener('load', this.renderCode); 94 | } 95 | }, 96 | methods: { 97 | renderCode: function renderCode () { 98 | var this$1 = this; 99 | 100 | // Firefox needs the iframe to be loaded 101 | if (this.iframe && this.$el.contentDocument.readyState !== 'complete') { 102 | return 103 | } 104 | 105 | var val = this.value; 106 | var lastData = this.keepData && this.codeVM && assign({}, this.codeVM.$data); 107 | var container = this.iframe ? this.$el.contentDocument.body : this.$el; 108 | 109 | if (this.codeVM) { 110 | this.codeVM.$destroy(); 111 | container.removeChild(this.codeVM.$el); 112 | } 113 | 114 | this.codeEl = document.createElement('div'); 115 | container.appendChild(this.codeEl); 116 | 117 | if (this.iframe) { 118 | var head = this.$el.contentDocument.head; 119 | if (this.styleEl) { 120 | head.removeChild(this.styleEl); 121 | for (var key in this$1.styleNodes) { 122 | head.removeChild(this$1.styleNodes[key]); 123 | } 124 | } 125 | this.styleEl = document.createElement('style'); 126 | this.styleEl.appendChild(document.createTextNode(this.styles)); 127 | this.styleNodes = []; 128 | var documentStyles = getDocumentStyle(); 129 | for (var key$1 in documentStyles) { 130 | this$1.styleNodes[key$1] = documentStyles[key$1].cloneNode(true); 131 | head.appendChild(this$1.styleNodes[key$1]); 132 | } 133 | head.appendChild(this.styleEl); 134 | } 135 | 136 | try { 137 | var parent = this; 138 | this.codeVM = new Vue$1(assign({}, {parent: parent}, val)).$mount(this.codeEl); 139 | 140 | if (lastData) { 141 | for (var key$2 in lastData) { 142 | this$1.codeVM[key$2] = lastData[key$2]; 143 | } 144 | } 145 | } catch (e) { 146 | /* istanbul ignore next */ 147 | this.$emit('error', e); 148 | } 149 | } 150 | } 151 | }; 152 | 153 | function insertScope (style, scope) { 154 | var regex = /(^|\})\s*([^{]+)/g; 155 | return style.trim().replace(regex, function (m, g1, g2) { 156 | return g1 ? (g1 + " " + scope + " " + g2) : (scope + " " + g2) 157 | }) 158 | } 159 | 160 | function getDocumentStyle () { 161 | var links = document.querySelectorAll('link[rel="stylesheet"]'); 162 | var styles = document.querySelectorAll('style'); 163 | return Array.from(links).concat(Array.from(styles)) 164 | } 165 | 166 | var parser = function (input) { 167 | var html = document.createElement('div'); 168 | var content = html.innerHTML = input.trim(); 169 | 170 | try { 171 | var template = html.querySelector('template'); 172 | var script = html.querySelector('script'); 173 | var styles = Array.prototype.slice.call(html.querySelectorAll('style')).map(function (n) { return n.innerHTML; }); 174 | 175 | if (!template && !script && !styles.length) { 176 | return { 177 | content: content, 178 | script: content 179 | } 180 | } 181 | 182 | return { 183 | content: /<\/script>$/g.test(content) ? content : (content + '\n'), 184 | template: template ? template.innerHTML : '', 185 | script: script ? script.innerHTML : '', 186 | styles: styles 187 | } 188 | } catch (error) { 189 | /* istanbul ignore next */ 190 | return { error: error } 191 | } 192 | }; 193 | 194 | var JSMODULE_REG = /\.((js)|(jsx))$/; 195 | 196 | function require$1 (url) { 197 | if (JSMODULE_REG.test(url)) { 198 | return getAndCache(url) 199 | } 200 | } 201 | 202 | // modify from docsify: https://github.com/QingWei-Li/docsify/blob/master/src/core/fetch/ajax.js 203 | 204 | var cache = {}; 205 | 206 | /** 207 | * Simple ajax get 208 | * @param {string} url 209 | * @return { then(resolve, reject), abort } 210 | */ 211 | function getAndCache (url) { 212 | var xhr = new XMLHttpRequest(); // eslint-disable-line 213 | 214 | if (cache[url]) { 215 | return cache[url] 216 | } 217 | 218 | xhr.open('GET', url, false); 219 | xhr.send(); 220 | var script = xhr.responseText; 221 | cache[url] = evalJS(script); 222 | return cache[url] 223 | } 224 | 225 | window.require = require$1; 226 | 227 | function evalJS (script, scope) { 228 | if ( scope === void 0 ) scope = {}; 229 | 230 | // https://www.npmjs.com/package/babel-standalone 231 | /* istanbul ignore next */ 232 | 233 | if (typeof Babel !== 'undefined') { 234 | var plugins = []; 235 | 236 | // Register jsx plugin 237 | if (window['babel-plugin-transform-vue-jsx']) { 238 | if (!Babel.availablePlugins['transform-vue-jsx']) { // eslint-disable-line 239 | Babel.registerPlugin('transform-vue-jsx', window['babel-plugin-transform-vue-jsx']); // eslint-disable-line 240 | } 241 | plugins.push('transform-vue-jsx'); 242 | } 243 | 244 | script = Babel.transform(script, { // eslint-disable-line 245 | presets: [['es2015', { 'loose': true }], 'stage-2'], 246 | plugins: plugins, 247 | comments: false 248 | }).code; 249 | } 250 | 251 | var scopeDecl = ''; 252 | for (var variable in scope) { 253 | if (scope.hasOwnProperty(variable)) { 254 | scopeDecl += 'var ' + variable + ' = __vuep[\'' + variable + '\'];'; 255 | } 256 | } 257 | 258 | script = "(function(exports){var module={};module.exports=exports;" + scopeDecl + ";" + script + ";return module.exports.__esModule?module.exports.default:module.exports;})({})"; 259 | var result = new Function('__vuep', 'return ' + script)(scope) || {}; // eslint-disable-line 260 | return result 261 | } 262 | 263 | var compiler = function (ref, scope) { 264 | var template = ref.template; 265 | var script = ref.script; if ( script === void 0 ) script = 'module.exports={}'; 266 | var styles = ref.styles; 267 | if ( scope === void 0 ) scope = {}; 268 | 269 | try { 270 | if (script === 'module.exports={}' && !template) { throw Error('no data') } 271 | var result = evalJS(script, scope); 272 | if (template) { 273 | result.template = template; 274 | } 275 | return { 276 | result: result, 277 | styles: styles && styles.join(' ') 278 | } 279 | } catch (error) { 280 | return { error: error } 281 | } 282 | }; 283 | 284 | var Vuep$2 = { 285 | name: 'Vuep', 286 | 287 | props: { 288 | template: String, 289 | options: {}, 290 | keepData: Boolean, 291 | value: String, 292 | scope: Object, 293 | iframe: Boolean 294 | }, 295 | 296 | data: function data () { 297 | return { 298 | content: '', 299 | preview: '', 300 | styles: '', 301 | error: '' 302 | } 303 | }, 304 | 305 | render: function render (h) { 306 | var this$1 = this; 307 | 308 | var win; 309 | 310 | /* istanbul ignore next */ 311 | if (this.error) { 312 | win = h('div', { 313 | class: 'vuep-error' 314 | }, [this.error]); 315 | } else { 316 | win = h(Preview, { 317 | class: 'vuep-preview', 318 | props: { 319 | value: this.preview, 320 | styles: this.styles, 321 | keepData: this.keepData, 322 | iframe: this.iframe 323 | }, 324 | on: { 325 | error: this.handleError 326 | } 327 | }); 328 | } 329 | 330 | return h('div', { class: 'vuep' }, [ 331 | h(Editor, { 332 | class: 'vuep-editor', 333 | props: { 334 | value: this.content, 335 | options: this.options 336 | }, 337 | on: { 338 | change: [this.executeCode, function (val) { return this$1.$emit('input', val); }] 339 | } 340 | }), 341 | win 342 | ]) 343 | }, 344 | 345 | watch: { 346 | value: { 347 | immediate: true, 348 | handler: function handler (val) { 349 | val && this.executeCode(val); 350 | } 351 | } 352 | }, 353 | 354 | created: function created () { 355 | /* istanbul ignore next */ 356 | if (this.$isServer) { return } 357 | var content = this.template; 358 | 359 | if (/^[\.#]/.test(this.template)) { 360 | var html = document.querySelector(this.template); 361 | if (!html) { throw Error(((this.template) + " is not found")) } 362 | 363 | /* istanbul ignore next */ 364 | content = html.innerHTML; 365 | } 366 | 367 | if (content) { 368 | this.executeCode(content); 369 | this.$emit('input', content); 370 | } 371 | }, 372 | 373 | methods: { 374 | handleError: function handleError (err) { 375 | /* istanbul ignore next */ 376 | this.error = err; 377 | }, 378 | 379 | executeCode: function executeCode (code) { 380 | this.error = ''; 381 | var result = parser(code); 382 | 383 | /* istanbul ignore next */ 384 | if (result.error) { 385 | this.error = result.error.message; 386 | return 387 | } 388 | 389 | var compiledCode = compiler(result, this.scope); 390 | 391 | /* istanbul ignore next */ 392 | if (compiledCode.error) { 393 | this.error = compiledCode.error.message; 394 | return 395 | } 396 | 397 | this.content = result.content; 398 | this.preview = compiledCode.result; 399 | if (compiledCode.styles) { this.styles = compiledCode.styles; } 400 | } 401 | } 402 | }; 403 | 404 | Vuep$2.config = function (opts) { 405 | Vuep$2.props.options.default = function () { return opts; }; 406 | }; 407 | 408 | function install (Vue, opts) { 409 | Vuep$2.config(opts); 410 | Vue.component(Vuep$2.name, Vuep$2); 411 | } 412 | 413 | Vuep$2.install = install; 414 | 415 | if (typeof Vue !== 'undefined') { 416 | Vue.use(install); // eslint-disable-line 417 | } 418 | 419 | if (typeof require !== 'undefined') { 420 | require('codemirror/addon/mode/overlay'); 421 | require('codemirror/addon/mode/simple'); 422 | require('codemirror/mode/css/css'); 423 | require('codemirror/mode/htmlmixed/htmlmixed'); 424 | require('codemirror/mode/javascript/javascript'); 425 | require('codemirror/mode/vue/vue'); 426 | require('codemirror/mode/xml/xml'); 427 | require('codemirror/mode/jsx/jsx'); 428 | } 429 | 430 | module.exports = Vuep$2; 431 | -------------------------------------------------------------------------------- /dist/vuep.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | .CodeMirror { 3 | /* Set height, width, borders, and global font properties here */ 4 | font-family: monospace; 5 | height: 300px; 6 | color: black; 7 | } 8 | /* PADDING */ 9 | .CodeMirror-lines { 10 | padding: 4px 0; 11 | /* Vertical padding around content */ 12 | } 13 | .CodeMirror pre { 14 | padding: 0 4px; 15 | /* Horizontal padding of content */ 16 | } 17 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 18 | background-color: white; 19 | /* The little square between H and V scrollbars */ 20 | } 21 | /* GUTTER */ 22 | .CodeMirror-gutters { 23 | border-right: 1px solid #ddd; 24 | background-color: #f7f7f7; 25 | white-space: nowrap; 26 | } 27 | .CodeMirror-linenumbers {} 28 | .CodeMirror-linenumber { 29 | padding: 0 3px 0 5px; 30 | min-width: 20px; 31 | text-align: right; 32 | color: #999; 33 | white-space: nowrap; 34 | } 35 | .CodeMirror-guttermarker { 36 | color: black; 37 | } 38 | .CodeMirror-guttermarker-subtle { 39 | color: #999; 40 | } 41 | /* CURSOR */ 42 | .CodeMirror-cursor { 43 | border-left: 1px solid black; 44 | border-right: none; 45 | width: 0; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .cm-fat-cursor .CodeMirror-cursor { 52 | width: auto; 53 | border: 0 !important; 54 | background: #7e7; 55 | } 56 | .cm-fat-cursor div.CodeMirror-cursors { 57 | z-index: 1; 58 | } 59 | .cm-animate-fat-cursor { 60 | width: auto; 61 | border: 0; 62 | -webkit-animation: blink 1.06s steps(1) infinite; 63 | animation: blink 1.06s steps(1) infinite; 64 | background-color: #7e7; 65 | } 66 | @-webkit-keyframes blink { 67 | 0% {} 68 | 50% { 69 | background-color: transparent; 70 | } 71 | 100% {} 72 | } 73 | @keyframes blink { 74 | 0% {} 75 | 50% { 76 | background-color: transparent; 77 | } 78 | 100% {} 79 | } 80 | /* Can style cursor different in overwrite (non-insert) mode */ 81 | .CodeMirror-overwrite .CodeMirror-cursor {} 82 | .cm-tab { 83 | display: inline-block; 84 | text-decoration: inherit; 85 | } 86 | .CodeMirror-rulers { 87 | position: absolute; 88 | left: 0; 89 | right: 0; 90 | top: -50px; 91 | bottom: -20px; 92 | overflow: hidden; 93 | } 94 | .CodeMirror-ruler { 95 | border-left: 1px solid #ccc; 96 | top: 0; 97 | bottom: 0; 98 | position: absolute; 99 | } 100 | /* DEFAULT THEME */ 101 | .cm-s-default .cm-header { 102 | color: blue; 103 | } 104 | .cm-s-default .cm-quote { 105 | color: #090; 106 | } 107 | .cm-negative { 108 | color: #d44; 109 | } 110 | .cm-positive { 111 | color: #292; 112 | } 113 | .cm-header, .cm-strong { 114 | font-weight: 700; 115 | } 116 | .cm-em { 117 | font-style: italic; 118 | } 119 | .cm-link { 120 | text-decoration: underline; 121 | } 122 | .cm-strikethrough { 123 | text-decoration: line-through; 124 | } 125 | .cm-s-default .cm-keyword { 126 | color: #708; 127 | } 128 | .cm-s-default .cm-atom { 129 | color: #219; 130 | } 131 | .cm-s-default .cm-number { 132 | color: #164; 133 | } 134 | .cm-s-default .cm-def { 135 | color: #00f; 136 | } 137 | .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} 138 | .cm-s-default .cm-variable-2 { 139 | color: #05a; 140 | } 141 | .cm-s-default .cm-variable-3 { 142 | color: #085; 143 | } 144 | .cm-s-default .cm-comment { 145 | color: #a50; 146 | } 147 | .cm-s-default .cm-string { 148 | color: #a11; 149 | } 150 | .cm-s-default .cm-string-2 { 151 | color: #f50; 152 | } 153 | .cm-s-default .cm-meta { 154 | color: #555; 155 | } 156 | .cm-s-default .cm-qualifier { 157 | color: #555; 158 | } 159 | .cm-s-default .cm-builtin { 160 | color: #30a; 161 | } 162 | .cm-s-default .cm-bracket { 163 | color: #997; 164 | } 165 | .cm-s-default .cm-tag { 166 | color: #170; 167 | } 168 | .cm-s-default .cm-attribute { 169 | color: #00c; 170 | } 171 | .cm-s-default .cm-hr { 172 | color: #999; 173 | } 174 | .cm-s-default .cm-link { 175 | color: #00c; 176 | } 177 | .cm-s-default .cm-error { 178 | color: #f00; 179 | } 180 | .cm-invalidchar { 181 | color: #f00; 182 | } 183 | .CodeMirror-composing { 184 | border-bottom: 2px solid; 185 | } 186 | /* Default styles for common addons */ 187 | div.CodeMirror span.CodeMirror-matchingbracket { 188 | color: #0f0; 189 | } 190 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 191 | color: #f22; 192 | } 193 | .CodeMirror-matchingtag { 194 | background: rgba(255, 150, 0, .3); 195 | } 196 | .CodeMirror-activeline-background { 197 | background: #e8f2ff; 198 | } 199 | /* STOP */ 200 | /* The rest of this file contains styles related to the mechanics of 201 | the editor. You probably shouldn't touch them. */ 202 | .CodeMirror { 203 | position: relative; 204 | overflow: hidden; 205 | background: white; 206 | } 207 | .CodeMirror-scroll { 208 | overflow: scroll !important; 209 | /* Things will break if this is overridden */ 210 | /* 30px is the magic margin used to hide the element's real scrollbars */ 211 | /* See overflow: hidden in .CodeMirror */ 212 | margin-bottom: -30px; 213 | margin-right: -30px; 214 | padding-bottom: 30px; 215 | height: 100%; 216 | outline: none; 217 | /* Prevent dragging from highlighting the element */ 218 | position: relative; 219 | } 220 | .CodeMirror-sizer { 221 | position: relative; 222 | border-right: 30px solid transparent; 223 | } 224 | /* The fake, visible scrollbars. Used to force redraw during scrolling 225 | before actual scrolling happens, thus preventing shaking and 226 | flickering artifacts. */ 227 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 228 | position: absolute; 229 | z-index: 6; 230 | display: none; 231 | } 232 | .CodeMirror-vscrollbar { 233 | right: 0; 234 | top: 0; 235 | overflow-x: hidden; 236 | overflow-y: scroll; 237 | } 238 | .CodeMirror-hscrollbar { 239 | bottom: 0; 240 | left: 0; 241 | overflow-y: hidden; 242 | overflow-x: scroll; 243 | } 244 | .CodeMirror-scrollbar-filler { 245 | right: 0; 246 | bottom: 0; 247 | } 248 | .CodeMirror-gutter-filler { 249 | left: 0; 250 | bottom: 0; 251 | } 252 | .CodeMirror-gutters { 253 | position: absolute; 254 | left: 0; 255 | top: 0; 256 | min-height: 100%; 257 | z-index: 3; 258 | } 259 | .CodeMirror-gutter { 260 | white-space: normal; 261 | height: 100%; 262 | display: inline-block; 263 | vertical-align: top; 264 | margin-bottom: -30px; 265 | } 266 | .CodeMirror-gutter-wrapper { 267 | position: absolute; 268 | z-index: 4; 269 | background: none !important; 270 | border: none !important; 271 | } 272 | .CodeMirror-gutter-background { 273 | position: absolute; 274 | top: 0; 275 | bottom: 0; 276 | z-index: 4; 277 | } 278 | .CodeMirror-gutter-elt { 279 | position: absolute; 280 | cursor: default; 281 | z-index: 4; 282 | } 283 | .CodeMirror-gutter-wrapper { 284 | -webkit-user-select: none; 285 | -moz-user-select: none; 286 | -ms-user-select: none; 287 | user-select: none; 288 | } 289 | .CodeMirror-lines { 290 | cursor: text; 291 | min-height: 1px; 292 | /* prevents collapsing before first draw */ 293 | } 294 | .CodeMirror pre { 295 | /* Reset some styles that the rest of the page might have set */ 296 | border-radius: 0; 297 | border-width: 0; 298 | background: transparent; 299 | font-family: inherit; 300 | font-size: inherit; 301 | margin: 0; 302 | white-space: pre; 303 | word-wrap: normal; 304 | line-height: inherit; 305 | color: inherit; 306 | z-index: 2; 307 | position: relative; 308 | overflow: visible; 309 | -webkit-tap-highlight-color: transparent; 310 | -webkit-font-variant-ligatures: contextual; 311 | font-variant-ligatures: contextual; 312 | } 313 | .CodeMirror-wrap pre { 314 | word-wrap: break-word; 315 | white-space: pre-wrap; 316 | word-break: normal; 317 | } 318 | .CodeMirror-linebackground { 319 | position: absolute; 320 | left: 0; 321 | right: 0; 322 | top: 0; 323 | bottom: 0; 324 | z-index: 0; 325 | } 326 | .CodeMirror-linewidget { 327 | position: relative; 328 | z-index: 2; 329 | overflow: auto; 330 | } 331 | .CodeMirror-widget {} 332 | .CodeMirror-code { 333 | outline: none; 334 | } 335 | /* Force content-box sizing for the elements where we expect it */ 336 | .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { 337 | box-sizing: content-box; 338 | } 339 | .CodeMirror-measure { 340 | position: absolute; 341 | width: 100%; 342 | height: 0; 343 | overflow: hidden; 344 | visibility: hidden; 345 | } 346 | .CodeMirror-cursor { 347 | position: absolute; 348 | pointer-events: none; 349 | } 350 | .CodeMirror-measure pre { 351 | position: static; 352 | } 353 | div.CodeMirror-cursors { 354 | visibility: hidden; 355 | position: relative; 356 | z-index: 3; 357 | } 358 | div.CodeMirror-dragcursors { 359 | visibility: visible; 360 | } 361 | .CodeMirror-focused div.CodeMirror-cursors { 362 | visibility: visible; 363 | } 364 | .CodeMirror-selected { 365 | background: #d9d9d9; 366 | } 367 | .CodeMirror-focused .CodeMirror-selected { 368 | background: #d7d4f0; 369 | } 370 | .CodeMirror-crosshair { 371 | cursor: crosshair; 372 | } 373 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { 374 | background: #d7d4f0; 375 | } 376 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { 377 | background: #d7d4f0; 378 | } 379 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { 380 | background: #d7d4f0; 381 | } 382 | .cm-searching { 383 | background: #ffa; 384 | background: rgba(255, 255, 0, .4); 385 | } 386 | /* Used to force a border model for a node */ 387 | .cm-force-border { 388 | padding-right: .1px; 389 | } 390 | @media print { 391 | /* Hide the cursor when printing */ 392 | .CodeMirror div.CodeMirror-cursors { 393 | visibility: hidden; 394 | } 395 | } 396 | /* See issue #2901 */ 397 | .cm-tab-wrap-hack:after { 398 | content: ''; 399 | } 400 | /* Help users use markselection to safely style text background */ 401 | span.CodeMirror-selectedtext { 402 | background: none; 403 | } 404 | /* 405 | 406 | Name: material 407 | Author: Michael Kaminsky (http://github.com/mkaminsky11) 408 | 409 | Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme) 410 | 411 | */ 412 | .cm-s-material.CodeMirror { 413 | background-color: #263238; 414 | color: rgba(233, 237, 237, 1); 415 | } 416 | .cm-s-material .CodeMirror-gutters { 417 | background: #263238; 418 | color: rgb(83,127,126); 419 | border: none; 420 | } 421 | .cm-s-material .CodeMirror-guttermarker, .cm-s-material .CodeMirror-guttermarker-subtle, .cm-s-material .CodeMirror-linenumber { 422 | color: rgb(83,127,126); 423 | } 424 | .cm-s-material .CodeMirror-cursor { 425 | border-left: 1px solid #f8f8f0; 426 | } 427 | .cm-s-material div.CodeMirror-selected { 428 | background: rgba(255, 255, 255, 0.15); 429 | } 430 | .cm-s-material.CodeMirror-focused div.CodeMirror-selected { 431 | background: rgba(255, 255, 255, 0.10); 432 | } 433 | .cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { 434 | background: rgba(255, 255, 255, 0.10); 435 | } 436 | .cm-s-material .CodeMirror-line::selection, .cm-s-material .CodeMirror-line > span::selection, .cm-s-material .CodeMirror-line > span > span::selection { 437 | background: rgba(255, 255, 255, 0.10); 438 | } 439 | .cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { 440 | background: rgba(255, 255, 255, 0.10); 441 | } 442 | .cm-s-material .CodeMirror-activeline-background { 443 | background: rgba(0, 0, 0, 0); 444 | } 445 | .cm-s-material .cm-keyword { 446 | color: rgba(199, 146, 234, 1); 447 | } 448 | .cm-s-material .cm-operator { 449 | color: rgba(233, 237, 237, 1); 450 | } 451 | .cm-s-material .cm-variable-2 { 452 | color: #80CBC4; 453 | } 454 | .cm-s-material .cm-variable-3 { 455 | color: #82B1FF; 456 | } 457 | .cm-s-material .cm-builtin { 458 | color: #DECB6B; 459 | } 460 | .cm-s-material .cm-atom { 461 | color: #F77669; 462 | } 463 | .cm-s-material .cm-number { 464 | color: #F77669; 465 | } 466 | .cm-s-material .cm-def { 467 | color: rgba(233, 237, 237, 1); 468 | } 469 | .cm-s-material .cm-string { 470 | color: #C3E88D; 471 | } 472 | .cm-s-material .cm-string-2 { 473 | color: #80CBC4; 474 | } 475 | .cm-s-material .cm-comment { 476 | color: #546E7A; 477 | } 478 | .cm-s-material .cm-variable { 479 | color: #82B1FF; 480 | } 481 | .cm-s-material .cm-tag { 482 | color: #80CBC4; 483 | } 484 | .cm-s-material .cm-meta { 485 | color: #80CBC4; 486 | } 487 | .cm-s-material .cm-attribute { 488 | color: #FFCB6B; 489 | } 490 | .cm-s-material .cm-property { 491 | color: #80CBAE; 492 | } 493 | .cm-s-material .cm-qualifier { 494 | color: #DECB6B; 495 | } 496 | .cm-s-material .cm-variable-3 { 497 | color: #DECB6B; 498 | } 499 | .cm-s-material .cm-tag { 500 | color: rgba(255, 83, 112, 1); 501 | } 502 | .cm-s-material .cm-error { 503 | color: rgba(255, 255, 255, 1.0); 504 | background-color: #EC5F67; 505 | } 506 | .cm-s-material .CodeMirror-matchingbracket { 507 | text-decoration: underline; 508 | color: white !important; 509 | } 510 | 511 | .vuep { 512 | display: -webkit-box; 513 | display: -ms-flexbox; 514 | display: flex; 515 | font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; 516 | height: 400px; 517 | } 518 | 519 | .vuep ::-webkit-scrollbar-track { 520 | border-radius: 10px; 521 | background-color: #F5F5F5; 522 | } 523 | 524 | .vuep ::-webkit-scrollbar { 525 | width: 8px; 526 | height: 8px; 527 | background-color: #F5F5F5; 528 | } 529 | 530 | .vuep ::-webkit-scrollbar-thumb { 531 | border-radius: 8px; 532 | background-color: #bbb; 533 | -webkit-transition: all 0.5s; 534 | transition: all 0.5s; 535 | } 536 | 537 | .vuep ::-webkit-scrollbar-thumb:hover { 538 | border-radius: 8px; 539 | background-color: #777; 540 | } 541 | 542 | .vuep-editor, .vuep-preview, .vuep-error { 543 | border-radius: 2px; 544 | height: inherit; 545 | margin-right: 10px; 546 | overflow: auto; 547 | width: 50%; 548 | } 549 | 550 | .vuep-editor .CodeMirror, .vuep-preview .CodeMirror, .vuep-error .CodeMirror { 551 | height: inherit; 552 | } 553 | 554 | .vuep-editor:last-child, .vuep-preview:last-child, .vuep-error:last-child { 555 | margin-right: 0; 556 | } 557 | 558 | .vuep-editor { 559 | line-height: 1.2em; 560 | } 561 | 562 | .vuep-error { 563 | color: #f66; 564 | } 565 | 566 | .vuep-preview, .vuep-error { 567 | border: 1px solid #eee; 568 | box-sizing: border-box; 569 | padding: 25px 35px; 570 | } 571 | 572 | @media (max-width: 600px) { 573 | .vuep { 574 | display: block; 575 | height: auto; 576 | } 577 | 578 | .vuep-editor, .vuep-preview, .vuep-error { 579 | margin: 0 0 15px 0; 580 | height: 400px; 581 | width: 100%; 582 | } 583 | } 584 | --------------------------------------------------------------------------------