bootstrap-email 0.3.4 → 1.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/bootstrap-email +2 -0
- data/core/bootstrap-email.scss +27 -18
- data/core/bootstrap-head.scss +143 -0
- data/core/layout.html.erb +11 -0
- data/core/scss/_colors.scss +161 -0
- data/core/scss/_functions.scss +29 -0
- data/core/scss/_helper_groups.scss +21 -0
- data/core/{sass → scss}/_reboot_email.scss +7 -22
- data/core/scss/_selectors_for_utils.scss +100 -0
- data/core/scss/_utilities.scss +125 -0
- data/core/scss/_variables.scss +52 -0
- data/core/{sass → scss/components}/_alert.scss +0 -1
- data/core/{sass → scss/components}/_badge.scss +8 -15
- data/core/{sass → scss/components}/_button.scss +21 -17
- data/core/{sass → scss/components}/_card.scss +3 -2
- data/core/{sass → scss/components}/_container.scss +0 -0
- data/core/scss/components/_grid.scss +35 -0
- data/core/scss/components/_hr.scss +8 -0
- data/core/{sass → scss/components}/_image.scss +2 -1
- data/core/{sass → scss/components}/_preview.scss +0 -0
- data/core/{sass → scss/components}/_table.scss +0 -0
- data/core/scss/utilities/_background.scss +5 -0
- data/core/scss/utilities/_border-radius.scss +21 -0
- data/core/scss/utilities/_border.scss +13 -0
- data/core/scss/utilities/_color.scss +9 -0
- data/core/scss/utilities/_display.scss +7 -0
- data/core/scss/utilities/_sizing.scss +16 -0
- data/core/scss/utilities/_spacing.scss +18 -0
- data/core/scss/utilities/_text-decoration.scss +9 -0
- data/core/scss/utilities/_typography.scss +54 -0
- data/core/templates/col.html.erb +2 -2
- data/core/templates/row.html.erb +2 -2
- data/lib/bootstrap-email/bootstrap_email_cli.rb +91 -0
- data/lib/bootstrap-email/compiler.rb +118 -0
- data/lib/bootstrap-email/components/alert.rb +11 -0
- data/lib/bootstrap-email/components/align.rb +21 -0
- data/lib/bootstrap-email/components/badge.rb +11 -0
- data/lib/bootstrap-email/components/base.rb +26 -0
- data/lib/bootstrap-email/components/body.rb +22 -0
- data/lib/bootstrap-email/components/button.rb +11 -0
- data/lib/bootstrap-email/components/card.rb +14 -0
- data/lib/bootstrap-email/components/color.rb +13 -0
- data/lib/bootstrap-email/components/container.rb +14 -0
- data/lib/bootstrap-email/components/grid.rb +14 -0
- data/lib/bootstrap-email/components/hr.rb +12 -0
- data/lib/bootstrap-email/components/margin.rb +22 -0
- data/lib/bootstrap-email/components/padding.rb +16 -0
- data/lib/bootstrap-email/components/paragraph.rb +24 -0
- data/lib/bootstrap-email/components/spacer.rb +11 -0
- data/lib/bootstrap-email/components/spacing.rb +18 -0
- data/lib/bootstrap-email/components/table.rb +13 -0
- data/lib/bootstrap-email/initialize.rb +1 -0
- data/lib/bootstrap-email/{action_mailer.rb → rails/action_mailer.rb} +3 -7
- data/lib/bootstrap-email/{engine.rb → rails/engine.rb} +0 -0
- data/lib/bootstrap-email/sass_cache.rb +47 -0
- data/lib/bootstrap-email/version.rb +3 -5
- data/lib/bootstrap_email.rb +26 -0
- metadata +70 -58
- data/core/head.scss +0 -269
- data/core/sass/_border.scss +0 -73
- data/core/sass/_color.scss +0 -33
- data/core/sass/_display.scss +0 -7
- data/core/sass/_functions.scss +0 -14
- data/core/sass/_grid.scss +0 -131
- data/core/sass/_hr.scss +0 -13
- data/core/sass/_spacing.scss +0 -100
- data/core/sass/_typography.scss +0 -50
- data/core/sass/_variables.scss +0 -150
- data/core/template.html.erb +0 -11
- data/core/templates/align-center.html.erb +0 -9
- data/core/templates/align-left.html.erb +0 -9
- data/core/templates/align-right.html.erb +0 -9
- data/core/templates/hr.html.erb +0 -9
- data/lib/assets/stylesheets/bootstrap-email.scss +0 -1
- data/lib/bootstrap-email.rb +0 -230
- data/lib/bootstrap-email/premailer_railtie.rb +0 -5
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
@each $name, $radius in $border-radiuses {
|
2
|
+
@include border-radius-util('.rounded#{$name}') {
|
3
|
+
border-radius: $radius;
|
4
|
+
}
|
5
|
+
@include border-radius-util('.rounded-top#{$name}') {
|
6
|
+
border-top-left-radius: $radius;
|
7
|
+
border-top-right-radius: $radius;
|
8
|
+
}
|
9
|
+
@include border-radius-util('.rounded-right#{$name}') {
|
10
|
+
border-top-right-radius: $radius;
|
11
|
+
border-bottom-right-radius: $radius;
|
12
|
+
}
|
13
|
+
@include border-radius-util('.rounded-bottom#{$name}') {
|
14
|
+
border-bottom-right-radius: $radius;
|
15
|
+
border-bottom-left-radius: $radius;
|
16
|
+
}
|
17
|
+
@include border-radius-util('.rounded-left#{$name}') {
|
18
|
+
border-top-left-radius: $radius;
|
19
|
+
border-bottom-left-radius: $radius;
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
@each $name, $width in $border-widths {
|
2
|
+
.border#{$name} { border: $width solid $border-color !important; }
|
3
|
+
.border-top#{$name} { border-top: $width solid $border-color !important; }
|
4
|
+
.border-right#{$name} { border-right: $width solid $border-color !important; }
|
5
|
+
.border-bottom#{$name} { border-bottom: $width solid $border-color !important; }
|
6
|
+
.border-left#{$name} { border-left: $width solid $border-color !important; }
|
7
|
+
}
|
8
|
+
|
9
|
+
@each $name, $color in $all-colors {
|
10
|
+
@include border-color-util('.border-#{$name}') {
|
11
|
+
border-color: $color !important;
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
@each $prefix in $breakpoints {
|
2
|
+
@each $name, $property in $sizing-types {
|
3
|
+
@include sizing-util('.#{$name}-#{$prefix}full') {
|
4
|
+
max-#{$property}: 100%;
|
5
|
+
-premailer-#{$property}: 100%;
|
6
|
+
#{$property}: 100%;
|
7
|
+
}
|
8
|
+
@each $size, $value in $sizing {
|
9
|
+
@include sizing-util('.#{$name}-#{$prefix}#{$size}') {
|
10
|
+
max-#{$property}: $value;
|
11
|
+
-premailer-#{$property}: strip-unit($value);
|
12
|
+
#{$property}: 100%;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// Padding Util
|
2
|
+
@each $prefix in $breakpoints {
|
3
|
+
@each $size, $value in $spacers {
|
4
|
+
@include padding-group($prefix, $size, $value);
|
5
|
+
}
|
6
|
+
}
|
7
|
+
|
8
|
+
// Spacing Util
|
9
|
+
@each $prefix in $breakpoints {
|
10
|
+
@each $size, $value in $spacers {
|
11
|
+
@include spacer-util('.s-#{$prefix}#{$size}') {
|
12
|
+
font-size: $value;
|
13
|
+
line-height: $value;
|
14
|
+
height: $value;
|
15
|
+
-premailer-height: strip-unit($value);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
h1, h2, h3, h4, h5, h6,
|
2
|
+
.h1, .h2, .h3, .h4, .h5, .h6 {
|
3
|
+
margin: 0;
|
4
|
+
padding-top: $headings-margin-top;
|
5
|
+
padding-bottom: $headings-margin-bottom;
|
6
|
+
font-family: $headings-font-family;
|
7
|
+
font-weight: $headings-font-weight;
|
8
|
+
color: $headings-color;
|
9
|
+
text-align: left;
|
10
|
+
vertical-align: baseline;
|
11
|
+
}
|
12
|
+
|
13
|
+
@each $name, $size in $headings {
|
14
|
+
h#{$name}, .h#{$name} {
|
15
|
+
font-size: $size;
|
16
|
+
line-height: $size * $headings-line-height;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
@each $name, $size in $font-size {
|
21
|
+
.text-#{$name} {
|
22
|
+
font-size: $size;
|
23
|
+
line-height: $size * $headings-line-height;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
@each $name, $size in $line-height {
|
28
|
+
.lh-#{$name} {
|
29
|
+
line-height: $size;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
@for $n from 1 through 9 {
|
34
|
+
@include font-weight-util('.fw-#{$n * 100}') {
|
35
|
+
font-weight: #{$n * 100} !important;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
.text-left {
|
40
|
+
text-align: left !important;
|
41
|
+
}
|
42
|
+
|
43
|
+
.text-right {
|
44
|
+
text-align: right !important;
|
45
|
+
}
|
46
|
+
|
47
|
+
.text-center {
|
48
|
+
text-align: center !important;
|
49
|
+
}
|
50
|
+
|
51
|
+
p {
|
52
|
+
width: 100%;
|
53
|
+
margin: 0;
|
54
|
+
}
|
data/core/templates/col.html.erb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
<
|
1
|
+
<td class="<%= classes %>" align="left" valign="top">
|
2
2
|
<%= contents %>
|
3
|
-
</
|
3
|
+
</td>
|
data/core/templates/row.html.erb
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative '../bootstrap_email'
|
2
|
+
require 'optparse'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
input = nil
|
6
|
+
options = {
|
7
|
+
destination: 'output',
|
8
|
+
type: :file
|
9
|
+
}
|
10
|
+
|
11
|
+
parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Bootstrap 5 stylesheet, compiler, and inliner for responsive and consistent emails with the Bootstrap syntax you know and love.\n\n"
|
13
|
+
opts.define_head 'Usage: bootstrap-email <path> [options]'
|
14
|
+
opts.separator ''
|
15
|
+
opts.separator 'Examples:'
|
16
|
+
opts.separator ' bootstrap-email'
|
17
|
+
opts.separator ' bootstrap-email email.html > out.html'
|
18
|
+
opts.separator ' bootstrap-email ./public/index.html'
|
19
|
+
opts.separator ' bootstrap-email -s \'<a href="#" class="btn btn-primary">Some Button</a>\''
|
20
|
+
opts.separator ' bootstrap-email -p \'emails/*\' -d emails/output'
|
21
|
+
opts.separator ' bootstrap-email -p \'views/emails/*\' -d \'views/compiled_emails\''
|
22
|
+
opts.separator ' cat input.html | bootstrap-email'
|
23
|
+
opts.separator ''
|
24
|
+
opts.separator 'Options:'
|
25
|
+
|
26
|
+
opts.on('-s', '--string STRING', String, 'HTML string to be to be compiled rather than a file.') do |v|
|
27
|
+
input = v
|
28
|
+
options[:type] = :string
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-p', '--pattern STRING', String, 'Specify a pattern of files to compile and save multiple files at once.') do |v|
|
32
|
+
input = v
|
33
|
+
options[:type] = :pattern
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('-d', '--destination STRING', String, 'Destination for compiled files (used with the --pattern option).') do |v|
|
37
|
+
options[:destination] = v
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on('-c', '--config STRING', String, 'Relative path to SCSS config config file to customize Bootstrap Email.') do |v|
|
41
|
+
options[:config] = File.expand_path(v, Dir.pwd)
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on('-h', '--help', 'Prints this help') do
|
45
|
+
puts opts
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on('-v', '--version', 'Show version') do
|
50
|
+
puts BootstrapEmail::VERSION
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
end
|
54
|
+
parser.parse!
|
55
|
+
|
56
|
+
if input
|
57
|
+
# input already set by pattern
|
58
|
+
elsif ARGV.any?
|
59
|
+
# Executed via command line or shell script
|
60
|
+
input = ARGV.shift
|
61
|
+
elsif !STDIN.tty?
|
62
|
+
# Called in piped command
|
63
|
+
input = STDIN.read
|
64
|
+
options[:type] = :string
|
65
|
+
else
|
66
|
+
# Running just the blank command to compile all files in directory containing .html
|
67
|
+
input = '*.html*'
|
68
|
+
options[:type] = :pattern
|
69
|
+
end
|
70
|
+
|
71
|
+
if input
|
72
|
+
case options[:type]
|
73
|
+
when :pattern
|
74
|
+
Dir.glob(input, base: Dir.pwd).each do |path|
|
75
|
+
next unless File.file?(path)
|
76
|
+
|
77
|
+
puts "Compiling file #{path}"
|
78
|
+
compiled = BootstrapEmail::Compiler.new(path, type: :file, options: {config_path: options[:config]}).perform_full_compile
|
79
|
+
FileUtils.mkdir_p("#{Dir.pwd}/#{options[:destination]}")
|
80
|
+
File.write(File.expand_path("#{options[:destination]}/#{path.split('/').last}", Dir.pwd), compiled)
|
81
|
+
end
|
82
|
+
when :file
|
83
|
+
path = File.expand_path(input, Dir.pwd)
|
84
|
+
puts BootstrapEmail::Compiler.new(File.join(Dir.pwd, path), type: :file, options: {config_path: options[:config]}).perform_full_compile
|
85
|
+
when :string
|
86
|
+
puts BootstrapEmail::Compiler.new(input, options: {config_path: options[:config]}).perform_full_compile
|
87
|
+
end
|
88
|
+
else
|
89
|
+
puts opts
|
90
|
+
exit 1
|
91
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
class Compiler
|
3
|
+
attr_accessor :type, :doc, :premailer
|
4
|
+
|
5
|
+
def initialize(input, type: :string, options: {})
|
6
|
+
self.type = type
|
7
|
+
case type
|
8
|
+
when :rails
|
9
|
+
@mail = input
|
10
|
+
html = (@mail.html_part || @mail).body.raw_source
|
11
|
+
when :string
|
12
|
+
html = input
|
13
|
+
when :file
|
14
|
+
html = File.read(input)
|
15
|
+
end
|
16
|
+
build_premailer_doc(html, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform_full_compile
|
20
|
+
compile_html!
|
21
|
+
inline_css!
|
22
|
+
inject_head!
|
23
|
+
inject_comment!
|
24
|
+
finalize_document!
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def build_premailer_doc(html, options)
|
30
|
+
html = add_layout!(html)
|
31
|
+
SassC.load_paths << File.expand_path('../../core', __dir__)
|
32
|
+
css_string = BootstrapEmail::SassCache.compile('bootstrap-email', config_path: options[:config_path], style: :expanded)
|
33
|
+
self.premailer = Premailer.new(
|
34
|
+
html,
|
35
|
+
with_html_string: true,
|
36
|
+
preserve_reset: false,
|
37
|
+
css_string: css_string
|
38
|
+
)
|
39
|
+
self.doc = premailer.doc
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_layout!(html)
|
43
|
+
document = Nokogiri::HTML(html)
|
44
|
+
return document.to_html unless document.at_css('head').nil?
|
45
|
+
|
46
|
+
namespace = OpenStruct.new(contents: ERB.new(document.to_html).result)
|
47
|
+
template_html = File.read(File.expand_path('../../core/layout.html.erb', __dir__))
|
48
|
+
ERB.new(template_html).result(namespace.instance_eval { binding })
|
49
|
+
end
|
50
|
+
|
51
|
+
def compile_html!
|
52
|
+
BootstrapEmail::Component::Button.build(doc)
|
53
|
+
BootstrapEmail::Component::Badge.build(doc)
|
54
|
+
BootstrapEmail::Component::Alert.build(doc)
|
55
|
+
BootstrapEmail::Component::Card.build(doc)
|
56
|
+
BootstrapEmail::Component::Paragraph.build(doc)
|
57
|
+
BootstrapEmail::Component::Hr.build(doc)
|
58
|
+
BootstrapEmail::Component::Container.build(doc)
|
59
|
+
BootstrapEmail::Component::Grid.build(doc)
|
60
|
+
BootstrapEmail::Component::Align.build(doc)
|
61
|
+
BootstrapEmail::Component::Color.build(doc)
|
62
|
+
BootstrapEmail::Component::Padding.build(doc)
|
63
|
+
BootstrapEmail::Component::Margin.build(doc)
|
64
|
+
BootstrapEmail::Component::Spacing.build(doc)
|
65
|
+
BootstrapEmail::Component::Spacer.build(doc)
|
66
|
+
BootstrapEmail::Component::Table.build(doc)
|
67
|
+
BootstrapEmail::Component::Body.build(doc)
|
68
|
+
end
|
69
|
+
|
70
|
+
def inline_css!
|
71
|
+
premailer.to_inline_css
|
72
|
+
end
|
73
|
+
|
74
|
+
def inject_head!
|
75
|
+
doc.at_css('head').add_child(bootstrap_email_head)
|
76
|
+
end
|
77
|
+
|
78
|
+
def inject_comment!
|
79
|
+
doc.at_css('head').prepend_child(bootstrap_email_comment)
|
80
|
+
end
|
81
|
+
|
82
|
+
def finalize_document!
|
83
|
+
case type
|
84
|
+
when :rails
|
85
|
+
(@mail.html_part || @mail).body = doc.to_html
|
86
|
+
@mail
|
87
|
+
when :string, :file
|
88
|
+
doc.to_html
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def bootstrap_email_head
|
93
|
+
html_string = <<-INLINE
|
94
|
+
<style type="text/css">
|
95
|
+
#{purged_css_from_head}
|
96
|
+
</style>
|
97
|
+
INLINE
|
98
|
+
html_string
|
99
|
+
end
|
100
|
+
|
101
|
+
def bootstrap_email_comment
|
102
|
+
"\n <!-- Compiled with Bootstrap Email version: #{BootstrapEmail::VERSION} -->"
|
103
|
+
end
|
104
|
+
|
105
|
+
def purged_css_from_head
|
106
|
+
default, custom = BootstrapEmail::SassCache.compile('bootstrap-head').split('/*! allow_purge_after */')
|
107
|
+
# get each CSS declaration
|
108
|
+
custom.scan(/\w*\.[\w\-]*[\s\S\n]+?(?=})}{1}/).each do |group|
|
109
|
+
# get the first class for each comma separated CSS declaration
|
110
|
+
exist = group.scan(/(\.[\w\-]*).*?((,+?)|{+?)/).map(&:first).uniq.any? do |selector|
|
111
|
+
!doc.at_css(selector).nil?
|
112
|
+
end
|
113
|
+
custom.sub!(group, '') unless exist
|
114
|
+
end
|
115
|
+
(default + custom).gsub(/\n\s*\n+/, "\n")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
module Component
|
3
|
+
class Align < Base
|
4
|
+
def build
|
5
|
+
['left', 'center', 'right'].each do |type|
|
6
|
+
each_node(".align-#{type}") do |node|
|
7
|
+
align_helper(node, type)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def align_helper(node, type)
|
13
|
+
if node.name != 'table'
|
14
|
+
node['class'] = node['class'].sub("align-#{type}", '')
|
15
|
+
node = node.replace(template('table', classes: "align-#{type}", contents: node.to_html))[0]
|
16
|
+
end
|
17
|
+
node['align'] = type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|