Skip to content

Commit

Permalink
Merge pull request #8 from privatenumber/body-class
Browse files Browse the repository at this point in the history
feat: document.body and class support
  • Loading branch information
privatenumber authored Mar 26, 2020
2 parents 44869d4 + 43e4ebd commit e92e2cf
Show file tree
Hide file tree
Showing 13 changed files with 417 additions and 38 deletions.
8 changes: 6 additions & 2 deletions .babelrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
const isProd = process.env.NODE_ENV === 'production';

module.exports = {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime'],
presets: [['@babel/preset-env', isProd ? {} : {
useBuiltIns: 'usage',
corejs: 2,
}]],
};
54 changes: 48 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ npm i vue-pseudo-window

## :beginner: Use case [![JSFiddle Demo](https://flat.badgen.net/badge/JSFiddle/Open%20Demo/blue)](https://jsfiddle.net/hirokiosame/p5Lz419s/)

### Listening to `window` events
### Adding event listeners to `window`
```vue
<template>
<div>
<div>
Window width: {{ winWidth }}
</div>
<pseudo-window @resize.passive="onResize" />
<!-- Handle window resize ^ -->
<pseudo-window
@resize.passive="onResize" <!-- Handle window resize with "passive" option -->
/>
</div>
</template>
Expand All @@ -54,12 +55,14 @@ export default {
</script>
```

### Listening to `document` events
### Adding event listeners to `document`
```vue
<template>
<div>
<pseudo-window document @click="onClick" />
<!-- Handle document click ^ -->
<pseudo-window
document
@click="onClick" <!-- Handle document click -->
/>
</div>
</template>
Expand All @@ -80,6 +83,45 @@ export default {
</script>
```

### Adding event listeners and classes to `document.body`
```vue
<template>
<div>
<pseudo-window
body
<!-- Add a class to document.body -->
:class="$style.lockScroll"
<!-- Handle document.body click -->
@click="onClick"
/>
</div>
</template>
<script>
import PseudoWindow from 'vue-pseudo-window';
export default {
components: {
PseudoWindow
},
methods: {
onClick() {
console.log('Body click!')
}
}
}
</script>
<style module>
.lockScroll {
overflow: hidden;
}
</style>
```

### When you only want one root element
The PseudoWindow is a functional component that returns exactly what's passed into it. By using it as the root component, its contents will pass-through.
```vue
Expand Down
2 changes: 1 addition & 1 deletion dist/pseudo-window.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 0 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
},
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.8.4",
"@vue/server-test-utils": "^1.0.0-beta.32",
"@vue/test-utils": "^1.0.0-beta.32",
Expand Down
3 changes: 1 addition & 2 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ export default {
input: 'src/pseudo-window.js',
plugins: [
babel({
exclude: 'node_modules/**',
runtimeHelpers: true,
exclude: 'src/bind-class/class*.js',
}),
isProd && terser({
mangle: {
Expand Down
32 changes: 32 additions & 0 deletions src/bind-class/class-util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// From https://github.com/vuejs/vue/blob/265dd457789bf726f95dd0578c1ce65a8b6ae9fd/src/platforms/web/runtime/class-util.js
/* eslint-disable */

export function addClass(el, classes) {
if (!classes.length) {
return;
}

const { classList } = el;

return classes.filter((c) => {
if (!classList.contains(c)) {
classList.add(c);
return true;
}
});
}

export function removeClass(el, classes) {
if (!classes || !classes.length) {
return;
}

let c;
while (c = classes.shift()) {
el.classList.remove(c);
}

if (!el.classList.length) {
el.removeAttribute('class');
}
};
29 changes: 29 additions & 0 deletions src/bind-class/class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// From https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/class.js
/* eslint-disable */

const isObject = (obj) => obj !== null && typeof obj === 'object';

const flatMap = (arr, f) => arr.reduce((acc, e) => acc.concat(f(e)), []);

function stringifyObject(value) {
let res = [];
for (const key in value) {
if (value[key]) {
res.push(key);
}
}
return res;
}

export function stringifyClass(value) {
if (Array.isArray(value)) {
return flatMap(value, v => stringifyClass(v))
}
if (isObject(value)) {
return stringifyObject(value);
}
if (typeof value === 'string' && value) {
return [value];
}
return [];
};
14 changes: 14 additions & 0 deletions src/bind-class/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { stringifyClass } from './class';
import { addClass, removeClass } from './class-util';

const BODY = typeof window !== 'undefined' && window.document.body;

export default ({ data, parent }) => {
const classStr = stringifyClass([data.staticClass, data.class]);
if (!classStr) { return; }

const added = addClass(BODY, classStr);
const off = () => { removeClass(BODY, added); };
parent.$once('hook:beforeUpdate', off);
parent.$once('hook:destroyed', off);
};
19 changes: 17 additions & 2 deletions src/bind-event-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,22 @@ const bindEventListners = (element, $listeners) => {
e.M_target.addEventListener(e.M_name, e.M_handler, e.M_opts);
handlers.push(e);
}
return () => unbindEventListeners(handlers);
return handlers;
};

export default bindEventListners;
const getTarget = ({ props }) => {
if (props.body) { return window.document.body; }
if (props.document) { return window.document; }
return window;
};

export default (ctx) => {
const handlers = bindEventListners(
getTarget(ctx),
ctx.listeners,
);

const off = () => { unbindEventListeners(handlers); };
ctx.parent.$once('hook:beforeUpdate', off);
ctx.parent.$once('hook:destroyed', off);
};
20 changes: 10 additions & 10 deletions src/pseudo-window.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import bindEventListners from './bind-event-listeners';
import bindClass from './bind-class/index';
import bindEventListeners from './bind-event-listeners';

const addEventListeners = (ctx) => {
const unbindEventListeners = bindEventListners(
ctx.props.document ? window.document : window,
ctx.listeners,
);
const init = (ctx) => {
bindEventListeners(ctx);

ctx.parent.$once('hook:beforeUpdate', unbindEventListeners);
ctx.parent.$once('hook:destroyed', unbindEventListeners);
if (ctx.props.body) {
bindClass(ctx);
}
};

export default {
Expand All @@ -17,14 +16,15 @@ export default {

props: {
document: Boolean,
body: Boolean,
},

render(h, ctx) {
if (ctx.parent._isMounted) { // eslint-disable-line no-underscore-dangle
addEventListeners(ctx);
init(ctx);
} else {
ctx.parent.$once('hook:mounted', () => {
addEventListeners(ctx);
init(ctx);
});
}

Expand Down
Loading

0 comments on commit e92e2cf

Please sign in to comment.