Commit 22381e3f authored by Eric Eastwood's avatar Eric Eastwood

Update cosmetic default. Add popout action - v0.2.1

parent b8bc8a21
# v0.2.1 - 2015-8-20
- Update basic example(cosmetic)
- Add `popout` action button that opens the room in a new tab.
- Update snippets for setting options on the `window` object so it properly deep sets properties
- Proper immutable default options property
# v0.2.0 - 2015-8-16
- Now using `window.___gitter.chat.options` instead of `window.___gitterEmbedConfig`
......
......@@ -2,7 +2,7 @@
Gitter embed widget
# Latest version: 0.2.0
# Latest version: 0.2.1
### [Changelog](https://github.com/gitterHQ/sidecar/blob/master/CHANGELOG.md)
......@@ -25,8 +25,8 @@ Set options with the global window option:
```html
<script>
window.___gitter.chat.options = {
container: '.sidebar'
((window.___gitter = {}).chat = {}).options = {
room: 'gitterHQ/gitter'
};
</script>
```
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -70,7 +70,7 @@
<aside class="sidebar"></aside>
<script>
window.___gitter.chat.options = {
((window.___gitter = {}).chat = {}).options = {
container: '.sidebar',
showChatByDefault: false
};
......
......@@ -31,7 +31,7 @@
body {
overflow: hidden;
font-family: Arial, sans-serif;
}
......@@ -45,6 +45,11 @@
</head>
<body>
<iframe src="http://marionettejs.com/"></iframe>
<script>
((window.___gitter = {}).chat = {}).options = {
room: 'marionettejs/backbone.marionette'
};
</script>
<script src="../../dist/sidecar.js" async defer></script>
</body>
</html>
......
......@@ -70,7 +70,7 @@
<aside class="sidebar"></aside>
<script>
window.___gitter.chat.options = {
((window.___gitter = {}).chat = {}).options = {
container: '.sidebar'
};
</script>
......
{
"name": "gitter-sidecar",
"version": "0.2.0",
"version": "0.2.1",
"description": "",
"main": "index.js",
"scripts": {
......
......@@ -4,6 +4,18 @@
box-sizing: border-box;
}
.gitter-hidden {
display: none;
}
.gitter-icon {
width: 24px;
height: 24px;
fill: currentColor;
}
.gitter-chat-embed {
z-index: 100;
position: fixed;
......@@ -15,10 +27,13 @@
display: flex;
flex-direction: row;
border-left: 1px solid #333;
box-shadow: -12px 0 18px 0 rgba(50, 50, 50, 0.3);
transition: transform 0.3s cubic-bezier(0.16, 0.22, 0.22, 1.7);
&.is-collapsed {
transform: translateX(100%);
transform: translateX(110%);
}
& > iframe {
......@@ -31,13 +46,19 @@
}
.gitter-chat-embed-action-bar {
position: absolute;
top: 0;
right: 0;
display: flex;
flex-direction: column;
width: 40px;
color: #3a3133;
color: rgba(58, 49, 51, 0.65);
background: #383435;
&:hover {
color: rgba(58, 49, 51, 1);
}
}
.gitter-chat-embed-action-bar-item {
......@@ -50,11 +71,12 @@
width: 100%;
background: 0;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #f2f0ed;
color: rgba(255, 255, 255, 0.9);
color: inherit;
font-size: 48px;
font-weight: bold;
......@@ -63,14 +85,19 @@
transition: all 0.2s ease;
&:hover,
&:focus {
background-color: rgba(255, 255, 255, 0.2);
outline: none;
box-shadow: inset 0 32px 32px -32px rgba(0, 0, 0, 0.25);
}
color: #b2ebda;
&:active {
box-shadow: inset 0 32px 72px -32px rgba(0, 0, 0, 0.25);
color: #f68d42;
}
}
......
var $ = document.querySelectorAll.bind(document);
let $ = document.querySelectorAll.bind(document);
Node.prototype.on = window.on = function(names, fn) {
names.split(/\s/).forEach(function(name) {
this.addEventListener(name, fn);
}.bind(this));
// Keep the chaining going
return this;
};
HTMLCollection.prototype.__proto__ = Array.prototype;
NodeList.prototype.__proto__ = Array.prototype;
NodeList.prototype.on = NodeList.prototype.addEventListener = function(name, fn) {
this.forEach(function(elem, i) {
elem.on(name, fn);
});
// Keep the chaining going
return this;
};
......
......@@ -33,6 +33,28 @@ let coerceIntoElementsArray = function(stuff) {
};
// 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
let getDataOptionsFromElement = function(options, element) {
if(!element) {
return options;
}
let newOptions = {};
Object.keys(options).forEach((optionKey) => {
let attr = `data-${optionKey}`;
if(element.hasAttribute(attr)) {
let optionValue = element.getAttribute(attr);
newOptions[optionKey] = optionValue;
}
});
return objectAssign({}, options, newOptions);
};
// Helper method that detects whether an element was "activated"
// Accibility in mind: click, spacebar, enter
const spacebarKey = 32;
......@@ -58,6 +80,31 @@ let embedGitterStyles = function() {
return elementStore;
};
let gitterSvgSprites = `
<svg class="gitter-hidden">
<defs>
<symbol id="gitter-shape-times-circle" viewBox="0 0 1792 1792">
<path d="M1225 1079l-146 146q-10 10-23 10t-23-10l-137-137-137 137q-10 10-23 10t-23-10l-146-146q-10-10-10-23t10-23l137-137-137-137q-10-10-10-23t10-23l146-146q10-10 23-10t23 10l137 137 137-137q10-10 23-10t23 10l146 146q10 10 10 23t-10 23l-137 137 137 137q10 10 10 23t-10 23zm215-183q0-148-73-273t-198-198-273-73-273 73-198 198-73 273 73 273 198 198 273 73 273-73 198-198 73-273zm224 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</symbol>
<symbol id="gitter-shape-external-link" viewBox="0 0 1792 1792">
<path d="M1408 928v320q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-320q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10l-114-114q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z"/>
</symbol>
</defs>
</svg>`;
let embedGitterSvgSprites = function() {
let elementStore = new ElementStore();
let tempContainer = document.createElement('div');
tempContainer.insertAdjacentHTML('beforeend', gitterSvgSprites);
let body = $('body')[0];
tempContainer.children.forEach(function(child) {
body.appendChild(child);
elementStore.push(child);
});
return elementStore;
};
let embedGitterChat = function(opts) {
let elementStore = new ElementStore();
......@@ -71,10 +118,12 @@ let embedGitterChat = function(opts) {
})());
containers.forEach((container) => {
let containerOpts = getDataOptionsFromElement(opts, container);
let iframe = elementStore.createElement('iframe');
iframe.setAttribute('frameborder', '0');
iframe.src = 'https://gitter.im/gitterHQ/gitter/~embed';
//iframe.src = 'https://gitter.im/gitterHQ/gitter/~chat';
iframe.src = `https://gitter.im/${containerOpts.room}/~embed`;
//iframe.src = `https://gitter.im/${containerOpts.room}/~chat`;
container.appendChild(iframe);
});
......@@ -86,8 +135,44 @@ let embedGitterChat = function(opts) {
};
let defaults = {
room: 'gitterHQ/gitter',
// container: single or array of dom elements, or string selector to embed chat in
container: null,
// Whether to show the chat embed when the page loads
showChatByDefault: false,
// The button element used to activate when the chat gets shown on the page
// Can be a dom node or a promise that optionally resolves to a dom node
// Note: Only applies if `options.showChatByDefault` is `false`
activation: null,
// Whether to preload the gitter chat iframe.
// We preload the chat so there isn't any jank when the chat opens
preload: true,
// Whether to embed a `<style>` tag with some pre-made CSS
useStyles: true,
// TODO: implement layouts (see todo.md)
// - `fixed`
// - `off-canvas`
// - `flex-aside`
layout: 'fixed'
//showLeftMenu: false
};
Object.keys(defaults).forEach((key) => {
Object.defineProperty(defaults, key, {
value: defaults[key],
writable: false,
configurable: false
});
});
// Keep some stuff behind symbols so people "can't" access the private data
const OPTS = Symbol();
const DEFAULTS = Symbol();
const OPTIONS = Symbol();
const ELEMENTSTORE = Symbol();
const INIT = Symbol();
const CONTAINERS = Symbol();
......@@ -97,54 +182,33 @@ class chatEmbed {
constructor(options = {}) {
this[ELEMENTSTORE] = new ElementStore();
this[DEFAULTS] = defaults;
let defaults = {
room: 'gitterHQ/gitter',
// container: single or array of dom elements, or string selector to embed chat in
container: null,
// Whether to show the chat embed when the page loads
showChatByDefault: false,
// The button element used to activate when the chat gets shown on the page
// Can be a dom node or a promise that optionally resolves to a dom node
// Note: Only applies if `options.showChatByDefault` is `false`
activation: null,
// Whether to preload the gitter chat iframe.
// We preload the chat so there isn't any jank when the chat opens
preload: true,
// Whether to embed a `<style>` tag with some pre-made CSS
useStyles: true,
// TODO: implement layouts (see todo.md)
// - `fixed`
// - `off-canvas`
// - `flex-aside`
layout: 'fixed'
//showLeftMenu: false
};
// Coerce into array of dom elements on what they pass in
if(options.container) {
options.container = coerceIntoElementsArray(options.container);
}
this[OPTS] = objectAssign({}, defaults, options);
this[OPTIONS] = objectAssign({}, this[DEFAULTS], options);
this[INIT]();
}
[INIT]() {
let opts = this[OPTS];
let opts = this[OPTIONS];
if(opts.useStyles) {
this[ELEMENTSTORE] = this[ELEMENTSTORE].concat(embedGitterStyles());
this[ELEMENTSTORE] = this[ELEMENTSTORE].concat(embedGitterSvgSprites());
}
if(opts.preload) {
this.toggleChat(false);
}
if(opts.showChatByDefault) {
this.toggleChat(true);
}
......@@ -153,6 +217,7 @@ class chatEmbed {
.then((activationElement) => {
activationElement = coerceIntoElementsArray(activationElement || (() => {
let button = this[ELEMENTSTORE].createElement('button');
// We use the option for the room, not pertaining to a particular container if set
button.href = opts.room;
button.innerHTML = 'Open Chat';
button.classList.add('gitter-open-chat-button');
......@@ -184,7 +249,7 @@ class chatEmbed {
[EMBEDCHATONCE]() {
if(!this[CONTAINERS]) {
let embedResult = embedGitterChat(this[OPTS]);
let embedResult = embedGitterChat(this[OPTIONS]);
this[CONTAINERS] = embedResult.containers;
this[ELEMENTSTORE] = this[ELEMENTSTORE].concat(embedResult.elementStore);
......@@ -198,6 +263,7 @@ class chatEmbed {
let collapseActionElement = this[ELEMENTSTORE].createElement('button');
collapseActionElement.classList.add('gitter-chat-embed-action-bar-item');
collapseActionElement.setAttribute('aria-label', 'Collapse Gitter Chat');
collapseActionElement.innerHTML = '<svg class="gitter-icon"><use xlink:href="#gitter-shape-times-circle"></use></svg>';
elementOnActivate(collapseActionElement, (e) => {
// Hide the chat
this.toggleChat(false);
......@@ -207,11 +273,25 @@ class chatEmbed {
actionBar.appendChild(collapseActionElement);
let collapseActionContentElement = this[ELEMENTSTORE].createElement('div');
collapseActionContentElement.classList.add('gitter-chat-embed-action-bar-item-content');
collapseActionContentElement.innerHTML = '-';
let popOutActionElement = this[ELEMENTSTORE].createElement('button');
popOutActionElement.classList.add('gitter-chat-embed-action-bar-item');
popOutActionElement.setAttribute('aria-label', 'Collapse Gitter Chat');
popOutActionElement.innerHTML = '<svg class="gitter-icon"><use xlink:href="#gitter-shape-external-link"></use></svg>';
elementOnActivate(popOutActionElement, (e) => {
// Hide the chat
this.toggleChat(false);
// Open in new tab
let win = window.open(`https://gitter.im/${this[OPTIONS].room}`, '_blank');
win.focus();
e.preventDefault();
});
actionBar.appendChild(popOutActionElement);
collapseActionElement.appendChild(collapseActionContentElement);
});
}
......
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