This commit is contained in:
chrosey
2017-09-13 07:52:34 +02:00
parent a1f16c37f4
commit 2340b0226b
24621 changed files with 2912161 additions and 149 deletions
+43
View File
@@ -0,0 +1,43 @@
## Why You Should Use Tether
Virtually every app includes some sort of overlay attached to an element on the page.
Things like [tooltips](http://github.hubspot.com/tooltip/docs/welcome),
[dropdowns](http://github.hubspot.com/select/docs/welcome), [hover-activated info boxes](http://github.hubspot.com/drop/docs/welcome), etc.
Those elements need to be attached to something on the page. Actually placing them next to
the element in the DOM causes problems though, if any parent element is anything
but `overflow: visible`, the element gets cut off. So you need absolute positioning
in the body.
Some of the time absolute positioning is right, but what about if the thing we're
attached to is fixed to the center of the screen? We'll have to move it every
time the user scrolls. What about if the element is in a scrollable container,
if the overlay is inside of it (so no clipping), it would be cool if the code
were smart enough to move it inside when that area is scrolled. That way we
need to reposition it even less.
It would also be nice if the code could somehow figure out whether positioning it
from the top, bottom, left, or right would result in the fewest repositionings
as the user scrolls or resizes.
Most of the time you're building these elements it would be nice for the element to
flip to the other side of the element if it hits the edge of the screen, or a scrollable
container it might be in. It would be nice if we could confine the element
to within some area, or even hide it when it leaves.
It would be nice for the element to be repositioned with CSS transforms
rather than top and left when possible, to allow the positioning to be done entirely
in the GPU.
Now that the positioning is so fancy, you're going to use it for more and more
elements. It would be cool if the library could optimize all of their repositioning
into a single repaint.
All of that is baked into Tether.
### tl;dr
- Optimized GPU-accelerated repositioning for 60fps scrolling
- Reliable positioning on any possible corner, edge or point in between.
- Support for repositioning or pinning the element when it would be offscreen
- Designed to be embeddable in other libraries
+46
View File
@@ -0,0 +1,46 @@
Repositioning
-----
Tethers will be automatically repositioned when the page is resized, and when any element containing the Tether is scrolled.
If the element moves for some other reason (e.g. with JavaScript), Tether won't know to reposition the element.
#### Manually Repositioning
The simplest way to reposition every Tether on the page is to call `Tether.position()`. It will efficiently reposition every
Tether in a single repaint, making it more efficient than manually repositioning many Tethers individually.
```javascript
Tether.position()
```
#### Repositioning a Single Tether
If you have many Tethers on screen, it may be more efficient to just reposition the tether that needs it. You can do this
by calling the `.position` method on the Tether instance:
```javascript
tether = new Tether({ ... })
// Later:
tether.position()
```
#### Tethering Hidden Elements
If you are creating a tether involving elements which are `display: none`, or not actually in the DOM,
your Tether may not be able to position itself properly. One way around this is to
ensure that a position call happens after all layouts have finished:
```javascript
myElement.style.display = 'block'
tether = new Tether({ ... })
setTimeout(function(){
tether.position();
})
```
In general however, you shouldn't have any trouble if both the element and the target are visible and in the DOM when you
create the Tether. If that is not the case, create the Tether disabled (option `enabled`: `false`), and enable it when
the elements are ready.
+47
View File
@@ -0,0 +1,47 @@
Why we don't support IE 8
-------------------------
We've been living in 2007 for a while now, pretending that new browser features don't
exist because they aren't in IE8. You might not even know about some of these features,
or think they are only enabled by jQuery or underscore, simply because it hasn't
been an option to rely upon them.
Here is the list of features you don't have if you choose to support IE 8:
- HTML5 audio and video
- SVG
- Canvas
- TrueType fonts
- Media Queries
- CSS Transforms
- Multiple Backgrounds
- CSS3 Units (vh, vw, rem)
- Custom DOM events
- Hardware accelerated graphics
- The DOMContentLoaded event
- addEventListener
- Object.create, .seal, .freeze, .defineProperty
- Array.isArray, .indexOf, .every, .some, .forEach, .map, .filter, .reduce
- A modern JavaScript engine
- A real developer tools
- A consistent box model
- jQuery 2
- Google Apps
- Tether
It's true that IE 8 still holds a big chunk of the browsing population, but the reasons
why they can't update are dwindling. There are two big reasons for continuing IE 8 support.
#### Enterprises
Microsoft is dropping support for XP in April, organizations who want security updates will have to upgrade.
#### China uses XP
Chrome, Firefox and Opera all support XP. Nothing prevents users from upgrading, except the inertia of
organizations who still support IE 8.
#### The Future
We are skating towards where the puck will be, and we hope that as you decide to drop IE 8 support,
you choose to add Tether to the list of awesome things you can do.
+27
View File
@@ -0,0 +1,27 @@
### Examples
It's our goal to create a wide variety of example of how Tether
can be used. Here's what we have so far, please send a PR with
any examples you might create.
#### Beginner
- [simple](../../examples/simple): A simple example to get you started
- [out-of-bounds](../../examples/out-of-bounds): How to hide the element when it would
otherwise be offscreen
- [pin](../../examples/pin): How to pin the element so it never goes offscreen
- [enable-disable](../../examples/enable-disable): How to enable and disable the Tether
in JavaScript
#### Advanced
- [content-visible](../../examples/content-visible): Demonstrates using the `'visible'`
`targetModifier` to align an element with the visible portion of another.
- [dolls](../../examples/dolls): A performance test to show several dozen elements,
each tethered to the previous. Try dragging the top left tether.
- [element-scroll](../../examples/element-scroll): Demonstrates using the `'scroll-handle'`
`targetModifier` to align an element with the scrollbar of an element.
- [scroll](../../examples/scroll): Demonstrates using the `'scroll-handle'` `targetModifier`
to align an element with the body's scroll handle.
- [viewport](../../examples/viewport): Demonstrates aligning an element with the
viewport by using the `'visible'` `targetModifier` when tethered to the body.
+37
View File
@@ -0,0 +1,37 @@
## Projects Using Tether
Here at HubSpot we have built a bunch of libraries on top of Tether,
both because we wanted Tether-performance, and because we saw opportunities
to improve on what was available in the client-side ecosystem.
### [Select](http://github.hubspot.com/select/docs/welcome)
Select is a replacement for native browser select elements that is fully stylable.
### [Shepherd](http://github.hubspot.com/shepherd/docs/welcome)
Shepherd is a library for making tours of your app to help onboard users and show off
new features.
### [Tooltip](http://github.hubspot.com/tooltip/docs/welcome)
A simple, easy-to-use implementation of tooltips that works well.
### [Drop](http://github.hubspot.com/drop/docs/welcome)
Where Tether does general-purpose positioning, Drop assumes that you are interested
in making something which pops up next to something the user clicks or hovers on.
If you're building something that fits that pattern, Drop can make things a little easier.
### [React Datepicker](https://github.com/Hacker0x01/react-datepicker)
A simple and reusable datepicker component for React
### [ember-tether](https://github.com/yapplabs/ember-tether)
An Ember.js-friendly interface for tether.
### Your Project Here
If you have a cool open-source library built on Tether, PR this doc.
+9
View File
@@ -0,0 +1,9 @@
## Embedding Tether
Tether is designed to be embeddable in other libraries.
There is one thing you should think about doing to create an embedded Tether:
- Set the `classPrefix` of the tethers you create. That prefix will replace `'tether'` in
all of the classes. You can also disable classes you don't intend on using with the `classes`
option.
+54
View File
@@ -0,0 +1,54 @@
Extending Tether
-----
Tether has a module system which can be used to modify Tether's positioning, or just do something each time the Tether is moved.
Tether has an array called `Tether.modules`, push onto it to add a module:
```coffeescript
Tether.modules.push
position: ({top, left}) ->
top += 10
{top, left}
```
#### Position
Your position function can either return a new object with `top` and `left`, `null`/`undefined` to leave the coordinates unchanged, or
`false` to cancel the positioning.
The position function is passed an object with the following elements:
```javascript
{
left, // The element's new position, from the top left corner of the page
top,
targetAttachment, // The targetAttachment, with 'auto' resolved to an actual attachment
targetPos, // The coordinates of the target
attachment, // The attachment, as passed in the option
elementPos, // The coordinates of the element
offset, // The offset, after it's converted into pixels and the manual offset is added
targetOffset, // The attachment is converted into an offset and is included in these values
manualOffset, // The manual offset, in pixels
manualTargetOffset
}
```
It is called with the Tether instance as its context (`this`).
#### Initialize
Modules can also have an `initialize` function which will be called when a new tether is created. The initialize function
is also called with the Tether instance as its context.
```coffeescript
Tether.modules.push
initialize: ->
console.log "New Tether Created!", @
```
#### Examples
[Constraints](https://github.com/HubSpot/tether/blob/master/src/js/constraint.js) and [shift](https://github.com/HubSpot/tether/blob/master/src/js/shift.js) are both implemented as modules.
[Mark Attachment](https://github.com/HubSpot/tether/blob/master/src/js/markAttachment.js) is used by the docs.
+113
View File
@@ -0,0 +1,113 @@
{uniqueId} = Tether.Utils
SETUP_JS = """
yellowBox = $('.yellow-box', $output);
greenBox = $('.green-box', $output);
scrollBox = $('.scroll-box', $output);
"""
OUTPUT_HTML = (key) -> """
<div class="scroll-box">
<div class="scroll-content">
<div class="yellow-box" data-example="#{ key }"></div>
<div class="green-box" data-example="#{ key }"></div>
</div>
</div>
"""
tethers = {}
getOutput = ($block) ->
key = $block.data('example')
if key and typeof key is 'string'
return $("output[data-example='#{ key }']")
else
return $block.parents('pre').nextAll('output').first()
run = (key) ->
if typeof key is 'string'
$block = $("code[data-example='#{ key }']")
else
$block = key
key = $block.attr('data-example')
$output = getOutput $block
code = $block.text()
code = SETUP_JS + code
window.$output = $output
tethers[key] = eval code
setupBlock = ($block) ->
key = $block.data('example')
$output = getOutput $block
if not key
key = uniqueId()
$block.attr('data-example', key)
$output.attr('data-example', key)
$output.find('.tether-element').attr('data-example', key)
$output.html OUTPUT_HTML(key)
$scrollBox = $output.find('.scroll-box')
$scrollContent = $scrollBox.find('.scroll-content')
$scrollBox.scrollTop(parseInt($scrollContent.css('height')) / 2 - $scrollBox.height() / 2)
$scrollBox.scrollLeft(parseInt($scrollContent.css('width')) / 2 - $scrollBox.width() / 2)
setTimeout ->
$scrollBox.on 'scroll', ->
$output.addClass 'scrolled'
$scrollBox.css 'height', "#{ $block.parent().outerHeight() }px"
if not $output.attr('deactivated')?
run $block
$(document.body).on 'click', (e) ->
if $(e.target).is('output[deactivated]')
activate $(e.target)
false
else if $(e.target).is('output[activated]')
deactivate $(e.target)
false
activate = ($output) ->
$block = $output.prev().find('code')
run $block
$output.find('.tether-element').show()
key = $output.data('example')
$(tethers[key].element).show()
tethers[key].enable()
$output.removeAttr('deactivated')
$output.attr('activated', true)
deactivate = ($output) ->
$block = $output.prev().find('code')
key = $output.data('example')
tethers[key].disable()
$el = $(tethers[key].element)
$el.detach()
$output.find('.scroll-content').append $el
$el.hide()
$output.removeAttr('activated')
$output.attr('deactivated', true)
init = ->
$blocks = $('code[data-example]')
setupBlock($ block) for block in $blocks
window.EXECUTR_OPTIONS =
codeSelector: 'code[executable]'
$ init
+218
View File
@@ -0,0 +1,218 @@
@charset "UTF-8";
*, *:after, *:before {
box-sizing: border-box; }
body {
position: relative; }
.yellow-box {
width: 100px;
height: 100px;
background-color: #fe8;
pointer-events: none; }
.green-box {
margin-top: 65px;
margin-left: 100px;
width: 200px;
height: 50px;
background-color: #4e9; }
.no-green .green-box {
display: none; }
.scroll-box {
height: 150px;
border: 10px solid #eee;
background: #fbfbfb;
overflow: auto;
position: relative; }
.scroll-content {
height: 2000px;
width: 2000px;
padding: 910px 809px; }
pre.pre-with-output {
margin: 0;
width: 50%;
float: left; }
pre.pre-with-output code mark {
background: #b8daff;
color: #000; }
p, h2, h3 {
clear: both; }
output {
display: block;
position: relative;
width: 50%;
float: right;
margin-bottom: 15px; }
output.scroll-page .scroll-box {
overflow: hidden; }
output.scroll-page:after {
content: "↕ scroll the page ↕"; }
output:after {
content: "↕ scroll this area ↕";
position: absolute;
bottom: 25px;
width: 100%;
text-align: center;
font-size: 16px;
font-variant: small-caps;
color: #777;
opacity: 1;
-webkit-transition: opacity 0.2s;
transition: opacity 0.2s; }
output.scrolled:after {
opacity: 0; }
output[deactivated], output[activated] {
cursor: pointer; }
output[deactivated] .scroll-box, output[activated] .scroll-box {
pointer-events: none; }
output[deactivated]:after, output[activated]:after {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 1;
content: "Click To Show";
background-color: #AAA;
border-left: 10px solid #EEE;
color: white;
font-size: 24px;
font-variant: normal;
padding-top: 80px; }
output[activated]:after {
content: "Click To Hide"; }
output[activated].visible-enabled:after {
height: 35px;
padding-top: 5px; }
.attachment-mark, .tether-marker-dot {
position: relative; }
.attachment-mark:after, .tether-marker-dot:after {
content: "A";
width: 10px;
height: 10px;
background-color: red;
display: inline-block;
line-height: 10px;
font-size: 9px;
color: white;
text-align: center;
position: absolute; }
span.attachment-mark:after, span.tether-marker-dot:after {
position: relative;
top: -1px;
margin-right: 1px; }
.tether-marker-dot {
position: absolute; }
.tether-marker-dot:after {
top: -5px;
left: -5px; }
.tether-target-marker {
position: absolute; }
div.tether-target-attached-left .tether-target-marker {
left: 0; }
div.tether-target-attached-top .tether-target-marker {
top: 0; }
div.tether-target-attached-bottom .tether-target-marker {
bottom: 0; }
div.tether-target-attached-right .tether-target-marker {
right: 0; }
div.tether-target-attached-center .tether-target-marker {
left: 50%; }
.tether-element-marker {
position: absolute; }
div.tether-element-attached-left .tether-element-marker {
left: 0; }
div.tether-element-attached-top .tether-element-marker {
top: 0; }
div.tether-element-attached-bottom .tether-element-marker {
bottom: 0; }
div.tether-element-attached-right .tether-element-marker {
right: 0; }
div.tether-element-attached-center .tether-element-marker {
left: 50%; }
.tether-element-attached-middle .tether-element-marker {
top: 50px; }
.tether-target-attached-middle .tether-target-marker {
top: 25px; }
.tether-element {
position: relative; }
.tether-element.tether-pinned-left {
box-shadow: inset 2px 0 0 0 red; }
.tether-element.tether-pinned-right {
box-shadow: inset -2px 0 0 0 red; }
.tether-element.tether-pinned-top {
box-shadow: inset 0 2px 0 0 red; }
.tether-element.tether-pinned-bottom {
box-shadow: inset 0 -2px 0 0 red; }
.tether-target {
position: relative; }
.tether-element.tether-out-of-bounds[data-example="hide"] {
display: none; }
[data-example^="optimizer"].lang-javascript {
/* This should just be a `code` selector, but sass doesn't allow that with & */
min-height: 220px; }
[data-example^="optimizer"].tether-element:before {
margin-top: 26px;
display: block;
text-align: center;
content: "I'm in the body";
line-height: 1.2;
font-size: 15px;
padding: 4px;
color: #666; }
[data-example^="optimizer"] .scroll-box .tether-element:before {
content: "I'm in my scroll parent!"; }
.tether-element[data-example="scroll-visible"] {
height: 30px; }
.tether-element[data-example="scroll-visible"] .tether-marker-dot {
display: none; }
.hs-doc-content h2.projects-header {
text-align: center;
font-weight: 300; }
.projects-paragraph {
text-align: center; }
.projects-paragraph a {
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
*zoom: 1;
*display: inline;
text-align: center;
margin-right: 30px;
color: inherit; }
.projects-paragraph a span {
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
*zoom: 1;
*display: inline;
margin-bottom: 20px;
font-size: 20px;
color: inherit;
font-weight: 300; }
.projects-paragraph a img {
display: block;
max-width: 100%;
width: 100px; }
+591
View File
@@ -0,0 +1,591 @@
<script src="dist/js/tether.js"></script>
<script src="docs/js/markAttachment.js"></script>
<script src="docs/js/intro.js"></script>
<link rel="stylesheet" href="docs/css/intro.css"></link>
Tether
======
Tether is a JavaScript library for efficiently making an absolutely positioned
element stay next to another element on the page. For example, you might
want a tooltip or dialog to open, and remain, next to the relevant item
on the page.
Tether includes the ability to constrain the element within the viewport, its
scroll parent, any other element on the page, or a fixed bounding box. When it
exceeds those constraints it can be pinned to the edge, flip to the other
side of its target, or hide itself.
Tether optimizes its location placement to result in the minimum amount of
'jankyness' as the page is scrolled and resized. The page can maintain 60fps
scrolling even with dozens or hundreds of tethers on screen (pop open the
devtools timeline as you scroll this page).
Tether is 5kb minified and gzipped, and supports IE9+, and all modern
browsers.
<h2 class="projects-header">Projects Built With Tether</h2>
<p class="projects-paragraph">
<a href="http://github.hubspot.com/select/docs/welcome"><span>Select</span><img src="http://github.hubspot.com/os-icons/select-icon.png" /></a>
<a href="http://github.hubspot.com/drop/docs/welcome"><span>Drop</span><img src="http://github.hubspot.com/os-icons/drop-icon.png" /></a>
<a href="http://github.hubspot.com/tooltip/docs/welcome"><span>Tooltip</span><img src="http://github.hubspot.com/os-icons/tooltip-icon.png" /></a>
<a href="http://github.hubspot.com/shepherd/docs/welcome"><span>Shepherd</span><img src="http://github.hubspot.com/os-icons/shepherd-icon.png" /></a>
</p>
Usage
-----
The element to be moved is called the 'element'.
The element in the page it's to be attached to is called the 'target'.
To use Tether, you define a point on the target and a point on the element.
Tether moves the element to keep those two points on top of each other.
That point is called the attachment (we've marked it in the examples with
a red <span class="attachment-mark"></span>). For example, if you'd like
the element to sit on the left of the target:
<pre class="pre-with-output"><code class="lang-javascript" data-example='usage'>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top right',
targetAttachment: 'top left'
});
</code></pre><output data-example='usage'></output>
Attachment
----------
You can move the attachment points of both the element and the target.
For example, lets move the element's attachment:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: <mark>'bottom left'</mark>,
targetAttachment: 'top left'
});
</code></pre><output></output>
We can also change the target's attachment point:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'bottom left',
targetAttachment: <mark>'bottom right'</mark>
});
</code></pre><output></output>
There are two more attachment points we haven't seen yet, center and middle:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: <mark>'middle center'</mark>,
targetAttachment: <mark>'middle center'</mark>
});
</code></pre><output></output>
All told, Tether provides six built in attachment positions:
- left
- center
- right
- top
- middle
- bottom
The syntax of the attachment properties is: `"vertical-attachment horizontal-attachment"`.
You must always supply an `attachment`. If you don't supply a `target-attachment`, it is
assumed to be the mirror image of `attachment`.
### Offset
The six attachment points we provide are not always enough to place the element
exactly where you want it. To correct this, we provide two more properties,
`offset` and `targetOffset`.
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top right',
targetAttachment: 'top left',
<mark>offset: '0 10px'</mark>
});
</code></pre><output></output>
As you can see, we've moved the attachment point of the element 10px to the right.
We can also move the attachment point of the target:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top right',
targetAttachment: 'top left',
offset: '0 10px',
<mark>targetOffset: '20px 0'</mark>
});
</code></pre><output></output>
The offset properties also accept percentages. Percentages in `offset` refer to
the height and width of the element, `targetOffset` the height and width of
the target.
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top right',
targetAttachment: 'top left',
targetOffset: <mark>'0 75%'</mark>
});
</code></pre><output></output>
The syntax of the offset properties is `"vertical-offset horizontal-offset"`
Tether offers a couple of special attachments, using the `targetModifier`
option:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: scrollBox,
attachment: 'middle right',
targetAttachment: 'middle left',
targetModifier: 'scroll-handle'
});
</code></pre><output></output>
Set the target to `document.body` to have the element follow the page's scroll bar.
The `targetModifier` `visible` can be used to attach an element to the visible part
of an element:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: document.body,
attachment: 'middle center',
targetAttachment: 'middle center',
<mark>targetModifier: 'visible'</mark>
});
</code></pre><output deactivated></output>
<pre class="pre-with-output"><code class="lang-javascript" data-example="scroll-visible">new Tether({
element: yellowBox,
<mark>target: scrollBox</mark>,
attachment: 'middle center',
targetAttachment: 'middle center',
targetModifier: 'visible'
});
</code></pre><output class="no-green scroll-page" data-example="scroll-visible"></output>
Constraints
-----------
If you have tried any of the previous examples, you'll notice that it's pretty
easy to scroll the regions in such a way that the element is hanging out on
its own, with no target in sight.
Constraints allow you to control what happens when the tethered element would
have to fall outside of a defined region to maintain the attachment.
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'middle left',
targetAttachment: 'middle left',
<mark>constraints</mark>: [
{
to: 'scrollParent',
pin: true
}
]
});
</code></pre><output></output>
We've created a constraint which will keep the element within its scroll
parent by 'pinning' it to the edges if it tries to escape. For the sake
of the example, we're also highlighting the pinned edge in red.
Specify an array of sides if you'd only like to pin those edges:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'middle left',
targetAttachment: 'middle left',
constraints: [
{
to: 'scrollParent',
pin: <mark>['top']</mark>
}
]
});
</code></pre><output></output>
You might want to allow the element to change its attachment, if doing so
would keep more of it within its assigned region:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
constraints: [
{
to: 'scrollParent',
<mark>attachment: 'together'</mark>
}
]
});
</code></pre><output></output>
If you scroll the example a bit, you'll see it flip the attachment when necessary.
You can combine `pin` and `attachment` as well:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
constraints: [
{
to: 'scrollParent',
attachment: 'together',
<mark>pin: true</mark>
}
]
});
</code></pre><output></output>
Attachment will accept any of these values:
- `element`: Only change the element's attachment
- `target`: Only change the target's attachment
- `both`: Change either's attachment (or both), as needed
- `together`: Change both the element's and target's attachment at the same time (to
'flip' the element to the other side of the attachment)
- `none`: Don't allow changes to attachment (the default)
Together is the option you will use most commonly:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top right',
targetAttachment: 'bottom left',
constraints: [
{
to: 'scrollParent',
attachment: <mark>'together'</mark>
}
]
});
</code></pre><output></output>
You can also provide different settings for the vertical and horizontal attachments:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
constraints: [
{
to: 'scrollParent',
attachment: <mark>'together none'</mark>
}
]
});
</code></pre><output></output>
Whenever the element is out of the constrained area, we add the `tether-out-of-bounds`
class to it. If you add some CSS to make items with that class `display: none`, the
tether will hide.
<pre class="pre-with-output"><code class="lang-javascript" data-example="hide">new Tether({
element: yellowBox,
target: greenBox,
attachment: 'middle center',
targetAttachment: 'middle center',
constraints: [
{
to: 'scrollParent'
}
]
});
</code></pre><output data-example="hide"></output>
You can also constrain the element to the viewport, you'll have to scroll the
page to see this one.
<pre class="pre-with-output"><code class="lang-javascript" data-example="window">new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
constraints: [
{
to: <mark>'window'</mark>,
attachment: 'together'
}
]
});
</code></pre><output data-example="window" class="scroll-page"></output>
You can, of course, use pin with the window as well to
make it always visible no matter where the user scrolls:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
constraints: [
{
to: 'window',
attachment: 'together',
<mark>pin: true</mark>
}
]
});
</code></pre><output deactivated class="scroll-page visible-enabled"></output>
`to` can be any of:
- `'scrollParent'`
- `'window'`
- any DOM element
- an array of bound points relative to the body `[X1, Y1, X2, Y2]`
You can also provide multiple constraints, keeping in mind that they are
processed in the order supplied (the last one always has the final word).
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
constraints: [
{
to: <mark>'scrollParent'</mark>,
pin: true
},
{
to: <mark>'window'</mark>,
attachment: 'together'
}
]
});
</code></pre><output></output>
Optimization
------------
### Element Moving
The goal of Tether's optimizer is to not have to change the positioning
CSS as the page is scrolled or resized. To accomplish this it looks at the
last few positions, finds commonalities, and uses them to decide whether to
position the element absolutely or with fixed positioning.
If the element is fully contained within its scroll parent, its DOM node
can also be moved inside the scroll parent, to avoid repaints as the
container is scrolled.
<pre class="pre-with-output"><code class="lang-javascript" data-example="optimizer">new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left'
});
</code></pre><output data-example="optimizer"></output>
We are moving where the DOM node is, so if you have CSS which styles elements
within the offset parent, you may see some rendering changes. Also note
that this optimization works best if the scroll parent is the offset parent.
In other words, **the scroll parent should be made position relative, fixed or
absolute to enable this optimization.**
If you do see stylistic changes occur when the element is moved,
you might want to disable this optimization. You can do that by
setting `optimizations.moveElement` to false.
<pre class="pre-with-output"><code class="lang-javascript" data-example="optimizer2">new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
targetAttachment: 'bottom left',
optimizations: {
<mark>moveElement: false</mark>
}
});
</code></pre><output data-example="optimizer2"></output>
### GPU
By default tether positions elements using CSS transforms. These transforms allow the
tethered element to be moved as its own layer to not force a repaint of the underlying
page.
This method of positioning can cause some issues however, including color shifts and artifacts.
If you experience these issues, you can disable this optimization by setting `optimizations.gpu`
to false:
<pre class="pre-with-output"><code class="lang-javascript" data-example>new Tether({
element: yellowBox,
target: greenBox,
attachment: 'top left',
optimizations: {
<mark>gpu: false</mark>
}
});
</code></pre><output></output>
Methods
-------
The `Tether` constructor we've been using in these examples returns us a
`Tether` object.
The `Tether` object has these methods:
- `setOptions({ options })` - Update any of the options (such as attachment)
- `disable()` - Disable the tethering
- `enable()` - Enable the tethering
- `destroy()` - Disable and remove all references
- `position()` - Manually trigger a repositioning
Options
-------
The full list of options which can be passed to the `Tether` constructor and
`setOptions`:
- `element`: The DOM element, jQuery element, or a selector string of an element which will be moved
- `target`: The DOM element, jQuery element, or a selector string of an element which the `element` will be attached to
- `attachment`: A string of the form `'vert-attachment horiz-attachment'`
- `vert-attachment` can be any of `'top'`, `'middle'`, `'bottom'`
- `horiz-attachment` can be any of `'left'`, `'center'`, `'right'`
- `targetAttachment`: A string similar to `attachment`.
The one difference is that, if it's not provided, targetAttachment will assume the mirror
image of `attachment`.
- `offset`: A string of the form `'vert-offset horiz-offset'`
- `vert-offset` and `horiz-offset` can be of the form `"20px"` or `"55%"`
- `targetOffset`: A string similar to `offset`, but refering to the offset of the target
- `targetModifier`: Can be set to `'visible'` or `'scroll-handle'`
- `enabled`: Should the tether be enabled initially? Defaults to `true`.
- `classes`: A hash of classes which should be changed or disabled
- `classPrefix`: The prefix placed at the beginning of the default classes, defaults to `'tether'`
- `optimizations`: A hash of optimizations, used to disable them
- `constraints`: An array of constraint definition objects. Each definition is of the form:
- `to`: A DOM element, bounding box, the string `'window'`, or the string `'scrollParent'`
- `pin`: `true` or an array of strings representing the sides of the constraint
- `attachment`: A string of the form `"vert-modifier horiz-modifier"`, or a single value
representing both
- Each modifier should be one of `"none"`, `"together"`, `"element"`, `"target"`, or `"both"`.
- `outOfBoundsClass`: An alternative to `"tether-out-of-bounds"`, useful if the class
needs to be differentiated from that of another constraint.
- `pinnedClass`: An alternative to `"tether-pinned"`, similar to `outOfBoundsClass`.
Classes
-------
Tether adds a variety of classes to the element and target to allow you to style
them based on their tethering.
You can change the prefix of the classes with the `classPrefix` option. It is `'tether'` by
default, but you could, for example, change it to be `'bill'` if you were building the bill
library and all the classes would be `'bill-*'`.
```javascript
new Tether({
classPrefix: 'bill'
});
```
The sass/css is similarily configurable, see
[tooltip](https://github.com/HubSpot/tooltip/blob/master/sass/tooltip-theme-arrows.sass#L14) for
an example of how to make your own prefixed css file.
All classes can be changed or disabled with the `classes` option. For example, to change the
`tether-element` class to be `my-box`:
```javascript
new Tether({
classes: {
element: 'my-box'
}
});
```
You can also disable classes you're not going to use:
```javascript
new Tether({
classes: {
out-of-bounds: false
}
});
```
- `tether-element` is added to the element
- `tether-target` is added to the target
- `tether-enabled` is added to both elements when tether is not disabled
- `tether-element-attached-[left,right,top,bottom,middle,center]` is added to both
elements based on the elements attachment, if the element becomes detached (for
example, if it's pinned), that class is removed. The class reflects how the
element is actually attached, so if a constraint changes the attachment, that
change will be reflected in the class.
- `tether-target-attached-[left,right,top,bottom,middle,center]` is added to both
elements based on the target's attachment. All of the characteristics are the
same as for element-attached.
### Constraint-related Classes
- `tether-out-of-bounds`, `tether-out-of-bounds-[side]` are added to both the element and the target
when the element is placed outside of its constraint.
- `tether-pinned`, `tether-pinned-[side]` are added to both the element and target when a constraint
has pinned the element to the [side] of the container.
Browser Support
---------------
Tether supports IE9+, and all modern browsers.
Google doesn't support IE8, Microsoft is dropping support in a few months, and not supporting it saves
us a whole lot of trouble. If you are interested in adding support, get in touch, we're happy to accept
a PR.
Contributing
------------
Please contribute! Tether is developed in Coffeescript, but if that's problematic for you, feel free
to submit pull requests which just change the JavaScript files, we can adapt them as needed.
To build Tether, you need:
- Node.js
#### Instructions
- Install the build tool
```bash
npm install -g gulp
```
- Install the project
```bash
# In the project directory
npm install
```
- Build / Watch
```bash
gulp
```
+117
View File
@@ -0,0 +1,117 @@
(function() {
var OUTPUT_HTML, SETUP_JS, activate, deactivate, getOutput, init, run, setupBlock, tethers, uniqueId;
uniqueId = Tether.Utils.uniqueId;
SETUP_JS = "yellowBox = $('.yellow-box', $output);\ngreenBox = $('.green-box', $output);\nscrollBox = $('.scroll-box', $output);";
OUTPUT_HTML = function(key) {
return "<div class=\"scroll-box\">\n <div class=\"scroll-content\">\n <div class=\"yellow-box\" data-example=\"" + key + "\"></div>\n <div class=\"green-box\" data-example=\"" + key + "\"></div>\n </div>\n</div>";
};
tethers = {};
getOutput = function($block) {
var key;
key = $block.data('example');
if (key && typeof key === 'string') {
return $("output[data-example='" + key + "']");
} else {
return $block.parents('pre').nextAll('output').first();
}
};
run = function(key) {
var $block, $output, code;
if (typeof key === 'string') {
$block = $("code[data-example='" + key + "']");
} else {
$block = key;
}
key = $block.attr('data-example');
$output = getOutput($block);
code = $block.text();
code = SETUP_JS + code;
window.$output = $output;
return tethers[key] = eval(code);
};
setupBlock = function($block) {
var $output, $scrollBox, $scrollContent, key;
key = $block.data('example');
$output = getOutput($block);
if (!key) {
key = uniqueId();
$block.attr('data-example', key);
$output.attr('data-example', key);
$output.find('.tether-element').attr('data-example', key);
}
$output.html(OUTPUT_HTML(key));
$scrollBox = $output.find('.scroll-box');
$scrollContent = $scrollBox.find('.scroll-content');
$scrollBox.scrollTop(parseInt($scrollContent.css('height')) / 2 - $scrollBox.height() / 2);
$scrollBox.scrollLeft(parseInt($scrollContent.css('width')) / 2 - $scrollBox.width() / 2);
setTimeout(function() {
return $scrollBox.on('scroll', function() {
return $output.addClass('scrolled');
});
});
$scrollBox.css('height', "" + ($block.parent().outerHeight()) + "px");
if ($output.attr('deactivated') == null) {
return run($block);
}
};
$(document.body).on('click', function(e) {
if ($(e.target).is('output[deactivated]')) {
activate($(e.target));
return false;
} else if ($(e.target).is('output[activated]')) {
deactivate($(e.target));
return false;
}
});
activate = function($output) {
var $block, key;
$block = $output.prev().find('code');
run($block);
$output.find('.tether-element').show();
key = $output.data('example');
$(tethers[key].element).show();
tethers[key].enable();
$output.removeAttr('deactivated');
return $output.attr('activated', true);
};
deactivate = function($output) {
var $block, $el, key;
$block = $output.prev().find('code');
key = $output.data('example');
tethers[key].disable();
$el = $(tethers[key].element);
$el.detach();
$output.find('.scroll-content').append($el);
$el.hide();
$output.removeAttr('activated');
return $output.attr('deactivated', true);
};
init = function() {
var $blocks, block, _i, _len, _results;
$blocks = $('code[data-example]');
_results = [];
for (_i = 0, _len = $blocks.length; _i < _len; _i++) {
block = $blocks[_i];
_results.push(setupBlock($(block)));
}
return _results;
};
window.EXECUTR_OPTIONS = {
codeSelector: 'code[executable]'
};
$(init);
}).call(this);
+51
View File
@@ -0,0 +1,51 @@
/* globals Tether */
'use strict';
Tether.modules.push({
initialize: function initialize() {
var _this = this;
this.markers = {};
['target', 'element'].forEach(function (type) {
var el = document.createElement('div');
el.className = _this.getClass('' + type + '-marker');
var dot = document.createElement('div');
dot.className = _this.getClass('marker-dot');
el.appendChild(dot);
_this[type].appendChild(el);
_this.markers[type] = { dot: dot, el: el };
});
},
position: function position(_ref) {
var manualOffset = _ref.manualOffset;
var manualTargetOffset = _ref.manualTargetOffset;
var offsets = {
element: manualOffset,
target: manualTargetOffset
};
for (var type in offsets) {
var offset = offsets[type];
for (var side in offset) {
var val = offset[side];
var notString = typeof val !== 'string';
if (notString || val.indexOf('%') === -1 && val.indexOf('px') === -1) {
val += 'px';
}
if (this.markers[type].dot.style[side] !== val) {
this.markers[type].dot.style[side] = val;
}
}
}
return true;
}
});
+233
View File
@@ -0,0 +1,233 @@
$scrollableArea: 2000px
$exampleWidth: 400px
$exampleHeight: 180px
@mixin inline-block
display: inline-block
vertical-align: middle
*vertical-align: auto
*zoom: 1
*display: inline
*, *:after, *:before
box-sizing: border-box
body
position: relative
.yellow-box
width: 100px
height: 100px
background-color: #fe8
pointer-events: none
.green-box
margin-top: ($exampleHeight - 50px) / 2
margin-left: ($exampleWidth - 200px) / 2
width: 200px
height: 50px
background-color: #4e9
.no-green &
display: none
.scroll-box
height: 150px
border: 10px solid #eee
background: #fbfbfb
overflow: auto
position: relative
.scroll-content
height: $scrollableArea
width: $scrollableArea
padding: ($scrollableArea - $exampleHeight)/2 ($scrollableArea - $exampleWidth)/2 + 9
pre.pre-with-output
margin: 0
width: 50%
float: left
code mark
background: #b8daff
color: #000
p, h2, h3
clear: both
output
display: block
position: relative
width: 50%
float: right
margin-bottom: 15px
&.scroll-page
.scroll-box
overflow: hidden
&:after
content: "↕ scroll the page ↕"
&:after
content: "↕ scroll this area ↕"
position: absolute
bottom: 25px
width: 100%
text-align: center
font-size: 16px
font-variant: small-caps
color: #777
opacity: 1
transition: opacity 0.2s
&.scrolled:after
opacity: 0
&[deactivated], &[activated]
.scroll-box
pointer-events: none
cursor: pointer
&:after
position: absolute
top: 0
left: 0
right: 0
bottom: 0
opacity: 1
content: "Click To Show"
background-color: #AAA
border-left: 10px solid #EEE
color: white
font-size: 24px
font-variant: normal
padding-top: 80px
&[activated]
&:after
content: "Click To Hide"
&.visible-enabled
&:after
height: 35px
padding-top: 5px
.attachment-mark
position: relative
&:after
content: "A"
width: 10px
height: 10px
background-color: red
display: inline-block
line-height: 10px
font-size: 9px
color: white
text-align: center
position: absolute
span.attachment-mark
&:after
position: relative
top: -1px
margin-right: 1px
.tether-marker-dot
@extend .attachment-mark
position: absolute
&:after
top: -5px
left: -5px
@each $type in target, element
.tether-#{ $type }-marker
position: absolute
@each $side in left, top, bottom, right
div.tether-#{ $type }-attached-#{ $side } &
#{ $side }: 0
div.tether-#{ $type }-attached-center &
left: 50%
.tether-element-attached-middle .tether-element-marker
top: 50px
.tether-target-attached-middle .tether-target-marker
top: 25px
.tether-element
position: relative
&.tether-pinned-left
box-shadow: inset 2px 0 0 0 red
&.tether-pinned-right
box-shadow: inset -2px 0 0 0 red
&.tether-pinned-top
box-shadow: inset 0 2px 0 0 red
&.tether-pinned-bottom
box-shadow: inset 0 -2px 0 0 red
.tether-target
position: relative
.tether-element.tether-out-of-bounds[data-example="hide"]
display: none
[data-example^="optimizer"]
&.lang-javascript
/* This should just be a `code` selector, but sass doesn't allow that with & */
min-height: 220px
&.tether-element
&:before
margin-top: 26px
display: block
text-align: center
content: "I'm in the body"
line-height: 1.2
font-size: 15px
padding: 4px
color: #666
.scroll-box .tether-element:before
content: "I'm in my scroll parent!"
.tether-element[data-example="scroll-visible"]
height: 30px
.tether-marker-dot
display: none
.hs-doc-content h2.projects-header
text-align: center
font-weight: 300
.projects-paragraph
text-align: center
a
+inline-block
text-align: center
margin-right: 30px
color: inherit
span
+inline-block
margin-bottom: 20px
font-size: 20px
color: inherit
font-weight: 300
img
display: block
max-width: 100%
width: 100px
+76
View File
@@ -0,0 +1,76 @@
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Drop Browser Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="icon" href="http://static.hubspot.com/favicon.ico">
<script type="text/javascript" src="//use.typekit.net/ghy0wve.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
<!-- Drop themes -->
<link rel="stylesheet" href="//github.hubspot.com/tether/dist/css/tether-theme-arrows-dark.css" />
<!-- Browser demo styles -->
<link rel="stylesheet" href="//github.hubspot.com/tether/docs/welcome/css/browser-demo.css" />
</head>
<body>
<div class="browser-demo">
<div class="top"><div class="title"></div></div>
<div class="bottom">
<div class="left">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="right">
<div class="title"></div>
<p>
<div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div>
</p>
<p>
<div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div><div class="word"></div>
</p>
</div>
</div>
</div>
<!-- Tether javascript -->
<script src="//github.hubspot.com/tether/dist/js/tether.min.js"></script>
<!-- Welcome docs javascript -->
<script src="//github.hubspot.com/tether/docs/welcome/js/log.js"></script>
<script src="//github.hubspot.com/tether/docs/welcome/js/jquery.js"></script>
<script src="//github.hubspot.com/tether/docs/welcome/js/drop.js"></script>
</body>
</html>
+212
View File
@@ -0,0 +1,212 @@
_Drop = Drop.createContext classPrefix: 'tether'
isMobile = $(window).width() < 567
init = ->
setupHero()
setupBrowserDemo()
setupHero = ->
$target = $('.tether-target-demo')
positions = [
'top left'
'left top'
'left middle'
'left bottom'
'bottom left'
'bottom center'
'bottom right'
'right bottom'
'right middle'
'right top'
'top right'
'top center'
]
if isMobile
positions = [
'top left'
'bottom left'
'bottom right'
'top right'
]
window.drops = {}
for position in positions
drops[position] = new _Drop
target: $target[0]
classes: 'tether-theme-arrows-dark'
position: position
constrainToWindow: false
openOn: ''
content: '<div style="height: 50px; width: 50px"></div>'
openIndex = 0
frames = 0
frameLengthMS = 10
openAllDrops = ->
for position, drop of drops
drop.open()
openNextDrop = ->
for position, drop of drops
drop.close()
drops[positions[openIndex]].open()
drops[positions[(openIndex + 6) % positions.length]].open()
openIndex = (openIndex + 1) % positions.length
if frames > 5
finalDropState()
return
frames += 1
setTimeout openNextDrop, frameLengthMS * frames
finalDropState = ->
$(drops['top left'].dropContent).html('Marrying DOM elements for life.')
$(drops['bottom right'].dropContent).html('<a class="button" href="http://github.com/HubSpot/tether">★ On Github</a>')
drops['top left'].open()
drops['bottom right'].open()
if true or isMobile
drops['top left'].open()
drops['top left'].tether.position()
drops['bottom right'].open()
drops['bottom right'].tether.position()
finalDropState()
else
openNextDrop()
setupBrowserDemo = ->
$browserDemo = $('.browser-demo.showcase')
$startPoint = $('.browser-demo-start-point')
$stopPoint = $('.browser-demo-stop-point')
$iframe = $('.browser-window iframe')
$browserContents = $('.browser-content .browser-demo-inner')
$sections = $('.browser-demo-section')
$('body').append """
<style>
table.showcase.browser-demo.fixed-bottom {
top: #{ $sections.length }00%
}
</style>
"""
$(window).scroll ->
scrollTop = $(window).scrollTop()
if $startPoint.position().top < scrollTop and scrollTop + window.innerHeight < $stopPoint.position().top
$browserDemo.removeClass('fixed-bottom')
$browserDemo.addClass('fixed')
$sections.each ->
$section = $ @
if $section.position().top < scrollTop < $section.position().top + $section.outerHeight()
setSection $section.data('section')
return true
else
$browserDemo.removeAttr('data-section')
$browserDemo.removeClass('fixed')
if scrollTop + window.innerHeight > $stopPoint.position().top
$browserDemo.addClass('fixed-bottom')
else
$browserDemo.removeClass('fixed-bottom')
$iframe.load ->
iframeWindow = $iframe[0].contentWindow
$items = $iframe.contents().find('.item')
$items.each (i) ->
$item = $(@)
_iframeWindowDrop = iframeWindow.Drop.createContext classPrefix: 'tether'
drop = new _iframeWindowDrop
target: $item[0]
classes: 'tether-theme-arrows-dark'
position: 'right top'
constrainToWindow: true
openOn: 'click'
content: '''
<ul>
<li>Action&nbsp;1</li>
<li>Action&nbsp;2</li>
<li>Action&nbsp;3</li>
</ul>
'''
$item.data('drop', drop)
scrollInterval = undefined
scrollTop = 0
scrollTopDirection = 1
setSection = (section) ->
$browserDemo.attr('data-section', section)
$('.section-copy').removeClass('active')
$(""".section-copy[data-section="#{ section }"]""").addClass('active')
openExampleItem = ->
if isMobile
$iframe.contents().find('.item:first').data().drop.open()
else
$iframe.contents().find('.item:eq(2)').data().drop.open()
closeAllItems = ->
$iframe.contents().find('.item').each -> $(@).data().drop.close() or true
scrollLeftSection = ->
scrollInterval = setInterval ->
$iframe.contents().find('.left').scrollTop scrollTop
scrollTop += scrollTopDirection
if scrollTop > 50
scrollTopDirection = -1
if scrollTop < 0
scrollTopDirection = 1
, 30
stopScrollingLeftSection = ->
clearInterval scrollInterval
switch section
when 'what'
closeAllItems()
openExampleItem()
stopScrollingLeftSection()
when 'how'
closeAllItems()
openExampleItem()
stopScrollingLeftSection()
scrollLeftSection()
when 'why'
closeAllItems()
openExampleItem()
stopScrollingLeftSection()
scrollLeftSection()
when 'outro'
closeAllItems()
openExampleItem()
stopScrollingLeftSection()
init()
+86
View File
@@ -0,0 +1,86 @@
html, body {
height: 100%;
overflow: hidden;
font-family: "proxima-nova", sans-serif; }
.tether.tether-theme-arrows-dark .tether-content {
-webkit-filter: none;
filter: none;
background: #000; }
.tether.tether-theme-arrows-dark .tether-content ul {
color: #fff;
list-style: none;
padding: 0;
margin: 0; }
.tether.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-right .tether-content:before {
border-right-color: #000; }
.browser-demo {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0; }
.browser-demo *, .browser-demo *:after, .browser-demo *:before {
box-sizing: border-box; }
.browser-demo .top {
position: absolute;
height: 60px;
padding: 20px;
line-height: 40px;
width: 100%;
border-bottom: 1px solid rgba(0, 0, 0, 0.1); }
.browser-demo .bottom {
position: absolute;
top: 60px;
bottom: 0;
width: 100%; }
.browser-demo .bottom .left {
border-right: 1px solid rgba(0, 0, 0, 0.1);
position: absolute;
width: 30%;
height: 100%;
overflow: auto; }
.browser-demo .bottom .left .item {
height: 64px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
cursor: pointer; }
.browser-demo .bottom .left .item:hover, .browser-demo .bottom .left .item.tether-open {
background: rgba(0, 0, 0, 0.1);
border-bottom: 1px solid transparent; }
.browser-demo .bottom .left .item:last-child {
border-bottom: 0; }
.browser-demo .bottom .right {
position: absolute;
width: 70%;
right: 0;
height: 100%;
padding: 20px; }
.browser-demo .title {
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
*zoom: 1;
*display: inline;
background: rgba(0, 0, 0, 0.1);
width: 150px;
height: 15px;
margin-bottom: 20px; }
.browser-demo .word {
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
*zoom: 1;
*display: inline;
background: rgba(0, 0, 0, 0.1);
width: 50px;
height: 8px;
margin-right: 5px;
margin-bottom: 5px; }
.browser-demo .word:nth-last-child(4n+1) {
width: 73px; }
.browser-demo .word:nth-last-child(10n+1) {
width: 14px; }
.browser-demo .word:nth-last-child(9n+1) {
width: 80px; }
+2
View File
@@ -0,0 +1,2 @@
/* Prism.js */
code[class*="language-"], pre[class*="language-"] {color: black; font-family: Consolas, Monaco, 'Andale Mono', monospace; direction: ltr; text-align: left; white-space: pre; word-spacing: normal; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; -webkit-hyphens: none; -moz-hyphens: none; -ms-hyphens: none; hyphens: none; } /* Code blocks */ pre[class*="language-"] {padding: 1em; margin: .5em 0; overflow: auto; font-size: 14px; } :not(pre) > code[class*="language-"], pre[class*="language-"] {background: rgba(0, 0, 0, .05); } /* Inline code */ :not(pre) > code[class*="language-"] {padding: .1em; border-radius: .3em; } .token.comment, .token.prolog, .token.doctype, .token.cdata {color: slategray; } .token.punctuation {color: #999; } .namespace {opacity: .7; } .token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol {color: #905; } .token.selector, .token.attr-name, .token.string, .token.builtin {color: #690; } .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string, .token.variable {color: #a67f59; } .token.atrule, .token.attr-value, .token.keyword {color: #07a; } .token.regex, .token.important {color: #e90; } .token.important {font-weight: bold; } .token.entity {cursor: help; }
+247
View File
@@ -0,0 +1,247 @@
html, body {
height: 100%; }
body {
margin: 0;
font-family: "proxima-nova", "Helvetica Neue", sans-serif; }
.button {
display: inline-block;
border: 2px solid #333;
color: #333;
padding: 1em 1.25em;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 3px;
text-decoration: none;
cursor: pointer;
width: 140px;
font-size: .8em;
line-height: 1.3em;
text-align: center; }
.tether-element.tether-theme-arrows-dark .tether-content {
padding: 1em;
font-size: 1.1em; }
.tether-element.tether-theme-arrows-dark .tether-content .button {
border-color: #fff;
color: #fff;
width: 170px;
pointer-events: all; }
.mobile-copy {
display: none; }
@media (max-width: 568px) {
.mobile-copy {
display: block; } }
.button.dark {
background: #333;
color: #fff; }
.hero-wrap {
height: 100%;
overflow: hidden; }
table.showcase {
height: 100%;
width: 100%;
position: relative; }
table.showcase:after {
content: "";
display: block;
position: absolute;
left: 0;
right: 0;
bottom: 20px;
margin: auto;
height: 0;
width: 0;
border-width: 18px;
border-style: solid;
border-color: transparent;
border-top-color: rgba(0, 0, 0, 0.2); }
table.showcase.no-next-arrow:after {
display: none; }
table.showcase .showcase-inner {
margin: 40px auto 60px;
padding: 10px; }
table.showcase .showcase-inner h1 {
font-size: 50px;
text-align: center;
font-weight: 300; }
@media (max-width: 567px) {
table.showcase .showcase-inner h1 {
font-size: 40px; } }
table.showcase .showcase-inner h2 {
font-size: 24px;
text-align: center;
font-weight: 300;
margin: 1em 0 1em; }
@media (max-width: 567px) {
table.showcase .showcase-inner h2 {
font-size: 14px; } }
table.showcase .showcase-inner p {
text-align: center; }
table.showcase.hero {
text-align: center; }
table.showcase.hero .tether-target-demo {
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
*zoom: 1;
*display: inline;
border: 2px dotted #000;
margin: 5rem auto;
padding: 5rem; }
@media (max-width: 567px) {
table.showcase.hero .tether-target-demo {
padding: 1rem; } }
table.showcase.share {
background: #f3f3f3; }
table.showcase.projects-showcase .showcase-inner .projects-list {
width: 80%;
max-width: 1200px;
margin: 0 auto; }
table.showcase.projects-showcase .showcase-inner .projects-list .project {
color: inherit;
text-decoration: none;
position: relative;
width: 50%;
float: left;
text-align: center;
margin-bottom: 2rem; }
table.showcase.projects-showcase .showcase-inner .projects-list .project:nth-child(odd) {
clear: left; }
table.showcase.projects-showcase .showcase-inner .projects-list .os-icon {
width: 8rem;
height: 8rem;
margin-bottom: 1rem;
background-size: 100%; }
table.showcase.projects-showcase .showcase-inner .projects-list h1 {
font-size: 2.5rem; }
table.showcase.projects-showcase .showcase-inner .projects-list p {
font-size: 1.3rem; }
table.showcase.browser-demo {
background-image: -webkit-linear-gradient(top left, #723362 0%, #9d223c 100%);
background-image: linear-gradient(top left, #723362 0%, #9d223c 100%);
background-color: #9d223c;
position: absolute;
top: 100%; }
table.showcase.browser-demo.fixed {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1; }
table.showcase.browser-demo.fixed .browser-demo-inner {
-webkit-transition: width 2s ease-in-out, height 2s ease-in-out;
transition: width 2s ease-in-out, height 2s ease-in-out; }
table.showcase.browser-demo.fixed[data-section="what"] {
box-shadow: 0 0 0 0; }
table.showcase.browser-demo.fixed[data-section="why"] .browser-demo-inner {
width: 70%; }
table.showcase.browser-demo.fixed[data-section="outro"] .showcase-inner {
pointer-events: all; }
table.showcase.browser-demo .showcase-inner {
pointer-events: none;
position: absolute;
left: 10%;
right: 40%;
top: 220px;
bottom: 120px;
margin: 0;
padding: 0; }
@media (max-width: 567px) {
table.showcase.browser-demo .showcase-inner {
bottom: 90px;
top: 180px; } }
table.showcase.browser-demo .browser-demo-inner {
height: 100%;
width: 100%; }
table.showcase.browser-demo .section-copy {
-webkit-transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out;
transition: opacity 0.5s ease-in-out, top 0.5s ease-in-out;
opacity: 0;
position: absolute;
top: 0;
position: absolute;
height: 200px;
color: #fff;
text-align: center;
width: 100%; }
table.showcase.browser-demo .section-copy.active {
opacity: 1;
top: -150px; }
@media (max-width: 567px) {
table.showcase.browser-demo .section-copy.active {
top: -130px; } }
table.showcase.browser-demo .section-copy h2 {
font-size: 40px;
font-weight: bold;
line-height: 1;
margin: 25px 0 15px; }
@media (max-width: 567px) {
table.showcase.browser-demo .section-copy h2 {
font-size: 30px; } }
table.showcase.browser-demo .browser-window {
border-radius: 4px;
background: #fff;
position: relative;
height: 100%;
width: 100%;
max-width: 1200px;
margin: 0 auto; }
table.showcase.browser-demo .browser-window .browser-titlebar {
position: absolute;
top: 0;
left: 0;
right: 0;
border-bottom: 1px solid #eee;
height: 55px; }
table.showcase.browser-demo .browser-window .browser-titlebar .browser-dots {
padding: 16px; }
table.showcase.browser-demo .browser-window .browser-titlebar .browser-dots b {
display: inline-block;
vertical-align: middle;
*vertical-align: auto;
*zoom: 1;
*display: inline;
border-radius: 50%;
width: 10px;
height: 10px;
margin-right: 7px;
background: rgba(0, 0, 0, 0.1); }
table.showcase.browser-demo .browser-window .browser-frame {
position: absolute;
top: 55px;
left: 0;
right: 0;
bottom: 0; }
table.showcase.browser-demo .browser-window .browser-frame iframe {
border-radius: 0 0 4px 4px;
border: 0;
width: 100%;
height: 100%; }
table.showcase.browser-demo-section .section-scroll-copy {
position: relative;
z-index: 10;
color: #fff;
width: 100%;
font-size: 22px; }
table.showcase.browser-demo-section .section-scroll-copy .section-scroll-copy-inner {
position: absolute;
z-index: 10;
color: #fff;
right: 10%;
width: 23%; }
table.showcase.browser-demo-section .section-scroll-copy .section-scroll-copy-inner a {
color: inherit; }
table.showcase.browser-demo-section .section-scroll-copy .section-scroll-copy-inner .example-paragraph {
border-radius: 4px;
background: #000;
padding: 1rem; }
.browser-content {
display: none; }
+226
View File
@@ -0,0 +1,226 @@
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Tether Marrying elements for life</title>
<meta name="description" content="Tether is a JavaScript and CSS library. It is free and open source and was developed by HubSpot developers Adam Schwartz (@adamfschwartz) and Zack Bloom (@zackbloom).">
<link rel="icon" href="http://static.hubspot.com/favicon.ico">
<script type="text/javascript" src="//use.typekit.net/ghy0wve.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
<!-- Tether themes -->
<link rel="stylesheet" href="//github.hubspot.com/tether/dist/css/tether-theme-arrows-dark.css" />
<!-- Welcome docs styles -->
<link rel="stylesheet" href="//github.hubspot.com/tether/docs/welcome/css/prism.css" />
<link rel="stylesheet" href="//github.hubspot.com/tether/docs/welcome/css/welcome.css" />
<!-- OS icons -->
<link rel="stylesheet" href="http://github.hubspot.com/os-icons/os-icons.css" />
</head>
<body>
<div class="hero-wrap">
<table class="showcase hero"><tr><td>
<div class="showcase-inner">
<div class="tether-target-demo">
<h1>Tether</h1>
<div class="mobile-copy">
<h2>Marrying elements for life</h2>
<p>
<a class="button" href="http://github.com/HubSpot/tether">★ On Github</a>
</p>
</div>
</div>
</div>
</td></tr></table>
</div>
<div class="browser-demo-start-point"></div>
<table class="showcase browser-demo"><tr><td>
<div class="showcase-inner">
<div class="section-copy" data-section="what">
<h2>What is Tether?</h2>
</div>
<div class="section-copy" data-section="how">
<h2>How Tether works.</h2>
</div>
<div class="section-copy" data-section="why">
<h2>Tether is powerful.</h2>
</div>
<div class="section-copy" data-section="outro">
<h2>Play with Tether</h2>
</div>
<div class="browser-demo-inner">
<div class="browser-window">
<div class="browser-titlebar">
<div class="browser-dots"><b></b><b></b><b></b></div>
</div>
<div class="browser-frame">
<iframe src="browser-demo.html"></iframe>
</div>
</div>
</div>
</div>
</td></tr></table>
<table class="showcase browser-demo-section no-next-arrow" data-section="what"><tr><td>
<div class="section-scroll-copy">
<div class="section-scroll-copy-inner">
<p>Tether is a low-level UI library that can be used to position any element on a page <i>next to any other element</i>.</p>
<p>It can be used for dropdown menus, tooltips, popovers, tours, help information, scroll guides, autocompletes, etc. The possibilities are endless.</p>
<p class="example-paragraph">In this example we're showing an action menu <em>tethered</em> to a list item.</p>
</div>
</div>
</td></tr></table>
<table class="showcase browser-demo-section no-next-arrow" data-section="how"><tr><td>
<div class="section-scroll-copy">
<div class="section-scroll-copy-inner">
<p>Tether works by creating an absolutely positioned element and meticulously tracking the movements of a <i>target</i> which you specify.</p>
<p>The <i>target</i> and <i>element</i> can be tethered together in a variety of different ways.</p>
<p class="example-paragraph">Notice how the <i>tethered element</i> stays tethered to its <i>target</i> list item even as the left pane is scrolled up and down.</p>
</div>
</div>
</td></tr></table>
<table class="showcase browser-demo-section no-next-arrow" data-section="why"><tr><td>
<div class="section-scroll-copy">
<div class="section-scroll-copy-inner">
<p>Tether can keep your element positioned properly even in some tough situations.</p>
<p>Tether handles all of the common pain points:</p>
<ul>
<li>Automatically detect collisions with the edge of the page or edge of the scrollParent</li>
<li>Automatically reposition on browser resize, scroll, and other events,</li>
<li>Constrain the position to any bounding box,</li>
</ul>
<p>...and a lot more.</p>
</div>
</div>
</td></tr></table>
<table class="showcase browser-demo-section no-next-arrow" data-section="outro"><tr><td>
<div class="section-scroll-copy">
<div class="section-scroll-copy-inner">
<p class="example-paragraph">Interact with this demo.</p>
<p>&nbsp;</p>
<p>To learn more, check out our <a href="/">documentation</a>.</p>
</div>
</div>
</td></tr></table>
<table class="showcase browser-demo-section no-next-arrow" data-section="__empty"><tr><td></td></tr></table>
<div class="browser-demo-stop-point"></div>
<table class="showcase projects-showcase no-next-arrow"><tr><td>
<div class="showcase-inner">
<h1>Tether Family</h1>
<h2>These projects are all powered by Tether's positioning engine.</h2>
<div class="projects-list">
<a href="//github.hubspot.com/drop/docs/welcome" class="project">
<h1>Drop</h1>
<span class="os-icon drop-icon"></span>
<p>Create dropdowns, popovers, and more.</p>
</a>
<a href="//github.hubspot.com/tooltip/docs/welcome" class="project">
<h1>Tooltip</h1>
<span class="os-icon tooltip-icon"></span>
<p>Stylable tooltips built on Tether.</p>
</a>
<a href="//github.hubspot.com/select/docs/welcome" class="project">
<h1>Select</h1>
<span class="os-icon select-icon"></span>
<p>Stylable select elements built on Tether.</p>
</a>
<a href="//github.hubspot.com/shepherd/docs/welcome" class="project">
<h1>Shepherd</h1>
<span class="os-icon shepherd-icon"></span>
<p>Guide your users through a tour of your app.</p>
</a>
</div>
</div>
</td></tr></table>
<table class="showcase last-showcase no-next-arrow share"><tr><td>
<div class="showcase-inner">
<h1>Share</h1>
<h2>Help us spread the word.</h2>
<!-- Share -->
<style>
.share-buttons {
margin: 4em auto;
text-align: center;
}
.share-button {
display: inline-block;
}
.retweet-button {
width: 100px;
margin-left: 20px;
}
.github-stars {
width: 100px;
}
</style>
<div class="share-buttons">
<div class="share-button retweet-button">
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://github.hubspot.com/tether/docs/welcome" data-text="Tether.js - A positioning engine for JavaScript" data-count="horizontal" data-via="HubSpotDev">Tweet</a>
<script>
(function(){
var recommends, button;
if (Math.random() >= 0.5) {
recommends = ['hubspotdev', 'zackbloom', 'adamfschwartz'];
} else {
recommends = ['hubspotdev', 'adamfschwartz', 'zackbloom'];
}
button = document.querySelector('.twitter-share-button');
if (button) {
button.setAttribute('data-related', recommends.join(','));
}
})();
</script>
<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
</div>
<div class="share-button github-stars-button">
<iframe src="http://ghbtns.com/github-btn.html?user=HubSpot&amp;repo=tether&amp;type=watch&amp;count=true&amp;size=small" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
</div>
</p>
</div>
</td></tr></table>
<!-- Tether javascript -->
<script src="//github.hubspot.com/tether/dist/js/tether.min.js"></script>
<!-- Welcome docs javascript -->
<script src="//github.hubspot.com/tether/docs/welcome/js/log.js"></script>
<script src="//github.hubspot.com/tether/docs/welcome/js/jquery.js"></script>
<script src="//github.hubspot.com/tether/docs/welcome/js/drop.js"></script>
<script src="//github.hubspot.com/tether/docs/welcome/js/welcome.js"></script>
<!-- HubSpot analytics -->
<script type="text/javascript">
(function(d,s,i,r) {
if (d.getElementById(i)){return;}
var n=d.createElement(s),e=d.getElementsByTagName(s)[0];
n.id=i;n.src='//js.hubspot.com/analytics/'+(Math.ceil(new Date()/r)*r)+'/51294.js';
e.parentNode.insertBefore(n, e);
})(document,"script","hs-analytics",300000);
</script>
<!-- Google analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-45159009-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>
+239
View File
@@ -0,0 +1,239 @@
(function() {
var Evented, MIRROR_ATTACH, addClass, allDrops, clickEvent, createContext, extend, hasClass, removeClass, sortAttach, touchDevice, _ref,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
_ref = Tether.Utils, extend = _ref.extend, addClass = _ref.addClass, removeClass = _ref.removeClass, hasClass = _ref.hasClass, Evented = _ref.Evented;
touchDevice = 'ontouchstart' in document.documentElement;
clickEvent = touchDevice ? 'touchstart' : 'click';
sortAttach = function(str) {
var first, second, _ref1, _ref2;
_ref1 = str.split(' '), first = _ref1[0], second = _ref1[1];
if (first === 'left' || first === 'right') {
_ref2 = [second, first], first = _ref2[0], second = _ref2[1];
}
return [first, second].join(' ');
};
MIRROR_ATTACH = {
left: 'right',
right: 'left',
top: 'bottom',
bottom: 'top',
middle: 'middle',
center: 'center'
};
allDrops = {};
createContext = function(options) {
var DropInstance, defaultOptions, drop, _name;
if (options == null) {
options = {};
}
drop = function() {
return (function(func, args, ctor) {
ctor.prototype = func.prototype;
var child = new ctor, result = func.apply(child, args);
return Object(result) === result ? result : child;
})(DropInstance, arguments, function(){});
};
extend(drop, {
createContext: createContext,
drops: [],
defaults: {}
});
defaultOptions = {
classPrefix: 'drop',
defaults: {
attach: 'bottom left',
openOn: 'click',
constrainToScrollParent: true,
constrainToWindow: true,
classes: '',
tetherOptions: {}
}
};
extend(drop, defaultOptions, options);
extend(drop.defaults, defaultOptions.defaults, options.defaults);
if (allDrops[_name = drop.classPrefix] == null) {
allDrops[_name] = [];
}
drop.updateBodyClasses = function() {
var anyOpen, _drop, _i, _len, _ref1;
anyOpen = false;
_ref1 = allDrops[drop.classPrefix];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
_drop = _ref1[_i];
if (!(_drop.isOpened())) {
continue;
}
anyOpen = true;
break;
}
if (anyOpen) {
return addClass(document.body, "" + drop.classPrefix + "-open");
} else {
return removeClass(document.body, "" + drop.classPrefix + "-open");
}
};
DropInstance = (function(_super) {
__extends(DropInstance, _super);
function DropInstance(options) {
this.options = options;
this.options = extend({}, drop.defaults, this.options);
this.target = this.options.target;
if (this.target == null) {
throw new Error('Drop Error: You must provide a target.');
}
drop.drops.push(this);
allDrops[drop.classPrefix].push(this);
this.setupElements();
this.setupEvents();
this.setupTether();
}
DropInstance.prototype.setupElements = function() {
this.drop = document.createElement('div');
addClass(this.drop, drop.classPrefix);
if (this.options.classes) {
addClass(this.drop, this.options.classes);
}
this.dropContent = document.createElement('div');
addClass(this.dropContent, "" + drop.classPrefix + "-content");
if (typeof this.options.content === 'object') {
this.dropContent.appendChild(this.options.content);
} else {
this.dropContent.innerHTML = this.options.content;
}
return this.drop.appendChild(this.dropContent);
};
DropInstance.prototype.setupTether = function() {
var constraints, dropAttach;
dropAttach = this.options.position.split(' ');
dropAttach[0] = MIRROR_ATTACH[dropAttach[0]];
dropAttach = dropAttach.join(' ');
constraints = [];
if (this.options.constrainToScrollParent) {
constraints.push({
to: 'scrollParent',
pin: 'top, bottom',
attachment: 'together none'
});
}
if (this.options.constrainToWindow !== false) {
constraints.push({
to: 'window',
pin: true,
attachment: 'together'
});
}
constraints.push({
to: 'scrollParent'
});
options = {
element: this.drop,
target: this.target,
attachment: sortAttach(dropAttach),
targetAttachment: sortAttach(this.options.position),
classPrefix: drop.classPrefix,
offset: '0 0',
targetOffset: '0 0',
enabled: false,
constraints: constraints
};
if (this.options.tether !== false) {
return this.tether = new Tether(extend({}, options, this.options.tether));
}
};
DropInstance.prototype.setupEvents = function() {
var events,
_this = this;
if (!this.options.openOn) {
return;
}
events = this.options.openOn.split(' ');
if (__indexOf.call(events, 'click') >= 0) {
this.target.addEventListener(clickEvent, function() {
return _this.toggle();
});
document.addEventListener(clickEvent, function(event) {
if (!_this.isOpened()) {
return;
}
if (event.target === _this.drop || _this.drop.contains(event.target)) {
return;
}
if (event.target === _this.target || _this.target.contains(event.target)) {
return;
}
return _this.close();
});
}
if (__indexOf.call(events, 'hover') >= 0) {
this.target.addEventListener('mouseover', function() {
return _this.open();
});
return this.target.addEventListener('mouseout', function() {
return _this.close();
});
}
};
DropInstance.prototype.isOpened = function() {
return hasClass(this.drop, "" + drop.classPrefix + "-open");
};
DropInstance.prototype.toggle = function() {
if (this.isOpened()) {
return this.close();
} else {
return this.open();
}
};
DropInstance.prototype.open = function() {
var _ref1;
if (!this.drop.parentNode) {
document.body.appendChild(this.drop);
}
addClass(this.target, "" + drop.classPrefix + "-open");
addClass(this.drop, "" + drop.classPrefix + "-open");
if ((_ref1 = this.tether) != null) {
_ref1.enable();
}
this.trigger('open');
return drop.updateBodyClasses();
};
DropInstance.prototype.close = function() {
var _ref1;
removeClass(this.target, "" + drop.classPrefix + "-open");
removeClass(this.drop, "" + drop.classPrefix + "-open");
this.trigger('close');
if ((_ref1 = this.tether) != null) {
_ref1.disable();
}
return drop.updateBodyClasses();
};
return DropInstance;
})(Evented);
return drop;
};
window.Drop = createContext();
document.addEventListener('DOMContentLoaded', function() {
return Drop.updateBodyClasses();
});
}).call(this);
+9597
View File
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
(function() {
var ffSupport, formats, getOrderedMatches, hasMatches, isFF, isIE, isOpera, isSafari, log, makeArray, operaSupport, safariSupport, stringToArgs, _log;
if (!(window.console && window.console.log)) {
return;
}
log = function() {
var args;
args = [];
makeArray(arguments).forEach(function(arg) {
if (typeof arg === 'string') {
return args = args.concat(stringToArgs(arg));
} else {
return args.push(arg);
}
});
return _log.apply(window, args);
};
_log = function() {
return console.log.apply(console, makeArray(arguments));
};
makeArray = function(arrayLikeThing) {
return Array.prototype.slice.call(arrayLikeThing);
};
formats = [
{
regex: /\*([^\*]+)\*/,
replacer: function(m, p1) {
return "%c" + p1 + "%c";
},
styles: function() {
return ['font-style: italic', ''];
}
}, {
regex: /\_([^\_]+)\_/,
replacer: function(m, p1) {
return "%c" + p1 + "%c";
},
styles: function() {
return ['font-weight: bold', ''];
}
}, {
regex: /\`([^\`]+)\`/,
replacer: function(m, p1) {
return "%c" + p1 + "%c";
},
styles: function() {
return ['background: rgb(255, 255, 219); padding: 1px 5px; border: 1px solid rgba(0, 0, 0, 0.1)', ''];
}
}, {
regex: /\[c\=(?:\"|\')?((?:(?!(?:\"|\')\]).)*)(?:\"|\')?\]((?:(?!\[c\]).)*)\[c\]/,
replacer: function(m, p1, p2) {
return "%c" + p2 + "%c";
},
styles: function(match) {
return [match[1], ''];
}
}
];
hasMatches = function(str) {
var _hasMatches;
_hasMatches = false;
formats.forEach(function(format) {
if (format.regex.test(str)) {
return _hasMatches = true;
}
});
return _hasMatches;
};
getOrderedMatches = function(str) {
var matches;
matches = [];
formats.forEach(function(format) {
var match;
match = str.match(format.regex);
if (match) {
return matches.push({
format: format,
match: match
});
}
});
return matches.sort(function(a, b) {
return a.match.index - b.match.index;
});
};
stringToArgs = function(str) {
var firstMatch, matches, styles;
styles = [];
while (hasMatches(str)) {
matches = getOrderedMatches(str);
firstMatch = matches[0];
str = str.replace(firstMatch.format.regex, firstMatch.format.replacer);
styles = styles.concat(firstMatch.format.styles(firstMatch.match));
}
return [str].concat(styles);
};
isSafari = function() {
return /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
};
isOpera = function() {
return /OPR/.test(navigator.userAgent) && /Opera/.test(navigator.vendor);
};
isFF = function() {
return /Firefox/.test(navigator.userAgent);
};
isIE = function() {
return /MSIE/.test(navigator.userAgent);
};
safariSupport = function() {
var m;
m = navigator.userAgent.match(/AppleWebKit\/(\d+)\.(\d+)(\.|\+|\s)/);
if (!m) {
return false;
}
return 537.38 <= parseInt(m[1], 10) + (parseInt(m[2], 10) / 100);
};
operaSupport = function() {
var m;
m = navigator.userAgent.match(/OPR\/(\d+)\./);
if (!m) {
return false;
}
return 15 <= parseInt(m[1], 10);
};
ffSupport = function() {
return window.console.firebug || window.console.exception;
};
if (isIE() || (isFF() && !ffSupport()) || (isOpera() && !operaSupport()) || (isSafari() && !safariSupport())) {
window.log = _log;
} else {
window.log = log;
}
window.log.l = _log;
}).call(this);
File diff suppressed because one or more lines are too long
+193
View File
@@ -0,0 +1,193 @@
(function() {
var init, isMobile, setupBrowserDemo, setupHero, _Drop;
_Drop = Drop.createContext({
classPrefix: 'tether'
});
isMobile = $(window).width() < 567;
init = function() {
setupHero();
return setupBrowserDemo();
};
setupHero = function() {
var $target, finalDropState, frameLengthMS, frames, openAllDrops, openIndex, openNextDrop, position, positions, _i, _len;
$target = $('.tether-target-demo');
positions = ['top left', 'left top', 'left middle', 'left bottom', 'bottom left', 'bottom center', 'bottom right', 'right bottom', 'right middle', 'right top', 'top right', 'top center'];
if (isMobile) {
positions = ['top left', 'bottom left', 'bottom right', 'top right'];
}
window.drops = {};
for (_i = 0, _len = positions.length; _i < _len; _i++) {
position = positions[_i];
drops[position] = new _Drop({
target: $target[0],
classes: 'tether-theme-arrows-dark',
position: position,
constrainToWindow: false,
openOn: '',
content: '<div style="height: 50px; width: 50px"></div>'
});
}
openIndex = 0;
frames = 0;
frameLengthMS = 10;
openAllDrops = function() {
var drop, _results;
_results = [];
for (position in drops) {
drop = drops[position];
_results.push(drop.open());
}
return _results;
};
openNextDrop = function() {
var drop;
for (position in drops) {
drop = drops[position];
drop.close();
}
drops[positions[openIndex]].open();
drops[positions[(openIndex + 6) % positions.length]].open();
openIndex = (openIndex + 1) % positions.length;
if (frames > 5) {
finalDropState();
return;
}
frames += 1;
return setTimeout(openNextDrop, frameLengthMS * frames);
};
finalDropState = function() {
$(drops['top left'].dropContent).html('Marrying DOM elements for life.');
$(drops['bottom right'].dropContent).html('<a class="button" href="http://github.com/HubSpot/tether">★ On Github</a>');
drops['top left'].open();
return drops['bottom right'].open();
};
if (true || isMobile) {
drops['top left'].open();
drops['top left'].tether.position();
drops['bottom right'].open();
drops['bottom right'].tether.position();
return finalDropState();
} else {
return openNextDrop();
}
};
setupBrowserDemo = function() {
var $browserContents, $browserDemo, $iframe, $sections, $startPoint, $stopPoint, scrollInterval, scrollTop, scrollTopDirection, setSection;
$browserDemo = $('.browser-demo.showcase');
$startPoint = $('.browser-demo-start-point');
$stopPoint = $('.browser-demo-stop-point');
$iframe = $('.browser-window iframe');
$browserContents = $('.browser-content .browser-demo-inner');
$sections = $('.browser-demo-section');
$('body').append("<style>\n table.showcase.browser-demo.fixed-bottom {\n top: " + $sections.length + "00%\n }\n</style>");
$(window).scroll(function() {
var scrollTop;
scrollTop = $(window).scrollTop();
if ($startPoint.position().top < scrollTop && scrollTop + window.innerHeight < $stopPoint.position().top) {
$browserDemo.removeClass('fixed-bottom');
$browserDemo.addClass('fixed');
return $sections.each(function() {
var $section;
$section = $(this);
if (($section.position().top < scrollTop && scrollTop < $section.position().top + $section.outerHeight())) {
setSection($section.data('section'));
}
return true;
});
} else {
$browserDemo.removeAttr('data-section');
$browserDemo.removeClass('fixed');
if (scrollTop + window.innerHeight > $stopPoint.position().top) {
return $browserDemo.addClass('fixed-bottom');
} else {
return $browserDemo.removeClass('fixed-bottom');
}
}
});
$iframe.load(function() {
var $items, iframeWindow;
iframeWindow = $iframe[0].contentWindow;
$items = $iframe.contents().find('.item');
return $items.each(function(i) {
var $item, drop, _iframeWindowDrop;
$item = $(this);
_iframeWindowDrop = iframeWindow.Drop.createContext({
classPrefix: 'tether'
});
drop = new _iframeWindowDrop({
target: $item[0],
classes: 'tether-theme-arrows-dark',
position: 'right top',
constrainToWindow: true,
openOn: 'click',
content: '<ul>\n <li>Action&nbsp;1</li>\n <li>Action&nbsp;2</li>\n <li>Action&nbsp;3</li>\n</ul>'
});
return $item.data('drop', drop);
});
});
scrollInterval = void 0;
scrollTop = 0;
scrollTopDirection = 1;
return setSection = function(section) {
var closeAllItems, openExampleItem, scrollLeftSection, stopScrollingLeftSection;
$browserDemo.attr('data-section', section);
$('.section-copy').removeClass('active');
$(".section-copy[data-section=\"" + section + "\"]").addClass('active');
openExampleItem = function() {
if (isMobile) {
return $iframe.contents().find('.item:first').data().drop.open();
} else {
return $iframe.contents().find('.item:eq(2)').data().drop.open();
}
};
closeAllItems = function() {
return $iframe.contents().find('.item').each(function() {
return $(this).data().drop.close() || true;
});
};
scrollLeftSection = function() {
return scrollInterval = setInterval(function() {
$iframe.contents().find('.left').scrollTop(scrollTop);
scrollTop += scrollTopDirection;
if (scrollTop > 50) {
scrollTopDirection = -1;
}
if (scrollTop < 0) {
return scrollTopDirection = 1;
}
}, 30);
};
stopScrollingLeftSection = function() {
return clearInterval(scrollInterval);
};
switch (section) {
case 'what':
closeAllItems();
openExampleItem();
return stopScrollingLeftSection();
case 'how':
closeAllItems();
openExampleItem();
stopScrollingLeftSection();
return scrollLeftSection();
case 'why':
closeAllItems();
openExampleItem();
stopScrollingLeftSection();
return scrollLeftSection();
case 'outro':
closeAllItems();
openExampleItem();
return stopScrollingLeftSection();
}
};
};
init();
}).call(this);
+6
View File
@@ -0,0 +1,6 @@
@mixin inline-block
display: inline-block
vertical-align: middle
*vertical-align: auto
*zoom: 1
*display: inline
+93
View File
@@ -0,0 +1,93 @@
@import inline-block
html, body
height: 100%
overflow: hidden
font-family: "proxima-nova", sans-serif
.tether.tether-theme-arrows-dark .tether-content
filter: none
background: #000
ul
color: #fff
list-style: none
padding: 0
margin: 0
.tether.tether-theme-arrows-dark.tether-element-attached-top.tether-element-attached-left.tether-target-attached-right .tether-content:before
border-right-color: #000
.browser-demo
position: absolute
top: 0
left: 0
bottom: 0
right: 0
*, *:after, *:before
box-sizing: border-box
.top
position: absolute
height: 60px
padding: 20px
line-height: 40px
width: 100%
border-bottom: 1px solid rgba(0, 0, 0, .1)
.bottom
position: absolute
top: 60px
bottom: 0
width: 100%
.left
border-right: 1px solid rgba(0, 0, 0, .1)
position: absolute
width: 30%
height: 100%
overflow: auto
.item
height: 64px
border-bottom: 1px solid rgba(0, 0, 0, .1)
cursor: pointer
&:hover, &.tether-open
background: rgba(0, 0, 0, .1)
border-bottom: 1px solid rgba(0, 0, 0, 0)
&:last-child
border-bottom: 0
.right
position: absolute
width: 70%
right: 0
height: 100%
padding: 20px
.title
+inline-block
background: rgba(0, 0, 0, .1)
width: 150px
height: 15px
margin-bottom: 20px
.word
+inline-block
background: rgba(0, 0, 0, .1)
width: 50px
height: 8px
margin-right: 5px
margin-bottom: 5px
&:nth-last-child(4n+1)
width: 73px
&:nth-last-child(10n+1)
width: 14px
&:nth-last-child(9n+1)
width: 80px
+285
View File
@@ -0,0 +1,285 @@
@import inline-block
html, body
height: 100%
body
margin: 0
font-family: "proxima-nova", "Helvetica Neue", sans-serif
.button
display: inline-block
border: 2px solid #333
color: #333
padding: 1em 1.25em
font-weight: 500
text-transform: uppercase
letter-spacing: 3px
text-decoration: none
cursor: pointer
width: 140px
font-size: .8em
line-height: 1.3em
text-align: center
.tether-element.tether-theme-arrows-dark .tether-content
padding: 1em
font-size: 1.1em
.button
border-color: #fff
color: #fff
width: 170px
pointer-events: all
.mobile-copy
display: none
@media (max-width: 568px)
display: block
.button.dark
background: #333
color: #fff
.hero-wrap
height: 100%
overflow: hidden
table.showcase
height: 100%
width: 100%
position: relative
&:after
content: ""
display: block
position: absolute
left: 0
right: 0
bottom: 20px
margin: auto
height: 0
width: 0
border-width: 18px
border-style: solid
border-color: transparent
border-top-color: rgba(0, 0, 0, 0.2)
&.no-next-arrow:after
display: none
.showcase-inner
margin: 40px auto 60px
padding: 10px
h1
font-size: 50px
text-align: center
font-weight: 300
@media (max-width: 567px)
font-size: 40px
h2
font-size: 24px
text-align: center
font-weight: 300
margin: 1em 0 1em
@media (max-width: 567px)
font-size: 14px
p
text-align: center
&.hero
text-align: center
.tether-target-demo
+inline-block
border: 2px dotted #000
margin: 5rem auto
padding: 5rem
@media (max-width: 567px)
padding: 1rem
&.share
background: #f3f3f3
&.projects-showcase .showcase-inner
.projects-list
width: 80%
max-width: 1200px
margin: 0 auto
.project
color: inherit
text-decoration: none
position: relative
width: 50%
float: left
text-align: center
margin-bottom: 2rem
&:nth-child(odd)
clear: left
.os-icon
width: 8rem
height: 8rem
margin-bottom: 1rem
background-size: 100%
h1
font-size: 2.5rem
p
font-size: 1.3rem
&.browser-demo
background-image: linear-gradient(top left, #723362 0%, #9d223c 100%)
background-color: #9d223c
position: absolute
top: 100%
&.fixed
position: fixed
top: 0
bottom: 0
left: 0
right: 0
z-index: 1
.browser-demo-inner
transition: width 2s ease-in-out, height 2s ease-in-out
// Sections
&[data-section="what"]
box-shadow: 0 0 0 0
&[data-section="why"]
.browser-demo-inner
width: 70%
&[data-section="outro"]
.showcase-inner
pointer-events: all
.showcase-inner
pointer-events: none
position: absolute
left: 10%
right: 40%
top: 220px
bottom: 120px
margin: 0
padding: 0
@media (max-width: 567px)
bottom: 90px
top: 180px
.browser-demo-inner
height: 100%
width: 100%
.section-copy
transition: opacity .5s ease-in-out, top .5s ease-in-out
opacity: 0
position: absolute
top: 0
position: absolute
height: 200px
color: #fff
text-align: center
width: 100%
&.active
opacity: 1
top: -150px
@media (max-width: 567px)
top: -130px
h2
font-size: 40px
font-weight: bold
line-height: 1
margin: 25px 0 15px
@media (max-width: 567px)
font-size: 30px
.browser-window
border-radius: 4px
background: #fff
position: relative
height: 100%
width: 100%
max-width: 1200px
margin: 0 auto
.browser-titlebar
position: absolute
top: 0
left: 0
right: 0
border-bottom: 1px solid #eee
height: 55px
.browser-dots
padding: 16px
b
+inline-block
border-radius: 50%
width: 10px
height: 10px
margin-right: 7px
background: rgba(0, 0, 0, .1)
.browser-frame
position: absolute
top: 55px
left: 0
right: 0
bottom: 0
iframe
border-radius: 0 0 4px 4px
border: 0
width: 100%
height: 100%
&.browser-demo-section
.section-scroll-copy
position: relative
z-index: 10
color: #fff
width: 100%
font-size: 22px
.section-scroll-copy-inner
position: absolute
z-index: 10
color: #fff
right: 10%
width: 23%
a
color: inherit
.example-paragraph
border-radius: 4px
background: #000
padding: 1rem
.browser-content
display: none