diff --git a/docs/embed.md b/docs/embed.md index 9b190eda..ce32e0ab 100644 --- a/docs/embed.md +++ b/docs/embed.md @@ -1,3 +1,11 @@ You can embed the URL using an iframe in your website. Optionally append `?readonly` to make the editor read-only. + +Use `show` with `widths` to control which panes are visible and how wide they are: + +```html + +``` + +The `widths` value is a comma-separated list that matches the visible panes from `show`. diff --git a/package.json b/package.json index 7ba66a1b..f1181a8e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "repository": {}, "scripts": { "test": "npm run lint", + "test:pan-widths": "node test/pan-widths.js", "lint": "xo", "dev": "poi", "build": "poi build", diff --git a/src/components/ConsolePan.vue b/src/components/ConsolePan.vue index d9df33b3..3161be1b 100644 --- a/src/components/ConsolePan.vue +++ b/src/components/ConsolePan.vue @@ -56,8 +56,11 @@ visiblePans: { immediate: true, handler(val) { - this.style = panPosition(val, 'console') + this.style = panPosition(val, 'console', this.panWidths) } + }, + panWidths(val) { + this.style = panPosition(this.visiblePans, 'console', val) } }, mounted() { @@ -69,7 +72,7 @@ }) }, computed: { - ...mapState(['logs', 'visiblePans', 'activePan']), + ...mapState(['logs', 'visiblePans', 'activePan', 'panWidths']), enableResizer() { return hasNextPan(this.visiblePans, 'console') }, diff --git a/src/components/OutputPan.vue b/src/components/OutputPan.vue index 16084e3b..038dbbcf 100644 --- a/src/components/OutputPan.vue +++ b/src/components/OutputPan.vue @@ -76,8 +76,11 @@ export default { visiblePans: { immediate: true, handler(val) { - this.style = panPosition(val, 'output') + this.style = panPosition(val, 'output', this.panWidths) } + }, + panWidths(val) { + this.style = panPosition(this.visiblePans, 'output', val) } }, computed: { @@ -86,6 +89,7 @@ export default { 'css', 'html', 'visiblePans', + 'panWidths', 'activePan', 'githubToken', 'iframeStatus' diff --git a/src/store/index.js b/src/store/index.js index dd52913f..99436b7c 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -17,6 +17,7 @@ import progress from 'nprogress' import api from '@/utils/github-api' import req from 'reqjs' import Event from '@/utils/event' +import { parsePanWidths } from '@/utils/pan-widths' Vue.use(Vuex) @@ -75,7 +76,8 @@ const store = new Vuex.Store({ userMeta: JSON.parse(localStorage.getItem('codepan:user-meta')) || {}, editorStatus: 'saved', iframeStatus: null, - transforming: false + transforming: false, + panWidths: null }, mutations: { UPDATE_CODE(state, { type, code }) { @@ -103,6 +105,9 @@ const store = new Vuex.Store({ SHOW_PANS(state, pans) { state.visiblePans = sortPans(pans) }, + SET_PAN_WIDTHS(state, widths) { + state.panWidths = widths + }, ACTIVE_PAN(state, pan) { state.activePan = pan }, @@ -150,6 +155,9 @@ const store = new Vuex.Store({ showPans({ commit }, pans) { commit('SHOW_PANS', pans) }, + setPanWidths({ commit }, value) { + commit('SET_PAN_WIDTHS', parsePanWidths(value)) + }, async updateTransformer({ commit }, { type, transformer }) { if ( transformer === 'babel' || diff --git a/src/utils/create-pan.js b/src/utils/create-pan.js index fc74fdad..19f5b9d8 100644 --- a/src/utils/create-pan.js +++ b/src/utils/create-pan.js @@ -17,7 +17,7 @@ export default ({ name, editor, components } = {}) => { } }, computed: { - ...mapState([name, 'visiblePans', 'activePan', 'autoRun']), + ...mapState([name, 'visiblePans', 'activePan', 'autoRun', 'panWidths']), ...mapState({ isVisible: state => state.visiblePans.indexOf(name) !== -1 }), @@ -38,9 +38,12 @@ export default ({ name, editor, components } = {}) => { visiblePans: { immediate: true, handler(val) { - this.style = panPosition(val, name) + this.style = panPosition(val, name, this.panWidths) } }, + panWidths(val) { + this.style = panPosition(this.visiblePans, name, val) + }, [`${name}.transformer`](val) { const mode = getEditorModeByTransfomer(val) this.editor.setOption('mode', mode) diff --git a/src/utils/pan-position.js b/src/utils/pan-position.js index 50ab4257..db1399de 100644 --- a/src/utils/pan-position.js +++ b/src/utils/pan-position.js @@ -1,4 +1,12 @@ -export default (pans, pan) => { +import { getPanPosition } from '@/utils/pan-widths' + +export default (pans, pan, widths) => { + const customPosition = getPanPosition(pans, pan, widths) + + if (customPosition) { + return customPosition + } + const panWidth = 100 / pans.length const pansCount = matchedPans => { return pans.filter(p => { diff --git a/src/utils/pan-widths.js b/src/utils/pan-widths.js new file mode 100644 index 00000000..7000b3b6 --- /dev/null +++ b/src/utils/pan-widths.js @@ -0,0 +1,43 @@ +const toNumber = value => { + const parsed = Number(value) + return Number.isFinite(parsed) ? parsed : null +} + +export const parsePanWidths = value => { + if (!value) { + return null + } + + const widths = String(value) + .split(',') + .map(item => toNumber(item.trim())) + + if (widths.some(width => width === null || width <= 0)) { + return null + } + + const total = widths.reduce((sum, width) => sum + width, 0) + + if (total <= 0) { + return null + } + + return widths.map(width => width / total * 100) +} + +export const getPanPosition = (pans, pan, widths) => { + const index = pans.indexOf(pan) + + if (!widths || index === -1 || widths.length !== pans.length) { + return null + } + + const left = widths.slice(0, index).reduce((sum, width) => sum + width, 0) + const width = widths[index] + const right = Math.max(0, 100 - left - width) + + return { + left: `${left}%`, + right: `${right}%` + } +} diff --git a/src/views/EditorPage.vue b/src/views/EditorPage.vue index 058100bd..f789843b 100644 --- a/src/views/EditorPage.vue +++ b/src/views/EditorPage.vue @@ -115,11 +115,18 @@ export default { handler(next, prev) { if (!next && prev) { this.showPans(['js', 'html', 'output']) + this.setPanWidths(null) } else if (next !== prev) { this.showPans(next.split(',')) } }, immediate: true + }, + '$route.query.widths': { + handler(next) { + this.setPanWidths(next) + }, + immediate: true } }, mounted() { @@ -154,7 +161,7 @@ export default { }) }, methods: { - ...mapActions(['setBoilerplate', 'setGist', 'showPans', 'setAutoRun']), + ...mapActions(['setBoilerplate', 'setGist', 'showPans', 'setAutoRun', 'setPanWidths']), isVisible(pan) { return this.visiblePans.indexOf(pan) !== -1 }, diff --git a/test/pan-widths.js b/test/pan-widths.js new file mode 100644 index 00000000..18a33db5 --- /dev/null +++ b/test/pan-widths.js @@ -0,0 +1,26 @@ +require('babel-register')({ + presets: [['env', { modules: 'commonjs' }]], + ignore: /node_modules/ +}) + +const assert = require('assert') +const { parsePanWidths, getPanPosition } = require('../src/utils/pan-widths') + +assert.deepStrictEqual(parsePanWidths('25,25,50'), [25, 25, 50]) +assert.deepStrictEqual(parsePanWidths('1,1,2'), [25, 25, 50]) +assert.strictEqual(parsePanWidths('25,nope,50'), null) +assert.strictEqual(parsePanWidths('25,-1,50'), null) +assert.strictEqual(parsePanWidths(''), null) +assert.deepStrictEqual( + getPanPosition(['js', 'console', 'output'], 'js', [25, 25, 50]), + { left: '0%', right: '75%' } +) +assert.deepStrictEqual( + getPanPosition(['js', 'console', 'output'], 'console', [25, 25, 50]), + { left: '25%', right: '50%' } +) +assert.deepStrictEqual( + getPanPosition(['js', 'console', 'output'], 'output', [25, 25, 50]), + { left: '50%', right: '0%' } +) +assert.strictEqual(getPanPosition(['js', 'output'], 'output', [25, 25, 50]), null)