Skip to content

Commit f04af5c

Browse files
committed
Initial setup
0 parents  commit f04af5c

File tree

18 files changed

+542
-0
lines changed

18 files changed

+542
-0
lines changed

.circleci/config.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Elixir CircleCI 2.0 configuration file
2+
#
3+
# Check https://circleci.com/docs/2.0/language-elixir/ for more details
4+
version: 2
5+
jobs:
6+
build:
7+
working_directory: ~/repo
8+
docker:
9+
- image: defactosoftware/elixir:1.5
10+
environment:
11+
MIX_ENV: test
12+
steps:
13+
- run: mix local.hex --force
14+
- run: mix local.rebar --force
15+
- checkout
16+
- restore_cache:
17+
keys:
18+
- v1-mix-deps-{{ checksum "mix.lock" }}
19+
- run: mix deps.get
20+
- run: mix compile
21+
- run: mix test
22+
- run: mix credo --strict
23+
- save_cache:
24+
key: v1-mix-deps-{{ checksum "mix.lock" }}
25+
paths:
26+
- "deps"
27+
- "_build"

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# App artifacts
2+
/_build
3+
/db
4+
/deps
5+
/*.ez
6+
/doc
7+
8+
.DS_Store
9+
10+
# Generated on crash by the VM
11+
erl_crash.dump
12+
13+
# Generated on crash by NPM
14+
npm-debug.log
15+
.nyc_output
16+
17+
cover/
18+
19+
# Files matching config/*.secret.exs pattern contain sensitive
20+
# data and you should not commit them into version control.
21+
/config/*.secret.exs

CHANGELOG.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# v0.0.1
2+
3+
## Added
4+
- Initial setup

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Defacto
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# ExCSSModules
2+
3+
[CSS Modules](https://github.com/css-modules/css-modules) for Elixir. Build to integrate well with the [Phoenix Framework](http://phoenixframework.org/).
4+
5+
[![Hex.pm](https://img.shields.io/hexpm/v/ex_cell.svg)](https://hex.pm/packages/ex_css_modules)
6+
[![CircleCI](https://circleci.com/gh/DefactoSoftware/ex_css_modules/tree/master.svg?style=shield)](https://circleci.com/gh/DefactoSoftware/ex_css_modules)
7+
8+
ExCSSModules currently requires the CSS Modules definitions JSON file to be compiled next to the CSS file itself and the files to be compiled before the Elixir application is build.
9+
10+
## Installation
11+
Install from [Hex.pm](https://hex.pm/packages/ex_css_modules):
12+
13+
```ex
14+
def deps do
15+
[{:ex_css_modules, "~> 0.0.1"}]
16+
end
17+
```
18+
19+
## Usage
20+
To use ExCSSModules in a view compile the CSS file (ie: through brunch of webpack) and add the following to the view:
21+
22+
```ex
23+
defmodule MyApplication.ExampleView do
24+
use ExCSSModules.View, stylesheet: Path.relative_to_cwd("assets/css/views/example.css")
25+
end
26+
```
27+
28+
This allows you to use the `class` and `class_name` functions in the template and views as followed:
29+
30+
**CSS:**
31+
```css
32+
.title {
33+
font-size: huge;
34+
}
35+
```
36+
37+
**Template:**
38+
```ex
39+
<h1 <%= class "title" %>>Hello world</h1>
40+
```
41+
42+
**Output:**
43+
```ex
44+
<h1 class="_2313dsc-title">Hello world</h1>
45+
```
46+
## Advanced usage
47+
48+
### Phoenix.View
49+
ExCSSModules is made for the Phoenix framework and can be easily be automatically be added to your view definition in `web.ex`. At [Defacto](https://github.com/DefactoSoftware) we automatically parse the View name and extract the correct stylesheet from it:
50+
51+
```ex
52+
def view() do
53+
quote do
54+
use Phoenix.View, root: "lib/my_application_web/templates",
55+
namespace: MyApplicationWeb
56+
use ExCSSModules.View, namespace: MyApplicationWeb,
57+
stylesheet: Path.join(
58+
"assets/css/templates",
59+
__MODULE__
60+
|> Module.split
61+
|> Enum.map(&Phoenix.Naming.underscore/1)
62+
|> Enum.join("/")
63+
|> String.replace_suffix("_view", ".css")
64+
)
65+
...
66+
end
67+
end
68+
```
69+
70+
### ExCSSModules
71+
ExCSSModules works perfectly with [ExCell](https://github.com/DefactoSoftware/ex_cell). By adding the following to your `web.ex` definition for cells you can automatically add the stylesheet in the same directory as your ExCell Cell:
72+
```ex
73+
def cell() do
74+
quote do
75+
use ExCell.Cell, namespace: MyApplicationWeb
76+
use ExCSSModules.View, namespace: MyApplicationWeb,
77+
stylesheet: __ENV__.file
78+
|> Path.dirname
79+
|> Path.join("./style.css")
80+
...
81+
end
82+
end
83+
```

config/config.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
use Mix.Config
2+
3+
import_config "#{Mix.env}.exs"

config/dev.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use Mix.Config

config/production.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use Mix.Config

config/test.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use Mix.Config
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
defmodule ExCSSModules do
2+
@moduledoc """
3+
CSS Modules helpers
4+
"""
5+
6+
alias __MODULE__
7+
alias Phoenix.HTML
8+
9+
@doc """
10+
Reads the stylesheet. Returns a map if the stylesheet is already a map. Reads
11+
the file if the stylesheet is a string.
12+
13+
## Examples
14+
15+
iex> stylesheet(%{})
16+
%{}
17+
18+
iex> stylesheet("../stylesheet.css")
19+
%{}
20+
"""
21+
def stylesheet(definition) when is_map(definition), do: definition
22+
23+
@doc false
24+
def stylesheet(definition), do: definition |> read_stylesheet()
25+
26+
def read_stylesheet(filename) do
27+
case File.exists?(filename) do
28+
true -> filename <> ".json"
29+
|> File.read!
30+
|> Poison.decode!
31+
false -> %{}
32+
end
33+
end
34+
35+
@doc """
36+
Reads the class definitions from the definition map and maps them to a class
37+
attribute. The classes argument takes any argument the class_name for the key.
38+
39+
## Examples
40+
iex> class(%{ "hello" => "world"}, "hello")
41+
{:safe, ~s(class="world")}
42+
"""
43+
def class(definition, classes) do
44+
definition
45+
|> class_name(classes)
46+
|> class_attribute
47+
end
48+
49+
@doc """
50+
Returns the class name from the definition map is value is true.
51+
52+
## Examples
53+
iex> class_name(%{"hello" => "world"}, "hello", true)
54+
"world"
55+
56+
iex> class_name(%{"hello" => "world"}, "hello", false)
57+
nil
58+
"""
59+
def class_name(definition, key, value) do
60+
if value do
61+
class_name(definition, key)
62+
end
63+
end
64+
65+
@doc """
66+
Returns the class name from the definition map is the second argument
67+
in the tuple is true.
68+
69+
## Examples
70+
iex> class_name(%{"hello" => "world"}, {"hello", true})
71+
"world"
72+
73+
iex> class_name(%{"hello" => "world"}, {"hello", false})
74+
nil
75+
"""
76+
def class_name(definition, {key, value}), do:
77+
class_name(definition, key, value)
78+
79+
@doc """
80+
Returns the class name sfrom the definition map when the argument is a list
81+
of values or tuples.
82+
83+
## Examples
84+
iex> class_name(%{"hello" => "world", "foo" => "bar"}, ["hello", "foo"])
85+
"world bar"
86+
87+
iex> class_name(%{"hello" => "world", "foo" => "bar"}, [{"hello", true}, {"foo", true}])
88+
"world bar"
89+
90+
iex> class_name(%{"hello" => "world", "foo" => "bar"}, [{"hello", true}, {"foo", false}])
91+
"world"
92+
"""
93+
def class_name(definition, keys) when is_list(keys) do
94+
keys
95+
|> Enum.map(&class_name(definition, &1))
96+
|> Enum.reject(&is_nil/1)
97+
|> Enum.join(" ")
98+
end
99+
100+
@doc """
101+
Returns the class name sfrom the definition map when the argument is a list
102+
of values or tuples.
103+
104+
## Examples
105+
iex> class_name(%{"hello" => "world"}, "hello")
106+
"world"
107+
108+
iex> class_name(%{"hello" => "world"}, "foo")
109+
nil
110+
"""
111+
def class_name(stylesheet, key) do
112+
stylesheet
113+
|> stylesheet()
114+
|> Map.get(key, nil)
115+
end
116+
117+
@doc """
118+
Takes the definition and makes a class selector that can be used in CSS out of
119+
the classes given. Takes either a single value or a list of classes.
120+
121+
## Examples
122+
iex> class_selector(%{ "hello" => "world"}, "hello")
123+
".world"
124+
iex> class_selector(%{ "hello" => "world", "foo" => "bar"}, ["hello", "foo"])
125+
".world.foo"
126+
"""
127+
def class_selector(definition, classes) when is_list(classes) do
128+
classes
129+
|> Enum.map(&class_selector(definition, &1))
130+
|> Enum.reject(&is_nil/1)
131+
|> Enum.join("")
132+
end
133+
134+
@doc false
135+
def class_selector(definition, class) do
136+
case class_name(definition, class) do
137+
nil -> nil
138+
value -> ".#{value}"
139+
end
140+
end
141+
142+
defp class_attribute(class), do: HTML.raw(~s(class="#{class}"))
143+
end

0 commit comments

Comments
 (0)