Commit 2bfad59f authored by Vadym Gidulian's avatar Vadym Gidulian

Initial commit

parents
/test/dist/
# Dependency directories
node_modules/
# Logs
logs/
*.log
npm-debug.log*
# Optional npm cache directory
.npm/
# Optional REPL history
.node_repl_history/
MIT License
Copyright (c) 2018 Vadym Gidulian
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# vue-stylize
A Vue directive to watch and change elements' style
## Description
The directive watch for changes of an element and its descendants.
When a change is detected (and at the init) it applies to specified elements corresponding style configs.
***Notes:***
*A change will be detected if `class` or `style` attribute changes.*
*When style configs are applying, watch is disabled.*
## Usage
```js
Vue.directive('stylize', require(...));
```
```vue
<... v-stylize="..."></...>
```
### Config
Config, which is passed via directive value, is an object with query selectors as keys and [style config](#style-config)s as values.
#### Style config
May be one of the following:
- [class names](#class-names)
- an `object` with the following optional keys:
- `class` - [class names](#class-names)
- `style` - [style properties](#style-properties)
- a `function` that takes the current element and returns one of the previous types
#### Class names
May be one of the following:
- a `string` with class names separated by whitespace characters
- an `array`, items of which may be one of the following:
- a `string` containing a class name
- a `function` that takes the current element and returns a `string`
- a `function` that takes the current element and returns one of the previous types
***Note:*** *If a class name is enclosed in `~` (i.e. `~classname~`), the class name will be removed from the element.*
#### Style properties
May be one of the following:
- an `object` with CSS properties as keys and one of the following as values:
- a `string` with property value
- a `function` that takes the current element and returns a `string`
- a `function` that takes the current element and returns an `object`
## Examples
```vue
<... class="a" v-stylize="{'.a': 'x', '.b': 'y ~b~'}">
<... class="a b">...</...>
</...>
```
will be rendered as
```vue
<... class="a x">
<... class="a x y">...</...>
</...>
```
{
"name": "@gviagroup/vue-stylize",
"version": "1.0.0",
"description": "A Vue directive to watch and change elements' style",
"license": "MIT",
"main": "src/index.js",
"scripts": {
"prestart": "yarn",
"start": "cd test && gulp && concurrently \"gulp watch\" \"lite-server\""
},
"devDependencies": {
"browserify": "^14.4.0",
"concurrently": "^3.4.0",
"lite-server": "^2.3.0",
"gulp-streamify": "^1.0.2",
"vinyl-source-stream": "^1.1.0",
"gulp": "^3.9.1",
"gulp-cssnano": "^2.1.2",
"gulp-htmlmin": "^3.0.0",
"gulp-inject-string": "^1.1.0",
"gulp-less": "^3.3.0",
"gulp-pug": "^3.3.0",
"gulp-rename": "^1.2.2",
"gulp-uglify": "^2.1.0",
"gulp-util": "^3.0.8",
"vue": "^2.5.22",
"vueify": "^9.4.1",
"envify": "^4.1.0",
"@gviagroup/vue-test-directive": "^1.0.0"
},
"peerDependencies": {
"vue": ">=2"
}
}
'use strict';
var observers = new WeakMap();
module.exports = {
bind: function (el, binding) {
observers.set(el, new MutationObserver(function () {
stopObserver(el);
updateDOM(el, binding.value);
startObserver(el);
}));
updateDOM(el, binding.value);
startObserver(el);
},
unbind: function (el) {
stopObserver(el);
observers.delete(el);
}
};
function startObserver(el) {
observers.get(el).observe(el, {
attributeFilter: ['class', 'style'],
childList: true,
subtree: true
});
}
function stopObserver(el) {
observers.get(el).disconnect();
}
function updateDOM(root, config) {
Object.keys(config).forEach(function (selector) {
const qEls = Array.from(root.querySelectorAll(selector));
if (root.matches(selector)) qEls.unshift(root);
qEls.forEach(function (qEl) {
applyConfig(qEl, config[selector]);
});
});
}
function applyConfig(el, config) {
config = valueOrFunctionValue(config, el);
if (!config) return;
if (typeof config === 'object' && !Array.isArray(config)) {
if (config.class) {
processAsClassNames(el, config.class);
}
if (config.style) {
applyStyleProperties(el, config.style);
}
} else {
processAsClassNames(el, config);
}
}
function processAsClassNames(el, config) {
config = valueOrFunctionValue(config, el);
if (!config) return;
if (Array.isArray(config)) {
applyClassNames(el, config);
} else if (typeof config === 'string') {
applyClassNames(el, config.split(/\s+/));
}
}
function applyClassNames(el, classNames) {
classNames.forEach(function (className) {
applyClassName(el, className);
});
}
function applyClassName(el, className) {
className = valueOrFunctionValue(className, el);
if (!className) return;
if (/^~.+~$/.test(className)) {
el.classList.remove(className.substring(1, className.length-1));
} else {
el.classList.add(className);
}
}
function applyStyleProperties(el, properties) {
properties = valueOrFunctionValue(properties, el);
if (!properties) return;
Object.keys(properties).forEach(function (property) {
el.style[property] = valueOrFunctionValue(properties[property], el);
});
}
function valueOrFunctionValue(value, el) {
return (typeof value === 'function') ? value(el) : value;
}
{
"files": [
"./dist/**/*.{css,html,htm,js}"
],
"reloadDebounce": 500,
"server": {
"baseDir": "dist"
}
}
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var vueify = require('vueify');
var envify = require('envify/custom');
var gulp = require('gulp');
var nano = require('gulp-cssnano');
var htmlMin = require('gulp-htmlmin');
var inject = require('gulp-inject-string');
var less = require('gulp-less');
var pug = require('gulp-pug');
var rename = require('gulp-rename');
var streamify = require('gulp-streamify');
var uglify = require('gulp-uglify');
var gutil = require('gulp-util');
var failsafe = false;
function isLocal() {
return !gutil.env.env;
}
function isTesting() {
return gutil.env.env === 'testing';
}
function isStaging() {
return gutil.env.env === 'staging';
}
function isProduction() {
return gutil.env.env === 'production';
}
function failsafePipe(pipe, callback) {
return failsafe
? pipe.on('error', shallow(callback))
: pipe;
}
function shallow(callback) {
return function (error) {
callback(error);
};
}
var paths = new function() {
this.srcDir = 'src';
this.distDir = 'dist';
// Files to be copied to `distDir`
this.files = [
// this.srcDir + '/folder/**/*' // Copy whole folder with subfolders
// this.srcDir + '/file.txt' // Copy specified file
];
this.cssDir = this.srcDir + '/css';
this.cssFiles = this.cssDir + '/**/*.css';
this.htmlDir = this.srcDir;
this.html = this.htmlDir + '/**/*.html';
this.htmlFiles = this.html;
this.htmlOut = this.distDir;
this.imgDir = this.srcDir + '/img';
this.imgFiles = this.imgDir + '/**/*';
this.imgOut = this.distDir + '/img';
this.jsDir = this.srcDir + '/js';
this.js = this.jsDir + '/index.js';
this.jsFiles = [
this.jsDir + '/**/*.js',
this.srcDir + '/vue/**/*',
'../src/**/*'
];
this.jsOut = this.distDir + '/js';
this.lessDir = this.srcDir + '/less';
this.less = this.lessDir + '/index.less';
this.lessFiles = this.lessDir + '/**/*.less';
this.lessOut = this.distDir + '/css';
this.pugDir = this.srcDir + '/pug';
this.pug = [
this.pugDir + '/**/*.pug',
'!' + this.pugDir + '/includes/**/*'
];
this.pugFiles = this.pugDir + '/**/*';
this.pugOut = this.distDir;
};
// Files
gulp.task('files-copy', function () {
return gulp.src(paths.files, {
base: paths.srcDir
})
.pipe(gulp.dest(paths.distDir));
});
gulp.task('files', ['files-copy']);
// Images
gulp.task('img-copy', function () {
return gulp.src(paths.imgFiles)
.pipe(gulp.dest(paths.imgOut));
});
gulp.task('images', ['img-copy']);
// Markup
gulp.task('html-min', function (callback) {
return gulp.src(paths.html)
.pipe(isLocal() || isTesting()
? inject.replace('<!--\\s*?weinre\\s*?-->', '<script async src="http://weinre.dev.gvia.group/target/target-script-min.js"></script>')
: gutil.noop())
.pipe(isStaging()
? inject.replace('<!--\\s*?weinre\\s*?-->', '<script async src="http://weinre.dev.gvia.group/target/target-script-min.js#staging"></script>')
: gutil.noop())
.pipe(isProduction()
? inject.replace('<!--\\s*?weinre\\s*?-->', '')
: gutil.noop())
.pipe(failsafePipe(htmlMin({
collapseWhitespace: true,
conservativeCollapse: true
}), callback))
.pipe(gulp.dest(paths.htmlOut));
});
gulp.task('pug-compile', function (callback) {
return gulp.src(paths.pug)
.pipe(isLocal() || isTesting()
? inject.replace('//-?\\s*?weinre', 'script(async src="http://weinre.dev.gvia.group/target/target-script-min.js")')
: gutil.noop())
.pipe(isStaging()
? inject.replace('//-?\\s*?weinre', 'script(async src="http://weinre.dev.gvia.group/target/target-script-min.js#staging")')
: gutil.noop())
.pipe(isProduction()
? inject.replace('//-?\\s*?weinre', '')
: gutil.noop())
.pipe(failsafePipe(pug(), callback))
.pipe(gulp.dest(paths.pugOut));
});
gulp.task('markup', ['html-min', 'pug-compile']);
// Scripts
gulp.task('js-browserify', function (callback) {
return failsafePipe(
browserify(paths.js, {
debug: isLocal(),
transform: [
vueify
]
})
.transform(
{global: !isLocal()},
envify(!isLocal() ? {NODE_ENV: 'production'} : {}))
.bundle(),
callback)
.pipe(source('script.all.min.js'))
.pipe(!isLocal() ? streamify(failsafePipe(uglify(), callback)) : gutil.noop())
.pipe(gulp.dest(paths.jsOut));
});
gulp.task('scripts', ['js-browserify']);
// Styles
gulp.task('less-compile', function (callback) {
return gulp.src(paths.less)
.pipe(failsafePipe(less(), callback))
.pipe(!isLocal() ? failsafePipe(nano(), callback) : gutil.noop())
.pipe(rename('style.all.min.css'))
.pipe(gulp.dest(paths.lessOut));
});
gulp.task('styles', ['less-compile']);
// Stages
gulp.task('watch', function () {
failsafe = true;
gulp.watch(paths.files, ['files']);
gulp.watch(paths.imgFiles, ['images']);
gulp.watch([paths.htmlFiles, paths.pugFiles], ['markup']);
gulp.watch(paths.jsFiles, ['scripts']);
gulp.watch([paths.cssFiles, paths.lessFiles], ['styles']);
});
gulp.task('build', ['files', 'images', 'markup', 'scripts', 'styles']);
gulp.task('default', ['build']);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
<script defer src="/js/script.all.min.js"></script>
<link rel="stylesheet" href="/css/style.all.min.css">
</head>
<body>
<!-- weinre -->
<div id="app"></div>
</body>
</html>
'use strict';
var Vue = require('vue/dist/vue.runtime.common');
var testDirective = require('@gviagroup/vue-test-directive');
Vue.directive('stylize', testDirective.wrap(require('../../../src/index')));
var app = new Vue({
el: '#app',
render: function (h) { return h(require('../vue/index.vue')); }
});
if (process.env.NODE_ENV !== 'production') {
window.app = app;
}
.block {
border: 1px solid #aaa;
}
.title {
color: #aaa;
}
<template lang="pug">
div
h1 vue-stylize
test-component(title="Root" :level="0" v-stylize:test:root="style")
</template>
<script>
module.exports = {
components: {
'test-component': require('./test-component.vue')
},
data: function () {
return {
style: {
// '.root': ['block', '~root~'],
// '.root': 'block ~root~',
'.root': function () {
return ['block', '~root~'];
// return 'block ~root~';
},
'.title': {
class: 'big',
style: {
color: '#faa'
}
},
// '.title': ['a', 'b'],
// '.title': 'a b'
}
};
}
};
</script>
<template lang="pug">
.root(:style="{marginLeft: (!!level * 20) + 'px'}")
h3.title {{title}}
test-component(:title="child.title" :level="level + 1" v-for="(child, index) in children" :key="index")
button(@click="addChild") Add child
</template>
<script>
module.exports = {
name: 'test-component',
props: ['title', 'level'],
data: function () {
return {
children: []
};
},
methods: {
addChild: function () {
this.children.push({title: this.children.length + 1});
}
}
};
</script>
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment