Commit 3a549a0e authored by Eric Eastwood's avatar Eric Eastwood

Add user buttons(defined by .class) for toggle chat

`.js-gitter-toggle-chat-button`

v0.2.5
parent 91d3e54e
# v0.2.5 - 2015-9-2
- Listen to `.js-gitter-toggle-chat-button` elements for "activiation"(click) which can toggle the chat panel. You can also set `data-gitter-toggle-chat-state` to an explicit value of `true` or `false` to make a open and close button respectively. By default the value is `'toggle'`.
- Add `dom-utility.js -> off` to remove event handlers
# v0.2.4 - 2015-9-1
- Emit `gitter-sidecar-ready` event on `document` when the script has loaded: `document.addEventListener('gitter-sidecar-ready', function(e) { var Chat = e.detail.Chat; var chat = new Chat(/*opts*/); });`
......@@ -6,6 +12,7 @@
- Emit `gitter-chat-started` event on container after a Sidecar chat instance is initialized: `document.querySelector('.gitter-chat-embed').addEventListener('gitter-chat-started', function(e) { var chat = e.detail.chat; chat.toggleChat(true); });`
- Use [`es6-promise`](https://www.npmjs.com/package/es6-promise) instead of [`bluebird`](https://github.com/petkaantonov/bluebird) for the sake of file size
- Stop using `bling.js` for DOM manipulation. Now using `dom-utility.js` which is fully encapsulated from the `window` world.
- Now using `window.gitter` instead of `window.___gitter`
# v0.2.3 - 2015-8-31
......
......@@ -2,7 +2,7 @@
Gitter embed widget
# Latest version: 0.2.4
# Latest version: 0.2.5
### [Changelog](https://github.com/gitterHQ/sidecar/blob/master/CHANGELOG.md)
......@@ -19,6 +19,17 @@ Gitter embed widget
- [Basic](https://github.com/gitterHQ/sidecar/tree/master/examples/basic)
### Toggle Buttons
You can define toggle/open/close buttons in your page using the `.js-gitter-toggle-chat-button` class and an optional `data-gitter-toggle-chat-state` attribute. If you do not provide a `data-gitter-toggle-chat-state`, it will default to `'toggle'`. See the `examples/toggle-chat-class-buttons` example.
```html
<button class="js-gitter-toggle-chat-button">Toggle Chat</button>
<button class="js-gitter-toggle-chat-button" data-gitter-toggle-chat-state="true">Open Chat</button>
<button class="js-gitter-toggle-chat-button" data-gitter-toggle-chat-state="false">Close Chat</button>
```
# Options
Set options with the global window option:
......
......@@ -92,6 +92,7 @@ return /******/ (function(modules) { // webpackBootstrap
});
document.dispatchEvent(event);
// Create the default instance
if (!((windowGitter.chat || {}).options || {}).disableDefaultChat) {
var windowGitterChat = getOrDefaultKey(windowGitter, 'chat');
windowGitterChat.defaultChat = new _libChatJs2['default'](windowGitterChat.options || {});
......@@ -185,6 +186,19 @@ return /******/ (function(modules) { // webpackBootstrap
var gitterUrl = 'https://gitter.im/';
var parseAttributeTruthiness = function parseAttributeTruthiness(value) {
if (value) {
var valueSanitized = value.trim().toLowerCase();
if (valueSanitized === 'true' || valueSanitized === '1') {
return true;
} else if (valueSanitized === 'false' || valueSanitized === '0') {
return false;
}
}
return value;
};
// Pass in a shape object of options and the element
// and we will extend and properties available
// NOTE: We will only look for keys present in `options` passed in
......@@ -206,12 +220,14 @@ return /******/ (function(modules) { // webpackBootstrap
};
// Helper method that detects whether an element was "activated"
// Returns a function that you can execute to remove the listeners
// Accibility in mind: click, spacebar, enter
var spacebarKey = 32;
var enterKey = 13;
var elementOnActivate = function elementOnActivate(elements, cb) {
elements = domUtility.coerceIntoElementsArray(elements);
domUtility.on(elements, 'click keydown', function (e) {
var handler = function handler(e) {
// If click or spacebar, or enter is pressed
if (e.type === 'click' || e.type === 'keydown' && (e.keyCode === spacebarKey || e.keyCode === enterKey)) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
......@@ -220,7 +236,12 @@ return /******/ (function(modules) { // webpackBootstrap
cb.call.apply(cb, [this, e].concat(args));
}
});
};
domUtility.on(elements, 'click keydown', handler);
return function () {
domUtility.off(elements, 'click keydown', handler);
};
};
var embedGitterStyles = function embedGitterStyles() {
......@@ -311,6 +332,7 @@ return /******/ (function(modules) { // webpackBootstrap
var DEFAULTS = Symbol();
var OPTIONS = Symbol();
var ELEMENTSTORE = Symbol();
var EVENTHANDLESTORE = Symbol();
var INIT = Symbol();
var ISEMBEDDED = Symbol();
var EMBEDCHATONCE = Symbol();
......@@ -325,6 +347,7 @@ return /******/ (function(modules) { // webpackBootstrap
_classCallCheck(this, chatEmbed);
this[ELEMENTSTORE] = new _elementStoreJs2['default']();
this[EVENTHANDLESTORE] = [];
this[DEFAULTS] = (0, _objectAssign2['default'])({}, defaults);
......@@ -411,6 +434,16 @@ return /******/ (function(modules) { // webpackBootstrap
});
}
// Listen to buttons with a class of `.js-gitter-toggle-chat-button`
// We also look for an options `data-gitter-toggle-chat-state` attribute
var classToggleButtonOff = elementOnActivate((0, _domUtilityJs2['default'])('.js-gitter-toggle-chat-button'), function (e) {
var state = parseAttributeTruthiness(e.target.getAttribute('data-gitter-toggle-chat-state'));
_this2.toggleChat(state !== null ? state : 'toggle');
e.preventDefault();
});
this[EVENTHANDLESTORE].push(classToggleButtonOff);
// Emit for each container
opts.container.forEach(function (container) {
var event = new CustomEvent('gitter-chat-started', {
......@@ -482,6 +515,8 @@ return /******/ (function(modules) { // webpackBootstrap
this[ISEMBEDDED] = true;
}
// state: true, false, 'toggle'
}, {
key: TOGGLECONTAINERS,
value: function value(state) {
......@@ -493,7 +528,11 @@ return /******/ (function(modules) { // webpackBootstrap
var containers = opts.container;
containers.forEach(function (container) {
container.classList.toggle('is-collapsed', !state);
if (state === 'toggle') {
container.classList.toggle('is-collapsed');
} else {
container.classList.toggle('is-collapsed', !state);
}
var event = new CustomEvent('gitter-chat-toggle', {
detail: {
......@@ -505,6 +544,7 @@ return /******/ (function(modules) { // webpackBootstrap
}
// Public API
// state: true, false, 'toggle'
}, {
key: 'toggleChat',
value: function toggleChat(state) {
......@@ -543,6 +583,12 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: 'destroy',
value: function destroy() {
// Remove all the event handlers
this[EVENTHANDLESTORE].forEach(function (fn) {
fn();
});
// Remove and DOM elements, we made
this[ELEMENTSTORE].destroy();
}
}]);
......@@ -2096,6 +2142,7 @@ return /******/ (function(modules) { // webpackBootstrap
exports.coerceIntoElementsArray = coerceIntoElementsArray;
exports.forEach = forEach;
exports.on = on;
exports.off = off;
var $ = document.querySelectorAll.bind(document);
......@@ -2128,8 +2175,6 @@ return /******/ (function(modules) { // webpackBootstrap
return elements;
}
;
// `arrayLike` can be a single object, array, or array-like (NodeList, HTMLCollection)
function forEach(arrayLike, cb) {
......@@ -2144,8 +2189,6 @@ return /******/ (function(modules) { // webpackBootstrap
});
}
;
// Listen to events.
// Pass in a string name of events separated by spaces
......@@ -2160,7 +2203,19 @@ return /******/ (function(modules) { // webpackBootstrap
return this;
}
;
// Remove the event listener
// Pass in a string name of events separated by spaces
function off(elements, names, cb) {
names.split(/\s/).forEach(function (name) {
forEach(elements, function (element) {
element.removeEventListener(name, cb);
});
});
// Keep the chaining going
return this;
}
exports['default'] = $;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sidecar Activate with Button Example</title>
<style>
html,
body {
padding: 0;
margin: 0;
}
html {
width: 100%;
height: 100%;
box-sizing: border-box;
}
body {
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
overflow: hidden;
display: flex;
font-family: Arial, sans-serif;
}
iframe {
width: 100%;
height: 100%;
}
code {
display: inline-block;
}
.main-content {
flex: 1;
height: 100%;
background: #f2f0ed;
}
.sidebar:not(:empty) {
flex: 1;
height: 100%;
}
</style>
</head>
<body>
<main class="main-content">
<h1>Click the "Open Chat" button in the bottom right</h1>
<a href="https://github.com/gitterHQ/sidecar">Sidecar</a>
</main>
<aside class="sidebar"></aside>
<script>
((window.gitter = {}).chat = {}).options = {
container: '.sidebar',
showChatByDefault: false
};
</script>
<script src="../../dist/sidecar.js" async defer></script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sidecar Custom Container Example</title>
<style>
html,
body {
padding: 0;
margin: 0;
}
html {
width: 100%;
height: 100%;
box-sizing: border-box;
}
body {
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
overflow: hidden;
display: flex;
font-family: Arial, sans-serif;
}
iframe {
width: 100%;
height: 100%;
}
code {
display: inline-block;
}
.main-content {
width: 60%;
height: 100%;
background: #f2f0ed;
}
.sidebar {
flex: 1;
height: 100%;
}
</style>
</head>
<body>
<main class="main-content">
<h1>Whoa, it is in the <code>.sidebar</code></h1>
<a href="https://github.com/gitterHQ/sidecar">Sidecar</a>
</main>
<aside class="sidebar"></aside>
<script>
((window.gitter = {}).chat = {}).options = {
container: '.sidebar'
};
</script>
<script src="../../dist/sidecar.js" async defer></script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sidecar Basic Example</title>
<link href="../common.css" rel="stylesheet">
</head>
<body>
<button class="js-gitter-toggle-chat-button">Toggle Chat</button>
<button class="js-gitter-toggle-chat-button" data-gitter-toggle-chat-state="true">Open Chat</button>
<button class="js-gitter-toggle-chat-button" data-gitter-toggle-chat-state="false">Close Chat</button>
<script>
((window.gitter = {}).chat = {}).options = {
room: 'marionettejs/backbone.marionette'
};
</script>
<script src="../../dist/sidecar.js" async defer></script>
</body>
</html>
{
"name": "gitter-sidecar",
"version": "0.2.4",
"version": "0.2.5",
"description": "",
"main": "index.js",
"scripts": {
......
......@@ -30,7 +30,7 @@ document.dispatchEvent(event);
// Create the default instance
if(!((windowGitter.chat || {}).options || {}).disableDefaultChat) {
let windowGitterChat = getOrDefaultKey(windowGitter, 'chat');
windowGitterChat.defaultChat = new Chat(windowGitterChat.options || {});
......
......@@ -13,6 +13,22 @@ import * as domUtility from './dom-utility.js';
const gitterUrl = 'https://gitter.im/';
let parseAttributeTruthiness = function(value) {
if(value) {
let valueSanitized = value.trim().toLowerCase();
if(valueSanitized === 'true' || valueSanitized === '1') {
return true;
}
else if(valueSanitized === 'false' || valueSanitized === '0') {
return false;
}
}
return value;
};
// Pass in a shape object of options and the element
// and we will extend and properties available
// NOTE: We will only look for keys present in `options` passed in
......@@ -36,17 +52,24 @@ let getDataOptionsFromElement = function(options, element) {
// Helper method that detects whether an element was "activated"
// Returns a function that you can execute to remove the listeners
// Accibility in mind: click, spacebar, enter
const spacebarKey = 32;
const enterKey = 13;
let elementOnActivate = function(elements, cb) {
elements = domUtility.coerceIntoElementsArray(elements);
domUtility.on(elements, 'click keydown', function(e, ...args) {
let handler = function(e, ...args) {
// If click or spacebar, or enter is pressed
if(e.type === 'click' || (e.type === 'keydown' && (e.keyCode === spacebarKey || e.keyCode === enterKey))) {
cb.call(this, e, ...args);
}
});
};
domUtility.on(elements, 'click keydown', handler);
return function() {
domUtility.off(elements, 'click keydown', handler);
};
};
......@@ -158,6 +181,7 @@ Object.keys(defaults).forEach((key) => {
const DEFAULTS = Symbol();
const OPTIONS = Symbol();
const ELEMENTSTORE = Symbol();
const EVENTHANDLESTORE = Symbol();
const INIT = Symbol();
const ISEMBEDDED = Symbol();
const EMBEDCHATONCE = Symbol();
......@@ -166,6 +190,7 @@ const TOGGLECONTAINERS = Symbol();
class chatEmbed {
constructor(options = {}) {
this[ELEMENTSTORE] = new ElementStore();
this[EVENTHANDLESTORE] = [];
this[DEFAULTS] = objectAssign({}, defaults);
......@@ -252,6 +277,16 @@ class chatEmbed {
});
}
// Listen to buttons with a class of `.js-gitter-toggle-chat-button`
// We also look for an options `data-gitter-toggle-chat-state` attribute
let classToggleButtonOff = elementOnActivate($('.js-gitter-toggle-chat-button'), (e) => {
let state = parseAttributeTruthiness(e.target.getAttribute('data-gitter-toggle-chat-state'));
this.toggleChat(state !== null ? state : 'toggle');
e.preventDefault();
});
this[EVENTHANDLESTORE].push(classToggleButtonOff);
// Emit for each container
opts.container.forEach((container) => {
......@@ -325,6 +360,7 @@ class chatEmbed {
this[ISEMBEDDED] = true;
}
// state: true, false, 'toggle'
[TOGGLECONTAINERS](state) {
let opts = this[OPTIONS];
......@@ -334,7 +370,12 @@ class chatEmbed {
let containers = opts.container;
containers.forEach((container) => {
container.classList.toggle('is-collapsed', !state);
if(state === 'toggle') {
container.classList.toggle('is-collapsed');
}
else {
container.classList.toggle('is-collapsed', !state);
}
let event = new CustomEvent('gitter-chat-toggle', {
detail: {
......@@ -347,6 +388,7 @@ class chatEmbed {
// Public API
// state: true, false, 'toggle'
toggleChat(state) {
let opts = this[OPTIONS];
......@@ -378,6 +420,12 @@ class chatEmbed {
}
destroy() {
// Remove all the event handlers
this[EVENTHANDLESTORE].forEach(function(fn) {
fn();
});
// Remove and DOM elements, we made
this[ELEMENTSTORE].destroy();
}
}
......
......@@ -26,7 +26,7 @@ export function coerceIntoElementsArray(stuff) {
}
return elements;
};
}
// `arrayLike` can be a single object, array, or array-like (NodeList, HTMLCollection)
......@@ -36,7 +36,8 @@ export function forEach(arrayLike, cb) {
cb.apply(cb, args);
}
});
};
}
// Listen to events.
// Pass in a string name of events separated by spaces
......@@ -49,7 +50,20 @@ export function on(elements, names, cb) {
// Keep the chaining going
return this;
};
}
// Remove the event listener
// Pass in a string name of events separated by spaces
export function off(elements, names, cb) {
names.split(/\s/).forEach(function(name) {
forEach(elements, (element) => {
element.removeEventListener(name, cb);
});
});
// Keep the chaining going
return this;
}
......
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