fron 0.2.0rc1 → 1.0.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +20 -0
- data/.reek +2 -0
- data/.rubocop.yml +14 -11
- data/Gemfile +6 -3
- data/Gemfile.lock +73 -86
- data/Rakefile +11 -15
- data/Readme.md +1 -1
- data/fron.gemspec +2 -2
- data/lib/fron/version.rb +1 -1
- data/opal/fron.rb +2 -0
- data/opal/fron/core.rb +1 -0
- data/opal/fron/core/behaviors/components.rb +18 -10
- data/opal/fron/core/behaviors/events.rb +9 -10
- data/opal/fron/core/behaviors/routes.rb +6 -10
- data/opal/fron/core/behaviors/style.rb +30 -0
- data/opal/fron/core/component.rb +44 -23
- data/opal/fron/core/eventable.rb +3 -3
- data/opal/fron/core/logger.rb +1 -1
- data/opal/fron/core/sheet.rb +140 -0
- data/opal/fron/core_ext.rb +1 -0
- data/opal/fron/core_ext/array.rb +23 -0
- data/opal/fron/core_ext/hash.rb +6 -6
- data/opal/fron/core_ext/kernel.rb +10 -1
- data/opal/fron/core_ext/numeric.rb +7 -0
- data/opal/fron/core_ext/time.rb +6 -0
- data/opal/fron/dom/document.rb +6 -3
- data/opal/fron/dom/element.rb +79 -19
- data/opal/fron/dom/event.rb +5 -1
- data/opal/fron/dom/modules/dimensions.rb +0 -14
- data/opal/fron/dom/modules/element_accessor.rb +25 -0
- data/opal/fron/dom/modules/events.rb +1 -1
- data/opal/fron/dom/node.rb +7 -5
- data/opal/fron/dom/style.rb +0 -2
- data/opal/fron/dom/window.rb +14 -0
- data/opal/fron/event_mock.rb +24 -6
- data/opal/fron/js/scroll_into_view_if_needed.js +27 -0
- data/opal/fron/js/syntetic_event.js +20 -13
- data/opal/fron/request/request.rb +21 -19
- data/opal/fron/request/response.rb +1 -1
- data/opal/fron/storage.rb +2 -0
- data/opal/fron/storage/local_storage.rb +3 -45
- data/opal/fron/storage/session_storage.rb +12 -0
- data/opal/fron/storage/store.rb +54 -0
- data/opal/fron/utils/drag.rb +21 -18
- data/opal/fron/utils/keyboard.rb +14 -12
- data/opal/fron/utils/point.rb +12 -4
- data/opal/fron/utils/render_proc.rb +6 -2
- data/spec/core-ext/array_spec.rb +10 -2
- data/spec/core-ext/numeric_spec.rb +6 -0
- data/spec/core/behaviors/style_spec.rb +51 -0
- data/spec/core/component_inheritance_spec.rb +10 -15
- data/spec/core/component_spec.rb +10 -15
- data/spec/dom/element_spec.rb +12 -1
- data/spec/dom/modules/classlist_spec.rb +8 -9
- data/spec/dom/modules/dimensions_spec.rb +2 -1
- data/spec/dom/modules/events_spec.rb +42 -31
- data/spec/dom/style_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -1
- data/spec/utils/drag_spec.rb +2 -2
- data/spec/utils/keyboard_spec.rb +4 -1
- data/website/application.rb +4 -0
- data/website/config.ru +30 -0
- data/website/examples/content_editable.rb +29 -0
- data/website/examples/converter.rb +49 -0
- data/website/examples/icon_button.rb +20 -0
- data/website/examples/image_paragraph.rb +33 -0
- data/website/examples/my_blue_box.rb +9 -0
- data/website/examples/my_box.rb +9 -0
- data/website/examples/my_button.rb +27 -0
- data/website/examples/my_green_box.rb +14 -0
- data/website/examples/source_reader.rb +32 -0
- data/website/examples/text_area.rb +42 -0
- data/website/pages/components.md.erb +16 -0
- data/website/pages/components/composition.md.erb +9 -0
- data/website/pages/components/events.md.erb +20 -0
- data/website/pages/components/inheritance.md.erb +19 -0
- data/website/pages/components/routes.md.erb +49 -0
- data/website/pages/components/styles.md.erb +38 -0
- data/website/pages/getting-started.md +8 -0
- data/website/pages/home.md +4 -0
- data/website/pages/intro.md +30 -0
- data/website/pages/utilities.md +10 -0
- data/website/pages/utilities/local-storage.md.erb +16 -0
- data/website/pages/utilities/request.md.erb +12 -0
- data/website/setup.rb +162 -0
- data/website/vendor/highlight.js +2 -0
- data/website/vendor/highlight.ruby.js +1 -0
- data/website/vendor/marked.min.js +6 -0
- metadata +43 -7
@@ -0,0 +1,27 @@
|
|
1
|
+
# Simple button class
|
2
|
+
class MyButton < Fron::Component
|
3
|
+
# Set the tag
|
4
|
+
tag 'my-button'
|
5
|
+
|
6
|
+
# When clicked greet the user
|
7
|
+
on :click, :greet
|
8
|
+
|
9
|
+
# Some styling...
|
10
|
+
style border: '1px solid #CCC',
|
11
|
+
display: 'inline-block',
|
12
|
+
background: '#F9F9F9',
|
13
|
+
padding: '5px 15px',
|
14
|
+
borderRadius: 5.px,
|
15
|
+
cursor: :pointer
|
16
|
+
|
17
|
+
# Set default text
|
18
|
+
def initialize
|
19
|
+
super
|
20
|
+
self.text = 'Click Me!'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Greet the user
|
24
|
+
def greet
|
25
|
+
alert('Hello there!')
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Hulk SMASH!
|
2
|
+
class MyGreenBox < MyBox
|
3
|
+
tag 'my-green-box'
|
4
|
+
|
5
|
+
style justifyContent: :center,
|
6
|
+
alignItems: :center,
|
7
|
+
background: :green,
|
8
|
+
display: :flex,
|
9
|
+
span: { fontWeight: :bold,
|
10
|
+
fontSize: 20.px,
|
11
|
+
color: '#FFF' }
|
12
|
+
|
13
|
+
component :span, :span, text: 'Doh!'
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Source Reader
|
2
|
+
class SourceReader < Fron::Component
|
3
|
+
# Set the tag
|
4
|
+
tag 'source-reader'
|
5
|
+
|
6
|
+
# Button for initialize loading
|
7
|
+
component :button, 'button', text: 'Load source...'
|
8
|
+
|
9
|
+
# Styles
|
10
|
+
style whiteSpace: 'pre-wrap',
|
11
|
+
background: '#F9F9F9',
|
12
|
+
borderRadius: 5.px,
|
13
|
+
display: :block,
|
14
|
+
padding: 20.px
|
15
|
+
|
16
|
+
# Event to load
|
17
|
+
on :click, :button, :load
|
18
|
+
|
19
|
+
# Load source
|
20
|
+
def load
|
21
|
+
request.get do |response|
|
22
|
+
self.text = response.body.split("\n")[0..2].join("\n") + "\n..."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Request object
|
29
|
+
def request
|
30
|
+
@request ||= Fron::Request.new '/assets/pages/utilities/request.md'
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Text Area component
|
2
|
+
class TextArea < Fron::Component
|
3
|
+
# Set the tag
|
4
|
+
tag 'text-area'
|
5
|
+
|
6
|
+
# Create textarea
|
7
|
+
component :textarea, :textarea
|
8
|
+
|
9
|
+
# Save on input
|
10
|
+
on :input, :save
|
11
|
+
|
12
|
+
# Styles
|
13
|
+
style textarea: { border: '1px solid #EEE',
|
14
|
+
fontFamily: 'Open Sans',
|
15
|
+
minHeight: 100.px,
|
16
|
+
fontSize: 18.px,
|
17
|
+
padding: 20.px,
|
18
|
+
width: '100%' }
|
19
|
+
|
20
|
+
# Load on initialization
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
load
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load from LocalStorage
|
27
|
+
def load
|
28
|
+
@textarea.value = storage.get(:value).to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# Save to LocalStorage
|
32
|
+
def save
|
33
|
+
storage.set :value, @textarea.value
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Helper method
|
39
|
+
def storage
|
40
|
+
Fron::Storage::LocalStorage
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# 4. Components
|
2
|
+
Components are the main part of Fron. Everything is built around / for them, although nothing in the library is using them so you can say that they are the end product.
|
3
|
+
|
4
|
+
## What is a component?
|
5
|
+
A **component** is basically a custom **HTML Element** that you can **add methods to**, **extend** and **compose** into other components. It is defined by **Fron::Component** class.
|
6
|
+
|
7
|
+
An input field using contenteditable attribute:
|
8
|
+
<%= example 'ContentEditable' %>
|
9
|
+
|
10
|
+
## How does it work?
|
11
|
+
Basically the **native HTML Element** is **wrapped** in a Ruby class instance. If you are curious about the implementation check the source [here](https://github.com/digitalnatives/fron/blob/master/opal/fron/dom/element.rb) and [here](https://github.com/digitalnatives/fron/blob/master/opal/fron/core/component.rb)
|
12
|
+
|
13
|
+
## How it is different from Web Components?
|
14
|
+
[Web Components](https://css-tricks.com/modular-future-web-components/) use new technologies that are not available everywhere, also they introduce a lot of concepts that seem odd and complicated.
|
15
|
+
|
16
|
+
Fron components are straightforward and easy to learn.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# 4.2. Composition
|
2
|
+
Components can be composited together with the **component** DSL method.
|
3
|
+
This allows you to create more complex components like this paragraph component:
|
4
|
+
|
5
|
+
<%= example 'ImageParagraph' %>
|
6
|
+
|
7
|
+
As you can see the components we defined (**image** and **content**) are accessible with
|
8
|
+
**instance variables** also with **instance methods** but only if there is no method
|
9
|
+
defined with that name.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# 4.3. Events
|
2
|
+
Handling events are pretty easy by using the **on** DSL method in a component:
|
3
|
+
when an **event** happens on the element the given **instance method** is called.
|
4
|
+
|
5
|
+
There are two ways of adding events:
|
6
|
+
* Adding events directly to the component is used when it's not important which
|
7
|
+
child component triggered the event, or the event isn't delegateable like
|
8
|
+
**focus** or **blur**:
|
9
|
+
```ruby
|
10
|
+
on :blur, :close
|
11
|
+
```
|
12
|
+
* Delegating events with a **selector** is usefull if we only want to handle events
|
13
|
+
that were trigger child components matching a specific selector.
|
14
|
+
```ruby
|
15
|
+
on :click, 'button > span', :open
|
16
|
+
```
|
17
|
+
|
18
|
+
For example let's say there is a component for converting **celsius** to **fahrenheit**:
|
19
|
+
|
20
|
+
<%= example 'Converter' %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# 4.1. Inheritance
|
2
|
+
Because **components** are just **ruby classes**, that means that they can be exteded
|
3
|
+
and reused.
|
4
|
+
|
5
|
+
Let's say that there is a **Button** component that greets you when you click it,
|
6
|
+
and we would like to have an icon before the text, but it doesn't support that:
|
7
|
+
|
8
|
+
<%= example 'MyButton' %>
|
9
|
+
|
10
|
+
What we can do is to create an **IconButton** component that extends the **Button** component,
|
11
|
+
and add the functionality to that:
|
12
|
+
|
13
|
+
<%= example 'IconButton' %>
|
14
|
+
|
15
|
+
What we did was:
|
16
|
+
* we extended the **Button** component so the events, styles and methods are inherited
|
17
|
+
* we added two components one for the **icon** and one for the **text (span)**
|
18
|
+
* we delegated the two methods **text** and **text=** in order to not break the API
|
19
|
+
* we added some styles to the **icon** component
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# 4.4. Routes
|
2
|
+
Routing is supported in Fron via the **Fron::Behaviors::Routes** module, and
|
3
|
+
currently is very sparse.
|
4
|
+
|
5
|
+
When used in a **component**, routes can be defined in order to call an
|
6
|
+
_instance method_ when the page location changes to match a perticular
|
7
|
+
regular expression or string:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class Main < Fron::Component
|
11
|
+
include Fron::Behaviors::Routes
|
12
|
+
|
13
|
+
route '/users', :users
|
14
|
+
route '/posts', :posts
|
15
|
+
|
16
|
+
def users
|
17
|
+
# Display users
|
18
|
+
end
|
19
|
+
|
20
|
+
def posts
|
21
|
+
# Display posts
|
22
|
+
end
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
## Handling Parameters
|
27
|
+
|
28
|
+
If capture groups are defined for the regular expression than they will be
|
29
|
+
forwarded to the method as arguments:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class Main < Fron::Component
|
33
|
+
include Fron::Behaviors::Routes
|
34
|
+
|
35
|
+
route '/users/(.*)', :show_user
|
36
|
+
|
37
|
+
def show_user(id)
|
38
|
+
# Display user with the given id
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
Things to keep in mind when handling routes:
|
44
|
+
* Multiple components can include this module and define routes
|
45
|
+
* If multiple components matches a route then only one (the first) will
|
46
|
+
be called
|
47
|
+
* Components that are not in the DOM can handle routes too
|
48
|
+
* The best practice is to have one component to handle all the routes and then
|
49
|
+
delegate down the component tree
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# 4.5. Styles
|
2
|
+
One of the things that makes Fron unqiue is the ability to style components
|
3
|
+
directly on them. To achieve this we will use the **Fron::Behaviors::Style**
|
4
|
+
module (which is automatically included into every component).
|
5
|
+
|
6
|
+
Once the application is initailized all of the styles are rendered into a
|
7
|
+
**style** tag into the head of the document.
|
8
|
+
|
9
|
+
## Basic Usage
|
10
|
+
To define styles to a **component** you only need to call the **style** DSL
|
11
|
+
method with the object for the properties and values:
|
12
|
+
|
13
|
+
<%= example 'MyBox' %>
|
14
|
+
|
15
|
+
Tips on creating great style objects:
|
16
|
+
* For numbers there is two helper modules **px** and **em**, use them often
|
17
|
+
* Symbols can be used for non hypenated CSS values such as _block_ or _flex_
|
18
|
+
* Any value can be a Ruby expression
|
19
|
+
* Any value can be a lambda that is evaluated when the style is rendered
|
20
|
+
* Use caplitalized keys. They are converted to their hypenated versions
|
21
|
+
|
22
|
+
## Inheritance
|
23
|
+
When a component extends an other component that have styles, all of the style
|
24
|
+
definitions are copied over (this happens when the style DSL is used, otherwise
|
25
|
+
the **ensure_styles!** method needs to be called). Styles that are defined on the
|
26
|
+
child component have priority over the ones defined in the parent component.
|
27
|
+
|
28
|
+
Also if properties defined multiple times, only the last one will be used.
|
29
|
+
|
30
|
+
<%= example 'MyBlueBox' %>
|
31
|
+
|
32
|
+
## Styling Nested Components
|
33
|
+
|
34
|
+
If the component have **nested components** then they can be styled by having
|
35
|
+
a key with a selectors (like in raw CSS) and the associated value as a hash
|
36
|
+
containing the CSS properties and values:
|
37
|
+
|
38
|
+
<%= example 'MyGreenBox' %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# 2. Getting Started
|
2
|
+
This guide is for anyone who wants to get started with Fron. It's recommended to have a basic understanding of the **Ruby / JavaScript / CSS** programming languages and be familiar the **DOM**.
|
3
|
+
|
4
|
+
In order to try the examples the following prerequisites must be installed:
|
5
|
+
* Working Ruby installation. You can get the lastest Ruby from [Ruby download page](https://www.ruby-lang.org/en/downloads/)
|
6
|
+
* A web browser such as [Chrome](https://www.mozilla.org/en-US/firefox/new/) or [Firefox](http://www.google.com/chrome/)
|
7
|
+
|
8
|
+
You can find the examples [here]().
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# 1. Introduction
|
2
|
+
This guide will walk you through the basic concepts behind Fron. Fron is a library
|
3
|
+
for creating dynamic **composeable user interfaces** with Ruby (through Opal).
|
4
|
+
|
5
|
+
## Features
|
6
|
+
* **Custom components** that can be extended and composed
|
7
|
+
* **Behaviors** to specify how components behave
|
8
|
+
* **Utilities** such as **Request**, **Shortcuts** or **Drag & Drop**
|
9
|
+
* **Testable** with **RSpec**
|
10
|
+
|
11
|
+
## Resources
|
12
|
+
* Source Code: [https://github.com/digitalnatives/fron](https://github.com/digitalnatives/fron)
|
13
|
+
* Yard Documentation: [http://www.rubydoc.info/github/digitalnatives/fron/master](http://www.rubydoc.info/github/digitalnatives/fron/master)
|
14
|
+
|
15
|
+
## Why Ruby?
|
16
|
+
We chose ruby for these awesome features:
|
17
|
+
* It is a **mature language** (20 years)
|
18
|
+
* Has a very good **module** and **class** system
|
19
|
+
* A **coding style guide** is already present
|
20
|
+
* **Testing** is and always have been a core part (Rspec)
|
21
|
+
* The **tooling** is great (Rubycritic, Rubocop, Sprockets just to name a few)
|
22
|
+
|
23
|
+
## How it is different from [Volt](http://voltframework.com/) or [Vienna](https://github.com/opal/vienna)?
|
24
|
+
Both of those frameworks apply the same logic that has been used to create web sites for decades, namely the MVC pattern and templates, also they are not using the DOM
|
25
|
+
in a Rubyesque way, they treat the frontend as you would in a Rails application.
|
26
|
+
|
27
|
+
Fron is different because it is not trying to be a full featured web application, it is just the **fron(tend) part** in **Ruby**.
|
28
|
+
|
29
|
+
## It is used in production?
|
30
|
+
Currently we use it in production in one of our inhouse products [Nostromo](https://nostromo.io).
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# 5. Utilities
|
2
|
+
Fron provides a few **utility classes** by default:
|
3
|
+
|
4
|
+
* **Request** - Making and handling requests
|
5
|
+
* **LocalStorage** - LocalStorage wrapper
|
6
|
+
* **Keyboard** - Adding and handling shortcuts
|
7
|
+
* **Drag** - Low level dragging support
|
8
|
+
|
9
|
+
These classes are not connected to the interfaces directly but helps you solve a lot
|
10
|
+
of issues that generally come up for web applications.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# 5.2. Local Storage
|
2
|
+
|
3
|
+
*Notice: Only the LocalStorage and SessionStorage is implemented, we plan to implement CookieStorage with the same interface.*
|
4
|
+
|
5
|
+
This utility is for storing things in [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) you can see the API documentation [here](http://www.rubydoc.info/github/digitalnatives/fron/master/Fron/Storage/LocalStorage).
|
6
|
+
|
7
|
+
It implements the following class methods:
|
8
|
+
* **all** - Returns all keys and their values in a hash
|
9
|
+
* **clear** - Clears all keys
|
10
|
+
* **get(key)** - Get the value of the given key
|
11
|
+
* **remove(key)** - Removes the given key
|
12
|
+
* **set(key, value)** - Sets the given value to the given key
|
13
|
+
|
14
|
+
As an example the contents of this textarea persists between reloads:
|
15
|
+
|
16
|
+
<%= example 'TextArea' %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# 5.1. Request
|
2
|
+
|
3
|
+
*Warning: Not all features are available yet, but it's usable.*
|
4
|
+
|
5
|
+
This class is a wrapper for the [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). You can see the API documentation [here](http://www.rubydoc.info/github/digitalnatives/fron/master/Fron/Request).
|
6
|
+
|
7
|
+
As an example we want to display the first there lines of the source of this page in a component:
|
8
|
+
|
9
|
+
<%= example 'SourceReader' %>
|
10
|
+
|
11
|
+
As you can see we instantiated a new [**Fron::Request**](http://www.rubydoc.info/github/digitalnatives/fron/master/Fron/Request) object and called
|
12
|
+
the **get** method to get the source which returned a [**Fron::Response**](http://www.rubydoc.info/github/digitalnatives/fron/master/Fron/Response) object.
|
data/website/setup.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'fron'
|
2
|
+
require 'vendor/highlight'
|
3
|
+
require 'vendor/highlight.ruby'
|
4
|
+
require 'vendor/marked.min'
|
5
|
+
|
6
|
+
require 'examples/my_button'
|
7
|
+
require 'examples/my_box'
|
8
|
+
require_tree './examples'
|
9
|
+
|
10
|
+
%x{
|
11
|
+
marked.setOptions({
|
12
|
+
highlight: function(code) {
|
13
|
+
return hljs.highlightAuto(code).value;
|
14
|
+
}
|
15
|
+
});
|
16
|
+
}
|
17
|
+
|
18
|
+
class Sidebar < Fron::Component
|
19
|
+
class Item < Fron::Component
|
20
|
+
tag 'header-item'
|
21
|
+
end
|
22
|
+
|
23
|
+
component :title, 'sidebar-title', target: :home, text: 'Fron'
|
24
|
+
component :about, 'sidebar-item', target: 'intro', text: 'Introduction'
|
25
|
+
component :dom, 'sidebar-item', target: 'getting-started', text: 'Getting Started'
|
26
|
+
component :setup, 'sidebar-item', target: 'the-dom', text: 'The DOM'
|
27
|
+
component :components, 'sidebar-item', target: 'components', text: 'Components'
|
28
|
+
component :components, 'sidebar-sub-item', target: 'components/inheritance', text: 'Inheritance'
|
29
|
+
component :components, 'sidebar-sub-item', target: 'components/composition', text: 'Composition'
|
30
|
+
component :components, 'sidebar-sub-item', target: 'components/events', text: 'Events'
|
31
|
+
component :components, 'sidebar-sub-item', target: 'components/routes', text: 'Routes'
|
32
|
+
component :components, 'sidebar-sub-item', target: 'components/styles', text: 'Styles'
|
33
|
+
component :behaviors, 'sidebar-item', target: 'utilities', text: 'Utilities'
|
34
|
+
component :behaviors, 'sidebar-sub-item', target: 'utilities/request', text: 'Request'
|
35
|
+
component :behaviors, 'sidebar-sub-item', target: 'utilities/local-storage', text: 'Local Storage'
|
36
|
+
|
37
|
+
style counterReset: :items,
|
38
|
+
padding: 20.px,
|
39
|
+
'sidebar-title' => {
|
40
|
+
borderBottom: '2px solid #EEE',
|
41
|
+
paddingBottom: 5.px,
|
42
|
+
marginBottom: 10.px,
|
43
|
+
display: :block,
|
44
|
+
fontWeight: 600,
|
45
|
+
fontSize: 24.px
|
46
|
+
},
|
47
|
+
'[target]' => {
|
48
|
+
cursor: :pointer
|
49
|
+
},
|
50
|
+
'sidebar-item' => {
|
51
|
+
borderBottom: '1px solid #EEE',
|
52
|
+
counterIncrement: :items,
|
53
|
+
counterReset: :subitems,
|
54
|
+
padding: '10px 0',
|
55
|
+
display: :block,
|
56
|
+
'&:before' => {
|
57
|
+
content: 'counter(items) ". "'
|
58
|
+
}
|
59
|
+
},
|
60
|
+
'sidebar-sub-item' => {
|
61
|
+
borderBottom: '1px solid #EEE',
|
62
|
+
counterIncrement: :subitems,
|
63
|
+
padding: '10px 0',
|
64
|
+
paddingLeft: 10.px,
|
65
|
+
display: :block,
|
66
|
+
'&:before' => {
|
67
|
+
content: 'counter(items) "." counter(subitems) ". "'
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
on :click, '[target]', :navigate
|
72
|
+
|
73
|
+
def navigate(event)
|
74
|
+
DOM::Window.state = "/#{event.target[:target]}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Main < Fron::Component
|
79
|
+
include Fron::Behaviors::Routes
|
80
|
+
|
81
|
+
component :sidebar, Sidebar
|
82
|
+
component :wrapper, :wrapper do
|
83
|
+
component :container, :container
|
84
|
+
end
|
85
|
+
|
86
|
+
style fontFamily: 'Open Sans',
|
87
|
+
width: 1200.px,
|
88
|
+
margin: '0 auto',
|
89
|
+
color: '#444',
|
90
|
+
a: {
|
91
|
+
textDecoration: :none,
|
92
|
+
color: '#00ACE6'
|
93
|
+
},
|
94
|
+
sidebar: {
|
95
|
+
width: 240.px,
|
96
|
+
float: :left
|
97
|
+
},
|
98
|
+
pre: {
|
99
|
+
background: '#F9F9F9',
|
100
|
+
borderRadius: 5.px,
|
101
|
+
padding: 20.px
|
102
|
+
},
|
103
|
+
wrapper: {
|
104
|
+
overflow: :auto,
|
105
|
+
padding: 40.px,
|
106
|
+
paddingBottom: 60.px,
|
107
|
+
marginLeft: 240.px,
|
108
|
+
display: :block
|
109
|
+
},
|
110
|
+
h1: {
|
111
|
+
lineHeight: '1em',
|
112
|
+
marginTop: 0
|
113
|
+
}
|
114
|
+
|
115
|
+
route '(.*)', :page
|
116
|
+
|
117
|
+
def initialize
|
118
|
+
super
|
119
|
+
@pages = {}
|
120
|
+
Fron::Sheet.render_style_tag
|
121
|
+
Fron::Behaviors::Routes.listen
|
122
|
+
end
|
123
|
+
|
124
|
+
def home
|
125
|
+
DOM::Window.state = '/home'
|
126
|
+
end
|
127
|
+
|
128
|
+
def page(page)
|
129
|
+
return home if page == '/'
|
130
|
+
load page do |html|
|
131
|
+
@wrapper.container.html = html
|
132
|
+
@wrapper.container.find_all('example').each do |example|
|
133
|
+
example << Module.const_get(example['class']).new
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def load(page)
|
139
|
+
return yield @pages[page] if @pages[page]
|
140
|
+
Fron::Request.new("/assets/pages#{page}.md").get do |response|
|
141
|
+
@pages[page] = `marked(#{response.body})`
|
142
|
+
yield @pages[page]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
Fron::Behaviors::Style::Sheet.add_rule '*', { boxSizing: 'border-box' }, '0'
|
148
|
+
Fron::Behaviors::Style::Sheet.add_rule 'body', { margin: 0,
|
149
|
+
overflowY: :scroll,
|
150
|
+
fontSize: 18.px,
|
151
|
+
lineHeight: 26.px }, '1'
|
152
|
+
|
153
|
+
['//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css',
|
154
|
+
'//fonts.googleapis.com/css?family=Open+Sans:400,600,700',
|
155
|
+
'//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.5/styles/tomorrow.min.css'
|
156
|
+
].each do |url|
|
157
|
+
link = DOM::Element.new 'link'
|
158
|
+
link[:rel] = :stylesheet
|
159
|
+
link[:type] = 'text/css'
|
160
|
+
link[:href] = url
|
161
|
+
link >> DOM::Document.head
|
162
|
+
end
|