├── .babelrc
├── .circleci
└── config.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── demo.png
├── deploy.sh
├── docs
├── .vuepress
│ ├── config.js
│ └── public
│ │ ├── bt.js
│ │ └── ga.js
├── README.md
├── react.md
├── settings.md
├── vanilla.md
├── vue.md
└── zh
│ ├── README.md
│ ├── react.md
│ ├── settings.md
│ ├── vanilla.md
│ └── vue.md
├── lib
├── clientRootMixin.js
└── index.js
├── package.json
├── rollup.config.js
├── src
├── clientRootMixin.js
├── common
│ ├── constants.js
│ └── utils.js
├── icons
│ ├── code-sandbox.svg
│ ├── codepen.svg
│ └── jsfiddle.svg
├── index.js
├── main.js
├── online
│ ├── codepen.js
│ └── jsfiddle.js
└── style.less
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/env", {"modules": false}]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Javascript Node CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details
4 | #
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | # specify the version you desire here
10 | - image: circleci/node:10
11 |
12 | # Specify service dependencies here if necessary
13 | # CircleCI maintains a library of pre-built images
14 | # documented at https://circleci.com/docs/2.0/circleci-images/
15 | # - image: circleci/mongo:3.4.4
16 |
17 | working_directory: ~/repo
18 |
19 | branches:
20 | only:
21 | - master
22 |
23 | steps:
24 | - checkout
25 |
26 | # Download and cache dependencies
27 | - restore_cache:
28 | keys:
29 | - v1-dependencies-{{ checksum "package.json" }}
30 | # fallback to using the latest cache if no exact match is found
31 | - v1-dependencies-
32 |
33 | - run: yarn install
34 |
35 | - save_cache:
36 | paths:
37 | - node_modules
38 | key: v1-dependencies-{{ checksum "package.json" }}
39 |
40 | - run: yarn comp && yarn build
41 |
42 |
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | .DS_Store
64 | dist
65 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | docs
2 | node_modules
3 | src
4 | rollup.config.js
5 | yarn.lock
6 | ./deploy.sh
7 | demo.png
8 | deploy.sh
9 | .babelrc
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 melon
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vuepress-plugin-demo-block
2 |
3 | 
4 | [](https://www.npmjs.com/package/vuepress-plugin-demo-block)
5 | 
6 | 
7 | [](https://circleci.com/gh/xiguaxigua/vuepress-plugin-demo-block)
8 |
9 | ## Introduction
10 |
11 | The Demo Block is used to help you add vue, react or native js examples when writing a document. When writing component documentation, you usually need to add some related examples to the document. These examples can usually be implemented using JSFiddle or Codepen's Iframe, but the maintenance cost will be relatively high. You can quickly add examples by using Demo Block, and it is very convenient to modify.
12 |
13 | > To show how to write the example, the three points used to mark the end of the code section are separated by spaces, and the spaces need to be removed when used.
14 |
15 | 
16 |
17 | ## Feature
18 |
19 | - Elegant display code and examples
20 | - Support vue, react and native js
21 | - Support codepen and jsfiddle online demo
22 |
23 | ## Install
24 |
25 | ### install vuepress
26 |
27 | Reference official document [Vuepress](https://vuepress.vuejs.org)
28 |
29 | ### install plugin
30 |
31 | ```
32 | npm i vuepress-plugin-demo-block --save-dev
33 | ```
34 |
35 | ### set vuepress config
36 |
37 | config.js
38 | ```js
39 | module.exports = {
40 | head: [
41 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
42 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
43 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
44 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
45 | ],
46 | plugins: [
47 | 'demo-block'
48 | ]
49 | }
50 |
51 | ```
52 |
53 | ## Start
54 |
55 | Write the following code in the Markdown file:
56 |
57 | ### Vue Demo
58 |
59 | ```html
60 | ::: demo
61 | ```html
62 |
63 | Vue {{ message }}
64 |
65 |
70 |
73 | ` ` ` <= delete spaces here
74 | :::
75 | ```
76 |
77 | ### React Demo
78 | ```js
79 | ::: demo [react]
80 | ```js
81 | export default class App extends React.Component {
82 | constructor (props) {
83 | super(props)
84 | this.state = { message: 'Hello World' }
85 | }
86 | render () {
87 | return (
88 |
89 | React {this.state.message}
90 |
91 | )
92 | }
93 | }
94 | App.__style__ = `
95 | .box-react { color: red; }
96 | `
97 | ` ` ` <= delete spaces here
98 | :::
99 | ```
100 |
101 | ### VanillaJs Demo
102 |
103 | ```html
104 | ::: demo [vanilla]
105 | ```html
106 |
107 |
108 |
109 |
113 |
118 | ` ` `
119 | :::
120 | ```
121 |
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiguaxigua/vuepress-plugin-demo-block/9ffbffa5edd1fa1a51b3b5fb36a9fd8657ddbdaf/demo.png
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e
4 |
5 | npm run build
6 |
7 | cd docs/.vuepress/dist
8 |
9 | git init
10 | git add -A
11 | git commit -m 'deploy docs'
12 |
13 | git push -f git@github.com:xiguaxigua/vuepress-plugin-demo-block.git master:gh-pages
14 |
15 | cd -
16 | rm -rf docs/.vuepress/dist
17 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | locales: {
3 | '/': {
4 | lang: 'en-US',
5 | title: 'Demo Block',
6 | description: 'plugin for vuepress to display vue or react demo'
7 | },
8 | '/zh/': {
9 | lang: 'zh-CN',
10 | title: 'Demo Block',
11 | description: '用于编写 vue 或 react 示例的vuepress插件'
12 | }
13 | },
14 | ga: 'UA-122325348-1',
15 | serviceWorker: true,
16 | themeConfig: {
17 | repo: 'xiguaxigua/vuepress-plugin-demo-block',
18 | editLinks: true,
19 | docsDir: 'docs',
20 | locales: {
21 | '/': {
22 | label: 'English',
23 | selectText: 'Languages',
24 | editLinkText: 'Edit this page on GitHub',
25 | lastUpdated: 'Last Updated',
26 | serviceWorker: {
27 | updatePopup: {
28 | message: "New content is available.",
29 | buttonText: "Refresh"
30 | }
31 | },
32 | sidebar: {
33 | '/': genSidebarConfig('Guide')
34 | }
35 | },
36 | '/zh/': {
37 | label: '简体中文',
38 | selectText: '选择语言',
39 | editLinkText: '在 GitHub 上编辑此页',
40 | lastUpdated: '上次更新',
41 | serviceWorker: {
42 | updatePopup: {
43 | message: "发现新内容可用",
44 | buttonText: "刷新"
45 | }
46 | },
47 | sidebar: {
48 | '/zh/': genSidebarConfig('指南')
49 | }
50 | }
51 | },
52 | },
53 | base: '/vuepress-plugin-demo-block/',
54 | head: [
55 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react@16.6.3/umd/react.production.min.js' }],
56 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom@16.6.3/umd/react-dom.production.min.js' }],
57 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
58 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
59 | ['script', { src: 'https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js' }],
60 | ['script', { src: 'https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js' }],
61 | ['script', { src: 'https://cdn.jsdelivr.net/npm/v-charts/lib/line.min.js' }],
62 | ['script', { src: 'https://www.googletagmanager.com/gtag/js?id=UA-122325348-1' }],
63 | ['script', { src: '/ga.js' }],
64 | ['script', { src: '/bt.js' }],
65 | ],
66 | plugins: [
67 | [require('../../lib/index.js'), {
68 | settings: {
69 | codepen: true
70 | }
71 | }]
72 | ]
73 | }
74 | function genSidebarConfig (title) {
75 | return [
76 | {
77 | title,
78 | collapsable: false,
79 | children: [
80 | '',
81 | 'vue',
82 | 'react',
83 | 'vanilla',
84 | 'settings'
85 | ]
86 | }
87 | ]
88 | }
89 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/bt.js:
--------------------------------------------------------------------------------
1 | window._hmt = window._hmt || [];
2 | (function() {
3 | var hm = document.createElement("script");
4 | hm.src = "https://hm.baidu.com/hm.js?72e1ef2463dc80e5a1d91214ddec2ba9";
5 | var s = document.getElementsByTagName("script")[0];
6 | s.parentNode.insertBefore(hm, s);
7 | })();
8 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/ga.js:
--------------------------------------------------------------------------------
1 | window.dataLayer = window.dataLayer || []
2 | function gtag() {
3 | dataLayer.push(arguments)
4 | }
5 | gtag('js', new Date())
6 | gtag('config', 'UA-122325348-1')
7 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | The Demo Block is used to help you add vue, react or native js examples when writing a document. When writing component documentation, you usually need to add some related examples to the document. These examples can usually be implemented using JSFiddle or Codepen's Iframe, but the maintenance cost will be relatively high. You can quickly add examples by using Demo Block, and it is very convenient to modify.
4 |
5 | ::: tip
6 | To show how to write the example, the three points used to mark the end of the code section are separated by spaces, and the spaces need to be removed when used.
7 | :::
8 |
9 | ## Install
10 |
11 | ### install vuepress
12 |
13 | Reference official document [Vuepress](https://vuepress.vuejs.org)
14 |
15 | ### install plugin
16 |
17 | ```
18 | npm i vuepress-plugin-demo-block --save-dev
19 | ```
20 |
21 | ### set vuepress config
22 |
23 | config.js
24 | ```js
25 | module.exports = {
26 | head: [
27 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
28 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
29 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
30 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
31 | ],
32 | plugins: [
33 | 'demo-block'
34 | ]
35 | }
36 | ```
37 |
38 | ## Start
39 |
40 | Write the following code in the Markdown file:
41 |
42 | ### Vue Demo
43 |
44 | ```html
45 | ::: demo
46 | ```html
47 |
48 | Vue {{ message }}
49 |
50 |
55 |
58 | ` ` ` <= delete spaces here
59 | :::
60 | ```
61 |
62 | ::: demo
63 | ```html
64 |
65 | Vue {{ message }}
66 |
67 |
72 |
75 | ```
76 | :::
77 |
78 | ### React Demo
79 |
80 | ```js
81 | ::: demo [react]
82 | ```js
83 | export default class App extends React.Component {
84 | constructor (props) {
85 | super(props)
86 | this.state = { message: 'Hello World' }
87 | }
88 | render () {
89 | return (
90 |
91 | React {this.state.message}
92 |
93 | )
94 | }
95 | }
96 | App.__style__ = `
97 | .box-react { color: red; }
98 | `
99 | ` ` ` <= delete spaces here
100 | :::
101 | ```
102 |
103 | ::: demo [react]
104 | ```js
105 | export default class App extends React.Component {
106 | constructor (props) {
107 | super(props)
108 | this.state = { message: 'Hello World' }
109 | }
110 | render () {
111 | return (
112 |
113 | React {this.state.message}
114 |
115 | )
116 | }
117 | }
118 | App.__style__ = `
119 | .box-react { color: red; }
120 | `
121 | ```
122 | :::
123 |
124 | ### Vanilla Demo
125 |
126 | ```html
127 | ::: demo [vanilla]
128 | ```html
129 |
130 |
131 |
132 |
136 |
141 | ` ` `
142 | :::
143 | ```
144 |
145 | ::: demo [vanilla]
146 | ```html
147 |
148 |
149 |
150 |
154 |
159 | ```
160 | :::
161 |
162 |
163 | ### horizontal Demo
164 |
165 | ```js
166 | ::: demo [react]
167 | ```js
168 | export default class App extends React.Component {
169 | constructor (props) {
170 | super(props)
171 | this.state = { message: 'Hello World' }
172 | }
173 | render () {
174 | return (
175 |
176 | React {this.state.message}
177 |
178 | )
179 | }
180 | }
181 | App.__style__ = `
182 | .box-react { color: red; }
183 | `
184 | ` ` ` <= delete spaces here
185 | ` ` `json
186 | {
187 | "horizontal": true
188 | }
189 | ` ` `
190 | :::
191 | ```
192 |
193 | ::: demo [react]
194 | ```js
195 | export default class App extends React.Component {
196 | constructor (props) {
197 | super(props)
198 | this.state = { message: 'Hello World' }
199 | }
200 | render () {
201 | return (
202 |
203 | React {this.state.message}
204 |
205 | )
206 | }
207 | }
208 | App.__style__ = `
209 | .box-react { color: red; }
210 | `
211 | ```
212 | ```json
213 | {
214 | "horizontal": true
215 | }
216 | ```
217 | :::
--------------------------------------------------------------------------------
/docs/react.md:
--------------------------------------------------------------------------------
1 | # React Demo
2 |
3 | A typical vue example is written in the following format:
4 |
5 | ```js
6 | ::: demo [react]
7 | ```js
8 | export default class App extends React.Component {
9 | constructor (props) {
10 | super(props)
11 | this.state = { message: 'Hello World' }
12 | }
13 | render () {
14 | return (
15 |
16 | React {this.state.message}
17 |
18 | )
19 | }
20 | }
21 | App.__style__ = `
22 | .box-react { color: red; }
23 | `
24 | ` ` ` <= delete spaces here
25 | :::
26 | ```
27 |
28 | It should be noted that for the convenience of parsing, the code needs to put back a Class named `App`, and `App.__style__` will be added to `body` to display the style.
29 |
30 | ## demo
31 |
32 | ```js
33 | ::: demo [react]
34 | ```js
35 | export default class App extends React.Component {
36 | constructor (props) {
37 | super(props)
38 | this.state = { number: 0 }
39 | }
40 | plus () {
41 | this.setState({ number: this.state.number + 1 })
42 | }
43 | minus () {
44 | this.setState({ number: this.state.number - 1 })
45 | }
46 | render () {
47 | return (
48 |
49 | +
50 | -
51 | {this.state.number}
52 |
53 | )
54 | }
55 | }
56 | App.__style__ = `
57 | .box-react { color: red; }
58 | `
59 | ` ` ` <= delete spaces here
60 | :::
61 | ```
62 |
63 |
64 | ::: demo [react]
65 | ```js
66 | export default class App extends React.Component {
67 | constructor (props) {
68 | super(props)
69 | this.state = { number: 0 }
70 | }
71 | plus () {
72 | this.setState({ number: this.state.number + 1 })
73 | }
74 | minus () {
75 | this.setState({ number: this.state.number - 1 })
76 | }
77 | render () {
78 | return (
79 |
80 | +
81 | -
82 | {this.state.number}
83 |
84 | )
85 | }
86 | }
87 | App.__style__ = `
88 | .box-react { color: red; }
89 | `
90 | ```
91 | :::
92 |
93 | ## use other lib
94 |
95 | If you want to use other libraries in the code, you can import the umd file of the corresponding library into config.js, and then use it directly in the code. At the same time, in order to be able to access resources in the online example (JSFiddle, Codepen), you need to configure jsLib or cssLib to the component configuration or global configuration (specific configuration reference settings)
96 |
97 | ```js
98 | module.exports = {
99 | head: [
100 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
101 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
102 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
103 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
104 | ['script', { src: 'https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js' }],
105 | ],
106 | plugins: [
107 | 'demo-block'
108 | ]
109 | }
110 | ```
111 |
112 | ```js
113 | ::: demo [react]
114 | ```js
115 | export default class App extends React.Component {
116 | constructor (props) {
117 | super(props)
118 | this.state = {
119 | form: { num: '123456.7000', format: '0,0.00[00]' },
120 | result: '123,456.70'
121 | }
122 | }
123 | formChangeHandler (e) {
124 | const { name, value } = e.target
125 | const changed = {}
126 | changed[name] = value
127 | const form = Object.assign({}, this.state.form, changed)
128 | const result = window.numerify(form.num, form.format)
129 | this.setState({ form, result })
130 | }
131 | render () {
132 | return (
133 |
134 |
135 | number:
136 |
140 |
141 |
142 | format:
143 |
147 |
148 |
149 | result:
150 | {this.state.result}
151 |
152 |
153 | )
154 | }
155 | }
156 | ` ` ` <= delete spaces here
157 | ` ` `json
158 | {
159 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
160 | }
161 | ` ` `
162 | :::
163 | ```
164 |
165 | ::: demo [react]
166 | ```js
167 | export default class App extends React.Component {
168 | constructor (props) {
169 | super(props)
170 | this.state = {
171 | form: { num: '123456.7000', format: '0,0.00[00]' },
172 | result: '123,456.70'
173 | }
174 | }
175 | formChangeHandler (e) {
176 | const { name, value } = e.target
177 | const changed = {}
178 | changed[name] = value
179 | const form = Object.assign({}, this.state.form, changed)
180 | const result = window.numerify(form.num, form.format)
181 | this.setState({ form, result })
182 | }
183 | render () {
184 | return (
185 |
186 |
187 | number:
188 |
192 |
193 |
194 | format:
195 |
199 |
200 |
201 | result:
202 | {this.state.result}
203 |
204 |
205 | )
206 | }
207 | }
208 | ```
209 | ```json
210 | {
211 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
212 | }
213 | ```
214 | :::
215 |
216 |
217 |
--------------------------------------------------------------------------------
/docs/settings.md:
--------------------------------------------------------------------------------
1 | # Settings
2 |
3 | The configuration of the plugin is divided into a global configuration and a component configuration, which is used to set the style of the plugin and the external dependencies of the code after jumping to Jsfiddle or Codepen.
4 |
5 | ## globel settings
6 |
7 | config.js
8 | ```js
9 | plugins: [
10 | ['demo-block', {
11 | settings: {
12 | jsLib: ['http://xxx'], // js dependency in the online example (jsfiddle, codepen)
13 | cssLib: ['http://xxx'], // css dependency in the online example (jsfiddle, codepen)
14 | vue: 'http://xxx', // vue dependency in the online example (jsfiddle, codepen)
15 | react: 'http://xxx', // react dependency in the online example (jsfiddle, codepen)
16 | reactDOM: 'http://xxx', // reactDOM dependency in the online example (jsfiddle, codepen)
17 | jsfiddle: true, // Whether to display the jsfiddle link
18 | codepen: true, // Whether to display the codepen link
19 | horizontal: false // Whether to display horizontal view
20 | }
21 | }],
22 | }
23 | ```
24 |
25 | ## component settings
26 |
27 | ```html
28 | ::: demo
29 | ```html
30 |
31 | Vue {{ message }}
32 |
33 |
38 |
41 | ` ` `
42 | ` ` `json
43 | {
44 | jsLib: ['xxx'], // js dependency in the online example (jsfiddle, codepen)
45 | cssLib: ['xxx'], // css dependency in the online example (jsfiddle, codepen)
46 | horizontal: false // Whether to display horizontal view
47 | }
48 | ` ` `
49 | :::
50 | ```
51 |
--------------------------------------------------------------------------------
/docs/vanilla.md:
--------------------------------------------------------------------------------
1 | # Vanilla Demo
2 |
3 | ```html
4 | ::: demo [vanilla]
5 | ```html
6 |
7 |
8 |
9 |
13 |
18 | ` ` `
19 | :::
20 | ```
21 |
22 | ::: demo [vanilla]
23 | ```html
24 |
25 |
26 |
27 |
31 |
36 | ```
37 | :::
38 |
--------------------------------------------------------------------------------
/docs/vue.md:
--------------------------------------------------------------------------------
1 | # Vue Demo
2 |
3 | A typical vue example is written in the following format:
4 |
5 | ```html
6 | ::: demo
7 | ```html
8 |
9 | Vue {{ message }}
10 |
11 |
16 |
19 | ` ` ` <= delete spaces here
20 | :::
21 | ```
22 |
23 | The contents of `template` will be combined with `script` to the corresponding node of the page, and `style` will be placed in `body`.
24 |
25 | ## demo
26 |
27 | ```html
28 | ::: demo
29 | ```html
30 |
31 |
32 | +
33 | -
34 | {{ number }}
35 |
36 |
37 |
46 |
49 | ` ` ` <= delete spaces here
50 | :::
51 | ```
52 |
53 | ::: demo
54 | ```html
55 |
56 |
57 | +
58 | -
59 | {{ number }}
60 |
61 |
62 |
71 |
74 | ```
75 | :::
76 |
77 | ## use other lib
78 |
79 | If you want to use other libraries in the code, you can import the umd file of the corresponding library into config.js, and then use it directly in the code. At the same time, in order to be able to access resources in the online example (JSFiddle, Codepen), you need to configure jsLib or cssLib to the component configuration or global configuration (specific configuration reference settings)
80 |
81 | ```js
82 | module.exports = {
83 | head: [
84 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
85 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
86 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
87 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
88 | ['script', { src: 'https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js' }],
89 | ],
90 | plugins: [
91 | 'demo-block'
92 | ]
93 | }
94 | ```
95 |
96 | ```html
97 | ::: demo
98 | ```html
99 |
100 |
101 |
number:
102 |
format:
103 |
result: {{ result }}
104 |
105 |
106 |
107 |
122 | ` ` ` <= delete spaces here
123 | ` ` `json
124 | {
125 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
126 | }
127 | ` ` `
128 | :::
129 | ```
130 |
131 | ::: demo
132 | ```html
133 |
134 |
135 |
number:
136 |
format:
137 |
result: {{ result }}
138 |
139 |
140 |
141 |
156 | ```
157 | ```json
158 | {
159 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
160 | }
161 | ```
162 | :::
163 |
164 | ### user other component
165 |
166 | Rendering other components is basically the same as above.
167 |
168 | ```html
169 | ::: demo
170 | ```html
171 |
172 |
173 |
174 |
175 |
176 |
177 |
197 | ` ` ` <= delete spaces here
198 | ` ` `json
199 | {
200 | "jsLib": [
201 | "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js",
202 | "https://cdn.jsdelivr.net/npm/v-charts/lib/line.min.js"
203 | ]
204 | }
205 | ` ` `
206 | :::
207 | ```
208 |
209 | ::: demo
210 | ```html
211 |
212 |
213 |
214 |
215 |
216 |
217 |
237 | ```
238 | ```json
239 | {
240 | "jsLib": [
241 | "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js",
242 | "https://cdn.jsdelivr.net/npm/v-charts/lib/line.min.js"
243 | ]
244 | }
245 | ```
246 | :::
247 |
--------------------------------------------------------------------------------
/docs/zh/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 |
3 | Demo Block 用于帮助你在编写文档时增加 vue ,react, 原生js 示例。在编写组件文档时,通常需要在文档上增加一些相关的示例,这些示例通常可以使用 JSFiddle 或 Codepen 的 Iframe 实现,但是维护成本会相对比较高,使用 Demo Block 可以快速的增加示例,并且可以很方便的修改。
4 |
5 | ::: tip
6 | 为了展示如何编写示例, 用于标记代码部分结束的三点增加了空格分隔,使用时需要将空格去除。
7 | :::
8 |
9 | ## 安装
10 |
11 | ### 安装 vuepress
12 |
13 | 参考官方文档 [Vuepress](https://vuepress.vuejs.org)
14 |
15 | ### 安装插件
16 |
17 | ```
18 | npm i vuepress-plugin-demo-block
19 | ```
20 |
21 | ### 配置 vuepress config
22 |
23 | config.js
24 | ```js
25 | module.exports = {
26 | head: [
27 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
28 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
29 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
30 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
31 | ],
32 | plugins: [
33 | 'demo-block'
34 | ]
35 | }
36 | ```
37 |
38 | ## 开始使用
39 |
40 | 在 Markdown 文件中编写以下代码:
41 |
42 | ### Vue示例
43 |
44 | ```html
45 | ::: demo
46 | ```html
47 |
48 | Vue {{ message }}
49 |
50 |
55 |
58 | ` ` ` <= 删除这里的空格
59 | :::
60 | ```
61 |
62 | ::: demo
63 | ```html
64 |
65 | Vue {{ message }}
66 |
67 |
72 |
75 | ```
76 | :::
77 |
78 | ### React示例
79 | ```js
80 | ::: demo react
81 | ```js
82 | export default class App extends React.Component {
83 | constructor (props) {
84 | super(props)
85 | this.state = { message: 'Hello World' }
86 | }
87 | render () {
88 | return (
89 |
90 | React {this.state.message}
91 |
92 | )
93 | }
94 | }
95 | App.__style__ = `
96 | .box-react { color: red; }
97 | `
98 | ` ` ` <= 删除这里的空格
99 | :::
100 | ```
101 |
102 | ::: demo react
103 | ```js
104 | export default class App extends React.Component {
105 | constructor (props) {
106 | super(props)
107 | this.state = { message: 'Hello World' }
108 | }
109 | render () {
110 | return (
111 |
112 | React {this.state.message}
113 |
114 | )
115 | }
116 | }
117 | App.__style__ = `
118 | .box-react { color: red; }
119 | `
120 | ```
121 | :::
122 |
123 | ### 原生JS示例
124 |
125 | ```html
126 | ::: demo [vanilla]
127 | ```html
128 |
129 |
130 |
131 |
135 |
140 | ` ` `
141 | :::
142 | ```
143 |
144 | ::: demo [vanilla]
145 | ```html
146 |
147 |
148 |
149 |
153 |
158 | ```
159 | :::
160 |
161 | ### 横向展示示例
162 |
163 | ```js
164 | ::: demo [react]
165 | ```js
166 | export default class App extends React.Component {
167 | constructor (props) {
168 | super(props)
169 | this.state = { message: 'Hello World' }
170 | }
171 | render () {
172 | return (
173 |
174 | React {this.state.message}
175 |
176 | )
177 | }
178 | }
179 | App.__style__ = `
180 | .box-react { color: red; }
181 | `
182 | ` ` ` <= delete spaces here
183 | ` ` `json
184 | {
185 | "horizontal": true
186 | }
187 | ` ` `
188 | :::
189 | ```
190 |
191 | ::: demo [react]
192 | ```js
193 | export default class App extends React.Component {
194 | constructor (props) {
195 | super(props)
196 | this.state = { message: 'Hello World' }
197 | }
198 | render () {
199 | return (
200 |
201 | React {this.state.message}
202 |
203 | )
204 | }
205 | }
206 | App.__style__ = `
207 | .box-react { color: red; }
208 | `
209 | ```
210 | ```json
211 | {
212 | "horizontal": true
213 | }
214 | ```
215 | :::
--------------------------------------------------------------------------------
/docs/zh/react.md:
--------------------------------------------------------------------------------
1 | # React示例
2 |
3 | 一个典型的 react 示例编写格式如下:
4 |
5 | ```js
6 | ::: demo [react]
7 | ```js
8 | export default class App extends React.Component {
9 | constructor (props) {
10 | super(props)
11 | this.state = { message: 'Hello World' }
12 | }
13 | render () {
14 | return (
15 |
16 | React {this.state.message}
17 |
18 | )
19 | }
20 | }
21 | App.__style__ = `
22 | .box-react { color: red; }
23 | `
24 | ` ` ` <= delete spaces here
25 | :::
26 | ```
27 |
28 | 需要注意的是,为了解析的方便,代码中需要放回一个命名为 `App` 的 Class,`App.__style__`会被加到 `body` 上用于展示样式。
29 |
30 | ## 示例
31 |
32 | ```js
33 | ::: demo [react]
34 | ```js
35 | export default class App extends React.Component {
36 | constructor (props) {
37 | super(props)
38 | this.state = { number: 0 }
39 | }
40 | plus () {
41 | this.setState({ number: this.state.number + 1 })
42 | }
43 | minus () {
44 | this.setState({ number: this.state.number - 1 })
45 | }
46 | render () {
47 | return (
48 |
49 | +
50 | -
51 | {this.state.number}
52 |
53 | )
54 | }
55 | }
56 | App.__style__ = `
57 | .box-react { color: red; }
58 | `
59 | ` ` ` <= delete spaces here
60 | :::
61 | ```
62 |
63 |
64 | ::: demo [react]
65 | ```js
66 | export default class App extends React.Component {
67 | constructor (props) {
68 | super(props)
69 | this.state = { number: 0 }
70 | }
71 | plus () {
72 | this.setState({ number: this.state.number + 1 })
73 | }
74 | minus () {
75 | this.setState({ number: this.state.number - 1 })
76 | }
77 | render () {
78 | return (
79 |
80 | +
81 | -
82 | {this.state.number}
83 |
84 | )
85 | }
86 | }
87 | App.__style__ = `
88 | .box-react { color: red; }
89 | `
90 | ```
91 | :::
92 |
93 | ## 使用其他库
94 |
95 | 在代码中如果要使用其他的库,可以引入对应库的 umd 文件到 config.js 中, 然后在代码里直接使用即可。为了能够在在线示例(JSFiddle, Codepen)中正常访问到资源,需要配置 jsLib或cssLib到组件配置或全局配置中(具体配置参考settings)。
96 |
97 | ```js
98 | module.exports = {
99 | head: [
100 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
101 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
102 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
103 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
104 | ['script', { src: 'https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js' }],
105 | ],
106 | plugins: [
107 | 'demo-block'
108 | ]
109 | }
110 | ```
111 |
112 | ```js
113 | ::: demo [react]
114 | ```js
115 | export default class App extends React.Component {
116 | constructor (props) {
117 | super(props)
118 | this.state = {
119 | form: { num: '123456.7000', format: '0,0.00[00]' },
120 | result: '123,456.70'
121 | }
122 | }
123 | formChangeHandler (e) {
124 | const { name, value } = e.target
125 | const changed = {}
126 | changed[name] = value
127 | const form = Object.assign({}, this.state.form, changed)
128 | const result = window.numerify(form.num, form.format)
129 | this.setState({ form, result })
130 | }
131 | render () {
132 | return (
133 |
134 |
135 | number:
136 |
140 |
141 |
142 | format:
143 |
147 |
148 |
149 | result:
150 | {this.state.result}
151 |
152 |
153 | )
154 | }
155 | }
156 | ` ` ` <= delete spaces here
157 | ` ` `json
158 | {
159 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
160 | }
161 | ` ` `
162 | :::
163 | ```
164 |
165 | ::: demo [react]
166 | ```js
167 | export default class App extends React.Component {
168 | constructor (props) {
169 | super(props)
170 | this.state = {
171 | form: { num: '123456.7000', format: '0,0.00[00]' },
172 | result: '123,456.70'
173 | }
174 | }
175 | formChangeHandler (e) {
176 | const { name, value } = e.target
177 | const changed = {}
178 | changed[name] = value
179 | const form = Object.assign({}, this.state.form, changed)
180 | const result = window.numerify(form.num, form.format)
181 | this.setState({ form, result })
182 | }
183 | render () {
184 | return (
185 |
186 |
187 | number:
188 |
192 |
193 |
194 | format:
195 |
199 |
200 |
201 | result:
202 | {this.state.result}
203 |
204 |
205 | )
206 | }
207 | }
208 | ```
209 | ```json
210 | {
211 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
212 | }
213 | ```
214 | :::
215 |
216 |
217 |
--------------------------------------------------------------------------------
/docs/zh/settings.md:
--------------------------------------------------------------------------------
1 | # 配置项
2 |
3 | 插件的配置分为全局配置和组件配置,用于设置插件的样式以及跳转到 JSFiddle 或 Codepen 后代码的外部依赖。
4 |
5 | ## 全局配置
6 |
7 | config.js
8 | ```js
9 | plugins: [
10 | ['demo-block', {
11 | settings: {
12 | jsLib: ['http://xxx'], // 在线示例(jsfiddle, codepen)中的js依赖
13 | cssLib: ['http://xxx'], // 在线示例中的css依赖
14 | vue: 'http://xxx', // 在线示例中的vue依赖
15 | react: 'http://xxx', // 在线示例中的react依赖
16 | reactDOM: 'http://xxx', // 在线示例中的reactDOM依赖
17 | jsfiddle: true, // 是否显示 jsfiddle 链接
18 | codepen: true, // 是否显示 codepen 链接
19 | horizontal: false // 是否展示为横向样式
20 | }
21 | }],
22 | }
23 | ```
24 |
25 | ## 组件配置
26 |
27 | ```html
28 | ::: demo
29 | ```html
30 |
31 | Vue {{ message }}
32 |
33 |
38 |
41 | ` ` `
42 | ` ` `json
43 | {
44 | jsLib: ['xxx'], // 在线示例中的js依赖
45 | cssLib: ['xxx'], // 在线示例中的css依赖
46 | horizontal: false // 是否展示为横向样式
47 | }
48 | ` ` `
49 | :::
50 | ```
51 |
--------------------------------------------------------------------------------
/docs/zh/vanilla.md:
--------------------------------------------------------------------------------
1 | # Vanilla示例
2 |
3 | The demo of writing a native JS is basically the same as writing a vue demo.
4 |
5 | ```html
6 | ::: demo [vanilla]
7 | ```html
8 |
9 |
10 |
11 |
15 |
20 | ` ` `
21 | :::
22 | ```
23 |
24 | ::: demo [vanilla]
25 | ```html
26 |
27 |
28 |
29 |
33 |
38 | ```
39 | :::
--------------------------------------------------------------------------------
/docs/zh/vue.md:
--------------------------------------------------------------------------------
1 | # Vue示例
2 |
3 | 一个典型的 vue 示例编写格式如下:
4 |
5 | ```html
6 | ::: demo
7 | ```html
8 |
9 | Vue {{ message }}
10 |
11 |
16 |
19 | ` ` ` <= delete spaces here
20 | :::
21 | ```
22 |
23 | 其中 `template` 中的内容会结合 `script` 渲染到页面的对应节点上去,`style` 会被放到 body 中。
24 |
25 | ## 示例
26 |
27 | ```html
28 | ::: demo
29 | ```html
30 |
31 |
32 | +
33 | -
34 | {{ number }}
35 |
36 |
37 |
46 |
49 | ` ` ` <= delete spaces here
50 | :::
51 | ```
52 |
53 | ::: demo
54 | ```html
55 |
56 |
57 | +
58 | -
59 | {{ number }}
60 |
61 |
62 |
71 |
74 | ```
75 | :::
76 |
77 | ## 使用其他库
78 |
79 | 在代码中如果要使用其他的库,可以引入对应库的 umd 文件到 config.js 中, 然后在代码里直接使用即可。同时,为了能够在在线示例(JSFiddle, Codepen)中正常访问到资源,需要配置 jsLib或cssLib到组件配置或全局配置中(具体配置参考settings)。
80 |
81 | ```js
82 | module.exports = {
83 | head: [
84 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js' }],
85 | ['script', { src: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js' }],
86 | ['script', { src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js' }],
87 | ['script', { src: 'https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js' }],
88 | ['script', { src: 'https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js' }],
89 | ],
90 | plugins: [
91 | 'demo-block'
92 | ]
93 | }
94 | ```
95 |
96 | ```html
97 | ::: demo
98 | ```html
99 |
100 |
101 |
number:
102 |
format:
103 |
result: {{ result }}
104 |
105 |
106 |
107 |
122 | ` ` ` <= delete spaces here
123 | ` ` `json
124 | {
125 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
126 | }
127 | ` ` `
128 | :::
129 | ```
130 |
131 | ::: demo
132 | ```html
133 |
134 |
135 |
number:
136 |
format:
137 |
result: {{ result }}
138 |
139 |
140 |
141 |
156 | ```
157 | ```json
158 | {
159 | "jsLib": ["https://cdn.jsdelivr.net/npm/numerify/lib/index.umd.min.js"]
160 | }
161 | ```
162 | :::
163 |
164 | ### 使用其他组件
165 |
166 | 渲染其他组件的方式与引入其他库的方式基本相同。
167 |
168 | ```html
169 | ::: demo
170 | ```html
171 |
172 |
173 |
174 |
175 |
176 |
177 |
197 | ` ` ` <= delete spaces here
198 | ` ` `json
199 | {
200 | "jsLib": [
201 | "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js",
202 | "https://cdn.jsdelivr.net/npm/v-charts/lib/line.min.js"
203 | ]
204 | }
205 | ` ` `
206 | :::
207 | ```
208 |
209 | ::: demo
210 | ```html
211 |
212 |
213 |
214 |
215 |
216 |
217 |
237 | ```
238 | ```json
239 | {
240 | "jsLib": [
241 | "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js",
242 | "https://cdn.jsdelivr.net/npm/v-charts/lib/line.min.js"
243 | ]
244 | }
245 | ```
246 | :::
247 |
--------------------------------------------------------------------------------
/lib/clientRootMixin.js:
--------------------------------------------------------------------------------
1 | function styleInject(css, ref) {
2 | if (ref === void 0) ref = {};
3 | var insertAt = ref.insertAt;
4 |
5 | if (!css || typeof document === 'undefined') {
6 | return;
7 | }
8 |
9 | var head = document.head || document.getElementsByTagName('head')[0];
10 | var style = document.createElement('style');
11 | style.type = 'text/css';
12 |
13 | if (insertAt === 'top') {
14 | if (head.firstChild) {
15 | head.insertBefore(style, head.firstChild);
16 | } else {
17 | head.appendChild(style);
18 | }
19 | } else {
20 | head.appendChild(style);
21 | }
22 |
23 | if (style.styleSheet) {
24 | style.styleSheet.cssText = css;
25 | } else {
26 | style.appendChild(document.createTextNode(css));
27 | }
28 | }
29 |
30 | var css = "@media (max-width: 1000px) {\n .vuepress-plugin-demo-block__h_code {\n display: none;\n }\n .vuepress-plugin-demo-block__app {\n margin-left: auto !important;\n margin-right: auto !important;\n }\n}\n.vuepress-plugin-demo-block__wrapper {\n margin-top: 10px;\n border: 1px solid #ebebeb;\n border-radius: 4px;\n transition: all 0.2s;\n}\n.vuepress-plugin-demo-block__wrapper.vuepress-plugin-demo-block__horizontal .vuepress-plugin-demo-block__display {\n height: 400px;\n display: flex;\n}\n.vuepress-plugin-demo-block__wrapper.vuepress-plugin-demo-block__horizontal .vuepress-plugin-demo-block__display .vuepress-plugin-demo-block__app {\n width: 300px;\n border: 1px solid #ebebeb;\n box-shadow: 1px 1px 3px #ebebeb;\n margin-right: 5px;\n overflow: auto;\n}\n.vuepress-plugin-demo-block__wrapper.vuepress-plugin-demo-block__horizontal .vuepress-plugin-demo-block__display .vuepress-plugin-demo-block__h_code {\n flex: 1;\n overflow: auto;\n height: 100%;\n}\n.vuepress-plugin-demo-block__wrapper.vuepress-plugin-demo-block__horizontal .vuepress-plugin-demo-block__display .vuepress-plugin-demo-block__h_code > pre {\n overflow: visible;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__display {\n max-height: 400px;\n overflow: auto;\n}\n.vuepress-plugin-demo-block__wrapper div {\n box-sizing: border-box;\n}\n.vuepress-plugin-demo-block__wrapper:hover {\n box-shadow: 0 0 11px rgba(33, 33, 33, 0.2);\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__code {\n overflow: hidden;\n height: 0;\n padding: 0 !important;\n background-color: #282c34;\n border-radius: 0 !important;\n transition: height 0.5s;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__code pre {\n margin: 0 !important;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__display {\n padding: 20px;\n border-bottom: 1px solid #ebebeb;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer {\n position: relative;\n text-align: center;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer.vuepress-plugin-demo-block__show-link .vuepress-plugin-demo-block__jsfiddle,\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer.vuepress-plugin-demo-block__show-link .vuepress-plugin-demo-block__codepen {\n opacity: 1;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer.vuepress-plugin-demo-block__show-link .vuepress-plugin-demo-block__expand::before {\n border-top: none;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #ccc;\n border-left: 6px solid transparent;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover .vuepress-plugin-demo-block__jsfiddle,\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover .vuepress-plugin-demo-block__codepen,\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover .vuepress-plugin-demo-block__expand span,\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover .vuepress-plugin-demo-block__expand {\n opacity: 1;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover .vuepress-plugin-demo-block__expand::before {\n border-top-color: #3eaf7c !important;\n border-bottom-color: #3eaf7c !important;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover svg {\n fill: #3eaf7c !important;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__expand-text {\n transition: all 0.5s;\n opacity: 0;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer form:nth-last-child(2) {\n right: 50px;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer form:last-child {\n right: 10px;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__button {\n border-color: transparent;\n background-color: transparent;\n font-size: 14px;\n color: #3eaf7c;\n cursor: pointer;\n outline: none;\n margin: 0;\n width: 46px;\n position: relative;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__button:hover::before {\n content: attr(data-tip);\n white-space: nowrap;\n position: absolute;\n top: -30px;\n left: 50%;\n color: #eee;\n line-height: 1;\n z-index: 1000;\n border-radius: 4px;\n padding: 6px;\n -webkit-transform: translateX(-50%);\n transform: translateX(-50%);\n background-color: rgba(0, 0, 0, 0.8);\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__button:hover::after {\n content: '' !important;\n display: block;\n position: absolute;\n left: 50%;\n top: -5px;\n -webkit-transform: translateX(-50%);\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: rgba(0, 0, 0, 0.8);\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__button svg {\n width: 34px;\n height: 20px;\n fill: #ccc;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__jsfiddle,\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__codepen {\n position: absolute;\n top: 10px;\n transition: all 0.5s;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__expand {\n position: relative;\n width: 100px;\n height: 40px;\n margin: 0;\n color: #3eaf7c;\n font-size: 14px;\n background-color: transparent;\n border-color: transparent;\n outline: none;\n transition: all 0.5s;\n cursor: pointer;\n}\n.vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer .vuepress-plugin-demo-block__expand::before {\n content: \"\";\n position: absolute;\n top: 50%;\n left: 50%;\n width: 0;\n height: 0;\n border-top: 6px solid #ccc;\n border-right: 6px solid transparent;\n border-left: 6px solid transparent;\n -webkit-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n}\n";
31 | styleInject(css);
32 |
33 | var CLASS_WRAPPER = 'vuepress-plugin-demo-block__wrapper';
34 | var CLASS_DISPLAY = 'vuepress-plugin-demo-block__display';
35 | var CLASS_CODE = 'vuepress-plugin-demo-block__code';
36 | var CLASS_FOOTER = 'vuepress-plugin-demo-block__footer';
37 | var CLASS_HORIZONTAL = 'vuepress-plugin-demo-block__horizontal';
38 | var CLASS_H_CODE = 'vuepress-plugin-demo-block__h_code';
39 | var CLASS_APP = 'vuepress-plugin-demo-block__app';
40 | var CLASS_SHOW_LINK = 'vuepress-plugin-demo-block__show-link';
41 | var CLASS_EXPAND = 'vuepress-plugin-demo-block__expand';
42 | var CLASS_CODEPEN = 'vuepress-plugin-demo-block__codepen';
43 | var CLASS_JSFIDDLE = 'vuepress-plugin-demo-block__jsfiddle';
44 | var CLASS_BUTTON = 'vuepress-plugin-demo-block__button';
45 | var DEFAULT_SETTINGS = {
46 | jsLib: [],
47 | cssLib: [],
48 | jsfiddle: true,
49 | codepen: true,
50 | codepenLayout: 'left',
51 | codepenJsProcessor: 'babel',
52 | codepenEditors: '101',
53 | horizontal: false,
54 | vue: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js',
55 | react: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js',
56 | reactDOM: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js'
57 | };
58 | var SETTINGS_KEY = '$VUEPRESS_DEMO_BLOCK';
59 |
60 | var _once = {};
61 |
62 | var getHtmlTpl = function getHtmlTpl(html) {
63 | return "\n".concat(html, "\n
");
64 | };
65 |
66 | var getVueJsTpl = function getVueJsTpl(js) {
67 | var jsContent = js.replace(/export\s+default\s*?\{\n*/, '').replace(/\n*\}\s*$/, '').trim();
68 | return "new Vue({\n el: '#app',\n ".concat(jsContent, "\n})");
69 | };
70 |
71 | var toArray = function toArray(value) {
72 | return Array.prototype.slice.call(value);
73 | };
74 |
75 | var getSettings = function getSettings(key) {
76 | return window[SETTINGS_KEY] && window[SETTINGS_KEY][key] !== undefined ? window[SETTINGS_KEY][key] : DEFAULT_SETTINGS[key];
77 | };
78 | var h = function h(tag, attrs, children) {
79 | var node = document.createElement(tag);
80 | attrs && Object.keys(attrs).forEach(function (key) {
81 | if (!key.indexOf('data')) {
82 | var k = key.replace('data', '');
83 | node.dataset[k] = attrs[key];
84 | } else {
85 | node[key] = attrs[key];
86 | }
87 | });
88 | children && children.forEach(function (_ref) {
89 | var tag = _ref.tag,
90 | attrs = _ref.attrs,
91 | children = _ref.children;
92 | node.appendChild(h(tag, attrs, children));
93 | });
94 | return node;
95 | };
96 | var $ = function $(parent, node, returnArray) {
97 | var result = toArray(parent.querySelectorAll(".".concat(node)));
98 | return result.length === 1 && !returnArray ? result[0] : result;
99 | };
100 |
101 | var getVueScript = function getVueScript(js, html) {
102 | var scripts = js.split(/export\s+default/);
103 | var scriptStrOrg = "(function() {".concat(scripts[0], " ; return ").concat(scripts[1], "})()");
104 | var scriptStr = window.Babel ? window.Babel.transform(scriptStrOrg, {
105 | presets: ['es2015']
106 | }).code : scriptStrOrg;
107 | var scriptObj = [eval][0](scriptStr);
108 | scriptObj.template = html;
109 | return scriptObj;
110 | };
111 |
112 | var getVanillaScript = function getVanillaScript(js) {
113 | return window.Babel ? window.Babel.transform(js, {
114 | presets: ['es2015']
115 | }).code : js;
116 | };
117 |
118 | var getVueDetail = function getVueDetail(code, config) {
119 | var cssBlock = code.match(/ ";
189 |
190 | function getCodepenBtn(_ref) {
191 | var css = _ref.css,
192 | htmlTpl = _ref.htmlTpl,
193 | jsTpl = _ref.jsTpl,
194 | jsLib = _ref.jsLib,
195 | cssLib = _ref.cssLib;
196 | var value = JSON.stringify({
197 | css: css,
198 | html: htmlTpl,
199 | js: jsTpl,
200 | js_external: jsLib.concat(getSettings('jsLib')).join(';'),
201 | css_external: cssLib.concat(getSettings('cssLib')).join(';'),
202 | layout: getSettings('codepenLayout'),
203 | js_pre_processor: getSettings('codepenJsProcessor'),
204 | editors: getSettings('codepenEditors')
205 | });
206 | var form = h('form', {
207 | className: CLASS_CODEPEN,
208 | target: '_blank',
209 | action: 'https://codepen.io/pen/define',
210 | method: 'post'
211 | }, [{
212 | tag: 'input',
213 | attrs: {
214 | type: 'hidden',
215 | name: 'data',
216 | value: value
217 | }
218 | }, {
219 | tag: 'button',
220 | attrs: {
221 | type: 'submit',
222 | innerHTML: codepenIcon,
223 | className: CLASS_BUTTON,
224 | datatip: 'Codepen'
225 | }
226 | }]);
227 | return form;
228 | }
229 |
230 | var jsfiddleIcon = " ";
231 |
232 | function getJsfiddleBtn(_ref) {
233 | var css = _ref.css,
234 | htmlTpl = _ref.htmlTpl,
235 | jsTpl = _ref.jsTpl,
236 | jsLib = _ref.jsLib,
237 | cssLib = _ref.cssLib;
238 | var resource = jsLib.concat(cssLib).concat(getSettings('cssLib')).concat(getSettings('jsLib')).join(',');
239 | var form = h('form', {
240 | className: CLASS_JSFIDDLE,
241 | target: '_blank',
242 | action: 'https://jsfiddle.net/api/post/library/pure/',
243 | method: 'post'
244 | }, [{
245 | tag: 'input',
246 | attrs: {
247 | type: 'hidden',
248 | name: 'css',
249 | value: css
250 | }
251 | }, {
252 | tag: 'input',
253 | attrs: {
254 | type: 'hidden',
255 | name: 'html',
256 | value: htmlTpl
257 | }
258 | }, {
259 | tag: 'input',
260 | attrs: {
261 | type: 'hidden',
262 | name: 'js',
263 | value: jsTpl
264 | }
265 | }, {
266 | tag: 'input',
267 | attrs: {
268 | type: 'hidden',
269 | name: 'panel_js',
270 | value: 3
271 | }
272 | }, {
273 | tag: 'input',
274 | attrs: {
275 | type: 'hidden',
276 | name: 'wrap',
277 | value: 1
278 | }
279 | }, {
280 | tag: 'input',
281 | attrs: {
282 | type: 'hidden',
283 | name: 'resources',
284 | value: resource
285 | }
286 | }, {
287 | tag: 'button',
288 | attrs: {
289 | type: 'submit',
290 | className: CLASS_BUTTON,
291 | innerHTML: jsfiddleIcon,
292 | datatip: 'JSFiddle'
293 | }
294 | }]);
295 | return form;
296 | }
297 |
298 | function webController() {
299 | var nodes = $(document, CLASS_WRAPPER, true);
300 |
301 | if (!nodes.length) {
302 | setTimeout(function (_) {
303 | webController();
304 | }, 300);
305 | return;
306 | }
307 |
308 | nodes.forEach(function (node) {
309 | if (node.dataset.created === 'true') return;
310 | node.style.display = 'block';
311 | var codeNode = $(node, CLASS_CODE);
312 | var displayNode = $(node, CLASS_DISPLAY);
313 | var footerNode = $(node, CLASS_FOOTER);
314 | var appNode = $(displayNode, CLASS_APP);
315 | var code = decodeURIComponent(node.dataset.code);
316 | var config = decodeURIComponent(node.dataset.config);
317 | var type = decodeURIComponent(node.dataset.type);
318 | config = config ? JSON.parse(config) : {};
319 | var height = codeNode.querySelector('div').clientHeight;
320 | var detail = type === 'react' ? getReactDetail(code, config) : type === 'vanilla' ? getVanillaDetail(code, config) : getVueDetail(code, config);
321 | var expandNode = createExpandNode();
322 | footerNode.appendChild(expandNode);
323 | expandNode.addEventListener('click', expandHandler.bind(null, expandNode, height, codeNode, footerNode));
324 |
325 | if (getSettings('jsfiddle')) {
326 | footerNode.appendChild(getJsfiddleBtn(detail));
327 | }
328 |
329 | if (getSettings('codepen')) {
330 | footerNode.appendChild(getCodepenBtn(detail));
331 | }
332 |
333 | var horizontalConfig = config.horizontal !== undefined ? config.horizontal : getSettings('horizontal');
334 |
335 | if (horizontalConfig) {
336 | node.classList.add(CLASS_HORIZONTAL);
337 | var hCodeNode = codeNode.firstChild.cloneNode(true);
338 | hCodeNode.classList.add(CLASS_H_CODE);
339 | displayNode.appendChild(hCodeNode);
340 | }
341 |
342 | detail.css && injectCss(detail.css);
343 |
344 | if (type === 'react') {
345 | ReactDOM.render(React.createElement(detail.js), appNode);
346 | } else if (type === 'vue') {
347 | var Comp = Vue.extend(detail.script);
348 | var app = new Comp().$mount();
349 | appNode.appendChild(app.$el);
350 | } else if (type === 'vanilla') {
351 | appNode.innerHTML = detail.html;
352 | new Function("return (function(){".concat(detail.script, "})()"))();
353 | }
354 |
355 | node.dataset.created = 'true';
356 | });
357 | }
358 |
359 | function createExpandNode() {
360 | return h('button', {
361 | className: "".concat(CLASS_EXPAND)
362 | });
363 | }
364 |
365 | function expandHandler(expandNode, height, codeNode, footerNode) {
366 | var isExpand = expandNode.dataset.isExpand !== '1';
367 | codeNode.style.height = isExpand ? "".concat(height, "px") : 0;
368 |
369 | if (isExpand) {
370 | footerNode.classList.add(CLASS_SHOW_LINK);
371 | } else {
372 | footerNode.classList.remove(CLASS_SHOW_LINK);
373 | }
374 |
375 | expandNode.dataset.isExpand = isExpand ? '1' : '0';
376 | }
377 |
378 | var clientRootMixin = {
379 | mounted: function mounted() {
380 | window.$VUEPRESS_DEMO_BLOCK = SETTINGS;
381 | webController();
382 | },
383 | updated: function updated() {
384 | webController();
385 | }
386 | };
387 |
388 | export default clientRootMixin;
389 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
4 |
5 | var path = _interopDefault(require('path'));
6 |
7 | var END_TYPE = 'container_demo_close';
8 | var CLASS_WRAPPER = 'vuepress-plugin-demo-block__wrapper';
9 | var CLASS_DISPLAY = 'vuepress-plugin-demo-block__display';
10 | var CLASS_CODE = 'vuepress-plugin-demo-block__code';
11 | var CLASS_FOOTER = 'vuepress-plugin-demo-block__footer';
12 | var CLASS_APP = 'vuepress-plugin-demo-block__app';
13 |
14 | module.exports = function (options, context) {
15 | return {
16 | name: 'vuepress-plugin-demo-block',
17 | define: {
18 | SETTINGS: options.settings || {}
19 | },
20 | clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js'),
21 | extendMarkdown: function extendMarkdown(md) {
22 | md.use(require('markdown-it-container'), 'demo', {
23 | render: function render(tokens, idx) {
24 | var _tokens$idx = tokens[idx],
25 | nesting = _tokens$idx.nesting,
26 | info = _tokens$idx.info;
27 |
28 | if (nesting === -1) {
29 | return "\n \n \n \n ");
30 | }
31 |
32 | var codeStr = '';
33 | var configStr = '';
34 | var typeStr = ~info.indexOf('react') ? 'react' : ~info.indexOf('vanilla') ? 'vanilla' : 'vue';
35 |
36 | for (var i = idx; i < tokens.length; i++) {
37 | var _tokens$i = tokens[i],
38 | type = _tokens$i.type,
39 | content = _tokens$i.content,
40 | _info = _tokens$i.info;
41 | if (type === END_TYPE) break;
42 | if (!content) continue;
43 |
44 | if (type === 'fence') {
45 | if (_info === 'json') {
46 | configStr = encodeURIComponent(content);
47 | } else {
48 | codeStr = encodeURIComponent(content);
49 | }
50 | }
51 | }
52 |
53 | return "\n \n
\n
\n ");
54 | }
55 | });
56 | }
57 | };
58 | };
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuepress-plugin-demo-block",
3 | "version": "0.7.2",
4 | "description": "demo block for vuepress both support vue and react",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "dev": "vuepress dev docs",
8 | "build": "vuepress build docs",
9 | "comp": "rollup -c rollup.config.js",
10 | "watch": "rollup -wc rollup.config.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/xiguaxigua/vuepress-plugin-demo-block.git"
15 | },
16 | "keywords": [
17 | "vuepress-plugin",
18 | "demo-block"
19 | ],
20 | "author": "xiguaxigua",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/xiguaxigua/vuepress-plugin-demo-block/issues"
24 | },
25 | "homepage": "https://github.com/xiguaxigua/vuepress-plugin-demo-block#readme",
26 | "devDependencies": {
27 | "@babel/core": "^7.2.2",
28 | "@babel/preset-env": "^7.2.3",
29 | "autoprefixer": "^9.4.5",
30 | "less": "^3.9.0",
31 | "markdown-it-container": "^2.0.0",
32 | "rollup": "^1.0.0",
33 | "rollup-plugin-babel": "^4.2.0",
34 | "rollup-plugin-commonjs": "^9.2.0",
35 | "rollup-plugin-node-resolve": "^4.0.0",
36 | "rollup-plugin-postcss": "^1.6.3",
37 | "rollup-plugin-string": "^2.0.2",
38 | "rollup-plugin-uglify": "^6.0.0",
39 | "vuepress": "^1.0.0-alpha.30"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import autoprefixer from "autoprefixer";
2 | import babel from "rollup-plugin-babel";
3 | import common from "rollup-plugin-commonjs";
4 | import resolve from "rollup-plugin-node-resolve";
5 | import postcss from "rollup-plugin-postcss";
6 | import string from 'rollup-plugin-string';
7 |
8 | export default [
9 | {
10 | input: "src/index.js",
11 | output: {
12 | file: "lib/index.js",
13 | format: "cjs"
14 | },
15 | external: ['path'],
16 | plugins: [babel(), common(), resolve()]
17 | },
18 | {
19 | input: "src/clientRootMixin.js",
20 | output: {
21 | file: "lib/clientRootMixin.js",
22 | format: "esm"
23 | },
24 | plugins: [
25 | postcss({ plugins: [autoprefixer] }),
26 | babel(),
27 | common(),
28 | resolve(),
29 | string({ include: '**/*.svg' })
30 | ]
31 | }
32 | ];
33 |
--------------------------------------------------------------------------------
/src/clientRootMixin.js:
--------------------------------------------------------------------------------
1 | import './style.less'
2 |
3 | import webController from './main'
4 |
5 | export default {
6 | mounted() {
7 | window.$VUEPRESS_DEMO_BLOCK = SETTINGS
8 | webController()
9 | },
10 |
11 | updated() {
12 | webController()
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/common/constants.js:
--------------------------------------------------------------------------------
1 | export const END_TYPE = 'container_demo_close'
2 |
3 | export const CLASS_WRAPPER = 'vuepress-plugin-demo-block__wrapper'
4 | export const CLASS_DISPLAY = 'vuepress-plugin-demo-block__display'
5 | export const CLASS_CODE = 'vuepress-plugin-demo-block__code'
6 | export const CLASS_FOOTER = 'vuepress-plugin-demo-block__footer'
7 | export const CLASS_HORIZONTAL = 'vuepress-plugin-demo-block__horizontal'
8 | export const CLASS_H_CODE = 'vuepress-plugin-demo-block__h_code'
9 |
10 | export const CLASS_APP = 'vuepress-plugin-demo-block__app'
11 | export const CLASS_SHOW_LINK = 'vuepress-plugin-demo-block__show-link'
12 | export const CLASS_EXPAND = 'vuepress-plugin-demo-block__expand'
13 | export const CLASS_OUTLINK = 'vuepress-plugin-demo-block__out-link'
14 | export const CLASS_CODEPEN = 'vuepress-plugin-demo-block__codepen'
15 | export const CLASS_JSFIDDLE = 'vuepress-plugin-demo-block__jsfiddle'
16 | export const CLASS_BUTTON = 'vuepress-plugin-demo-block__button'
17 |
18 | export const DEFAULT_SETTINGS = {
19 | jsLib: [],
20 | cssLib: [],
21 | jsfiddle: true,
22 | codepen: true,
23 | codepenLayout: 'left',
24 | codepenJsProcessor: 'babel',
25 | codepenEditors: '101',
26 | horizontal: false,
27 | vue: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js',
28 | react: 'https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js',
29 | reactDOM: 'https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js',
30 | }
31 |
32 | export const SETTINGS_KEY = '$VUEPRESS_DEMO_BLOCK'
33 |
--------------------------------------------------------------------------------
/src/common/utils.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_SETTINGS, SETTINGS_KEY } from './constants'
2 |
3 | const _once = {}
4 |
5 | const getHtmlTpl = html => `
6 | ${html}
7 |
`
8 |
9 | const getVueJsTpl = js => {
10 | const jsContent = js
11 | .replace(/export\s+default\s*?\{\n*/, '')
12 | .replace(/\n*\}\s*$/, '')
13 | .trim()
14 | return `new Vue({
15 | el: '#app',
16 | ${jsContent}
17 | })`
18 | }
19 |
20 | const toArray = value => Array.prototype.slice.call(value)
21 |
22 | export const getSettings = key =>
23 | window[SETTINGS_KEY] && window[SETTINGS_KEY][key] !== undefined
24 | ? window[SETTINGS_KEY][key]
25 | : DEFAULT_SETTINGS[key]
26 |
27 | export const h = (tag, attrs, children) => {
28 | const node = document.createElement(tag)
29 | attrs &&
30 | Object.keys(attrs).forEach(key => {
31 | if (!key.indexOf('data')) {
32 | const k = key.replace('data', '')
33 | node.dataset[k] = attrs[key]
34 | } else {
35 | node[key] = attrs[key]
36 | }
37 | })
38 | children &&
39 | children.forEach(({ tag, attrs, children }) => {
40 | node.appendChild(h(tag, attrs, children))
41 | })
42 | return node
43 | }
44 |
45 | export const $ = (parent, node, returnArray) => {
46 | const result = toArray(parent.querySelectorAll(`.${node}`))
47 | return result.length === 1 && !returnArray ? result[0] : result
48 | }
49 |
50 | const getVueScript = (js, html) => {
51 | const scripts = js.split(/export\s+default/)
52 | const scriptStrOrg = `(function() {${scripts[0]} ; return ${scripts[1]}})()`
53 | const scriptStr = window.Babel
54 | ? window.Babel.transform(scriptStrOrg, { presets: ['es2015'] }).code
55 | : scriptStrOrg
56 | const scriptObj = [eval][0](scriptStr)
57 | scriptObj.template = html
58 | return scriptObj
59 | }
60 |
61 | const getVanillaScript = js => {
62 | return window.Babel
63 | ? window.Babel.transform(js, { presets: ['es2015'] }).code
64 | : js
65 | }
66 |
67 | export const getVueDetail = (code, config) => {
68 | const cssBlock = code.match(/
--------------------------------------------------------------------------------
/src/icons/codepen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/jsfiddle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 |
3 | import {
4 | CLASS_APP,
5 | CLASS_CODE,
6 | CLASS_DISPLAY,
7 | CLASS_FOOTER,
8 | CLASS_WRAPPER,
9 | END_TYPE
10 | } from './common/constants'
11 |
12 | module.exports = (options, context) => ({
13 | name: 'vuepress-plugin-demo-block',
14 | define: {
15 | SETTINGS: options.settings || {}
16 | },
17 | clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js'),
18 | extendMarkdown: md => {
19 | md.use(require('markdown-it-container'), 'demo', {
20 | render: (tokens, idx) => {
21 | const { nesting, info } = tokens[idx]
22 | if (nesting === -1) {
23 | return `
24 |
25 |
26 |
27 | `
28 | }
29 | let codeStr = ''
30 | let configStr = ''
31 | let typeStr = ~info.indexOf('react')
32 | ? 'react'
33 | : ~info.indexOf('vanilla')
34 | ? 'vanilla'
35 | : 'vue'
36 | for (let i = idx; i < tokens.length; i++) {
37 | const { type, content, info } = tokens[i]
38 | if (type === END_TYPE) break
39 | if (!content) continue
40 | if (type === 'fence') {
41 | if (info === 'json') {
42 | configStr = encodeURIComponent(content)
43 | } else {
44 | codeStr = encodeURIComponent(content)
45 | }
46 | }
47 | }
48 | return `
49 |
55 |
58 |
59 | `
60 | }
61 | })
62 | }
63 | })
64 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import {
2 | CLASS_APP,
3 | CLASS_CODE,
4 | CLASS_DISPLAY,
5 | CLASS_EXPAND,
6 | CLASS_FOOTER,
7 | CLASS_HORIZONTAL,
8 | CLASS_H_CODE,
9 | CLASS_SHOW_LINK,
10 | CLASS_WRAPPER,
11 | } from './common/constants';
12 | import {
13 | $,
14 | getReactDetail,
15 | getSettings,
16 | getVanillaDetail,
17 | getVueDetail,
18 | h,
19 | injectCss,
20 | } from './common/utils';
21 | import codepen from './online/codepen';
22 | import jsfiddle from './online/jsfiddle';
23 |
24 | export default function webController() {
25 | const nodes = $(document, CLASS_WRAPPER, true);
26 | if (!nodes.length) {
27 | setTimeout(_ => {
28 | webController()
29 | }, 300)
30 | return
31 | }
32 | nodes.forEach(node => {
33 | if (node.dataset.created === 'true') return;
34 | node.style.display = 'block';
35 |
36 | const codeNode = $(node, CLASS_CODE);
37 | const displayNode = $(node, CLASS_DISPLAY);
38 | const footerNode = $(node, CLASS_FOOTER);
39 | const appNode = $(displayNode, CLASS_APP);
40 |
41 | const code = decodeURIComponent(node.dataset.code);
42 | let config = decodeURIComponent(node.dataset.config);
43 | let type = decodeURIComponent(node.dataset.type);
44 | config = config ? JSON.parse(config) : {};
45 | const height = codeNode.querySelector('div').clientHeight;
46 | const detail =
47 | type === 'react'
48 | ? getReactDetail(code, config)
49 | : type === 'vanilla'
50 | ? getVanillaDetail(code, config)
51 | : getVueDetail(code, config);
52 | const expandNode = createExpandNode();
53 | footerNode.appendChild(expandNode);
54 | expandNode.addEventListener(
55 | 'click',
56 | expandHandler.bind(null, expandNode, height, codeNode, footerNode)
57 | );
58 |
59 | if (getSettings('jsfiddle')) {
60 | footerNode.appendChild(jsfiddle(detail));
61 | }
62 | if (getSettings('codepen')) {
63 | footerNode.appendChild(codepen(detail));
64 | }
65 |
66 | const horizontalConfig =
67 | config.horizontal !== undefined
68 | ? config.horizontal
69 | : getSettings('horizontal');
70 |
71 | if (horizontalConfig) {
72 | node.classList.add(CLASS_HORIZONTAL);
73 | const hCodeNode = codeNode.firstChild.cloneNode(true);
74 | hCodeNode.classList.add(CLASS_H_CODE);
75 | displayNode.appendChild(hCodeNode);
76 | }
77 |
78 | detail.css && injectCss(detail.css);
79 | if (type === 'react') {
80 | ReactDOM.render(React.createElement(detail.js), appNode);
81 | } else if (type === 'vue') {
82 | const Comp = Vue.extend(detail.script);
83 | const app = new Comp().$mount();
84 | appNode.appendChild(app.$el);
85 | } else if (type === 'vanilla') {
86 | appNode.innerHTML = detail.html;
87 | new Function(`return (function(){${detail.script}})()`)();
88 | }
89 | node.dataset.created = 'true';
90 | });
91 | }
92 |
93 | function createExpandNode() {
94 | return h('button', {
95 | className: `${CLASS_EXPAND}`,
96 | });
97 | }
98 |
99 | function expandHandler(expandNode, height, codeNode, footerNode) {
100 | const isExpand = expandNode.dataset.isExpand !== '1';
101 | codeNode.style.height = isExpand ? `${height}px` : 0;
102 | if (isExpand) {
103 | footerNode.classList.add(CLASS_SHOW_LINK);
104 | } else {
105 | footerNode.classList.remove(CLASS_SHOW_LINK);
106 | }
107 | expandNode.dataset.isExpand = isExpand ? '1' : '0';
108 | }
109 |
--------------------------------------------------------------------------------
/src/online/codepen.js:
--------------------------------------------------------------------------------
1 | import { CLASS_BUTTON, CLASS_CODEPEN } from '../common/constants'
2 | import { getSettings, h } from '../common/utils'
3 | import codepenIcon from '../icons/codepen.svg'
4 | export default function getCodepenBtn({ css, htmlTpl, jsTpl, jsLib, cssLib }) {
5 | const value = JSON.stringify({
6 | css: css,
7 | html: htmlTpl,
8 | js: jsTpl,
9 | js_external: jsLib.concat(getSettings('jsLib')).join(';'),
10 | css_external: cssLib.concat(getSettings('cssLib')).join(';'),
11 | layout: getSettings('codepenLayout'),
12 | js_pre_processor: getSettings('codepenJsProcessor'),
13 | editors: getSettings('codepenEditors')
14 | })
15 | const form = h(
16 | 'form',
17 | {
18 | className: CLASS_CODEPEN,
19 | target: '_blank',
20 | action: 'https://codepen.io/pen/define',
21 | method: 'post'
22 | },
23 | [
24 | {
25 | tag: 'input',
26 | attrs: { type: 'hidden', name: 'data', value }
27 | },
28 | {
29 | tag: 'button',
30 | attrs: {
31 | type: 'submit',
32 | innerHTML: codepenIcon,
33 | className: CLASS_BUTTON,
34 | datatip: 'Codepen'
35 | }
36 | }
37 | ]
38 | )
39 | return form
40 | }
41 |
--------------------------------------------------------------------------------
/src/online/jsfiddle.js:
--------------------------------------------------------------------------------
1 | import { CLASS_BUTTON, CLASS_JSFIDDLE } from '../common/constants'
2 | import { getSettings, h } from '../common/utils'
3 | import jsfiddleIcon from '../icons/jsfiddle.svg'
4 | export default function getJsfiddleBtn({ css, htmlTpl, jsTpl, jsLib, cssLib }) {
5 | const resource = jsLib
6 | .concat(cssLib)
7 | .concat(getSettings('cssLib'))
8 | .concat(getSettings('jsLib'))
9 | .join(',')
10 | const form = h(
11 | 'form',
12 | {
13 | className: CLASS_JSFIDDLE,
14 | target: '_blank',
15 | action: 'https://jsfiddle.net/api/post/library/pure/',
16 | method: 'post'
17 | },
18 | [
19 | {
20 | tag: 'input',
21 | attrs: { type: 'hidden', name: 'css', value: css }
22 | },
23 | {
24 | tag: 'input',
25 | attrs: { type: 'hidden', name: 'html', value: htmlTpl }
26 | },
27 | {
28 | tag: 'input',
29 | attrs: { type: 'hidden', name: 'js', value: jsTpl }
30 | },
31 | {
32 | tag: 'input',
33 | attrs: { type: 'hidden', name: 'panel_js', value: 3 }
34 | },
35 | {
36 | tag: 'input',
37 | attrs: { type: 'hidden', name: 'wrap', value: 1 }
38 | },
39 | {
40 | tag: 'input',
41 | attrs: { type: 'hidden', name: 'resources', value: resource }
42 | },
43 | {
44 | tag: 'button',
45 | attrs: {
46 | type: 'submit',
47 | className: CLASS_BUTTON,
48 | innerHTML: jsfiddleIcon,
49 | datatip: 'JSFiddle'
50 | }
51 | }
52 | ]
53 | )
54 | return form
55 | }
56 |
--------------------------------------------------------------------------------
/src/style.less:
--------------------------------------------------------------------------------
1 | @media (max-width: 1000px) {
2 | .vuepress-plugin-demo-block__h_code {
3 | display: none;
4 | }
5 | .vuepress-plugin-demo-block__app {
6 | margin-left: auto !important;
7 | margin-right: auto !important;
8 | }
9 | }
10 |
11 | .vuepress-plugin-demo-block__wrapper {
12 | margin-top: 10px;
13 | border: 1px solid #ebebeb;
14 | border-radius: 4px;
15 | transition: all .2s;
16 |
17 | &.vuepress-plugin-demo-block__horizontal {
18 | .vuepress-plugin-demo-block__display {
19 | height: 400px;
20 | display: flex;
21 |
22 | .vuepress-plugin-demo-block__app {
23 | width: 300px;
24 | border: 1px solid #ebebeb;
25 | box-shadow: 1px 1px 3px #ebebeb;
26 | margin-right: 5px;
27 | overflow: auto;
28 | }
29 |
30 | .vuepress-plugin-demo-block__h_code {
31 | flex: 1;
32 | overflow: auto;
33 | height: 100%;
34 |
35 | > pre {
36 | overflow: visible;
37 | }
38 | }
39 | }
40 | }
41 |
42 | .vuepress-plugin-demo-block__display {
43 | max-height: 400px;
44 | overflow: auto;
45 | }
46 | div {
47 | box-sizing: border-box;
48 | }
49 |
50 | &:hover {
51 | box-shadow: 0 0 11px rgba(33,33,33,.2);
52 | }
53 |
54 | .vuepress-plugin-demo-block__code {
55 | overflow: hidden;
56 | height: 0;
57 | padding: 0 !important;
58 | background-color: #282c34;
59 | border-radius: 0 !important;
60 | transition: height .5s;
61 |
62 | pre {
63 | margin: 0 !important;
64 | }
65 | }
66 |
67 | .vuepress-plugin-demo-block__display {
68 | padding: 20px;
69 | border-bottom: 1px solid #ebebeb;
70 | }
71 |
72 | .vuepress-plugin-demo-block__footer {
73 | position: relative;
74 | text-align: center;
75 |
76 | &.vuepress-plugin-demo-block__show-link {
77 | .vuepress-plugin-demo-block__jsfiddle,
78 | .vuepress-plugin-demo-block__codepen {
79 | opacity: 1;
80 | }
81 |
82 | .vuepress-plugin-demo-block__expand::before {
83 | border-top: none;
84 | border-right: 6px solid transparent;
85 | border-bottom: 6px solid #ccc;
86 | border-left: 6px solid transparent;
87 | }
88 | }
89 |
90 | &:hover {
91 | .vuepress-plugin-demo-block__jsfiddle,
92 | .vuepress-plugin-demo-block__codepen,
93 | .vuepress-plugin-demo-block__expand span,
94 | .vuepress-plugin-demo-block__expand {
95 | opacity: 1;
96 | }
97 |
98 | .vuepress-plugin-demo-block__expand::before {
99 | border-top-color: #3eaf7c !important;
100 | border-bottom-color: #3eaf7c !important;
101 | }
102 |
103 | svg {
104 | fill: #3eaf7c !important;
105 | }
106 | }
107 |
108 | .vuepress-plugin-demo-block__expand-text {
109 | transition: all .5s;
110 | opacity: 0;
111 | }
112 |
113 | form:nth-last-child(2) {
114 | right: 50px;
115 | }
116 |
117 | form:last-child {
118 | right: 10px;
119 | }
120 |
121 | .vuepress-plugin-demo-block__button {
122 | border-color: transparent;
123 | background-color: transparent;
124 | font-size: 14px;
125 | color: #3eaf7c;
126 | cursor: pointer;
127 | outline: none;
128 | margin: 0;
129 | width: 46px;
130 | position: relative;
131 |
132 | &:hover::before {
133 | content: attr(data-tip);
134 | white-space: nowrap;
135 | position: absolute;
136 | top: -30px;
137 | left: 50%;
138 | color: #eee;
139 | line-height: 1;
140 | z-index: 1000;
141 | border-radius: 4px;
142 | padding: 6px;
143 | transform: translateX(-50%);
144 | background-color: rgba(0, 0, 0, .8)
145 | }
146 |
147 | &:hover::after {
148 | content: '' !important;
149 | display: block;
150 | position: absolute;
151 | left: 50%;
152 | top: -5px;
153 | transform: translateX(-50%);
154 | border: 5px solid transparent;
155 | border-top-color: rgba(0, 0, 0, .8);
156 | }
157 |
158 | svg {
159 | width: 34px;
160 | height: 20px;
161 | fill: #ccc;
162 | }
163 | }
164 |
165 | .vuepress-plugin-demo-block__jsfiddle,
166 | .vuepress-plugin-demo-block__codepen {
167 | position: absolute;
168 | top: 10px;
169 | transition: all .5s;
170 | }
171 |
172 | .vuepress-plugin-demo-block__expand {
173 | position: relative;
174 | width: 100px;
175 | height: 40px;
176 | margin: 0;
177 | color: #3eaf7c;
178 | font-size: 14px;
179 | background-color: transparent;
180 | border-color: transparent;
181 | outline: none;
182 | transition: all .5s;
183 | cursor: pointer;
184 |
185 | &::before {
186 | content: "";
187 | position: absolute;
188 | top: 50%;
189 | left: 50%;
190 | width: 0;
191 | height: 0;
192 | border-top: 6px solid #ccc;
193 | border-right: 6px solid transparent;
194 | border-left: 6px solid transparent;
195 | transform: translate(-50%, -50%);
196 | }
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------