diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..3bff21e --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [ "es2015-rollup" ] +} diff --git a/README.md b/README.md index e6ee52a..b757952 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# vue-prosemirror +# vue-input-autosize -> A simple Vue.js wrapper around the ProseMirror editor +> A simple Vue.js directive for autosizing a text input based on its content. ## Install ```js -$ npm install vue-prosemirror --save +$ npm install vue-input-autosize --save ``` ## Usage @@ -19,4 +19,4 @@ Coming soon... ## TODO - [ ] Tests -- [ ] Tidy up example +- [ ] Improve the example diff --git a/example/app.vue b/example/app.vue index fef3f73..f63afc7 100644 --- a/example/app.vue +++ b/example/app.vue @@ -1,40 +1,21 @@ - diff --git a/index.js b/index.js index cc1bac5..343496f 100644 --- a/index.js +++ b/index.js @@ -1,81 +1,55 @@ 'use strict'; -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } +exports.install = function (Vue, options) { + Vue.directive("input-autosize", { + mirror: null, + val: " ", + options: {}, + bind: function bind() { + var _this = this; -var prosemirror = _interopDefault(require('prosemirror')); -var prosemirror_dist_schemaBasic = require('prosemirror/dist/schema-basic'); -var prosemirror_dist_markdown = require('prosemirror/dist/markdown'); -var prosemirror_dist_exampleSetup = require('prosemirror/dist/example-setup'); -var prosemirror_dist_menu = require('prosemirror/dist/menu'); + var defaults = { maxWidth: 500, minWidth: 20, comfortZone: 0 }; + this.options = Object.assign(defaults, options || {}); -var index = { template: "
", - name: "ProseMirror", - props: { - content: String, - onChange: Function, - menuType: { - type: String, - default: "bar" + this.mirror = document.createElement("span"); + this.mirror.classList.add("vue-input-autosize-mirror"); + document.body.appendChild(this.mirror); + + this.el.addEventListener("input", this.check.bind(this, this.el), false); + setTimeout(function () { + var styles = window.getComputedStyle(_this.el, null); + Object.assign(_this.mirror.style, { + position: "absolute", + top: "-9999px", + left: "-9999px", + width: "auto", + whiteSpace: "nowrap", + fontSize: styles.getPropertyValue("font-size"), + fontFamily: styles.getPropertyValue("font-family"), + fontWeight: styles.getPropertyValue("font-weight"), + letterSpacing: styles.getPropertyValue("letter-spacing"), + textTransform: styles.getPropertyValue("text-transform"), + ariaHidden: true + }); + _this.check(_this.el); + }, 0); }, - options: Object - }, - data: function data() { - return { - editor: null - }; - }, - - computed: { - rawMarkdown: function rawMarkdown() { - return prosemirror_dist_markdown.defaultMarkdownSerializer.serialize(this.editor.doc); + update: function update() { + this.check(this.el); }, - renderedContent: function renderedContent() { - var docFrag = this.editor.doc.content.toDOM(); - var div = document.createElement("div"); - div.appendChild(docFrag.cloneNode(true)); - return div.innerHTML; - } - }, - ready: function ready() { - var _this = this; - - var editorOptions = Object.assign({ - schema: prosemirror_dist_schemaBasic.schema, - place: this.el, - doc: prosemirror_dist_markdown.defaultMarkdownParser.parse(this.content), - plugins: [prosemirror_dist_exampleSetup.exampleSetup.config({ menuBar: false, tooltipMenu: false })] - }, this.options); - - if (editorOptions.doc === undefined || editorOptions.doc === null) { - editorOptions.doc = null; - editorOptions.docFormat = null; - } - - this.editor = new prosemirror.ProseMirror(editorOptions); - - var menu = prosemirror_dist_exampleSetup.buildMenuItems(prosemirror_dist_schemaBasic.schema); - if (this.menuType === "bar") { - prosemirror_dist_menu.tooltipMenu.detach(this.editor); - prosemirror_dist_menu.menuBar.config({ float: true, content: menu.fullMenu }).attach(this.editor); - } else { - prosemirror_dist_menu.menuBar.detach(this.editor); - prosemirror_dist_menu.tooltipMenu.config({ - selectedBlockMenu: true, - inlineContent: menu.inlineMenu, - blockContent: menu.blockMenu - }).attach(this.editor); - } - - this.editor.on.change.add(function () { - if (_this.onChange && typeof _this.onChange === "function") { - _this.onChange.apply(null, [_this.rawMarkdown, _this.renderedContent]); + check: function check(el) { + this.val = el.value; + if (!this.val) this.val = el.placeholder || ""; + this.mirror.innerHTML = this.val.replace(/&/g, "&").replace(/\s/g, " ").replace(//g, ">"); + var newWidth = this.mirror.getBoundingClientRect().width + this.options.comfortZone; + if (newWidth > this.options.maxWidth) { + newWidth = this.options.maxWidth; + } else if (newWidth < this.options.minWidth) { + newWidth = this.options.minWidth; } - }); - - this.$watch("content", function (val) { - _this.editor.setDoc(prosemirror_dist_markdown.defaultMarkdownParser.parse(val)); - }); - } -}; - -module.exports = index; \ No newline at end of file + if (newWidth != el.getBoundingClientRect().width) { + el.style.width = newWidth + "px"; + } + } + }); +}; \ No newline at end of file diff --git a/index.umd.js b/index.umd.js new file mode 100644 index 0000000..54e8ab4 --- /dev/null +++ b/index.umd.js @@ -0,0 +1,61 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, function () { 'use strict'; + + exports.install = function (Vue, options) { + Vue.directive("input-autosize", { + mirror: null, + val: " ", + options: {}, + bind: function bind() { + var _this = this; + + var defaults = { maxWidth: 500, minWidth: 20, comfortZone: 0 }; + this.options = Object.assign(defaults, options || {}); + + this.mirror = document.createElement("span"); + this.mirror.classList.add("vue-input-autosize-mirror"); + document.body.appendChild(this.mirror); + + this.el.addEventListener("input", this.check.bind(this, this.el), false); + setTimeout(function () { + var styles = window.getComputedStyle(_this.el, null); + Object.assign(_this.mirror.style, { + position: "absolute", + top: "-9999px", + left: "-9999px", + width: "auto", + whiteSpace: "nowrap", + fontSize: styles.getPropertyValue("font-size"), + fontFamily: styles.getPropertyValue("font-family"), + fontWeight: styles.getPropertyValue("font-weight"), + letterSpacing: styles.getPropertyValue("letter-spacing"), + textTransform: styles.getPropertyValue("text-transform"), + ariaHidden: true + }); + _this.check(_this.el); + }, 0); + }, + update: function update() { + this.check(this.el); + }, + check: function check(el) { + this.val = el.value; + if (!this.val) this.val = el.placeholder || ""; + this.mirror.innerHTML = this.val.replace(/&/g, "&").replace(/\s/g, " ").replace(//g, ">"); + var newWidth = this.mirror.getBoundingClientRect().width + this.options.comfortZone; + if (newWidth > this.options.maxWidth) { + newWidth = this.options.maxWidth; + } else if (newWidth < this.options.minWidth) { + newWidth = this.options.minWidth; + } + if (newWidth != el.getBoundingClientRect().width) { + el.style.width = newWidth + "px"; + } + } + }); + }; + +})); \ No newline at end of file diff --git a/package.json b/package.json index 23d6c30..ad1cb3f 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "vue-prosemirror", - "version": "0.1.2", - "description": "A simple Vue.js wrapper around the ProseMirror editor", + "name": "vue-input-autosize", + "version": "0.1.0", + "description": "A simple Vue.js directive to autosize text input fields", "license": "MIT", - "repository": "syropian/vue-prosemirror", + "repository": "syropian/vue-input-autosize", "author": { "name": "Collin Henderson", "email": "collin@syropia.net", @@ -13,9 +13,11 @@ "node": ">=4" }, "scripts": { + "build:all": "npm run build && npm run build:umd", "build": "BUILD_ENV=cjs rollup -c", + "build:umd": "BUILD_ENV=umd rollup -c", "example": "vbuild --dev -e example", - "example:build": "vbuild -e example -t VueProsemirror" + "example:build": "vbuild -e example -t VueInputAutosize" }, "main": "index.js", "files": [ @@ -23,18 +25,16 @@ "index.umd.js" ], "keywords": [ - "prosemirror", - "editor", - "markdown", + "autosize", + "autogrow", + "autoshrink", + "input", "Vue" ], - "dependencies": { - "prosemirror": "^0.8.3" - }, + "dependencies": {}, "devDependencies": { "babel-preset-es2015-rollup": "^1.1.1", "rollup": "^0.33.0", - "rollup-plugin-babel": "^2.6.1", - "rollup-plugin-vue": "^2.0.1" + "rollup-plugin-babel": "^2.6.1" } } diff --git a/rollup.config.js b/rollup.config.js index 0daa655..29a8a8a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,16 +1,17 @@ import { rollup } from "rollup" import babel from "rollup-plugin-babel" -import vue from "rollup-plugin-vue"; const env = process.env.BUILD_ENV const dest = env === "cjs" ? "index.js" : "index.umd.js" export default { - entry: "./src/index.vue", - dest, + entry: "./src/index.js", plugins: [ - vue() + babel({ + exclude: "node_modules/**" + }) ], + dest, format: env, - moduleName: "VueProseMirror" + moduleName: "VueInputAutosize" } diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..8e8616a --- /dev/null +++ b/src/index.js @@ -0,0 +1,49 @@ +"use strict"; + +exports.install = function(Vue, options){ + Vue.directive("input-autosize", { + mirror: null, + val: " ", + options: {}, + bind(){ + const defaults = { maxWidth: 500, minWidth: 20, comfortZone: 0 }; + this.options = Object.assign(defaults, options || {}); + + this.mirror = document.createElement("span"); + this.mirror.classList.add("vue-input-autosize-mirror"); + document.body.appendChild(this.mirror); + + this.el.addEventListener("input", this.check.bind(this, this.el), false); + setTimeout(() => { + let styles = window.getComputedStyle(this.el, null); + Object.assign(this.mirror.style, { + position: "absolute", + top: "-9999px", + left: "-9999px", + width: "auto", + whiteSpace: "nowrap", + fontSize: styles.getPropertyValue("font-size"), + fontFamily: styles.getPropertyValue("font-family"), + fontWeight: styles.getPropertyValue("font-weight"), + letterSpacing: styles.getPropertyValue("letter-spacing"), + textTransform: styles.getPropertyValue("text-transform"), + ariaHidden: true + }); + this.check(this.el); + }, 0) + }, + update(){ + this.check(this.el); + }, + check(el){ + this.val = el.value; + if (!this.val) this.val = el.placeholder || ""; + this.mirror.innerHTML = this.val.replace(/&/g, "&").replace(/\s/g, " ").replace(//g, ">"); + let newWidth = this.mirror.getBoundingClientRect().width + this.options.comfortZone; + if( newWidth > this.options.maxWidth ){ newWidth = this.options.maxWidth; } + else if (newWidth < this.options.minWidth){ newWidth = this.options.minWidth; } + if( newWidth != el.getBoundingClientRect().width ){ el.style.width = `${newWidth}px`; } + } + }); + +} diff --git a/src/index.vue b/src/index.vue deleted file mode 100644 index a826996..0000000 --- a/src/index.vue +++ /dev/null @@ -1,77 +0,0 @@ - -