warped 0.1.0 → 1.0.0
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/.rubocop.yml +25 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +25 -19
- data/README.md +116 -270
- data/app/assets/config/warped_manifest.js +2 -0
- data/app/assets/javascript/warped/controllers/filter_controller.js +76 -0
- data/app/assets/javascript/warped/controllers/filters_controller.js +21 -0
- data/app/assets/javascript/warped/index.js +2 -0
- data/app/assets/stylesheets/warped/application.css +15 -0
- data/app/assets/stylesheets/warped/base.css +23 -0
- data/app/assets/stylesheets/warped/filters.css +115 -0
- data/app/assets/stylesheets/warped/pagination.css +74 -0
- data/app/assets/stylesheets/warped/search.css +33 -0
- data/app/assets/stylesheets/warped/table.css +114 -0
- data/app/views/warped/_actions.html.erb +9 -0
- data/app/views/warped/_cell.html.erb +3 -0
- data/app/views/warped/_column.html.erb +35 -0
- data/app/views/warped/_filters.html.erb +21 -0
- data/app/views/warped/_hidden_fields.html.erb +19 -0
- data/app/views/warped/_pagination.html.erb +34 -0
- data/app/views/warped/_row.html.erb +19 -0
- data/app/views/warped/_search.html.erb +21 -0
- data/app/views/warped/_table.html.erb +52 -0
- data/app/views/warped/filters/_filter.html.erb +40 -0
- data/config/importmap.rb +3 -0
- data/docs/controllers/FILTERABLE.md +193 -0
- data/docs/controllers/PAGEABLE.md +70 -0
- data/docs/controllers/README.md +8 -0
- data/docs/controllers/SEARCHABLE.md +95 -0
- data/docs/controllers/SORTABLE.md +94 -0
- data/docs/controllers/TABULATABLE.md +28 -0
- data/docs/controllers/views/PARTIALS.md +285 -0
- data/docs/jobs/README.md +22 -0
- data/docs/services/README.md +81 -0
- data/lib/generators/warped/install_generator.rb +1 -1
- data/lib/warped/api/filter/base/value.rb +52 -0
- data/lib/warped/api/filter/base.rb +84 -0
- data/lib/warped/api/filter/boolean.rb +41 -0
- data/lib/warped/api/filter/date.rb +26 -0
- data/lib/warped/api/filter/date_time.rb +32 -0
- data/lib/warped/api/filter/decimal.rb +31 -0
- data/lib/warped/api/filter/factory.rb +38 -0
- data/lib/warped/api/filter/integer.rb +38 -0
- data/lib/warped/api/filter/string.rb +25 -0
- data/lib/warped/api/filter/time.rb +25 -0
- data/lib/warped/api/filter.rb +14 -0
- data/lib/warped/api/sort/value.rb +40 -0
- data/lib/warped/api/sort.rb +65 -0
- data/lib/warped/controllers/filterable/ui.rb +46 -0
- data/lib/warped/controllers/filterable.rb +79 -42
- data/lib/warped/controllers/pageable/ui.rb +70 -0
- data/lib/warped/controllers/pageable.rb +11 -11
- data/lib/warped/controllers/searchable/ui.rb +37 -0
- data/lib/warped/controllers/searchable.rb +2 -0
- data/lib/warped/controllers/sortable/ui.rb +53 -0
- data/lib/warped/controllers/sortable.rb +53 -33
- data/lib/warped/controllers/tabulatable/ui.rb +54 -0
- data/lib/warped/controllers/tabulatable.rb +13 -27
- data/lib/warped/emails/components/align.rb +21 -0
- data/lib/warped/emails/components/base.rb +116 -0
- data/lib/warped/emails/components/button.rb +58 -0
- data/lib/warped/emails/components/divider.rb +15 -0
- data/lib/warped/emails/components/heading.rb +65 -0
- data/lib/warped/emails/components/layouts/columns.rb +36 -0
- data/lib/warped/emails/components/layouts/cta.rb +38 -0
- data/lib/warped/emails/components/layouts/main.rb +34 -0
- data/lib/warped/emails/components/link.rb +36 -0
- data/lib/warped/emails/components/spacer.rb +15 -0
- data/lib/warped/emails/components/stepper.rb +104 -0
- data/lib/warped/emails/components/table.rb +37 -0
- data/lib/warped/emails/components/text.rb +67 -0
- data/lib/warped/emails/helpers.rb +26 -0
- data/lib/warped/emails/slottable.rb +61 -0
- data/lib/warped/emails/styleable.rb +160 -0
- data/lib/warped/engine.rb +19 -0
- data/lib/warped/queries/filter.rb +32 -12
- data/lib/warped/table/action.rb +33 -0
- data/lib/warped/table/column.rb +34 -0
- data/lib/warped/version.rb +1 -1
- data/lib/warped.rb +2 -0
- data/warped.gemspec +1 -1
- metadata +73 -7
- data/lib/warped/emails/.keep +0 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
require "active_support/core_ext/object/blank"
|
5
|
+
|
6
|
+
module Warped
|
7
|
+
module Controllers
|
8
|
+
module Sortable
|
9
|
+
module Ui
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
include Sortable
|
13
|
+
|
14
|
+
included do
|
15
|
+
helper_method :attribute_name, :sorted?, :sorted_field?, :sortable_field?, :sort_url_params
|
16
|
+
end
|
17
|
+
|
18
|
+
# @see Sortable#sort
|
19
|
+
def sort(...)
|
20
|
+
@sorted = true
|
21
|
+
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param parameter_name [String]
|
26
|
+
# @return [Boolean]
|
27
|
+
def sorted_field?(parameter_name)
|
28
|
+
current_action_sort_value.parameter_name == parameter_name.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param parameter_name [String]
|
32
|
+
# @return [Boolean] Whether the parameter_name is sortable.
|
33
|
+
def sortable_field?(parameter_name)
|
34
|
+
current_action_sorts.any? { |sort| sort.parameter_name == parameter_name.to_s }
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Boolean] Whether the current action is sorted.
|
38
|
+
def sorted?
|
39
|
+
@sorted ||= false
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Hash] The sort_url_params
|
43
|
+
def sort_url_params(**options)
|
44
|
+
url_params = {
|
45
|
+
sort_key: current_action_sort_value.parameter_name,
|
46
|
+
sort_direction: current_action_sort_value.direction
|
47
|
+
}
|
48
|
+
url_params.merge!(options)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "active_support/concern"
|
4
4
|
require "active_support/core_ext/class/attribute"
|
5
|
+
require "active_support/core_ext/object/blank"
|
5
6
|
|
6
7
|
module Warped
|
7
8
|
module Controllers
|
@@ -35,7 +36,7 @@ module Warped
|
|
35
36
|
# class UsersController < ApplicationController
|
36
37
|
# include Sortable
|
37
38
|
#
|
38
|
-
# sortable_by :name, :created_at, 'accounts.referrals_count' => 'referrals'
|
39
|
+
# sortable_by :name, :created_at, 'accounts.referrals_count' => { alias_name: 'referrals' }
|
39
40
|
#
|
40
41
|
# def index
|
41
42
|
# scope = sort(User.joins(:account))
|
@@ -55,60 +56,79 @@ module Warped
|
|
55
56
|
extend ActiveSupport::Concern
|
56
57
|
|
57
58
|
included do
|
58
|
-
class_attribute :
|
59
|
-
class_attribute :
|
60
|
-
class_attribute :
|
61
|
-
|
59
|
+
class_attribute :sorts, default: []
|
60
|
+
class_attribute :default_sort, default: Sort.new("id")
|
61
|
+
class_attribute :default_sort_direction, default: "desc"
|
62
|
+
|
63
|
+
attr_reader :current_action_sorts
|
64
|
+
|
65
|
+
helper_method :current_action_sorts, :current_action_sort_value, :default_sort, :default_sort_direction
|
66
|
+
|
67
|
+
rescue_from Sort::DirectionError, with: :render_invalid_sort_direction
|
62
68
|
end
|
63
69
|
|
64
70
|
class_methods do
|
65
71
|
# @param keys [Array<Symbol,String>]
|
66
72
|
# @param mapped_keys [Hash<Symbol,String>]
|
67
73
|
def sortable_by(*keys, **mapped_keys)
|
68
|
-
self.
|
69
|
-
|
74
|
+
self.sorts = keys.map do |field|
|
75
|
+
Warped::Sort.new(field)
|
76
|
+
end
|
77
|
+
|
78
|
+
self.sorts += mapped_keys.map do |field, opts|
|
79
|
+
Warped::Sort.new(field, alias_name: opts[:alias_name])
|
80
|
+
end
|
81
|
+
|
82
|
+
return if self.sorts.any? { |sort| sort.name == default_sort.name }
|
83
|
+
|
84
|
+
self.sorts.push(default_sort)
|
70
85
|
end
|
71
86
|
end
|
72
87
|
|
73
88
|
# @param scope [ActiveRecord::Relation] The scope to sort.
|
74
|
-
# @param
|
75
|
-
# @param sort_direction [String, Symbol] The sort direction.
|
89
|
+
# @param sort_conditions [Array<Warped::Sort::Base>|nil] The sort conditions.
|
76
90
|
# @return [ActiveRecord::Relation]
|
77
|
-
def sort(scope,
|
78
|
-
|
79
|
-
|
80
|
-
validate_sort_key!
|
91
|
+
def sort(scope, sort_conditions: nil)
|
92
|
+
action_sorts = sort_conditions.presence || sorts
|
93
|
+
@current_action_sorts = action_sorts
|
81
94
|
|
82
|
-
Queries::Sort.call(scope, sort_key
|
95
|
+
Queries::Sort.call(scope, sort_key: current_action_sort_value.name,
|
96
|
+
sort_direction: current_action_sort_value.direction)
|
83
97
|
end
|
84
98
|
|
85
|
-
|
99
|
+
# @return [Warped::Sort::Value] The current sort value.
|
100
|
+
def current_action_sort_value
|
101
|
+
@current_action_sort_value ||= begin
|
102
|
+
sort_obj = current_action_sorts.find do |sort|
|
103
|
+
params[:sort_key] == sort.parameter_name
|
104
|
+
end
|
86
105
|
|
87
|
-
|
88
|
-
|
89
|
-
|
106
|
+
if sort_obj.present?
|
107
|
+
Sort::Value.new(sort_obj, params[:sort_direction] || default_sort_direction)
|
108
|
+
else
|
109
|
+
Sort::Value.new(default_sort, default_sort_direction)
|
110
|
+
end
|
111
|
+
end
|
90
112
|
end
|
91
113
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
114
|
+
protected
|
115
|
+
|
116
|
+
# @param exception [Sort::DirectionError]
|
117
|
+
def render_invalid_sort_direction(exception)
|
118
|
+
render json: { error: exception.message }, status: :bad_request
|
96
119
|
end
|
97
120
|
|
98
121
|
private
|
99
122
|
|
100
|
-
|
101
|
-
|
123
|
+
# @return [Warped::Sort] The current sort object.
|
124
|
+
def current_sort
|
125
|
+
@current_sort ||= begin
|
126
|
+
sort_obj = sorts.find do |sort|
|
127
|
+
params[:sort_key] == sort.parameter_name
|
128
|
+
end
|
102
129
|
|
103
|
-
|
104
|
-
|
105
|
-
raise ActionController::BadRequest, message
|
106
|
-
end
|
107
|
-
|
108
|
-
def valid_sort_key?
|
109
|
-
sort_key == default_sort_key ||
|
110
|
-
sort_fields.include?(sort_key) ||
|
111
|
-
mapped_sort_fields[sort_key].present?
|
130
|
+
sort_obj.presence || Warped::Sort.new(default_sort_key)
|
131
|
+
end
|
112
132
|
end
|
113
133
|
end
|
114
134
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Controllers
|
7
|
+
module Tabulatable
|
8
|
+
module Ui
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
include Tabulatable
|
12
|
+
include Filterable::Ui
|
13
|
+
include Pageable::Ui
|
14
|
+
include Searchable::Ui
|
15
|
+
include Sortable::Ui
|
16
|
+
|
17
|
+
included do
|
18
|
+
helper_method :tabulation, :tabulate_url_params, :tabulate_query
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Hash]
|
22
|
+
def tabulation
|
23
|
+
{
|
24
|
+
filters:,
|
25
|
+
current_filters:,
|
26
|
+
sorts:,
|
27
|
+
current_sorts:,
|
28
|
+
search_term:,
|
29
|
+
search_param:,
|
30
|
+
pagination:
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param options [Hash] Additional hash of options to include in the tabulation url_params
|
35
|
+
# @return [Hash] The tabulation url_params
|
36
|
+
def tabulate_url_params(**options)
|
37
|
+
base = paginate_url_params
|
38
|
+
base.merge!(search_url_params)
|
39
|
+
base.merge!(sort_url_params)
|
40
|
+
base.merge!(filter_url_params)
|
41
|
+
base.merge!(options)
|
42
|
+
|
43
|
+
base.tap(&:compact_blank!)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param options [Hash] Additional hash of options to include in the tabulation query
|
47
|
+
# @return [String] The tabulation query string
|
48
|
+
def tabulate_query(**options)
|
49
|
+
tabulate_url_params(**options).to_query
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -13,12 +13,12 @@ module Warped
|
|
13
13
|
# class UsersController < ApplicationController
|
14
14
|
# include Tabulatable
|
15
15
|
#
|
16
|
-
# tabulatable_by :
|
16
|
+
# tabulatable_by :email, :age, 'posts.created_at', 'posts.id' => { alias_name: 'post_id', kind: :integer }
|
17
17
|
#
|
18
18
|
# def index
|
19
19
|
# users = User.left_joins(:posts).group(:id)
|
20
20
|
# users = tabulate(users)
|
21
|
-
# render json: users, meta:
|
21
|
+
# render json: users, meta: tabulation
|
22
22
|
# end
|
23
23
|
# end
|
24
24
|
#
|
@@ -31,35 +31,32 @@ module Warped
|
|
31
31
|
# class PostsController < ApplicationController
|
32
32
|
# include Tabulatable
|
33
33
|
#
|
34
|
-
# tabulatable_by :title, :content, :created_at, user: 'users.name'
|
35
|
-
# filterable_by :created_at, user: 'users.name'
|
34
|
+
# tabulatable_by :title, :content, :created_at, user: { alias_name: 'users.name' }
|
35
|
+
# filterable_by :created_at, user: { alias_name: 'users.name' }
|
36
36
|
#
|
37
37
|
# def index
|
38
38
|
# posts = Post.left_joins(:user).group(:id)
|
39
39
|
# posts = tabulate(posts)
|
40
|
-
# render json: posts, meta:
|
40
|
+
# render json: posts, meta: tabulation
|
41
41
|
# end
|
42
42
|
# end
|
43
43
|
module Tabulatable
|
44
44
|
extend ActiveSupport::Concern
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
include Pageable
|
46
|
+
include Filterable
|
47
|
+
include Sortable
|
48
|
+
include Searchable
|
49
|
+
include Pageable
|
51
50
|
|
51
|
+
included do
|
52
52
|
class_attribute :tabulate_fields, default: []
|
53
|
-
class_attribute :mapped_tabulate_fields, default:
|
53
|
+
class_attribute :mapped_tabulate_fields, default: {}
|
54
54
|
end
|
55
55
|
|
56
56
|
class_methods do
|
57
57
|
def tabulatable_by(*keys, **mapped_keys)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
filterable_by(*keys, **mapped_keys) if filter_fields.empty? && mapped_filter_fields.empty?
|
62
|
-
sortable_by(*keys, **mapped_keys) if sort_fields.empty? && mapped_sort_fields.empty?
|
58
|
+
filterable_by(*keys, **mapped_keys) if filters.empty?
|
59
|
+
sortable_by(*keys, **mapped_keys) if sorts.empty?
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
@@ -75,17 +72,6 @@ module Warped
|
|
75
72
|
scope = sort(scope)
|
76
73
|
paginate(scope)
|
77
74
|
end
|
78
|
-
|
79
|
-
# @return [Hash]
|
80
|
-
def tabulate_info
|
81
|
-
{
|
82
|
-
filters: filter_conditions(*filter_fields, *mapped_filter_fields),
|
83
|
-
sorts: sort_conditions(*sort_fields, *mapped_sort_fields),
|
84
|
-
search_term:,
|
85
|
-
search_param:,
|
86
|
-
page_info:
|
87
|
-
}
|
88
|
-
end
|
89
75
|
end
|
90
76
|
end
|
91
77
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warped
|
4
|
+
module Emails
|
5
|
+
class Align < Base
|
6
|
+
variant do
|
7
|
+
base { ["text-align: #{@align}"] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(align: :left)
|
11
|
+
super()
|
12
|
+
@align = align
|
13
|
+
raise ArgumentError, "Invalid alignment: #{align}" unless %i[left center right].include?(align)
|
14
|
+
end
|
15
|
+
|
16
|
+
def template
|
17
|
+
tag.div(content, style:)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
require "action_view/helpers"
|
5
|
+
|
6
|
+
module Warped
|
7
|
+
module Emails
|
8
|
+
##
|
9
|
+
# Base class for all email components
|
10
|
+
#
|
11
|
+
# This class provides a number of helper methods for building email components.
|
12
|
+
#
|
13
|
+
# == Usage
|
14
|
+
#
|
15
|
+
# To create a new component, you can subclass this class and implement the +template+ method.
|
16
|
+
#
|
17
|
+
# class MyComponent < Warped::Emails::Base
|
18
|
+
# def template
|
19
|
+
# content_tag(:div, "Hello, World!")
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# You can then render the component using the +render+ method in your mailer views
|
24
|
+
#
|
25
|
+
# <%= render MyComponent.new %>
|
26
|
+
#
|
27
|
+
# == Variants
|
28
|
+
#
|
29
|
+
# Components can define variants using the +variant+ method. Variants are different styles for the component that
|
30
|
+
# can be used to change the appearance of the component.
|
31
|
+
#
|
32
|
+
# class MyComponent < Warped::Emails::Base
|
33
|
+
# variant do
|
34
|
+
# base { "color: red;" }
|
35
|
+
# size do
|
36
|
+
# sm { "font-size: 12px;" }
|
37
|
+
# md { "font-size: 16px;" }
|
38
|
+
# lg { "font-size: 20px;" }
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# default_variant size: :md
|
43
|
+
#
|
44
|
+
# def initialize(size: nil)
|
45
|
+
# super()
|
46
|
+
# @size = size
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# def template
|
50
|
+
# content_tag(:div, style: style())
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# You can then specify the variant to use using the +style+ method in your component
|
55
|
+
#
|
56
|
+
# <%= render MyComponent.new %> # => <div style="color: red; font-size: 16px;"></div>
|
57
|
+
# <%= render MyComponent.new(size: :lg) %> # => <div style="color: red; font-size: 20px;"></div>
|
58
|
+
#
|
59
|
+
# The variant blocks can return either a string or an array of strings. If an array is returned,
|
60
|
+
# the strings will be joined with a semi-colon.
|
61
|
+
#
|
62
|
+
# The +default_variant+ method can be used to specify the default variant to use if none is specified.
|
63
|
+
#
|
64
|
+
# The +style+ method can be used to apply the variant styles to the component, or to apply additional styles on
|
65
|
+
# top of the variant styles.
|
66
|
+
#
|
67
|
+
# == Slots
|
68
|
+
#
|
69
|
+
# Components can define slots using the +slot+ method. Slots are placeholders for content that can be passed
|
70
|
+
# in from the outside.
|
71
|
+
#
|
72
|
+
# class MyComponent < Warped::Emails::Base
|
73
|
+
# slot :title
|
74
|
+
#
|
75
|
+
# def template
|
76
|
+
# content_tag(:div, title)
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# You can then pass content to the slot using the +with_<slot_name>+ method in your mailer views
|
81
|
+
# <%= render MyComponent.new do |my_component| %>
|
82
|
+
# <% my_component.with_title("Hello, World!") %>
|
83
|
+
# <% end %>
|
84
|
+
class Base
|
85
|
+
include ActionView::Helpers::CaptureHelper
|
86
|
+
include Slottable
|
87
|
+
include Styleable
|
88
|
+
|
89
|
+
attr_reader :view_context
|
90
|
+
|
91
|
+
delegate :capture, :tag, :content_tag, to: :view_context
|
92
|
+
delegate_missing_to :view_context
|
93
|
+
|
94
|
+
def template
|
95
|
+
raise NotImplementedError
|
96
|
+
end
|
97
|
+
|
98
|
+
def content
|
99
|
+
@content_block
|
100
|
+
end
|
101
|
+
|
102
|
+
def helpers
|
103
|
+
return view_context if view_context.present?
|
104
|
+
|
105
|
+
raise ArgumentError, "helpers cannot be used during initialization, as it depends on the view context"
|
106
|
+
end
|
107
|
+
|
108
|
+
def render_in(view_context, &block)
|
109
|
+
@view_context = view_context
|
110
|
+
|
111
|
+
@content_block = capture { block.call(self) } if block_given?
|
112
|
+
template
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warped
|
4
|
+
module Emails
|
5
|
+
class Button < Base
|
6
|
+
variant do
|
7
|
+
base do
|
8
|
+
["font-weight: 400",
|
9
|
+
"color: #333",
|
10
|
+
"text-decoration: none",
|
11
|
+
"display: inline-block",
|
12
|
+
"padding: 10px 20px",
|
13
|
+
"border-radius: 4px",
|
14
|
+
"border: 1px solid #ccc",
|
15
|
+
"background-color: #f4f4f4",
|
16
|
+
"color: #fff"]
|
17
|
+
end
|
18
|
+
|
19
|
+
type do
|
20
|
+
primary { ["background-color: #3498db", "border-color: #3498db"] }
|
21
|
+
success { ["background-color: #2ecc71", "border-color: #2ecc71"] }
|
22
|
+
warning { ["background-color: #f39c12", "border-color: #f39c12"] }
|
23
|
+
danger { ["background-color: #e74c3c", "border-color: #e74c3c"] }
|
24
|
+
end
|
25
|
+
|
26
|
+
size do
|
27
|
+
sm { ["font-size: 13px", "padding: 6px 16px", "line-height: 16px"] }
|
28
|
+
md { ["font-size: 16px", "padding: 8px 24px", "line-height: 20px"] }
|
29
|
+
lg { ["font-size: 19px", "padding: 16px 28px", "line-height: 24px"] }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
default_variant type: :primary, size: :md
|
34
|
+
|
35
|
+
def initialize(text = nil, href, type: :primary, size: :md)
|
36
|
+
super()
|
37
|
+
@text = text
|
38
|
+
@href = href
|
39
|
+
@type = type
|
40
|
+
@size = size
|
41
|
+
end
|
42
|
+
|
43
|
+
def text
|
44
|
+
content.presence || @text
|
45
|
+
end
|
46
|
+
|
47
|
+
def template
|
48
|
+
style = style(type:, size:)
|
49
|
+
|
50
|
+
tag.a(text, href:, style:)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :href, :type, :size
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warped
|
4
|
+
module Emails
|
5
|
+
class Heading < Base
|
6
|
+
variant do
|
7
|
+
base { ["font-weight: 400"] }
|
8
|
+
|
9
|
+
level do
|
10
|
+
h1 { ["font-size: 45px", "line-height: 50px"] }
|
11
|
+
h2 { ["font-size: 40px", "line-height: 45px"] }
|
12
|
+
h3 { ["font-size: 35px", "line-height: 40px"] }
|
13
|
+
h4 { ["font-size: 30px", "line-height: 35px"] }
|
14
|
+
h5 { ["font-size: 25px", "line-height: 30px"] }
|
15
|
+
h6 { ["font-size: 20px", "line-height: 25px"] }
|
16
|
+
end
|
17
|
+
|
18
|
+
align do
|
19
|
+
left { "text-align: left" }
|
20
|
+
center { "text-align: center" }
|
21
|
+
right { "text-align: right" }
|
22
|
+
end
|
23
|
+
|
24
|
+
color do
|
25
|
+
regular { "color: #14181F" }
|
26
|
+
placeholder { "color: #8B939F" }
|
27
|
+
info { "color: #1C51A4" }
|
28
|
+
success { "color: #60830D" }
|
29
|
+
warning { "color: #82620F" }
|
30
|
+
error { "color: #AB2816" }
|
31
|
+
end
|
32
|
+
|
33
|
+
weight do
|
34
|
+
regular { "font-weight: 400" }
|
35
|
+
bold { "font-weight: 700" }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
default_variant level: :h1, align: :center, color: :regular, weight: :regular
|
40
|
+
|
41
|
+
def initialize(text = nil, level: nil, align: nil, color: nil, weight: nil)
|
42
|
+
super()
|
43
|
+
@text = text
|
44
|
+
@level = level
|
45
|
+
@align = align
|
46
|
+
@color = color
|
47
|
+
@weight = weight
|
48
|
+
end
|
49
|
+
|
50
|
+
def text
|
51
|
+
content.presence || @text
|
52
|
+
end
|
53
|
+
|
54
|
+
def template
|
55
|
+
style = style(level:, align:, color:, weight:)
|
56
|
+
|
57
|
+
tag.send(level, text, style:)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :level, :align, :color, :weight
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warped
|
4
|
+
module Emails
|
5
|
+
module Layouts
|
6
|
+
class Columns < Base
|
7
|
+
variant do
|
8
|
+
base { ["display: inline"] }
|
9
|
+
end
|
10
|
+
|
11
|
+
variant :col do
|
12
|
+
base do
|
13
|
+
[
|
14
|
+
"width: #{(100 / cols.size.to_f).round(4)}%",
|
15
|
+
"display: inline-block",
|
16
|
+
"vertical-align: top",
|
17
|
+
"text-align: center"
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
slots_many :cols
|
23
|
+
|
24
|
+
def template
|
25
|
+
tag.div(style:) do
|
26
|
+
capture do
|
27
|
+
cols.map do |col|
|
28
|
+
concat tag.div(col, style: style(:col))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warped
|
4
|
+
module Emails
|
5
|
+
module Layouts
|
6
|
+
class Cta < Base
|
7
|
+
variant :card do
|
8
|
+
base do
|
9
|
+
[
|
10
|
+
"border-collapse: unset", "width: 100%", "border-spacing: 0",
|
11
|
+
"border-radius: 8px", "border: 2px solid #ddd", "padding: 20px"
|
12
|
+
]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
slots_one :title
|
17
|
+
slots_one :body
|
18
|
+
slots_one :button
|
19
|
+
|
20
|
+
def template
|
21
|
+
tag.table(style: style(:card)) do
|
22
|
+
tag.tbody do
|
23
|
+
tag.tr do
|
24
|
+
tag.td(style:) do
|
25
|
+
concat title
|
26
|
+
concat render(Spacer.new)
|
27
|
+
concat body
|
28
|
+
concat render(Spacer.new)
|
29
|
+
concat button
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|