bootstrap-email 0.3.0 → 1.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/rails/action_mailer.rb +10 -0
- 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 +76 -64
- 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 -14
- 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/action_mailer.rb +0 -12
- 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
|