avo 0.3.2 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +5 -2
- data/Gemfile.lock +8 -1
- data/app/controllers/avo/application_controller.rb +2 -2
- data/app/controllers/avo/relations_controller.rb +2 -2
- data/app/controllers/avo/resource_overview_controller.rb +1 -0
- data/app/controllers/avo/resources_controller.rb +5 -3
- data/app/views/layouts/avo/_javascript.html.erb +2 -0
- data/app/views/layouts/avo/_translations.html.erb +5 -0
- data/app/views/layouts/avo/application.html.erb +7 -2
- data/avo.gemspec +1 -0
- data/lib/avo.rb +2 -0
- data/lib/avo/app/action.rb +8 -5
- data/lib/avo/app/app.rb +21 -3
- data/lib/avo/app/fields/belongs_to.rb +2 -2
- data/lib/avo/app/fields/code_field.rb +2 -0
- data/lib/avo/app/fields/country_field.rb +1 -1
- data/lib/avo/app/fields/field.rb +3 -1
- data/lib/avo/app/fields/field_extensions/visible_in_different_views.rb +4 -0
- data/lib/avo/app/fields/has_one.rb +2 -2
- data/lib/avo/app/fields/markdown_field.rb +27 -0
- data/lib/avo/app/fields/password_field.rb +3 -1
- data/lib/avo/app/fields/select_field.rb +1 -1
- data/lib/avo/app/licensing/hq.rb +4 -3
- data/lib/avo/app/resource.rb +18 -7
- data/lib/avo/app/{authorization_service.rb → services/authorization_service.rb} +0 -0
- data/lib/avo/configuration.rb +14 -2
- data/lib/avo/engine.rb +5 -5
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/install_generator.rb +2 -1
- data/lib/generators/avo/templates/{initializer.rb → initializer/avo.rb} +0 -0
- data/lib/generators/avo/templates/locales/avo.en.yml +60 -0
- data/public/avo-packs/css/application-2f609d81.css +3 -0
- data/public/avo-packs/css/application-2f609d81.css.br +0 -0
- data/public/avo-packs/css/application-2f609d81.css.gz +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js +3 -0
- data/public/avo-packs/js/{application-725ee54e3adbcd950843.js.LICENSE.txt → application-84e2d573c3c15df1fb7b.js.LICENSE.txt} +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.br +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.gz +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.map +1 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.map.br +0 -0
- data/public/avo-packs/js/application-84e2d573c3c15df1fb7b.js.map.gz +0 -0
- data/public/avo-packs/manifest.json +11 -6
- data/public/avo-packs/manifest.json.br +0 -0
- data/public/avo-packs/manifest.json.gz +0 -0
- data/public/avo-packs/media/font/fontello-068ca2b3.ttf +0 -0
- data/public/avo-packs/media/font/fontello-068ca2b3.ttf.br +0 -0
- data/public/avo-packs/media/font/fontello-068ca2b3.ttf.gz +0 -0
- data/public/avo-packs/media/font/fontello-8d4a4e6f.woff2 +0 -0
- data/public/avo-packs/media/font/fontello-9354499c.svg +72 -0
- data/public/avo-packs/media/font/fontello-9354499c.svg.br +0 -0
- data/public/avo-packs/media/font/fontello-9354499c.svg.gz +0 -0
- data/public/avo-packs/media/font/fontello-a782baa8.woff +0 -0
- data/public/avo-packs/media/font/fontello-e73a0647.eot +0 -0
- data/public/avo-packs/media/font/fontello-e73a0647.eot.br +0 -0
- data/public/avo-packs/media/font/fontello-e73a0647.eot.gz +0 -0
- metadata +42 -14
- data/public/avo-packs/css/application-73e568bc.css +0 -3
- data/public/avo-packs/css/application-73e568bc.css.br +0 -0
- data/public/avo-packs/css/application-73e568bc.css.gz +0 -0
- data/public/avo-packs/js/application-725ee54e3adbcd950843.js +0 -3
- data/public/avo-packs/js/application-725ee54e3adbcd950843.js.br +0 -0
- data/public/avo-packs/js/application-725ee54e3adbcd950843.js.gz +0 -0
- data/public/avo-packs/js/application-725ee54e3adbcd950843.js.map +0 -1
- data/public/avo-packs/js/application-725ee54e3adbcd950843.js.map.br +0 -0
- data/public/avo-packs/js/application-725ee54e3adbcd950843.js.map.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12b4c2f7a1b63d7296ac6f9b8edb5f133884ca5b10d1ed5c5a85e230364dad07
|
4
|
+
data.tar.gz: 5dd72ab388c40de38a1a36e3bf077360afabf0e75fe1922feec31f1cc9cd7913
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2369b3f99c6244a3e0a4966cac220283f2f2bef7c99edbc31e56407f4a3100460691b663486a7bf96626eee5086861e3295f6e04f066cedff79c25f9b4927c5
|
7
|
+
data.tar.gz: d98ce0432dfa0157ea480f21bb028c65bc33e9e2a59e88731efbde478806dcae96a04524ce04993741778329bddaa7083a8be69b71bea4b1282b2219550e2b01
|
data/Gemfile
CHANGED
@@ -59,8 +59,7 @@ gem 'spring'
|
|
59
59
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
60
60
|
|
61
61
|
gem 'factory_bot_rails'
|
62
|
-
gem 'faker'
|
63
|
-
|
62
|
+
gem 'faker', require: false
|
64
63
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
65
64
|
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
66
65
|
|
@@ -94,3 +93,7 @@ gem 'zeitwerk', '~> 2.3'
|
|
94
93
|
gem 'kaminari'
|
95
94
|
|
96
95
|
gem 'httparty'
|
96
|
+
|
97
|
+
gem 'iso'
|
98
|
+
|
99
|
+
gem 'i18n-js'
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (0.
|
4
|
+
avo (0.4.1)
|
5
5
|
countries
|
6
6
|
httparty
|
7
|
+
i18n-js
|
7
8
|
inline_svg
|
8
9
|
kaminari
|
9
10
|
pundit
|
@@ -132,10 +133,14 @@ GEM
|
|
132
133
|
multi_xml (>= 0.5.2)
|
133
134
|
i18n (1.8.5)
|
134
135
|
concurrent-ruby (~> 1.0)
|
136
|
+
i18n-js (3.8.0)
|
137
|
+
i18n (>= 0.6.6)
|
135
138
|
i18n_data (0.10.0)
|
136
139
|
inline_svg (1.7.1)
|
137
140
|
activesupport (>= 3.0)
|
138
141
|
nokogiri (>= 1.6)
|
142
|
+
iso (0.3.0)
|
143
|
+
i18n
|
139
144
|
jbuilder (2.10.1)
|
140
145
|
activesupport (>= 5.0.0)
|
141
146
|
kaminari (1.2.1)
|
@@ -341,7 +346,9 @@ DEPENDENCIES
|
|
341
346
|
fuubar
|
342
347
|
gem-release
|
343
348
|
httparty
|
349
|
+
i18n-js
|
344
350
|
inline_svg
|
351
|
+
iso
|
345
352
|
jbuilder (~> 2.7)
|
346
353
|
kaminari
|
347
354
|
listen (>= 3.0.5, < 3.2)
|
@@ -5,7 +5,7 @@ module Avo
|
|
5
5
|
before_action :init_app
|
6
6
|
|
7
7
|
def init_app
|
8
|
-
Avo::App.boot
|
8
|
+
Avo::App.boot if Avo::IN_DEVELOPMENT
|
9
9
|
Avo::App.init request
|
10
10
|
|
11
11
|
@license = Avo::App.license
|
@@ -58,7 +58,7 @@ module Avo
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def render_unauthorized
|
61
|
-
render json: { message: '
|
61
|
+
render json: { message: I18n.t('avo.unauthorized') }, status: 403
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -9,7 +9,7 @@ module Avo
|
|
9
9
|
|
10
10
|
render json: {
|
11
11
|
success: true,
|
12
|
-
message:
|
12
|
+
message: I18n.t('avo.attachment_class_attached', attachment_class: attachment_class),
|
13
13
|
}
|
14
14
|
end
|
15
15
|
|
@@ -18,7 +18,7 @@ module Avo
|
|
18
18
|
|
19
19
|
render json: {
|
20
20
|
success: true,
|
21
|
-
message:
|
21
|
+
message: I18n.t('avo.attachment_class_detached', attachment_class: attachment_class),
|
22
22
|
}
|
23
23
|
end
|
24
24
|
|
@@ -5,6 +5,7 @@ module Avo
|
|
5
5
|
def index
|
6
6
|
resources = App.get_resources
|
7
7
|
.select { |resource| AuthorizationService::authorize session_user, resource.model, Avo.configuration.authorization_methods.stringify_keys['index'] }
|
8
|
+
.sort_by(&:name)
|
8
9
|
.map do |resource|
|
9
10
|
{
|
10
11
|
name: resource.name,
|
@@ -95,7 +95,7 @@ module Avo
|
|
95
95
|
render json: {
|
96
96
|
success: true,
|
97
97
|
resource: Avo::Resources::Resource.hydrate_resource(model: resource, resource: avo_resource, view: :show, user: current_user),
|
98
|
-
message: '
|
98
|
+
message: I18n.t('avo.resource_updated'),
|
99
99
|
}
|
100
100
|
end
|
101
101
|
|
@@ -118,7 +118,7 @@ module Avo
|
|
118
118
|
render json: {
|
119
119
|
success: true,
|
120
120
|
resource: Avo::Resources::Resource.hydrate_resource(model: resource, resource: avo_resource, view: :create, user: current_user),
|
121
|
-
message: '
|
121
|
+
message: I18n.t('avo.resource_created'),
|
122
122
|
}
|
123
123
|
end
|
124
124
|
|
@@ -132,7 +132,7 @@ module Avo
|
|
132
132
|
resource.destroy!
|
133
133
|
|
134
134
|
render json: {
|
135
|
-
message: '
|
135
|
+
message: I18n.t('avo.resource_destroyed'),
|
136
136
|
}
|
137
137
|
end
|
138
138
|
|
@@ -255,8 +255,10 @@ module Avo
|
|
255
255
|
per_page_steps: Avo.configuration.per_page_steps,
|
256
256
|
available_view_types: avo_resource.available_view_types,
|
257
257
|
default_view_type: avo_resource.default_view_type || Avo.configuration.default_view_type,
|
258
|
+
translation_key: avo_resource.translation_key,
|
258
259
|
authorization: {
|
259
260
|
create: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['create']),
|
261
|
+
edit: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['edit']),
|
260
262
|
update: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['update']),
|
261
263
|
show: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['show']),
|
262
264
|
destroy: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['destroy']),
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<script>
|
2
2
|
var rootPath = '<%= Avo.configuration.root_path %>';
|
3
3
|
var timezone = '<%= Avo.configuration.timezone %>';
|
4
|
+
var locale = '<%= Avo.configuration.locale %>';
|
4
5
|
var defaultViewType = '<%= Avo.configuration.default_view_type %>';
|
5
6
|
var license = <%= Avo::App.license.properties.to_json.html_safe %>;
|
7
|
+
var avoResources = <%= Avo::App.get_available_resources(current_user).as_json.html_safe %>;
|
6
8
|
</script>
|
@@ -8,8 +8,10 @@
|
|
8
8
|
|
9
9
|
<%= javascript_pack_tag 'application' %>
|
10
10
|
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
11
|
+
<%= render partial: 'layouts/avo/translations' %>
|
11
12
|
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">
|
12
13
|
</head>
|
14
|
+
|
13
15
|
<body class="bg-gray-200">
|
14
16
|
|
15
17
|
<div id="app" class="flex min-h-screen flex-row h-full">
|
@@ -19,7 +21,7 @@
|
|
19
21
|
inline-template
|
20
22
|
>
|
21
23
|
<div>
|
22
|
-
<application-sidebar
|
24
|
+
<application-sidebar v-if="layout !== 'blank'">
|
23
25
|
<template #logo>
|
24
26
|
<%= render_logo %>
|
25
27
|
</template>
|
@@ -29,7 +31,7 @@
|
|
29
31
|
</application-sidebar>
|
30
32
|
|
31
33
|
<div class="flex-1 h-full overflow-auto">
|
32
|
-
<div class="relative bg-white p-2 shadow-md h-16 w-full flex items-center z-
|
34
|
+
<div class="relative bg-white p-2 shadow-md h-16 w-full flex items-center z-50" v-if="layout !== 'blank'">
|
33
35
|
<div class="ml-6">
|
34
36
|
<%= render_header %>
|
35
37
|
</div>
|
@@ -43,6 +45,7 @@
|
|
43
45
|
<div v-if="layout === 'blank'" class="h-full w-full flex justify-center items-center">
|
44
46
|
<%= yield %>
|
45
47
|
</div>
|
48
|
+
|
46
49
|
<div class="content p-8" v-else>
|
47
50
|
<%= yield %>
|
48
51
|
<%= render_footer %>
|
@@ -51,6 +54,8 @@
|
|
51
54
|
</div>
|
52
55
|
</app-layout>
|
53
56
|
</div>
|
57
|
+
|
54
58
|
<%= render_scripts %>
|
59
|
+
|
55
60
|
</body>
|
56
61
|
</html>
|
data/avo.gemspec
CHANGED
data/lib/avo.rb
CHANGED
data/lib/avo/app/action.rb
CHANGED
@@ -15,6 +15,7 @@ module Avo
|
|
15
15
|
# filename: String
|
16
16
|
# }
|
17
17
|
attr_accessor :response
|
18
|
+
attr_accessor :no_confirmation
|
18
19
|
|
19
20
|
@@default = nil
|
20
21
|
|
@@ -37,15 +38,16 @@ module Avo
|
|
37
38
|
|
38
39
|
def initialize
|
39
40
|
@name ||= name
|
40
|
-
@message ||= '
|
41
|
+
@message ||= I18n.t('avo.are_you_sure_you_want_to_run_this_option')
|
41
42
|
@default ||= ''
|
42
43
|
@fields ||= []
|
43
|
-
@confirm_text = '
|
44
|
-
@cancel_text = '
|
44
|
+
@confirm_text = I18n.t('avo.run')
|
45
|
+
@cancel_text = I18n.t('avo.cancel')
|
45
46
|
@response ||= {}
|
46
47
|
@response[:message_type] ||= :success
|
47
|
-
@response[:message] ||= '
|
48
|
+
@response[:message] ||= I18n.t('avo.action_ran_successfully')
|
48
49
|
@theme ||= 'success'
|
50
|
+
@no_confirmation ||= false
|
49
51
|
end
|
50
52
|
|
51
53
|
def render_response(model, resource)
|
@@ -61,6 +63,7 @@ module Avo
|
|
61
63
|
cancel_text: cancel_text,
|
62
64
|
default: default,
|
63
65
|
action_class: self.class.to_s,
|
66
|
+
no_confirmation: no_confirmation,
|
64
67
|
}
|
65
68
|
end
|
66
69
|
|
@@ -87,7 +90,7 @@ module Avo
|
|
87
90
|
end
|
88
91
|
|
89
92
|
def name
|
90
|
-
self.class.name.demodulize.underscore.humanize
|
93
|
+
self.class.name.demodulize.underscore.humanize(keep_id_suffix: true)
|
91
94
|
end
|
92
95
|
|
93
96
|
def succeed(text)
|
data/lib/avo/app/app.rb
CHANGED
@@ -4,7 +4,7 @@ require_relative 'filters/select_filter'
|
|
4
4
|
require_relative 'filters/boolean_filter'
|
5
5
|
require_relative 'resource'
|
6
6
|
require_relative 'tool'
|
7
|
-
require_relative 'authorization_service'
|
7
|
+
require_relative 'services/authorization_service'
|
8
8
|
|
9
9
|
module Avo
|
10
10
|
class App
|
@@ -12,6 +12,7 @@ module Avo
|
|
12
12
|
root_path: '',
|
13
13
|
resources: [],
|
14
14
|
field_names: {},
|
15
|
+
cache_store: nil
|
15
16
|
}
|
16
17
|
@@license = nil
|
17
18
|
|
@@ -19,6 +20,13 @@ module Avo
|
|
19
20
|
def boot
|
20
21
|
@@app[:root_path] = Pathname.new(File.join(__dir__, '..', '..'))
|
21
22
|
init_fields
|
23
|
+
I18n.locale = Avo.configuration.language_code
|
24
|
+
|
25
|
+
if Rails.cache.class == ActiveSupport::Cache::NullStore
|
26
|
+
@@app[:cache_store] ||= ActiveSupport::Cache::MemoryStore.new
|
27
|
+
else
|
28
|
+
@@app[:cache_store] = Rails.cache
|
29
|
+
end
|
22
30
|
end
|
23
31
|
|
24
32
|
def init(current_request = nil)
|
@@ -34,6 +42,10 @@ module Avo
|
|
34
42
|
@@license
|
35
43
|
end
|
36
44
|
|
45
|
+
def cache_store
|
46
|
+
@@app[:cache_store]
|
47
|
+
end
|
48
|
+
|
37
49
|
# This method will take all fields available in the Avo::Fields namespace and create a method for them.
|
38
50
|
#
|
39
51
|
# If the field has their `def_method` set up it will follow that convention, if not it will snake_case the name:
|
@@ -127,10 +139,16 @@ module Avo
|
|
127
139
|
name.to_s.camelize.singularize
|
128
140
|
end
|
129
141
|
|
130
|
-
def
|
142
|
+
def get_available_resources(user)
|
131
143
|
App.get_resources
|
132
144
|
.select { |resource| AuthorizationService::authorize user, resource.model, Avo.configuration.authorization_methods.stringify_keys['index'] }
|
133
|
-
.map
|
145
|
+
.map do |resource|
|
146
|
+
{
|
147
|
+
label: resource.plural_name.humanize(keep_id_suffix: true),
|
148
|
+
resource_name: resource.url.pluralize,
|
149
|
+
translation_key: resource.translation_key
|
150
|
+
}
|
151
|
+
end
|
134
152
|
.reject { |i| i.blank? }
|
135
153
|
.to_json
|
136
154
|
.to_s
|
@@ -7,7 +7,7 @@ module Avo
|
|
7
7
|
def initialize(name, **args, &block)
|
8
8
|
@defaults = {
|
9
9
|
component: 'belongs-to-field',
|
10
|
-
placeholder:
|
10
|
+
placeholder: I18n.t('avo.choose_an_option')
|
11
11
|
}
|
12
12
|
|
13
13
|
@searchable = args[:searchable] == true ? true : false
|
@@ -49,7 +49,7 @@ module Avo
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
fields[:
|
52
|
+
fields[:plural_name] = target_resource.plural_name
|
53
53
|
|
54
54
|
fields
|
55
55
|
end
|
@@ -12,12 +12,14 @@ module Avo
|
|
12
12
|
|
13
13
|
@language = args[:language].present? ? args[:language].to_s : 'javascript'
|
14
14
|
@theme = args[:theme].present? ? args[:theme].to_s : 'material-darker'
|
15
|
+
@height = args[:height].present? ? args[:height].to_s : 'auto'
|
15
16
|
end
|
16
17
|
|
17
18
|
def hydrate_field(fields, model, resource, view)
|
18
19
|
{
|
19
20
|
language: @language,
|
20
21
|
theme: @theme,
|
22
|
+
height: @height,
|
21
23
|
}
|
22
24
|
end
|
23
25
|
end
|
data/lib/avo/app/fields/field.rb
CHANGED
@@ -9,6 +9,7 @@ module Avo
|
|
9
9
|
|
10
10
|
attr_accessor :id
|
11
11
|
attr_accessor :name
|
12
|
+
attr_accessor :translation_key
|
12
13
|
attr_accessor :component
|
13
14
|
attr_accessor :updatable
|
14
15
|
attr_accessor :sortable
|
@@ -35,7 +36,8 @@ module Avo
|
|
35
36
|
# The field properties as a hash {property: default_value}
|
36
37
|
@field_properties = {
|
37
38
|
id: id,
|
38
|
-
name: id.to_s.humanize,
|
39
|
+
name: id.to_s.humanize(keep_id_suffix: true),
|
40
|
+
translation_key: nil,
|
39
41
|
block: block,
|
40
42
|
component: 'field',
|
41
43
|
required: false,
|
@@ -15,12 +15,16 @@ module Avo
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def show_on(*where)
|
18
|
+
return show_on_all if where.include? :all
|
19
|
+
|
18
20
|
normalize_views(where).flatten.each do |view|
|
19
21
|
show_on_view view
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
def hide_on(*where)
|
26
|
+
return hide_on_all if where.include? :all
|
27
|
+
|
24
28
|
normalize_views(where).flatten.each do |view|
|
25
29
|
hide_on_view view
|
26
30
|
end
|
@@ -13,7 +13,7 @@ module Avo
|
|
13
13
|
|
14
14
|
hide_on :create
|
15
15
|
|
16
|
-
@placeholder =
|
16
|
+
@placeholder = I18n.t 'avo.choose_an_option'
|
17
17
|
|
18
18
|
@relation_method = name.to_s.parameterize.underscore
|
19
19
|
end
|
@@ -43,7 +43,7 @@ module Avo
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
fields[:
|
46
|
+
fields[:plural_name] = target_resource.plural_name
|
47
47
|
|
48
48
|
fields
|
49
49
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'field'
|
2
|
+
|
3
|
+
module Avo
|
4
|
+
module Fields
|
5
|
+
class MarkdownField < Field
|
6
|
+
def initialize(name, **args, &block)
|
7
|
+
@defaults = {
|
8
|
+
component: 'markdown-field',
|
9
|
+
}
|
10
|
+
|
11
|
+
super(name, **args, &block)
|
12
|
+
|
13
|
+
hide_on :index
|
14
|
+
|
15
|
+
@always_show = args[:always_show].present? ? args[:always_show] : false
|
16
|
+
@height = args[:height].present? ? args[:height].to_s : 'auto'
|
17
|
+
end
|
18
|
+
|
19
|
+
def hydrate_field(fields, model, resource, view)
|
20
|
+
{
|
21
|
+
always_show: @always_show,
|
22
|
+
height: @height
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|