tramway 0.5.5.1 → 0.6
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/README.md +36 -6
- data/app/components/tailwinds/back_button_component.html.haml +2 -0
- data/app/components/tailwinds/back_button_component.rb +7 -0
- data/app/components/tailwinds/base_component.rb +7 -0
- data/app/components/tailwinds/button_component.html.haml +12 -0
- data/app/components/tailwinds/button_component.rb +60 -0
- data/app/components/tailwinds/containers/narrow_component.html.haml +3 -0
- data/app/components/tailwinds/containers/narrow_component.rb +10 -0
- data/app/components/tailwinds/form/builder.rb +18 -19
- data/config/tailwind.config.js +105 -25
- data/lib/generators/tramway/install/install_generator.rb +133 -0
- data/lib/tramway/helpers/views_helper.rb +39 -0
- data/lib/tramway/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff761d41cd4748bf3d7e1451825da24764f69077b842e92f7be6baf0f14dff55
|
|
4
|
+
data.tar.gz: d9c3b25519ab3eb4dfe9c7d2e26d8dffb339a2743b6c5c494495bd4c5b71fc6c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb0c9c5121dc9a8213a0011c07265b2c42a486218966725df559c31f48b1f1e0cbbb50dc7fd10ddaac13f00a14d55ae57be42e121a1c1d44b1560822425f54af
|
|
7
|
+
data.tar.gz: 4488ab5046c8fee6150ee80459df3859f0a2d8b62948cfb91af93052dce2f0788aa067dde8261c9df0708ad31c853c522adc65c91c907cc71b4418c17bfc4244
|
data/README.md
CHANGED
|
@@ -29,17 +29,18 @@ Add this line to your application's Gemfile:
|
|
|
29
29
|
|
|
30
30
|
```ruby
|
|
31
31
|
gem "tramway"
|
|
32
|
-
gem "haml-rails"
|
|
33
|
-
gem "kaminari"
|
|
34
|
-
gem "view_component"
|
|
35
32
|
```
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
Then install Tramway and its dependencies:
|
|
38
35
|
|
|
39
36
|
```shell
|
|
40
|
-
bundle
|
|
37
|
+
bundle install
|
|
38
|
+
bin/rails g tramway:install
|
|
41
39
|
```
|
|
42
40
|
|
|
41
|
+
The install generator adds the required gems (`haml-rails`, `kaminari`, `view_component`, and `dry-initializer`) to your
|
|
42
|
+
application's Gemfile—if they are not present—and appends the Tailwind safelist configuration Tramway ships with.
|
|
43
|
+
|
|
43
44
|
## Getting Started
|
|
44
45
|
|
|
45
46
|
**Step 1**
|
|
@@ -78,7 +79,9 @@ end
|
|
|
78
79
|
|
|
79
80
|
**Step 4**
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
If you ran `bin/rails g tramway:install`, the Tailwind safelist was already appended to `config/tailwind.config.js`.
|
|
83
|
+
Otherwise, copy this [file](https://github.com/Purple-Magic/tramway/blob/main/config/tailwind.config.js) to
|
|
84
|
+
`config/tailwind.config.js`.
|
|
82
85
|
|
|
83
86
|
|
|
84
87
|
**Step 5**
|
|
@@ -581,6 +584,7 @@ Tramway provides `tramway_form_for` helper that renders Tailwind-styled forms by
|
|
|
581
584
|
```ruby
|
|
582
585
|
= tramway_form_for @user do |f|
|
|
583
586
|
= f.text_field :text
|
|
587
|
+
= f.email_field :email
|
|
584
588
|
= f.password_field :password
|
|
585
589
|
= f.select :role, [:admin, :user]
|
|
586
590
|
= f.multiselect :permissions, [['Create User', 'create_user'], ['Update user', 'update_user']]
|
|
@@ -592,12 +596,38 @@ will render [this](https://play.tailwindcss.com/xho3LfjKkK)
|
|
|
592
596
|
|
|
593
597
|
Available form helpers:
|
|
594
598
|
* text_field
|
|
599
|
+
* email_field
|
|
595
600
|
* password_field
|
|
596
601
|
* file_field
|
|
597
602
|
* select
|
|
598
603
|
* multiselect ([Stimulus-based](https://github.com/Purple-Magic/tramway#stimulus-based-inputs))
|
|
599
604
|
* submit
|
|
600
605
|
|
|
606
|
+
**Examples**
|
|
607
|
+
|
|
608
|
+
1. Sign In Form for `devise` authentication
|
|
609
|
+
|
|
610
|
+
*app/views/devise/sessions/new.html.haml*
|
|
611
|
+
```haml
|
|
612
|
+
= tramway_form_for(resource, as: resource_name, url: session_path(resource_name), class: 'bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4') do |f|
|
|
613
|
+
= component 'forms/errors', record: resource
|
|
614
|
+
|
|
615
|
+
= f.text_field :email, placeholder: "Your email"
|
|
616
|
+
= f.password_field :password, placeholder: "Your password"
|
|
617
|
+
|
|
618
|
+
= f.submit "Sign In"
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
2. Sign In Form for Rails authorization
|
|
622
|
+
|
|
623
|
+
*app/views/sessions/new.html.haml*
|
|
624
|
+
```haml
|
|
625
|
+
= form_with url: login_path, scope: :session, local: true, builder: Tailwinds::Form::Builder do |form|
|
|
626
|
+
= form.email_field :email
|
|
627
|
+
= form.password_field :password
|
|
628
|
+
= form.submit "Log in"
|
|
629
|
+
```
|
|
630
|
+
|
|
601
631
|
#### Stimulus-based inputs
|
|
602
632
|
|
|
603
633
|
`tramway_form_for` provides Tailwind-styled Stimulus-based custom inputs.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
- if text.present?
|
|
2
|
+
- if method == :get
|
|
3
|
+
= link_to text, path, class: classes, **options.except(:class)
|
|
4
|
+
- else
|
|
5
|
+
= button_to text, path, method:, class: classes, **options.except(:class)
|
|
6
|
+
- else
|
|
7
|
+
- if method == :get
|
|
8
|
+
= link_to path, class: classes, **options.except(:class) do
|
|
9
|
+
= content
|
|
10
|
+
- else
|
|
11
|
+
= button_to path, method:, class: classes, **options.except(:class) do
|
|
12
|
+
= content
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailwinds
|
|
4
|
+
# Default Tramway button
|
|
5
|
+
#
|
|
6
|
+
class ButtonComponent < BaseComponent
|
|
7
|
+
option :text, optional: true, default: -> {}
|
|
8
|
+
option :path
|
|
9
|
+
option :color, default: -> { :blue }
|
|
10
|
+
option :type, default: -> { :default }
|
|
11
|
+
option :size, default: -> { :middle }
|
|
12
|
+
option :method, optional: true, default: -> { :get }
|
|
13
|
+
option :options, optional: true, default: -> { {} }
|
|
14
|
+
|
|
15
|
+
def size_classes
|
|
16
|
+
case size
|
|
17
|
+
when :small
|
|
18
|
+
'text-sm py-1 px-1'
|
|
19
|
+
when :middle
|
|
20
|
+
'py-2 px-4'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def classes
|
|
25
|
+
(default_classes +
|
|
26
|
+
light_mode_classes +
|
|
27
|
+
dark_mode_classes +
|
|
28
|
+
(method == :get ? %w[px-1 h-fit] : ['cursor-pointer'])).compact.join(' ')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def default_classes
|
|
32
|
+
[
|
|
33
|
+
'btn',
|
|
34
|
+
'btn-primary',
|
|
35
|
+
'font-bold',
|
|
36
|
+
'rounded-sm',
|
|
37
|
+
'flex',
|
|
38
|
+
'flex-row',
|
|
39
|
+
size_classes.to_s,
|
|
40
|
+
options[:class].to_s
|
|
41
|
+
]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def light_mode_classes
|
|
45
|
+
[
|
|
46
|
+
"bg-#{color}-500",
|
|
47
|
+
"hover:bg-#{color}-700",
|
|
48
|
+
'text-white'
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def dark_mode_classes
|
|
53
|
+
[
|
|
54
|
+
"dark:bg-#{color}-600",
|
|
55
|
+
"dark:hover:bg-#{color}-800",
|
|
56
|
+
'dark:text-gray-300'
|
|
57
|
+
]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -11,34 +11,33 @@ module Tailwinds
|
|
|
11
11
|
@form_size = options[:size] || options['size'] || :middle
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def common_field(component_name, input_method, attribute, **options, &)
|
|
15
15
|
sanitized_options = sanitize_options(options)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
component_class = "Tailwinds::Form::#{component_name.to_s.camelize}Component".constantize
|
|
18
|
+
|
|
19
|
+
render(component_class.new(
|
|
20
|
+
input: input(input_method),
|
|
19
21
|
value: get_value(attribute, sanitized_options),
|
|
20
22
|
**default_options(attribute, sanitized_options)
|
|
21
|
-
),
|
|
23
|
+
),
|
|
24
|
+
&)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
|
-
def
|
|
25
|
-
|
|
27
|
+
def text_field(attribute, **, &)
|
|
28
|
+
common_field(:text_field, :text_field, attribute, **, &)
|
|
29
|
+
end
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
value: get_value(attribute, sanitized_options),
|
|
30
|
-
**default_options(attribute, sanitized_options)
|
|
31
|
-
), &)
|
|
31
|
+
def email_field(attribute, **, &)
|
|
32
|
+
common_field(:text_field, :email_field, attribute, **, &)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
-
|
|
35
|
+
def number_field(attribute, **, &)
|
|
36
|
+
common_field(:number_field, :number_field, attribute, **, &)
|
|
37
|
+
end
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
value: get_value(attribute, sanitized_options),
|
|
40
|
-
**default_options(attribute, sanitized_options)
|
|
41
|
-
), &)
|
|
39
|
+
def text_area(attribute, **, &)
|
|
40
|
+
common_field(:text_area, :text_area, attribute, **, &)
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
def password_field(attribute, **options, &)
|
|
@@ -95,7 +94,7 @@ module Tailwinds
|
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
def get_value(attribute, options)
|
|
98
|
-
options[:value] || object.public_send(attribute)
|
|
97
|
+
options[:value] || (object.present? ? object.public_send(attribute) : nil)
|
|
99
98
|
end
|
|
100
99
|
|
|
101
100
|
def default_options(attribute, options)
|
data/config/tailwind.config.js
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
safelist: [
|
|
3
|
+
// === Navbar ===
|
|
4
|
+
'ml-4',
|
|
5
|
+
|
|
6
|
+
// === Custom table layout utilities ===
|
|
3
7
|
'div-table',
|
|
4
8
|
'div-table-row',
|
|
5
9
|
'div-table-cell',
|
|
10
|
+
'sm:text-base',
|
|
11
|
+
'last:border-b-0',
|
|
12
|
+
|
|
13
|
+
// === Visibility and typography helpers ===
|
|
6
14
|
'hidden',
|
|
15
|
+
'xl:hidden',
|
|
7
16
|
'text-xl',
|
|
8
17
|
'text-4xl',
|
|
9
18
|
'font-bold',
|
|
10
|
-
'
|
|
19
|
+
'text-right',
|
|
20
|
+
|
|
21
|
+
// === Grid templates used for configurable layouts ===
|
|
22
|
+
'grid',
|
|
11
23
|
'grid-cols-1',
|
|
12
24
|
'grid-cols-2',
|
|
13
25
|
'grid-cols-3',
|
|
@@ -18,44 +30,112 @@ module.exports = {
|
|
|
18
30
|
'grid-cols-8',
|
|
19
31
|
'grid-cols-9',
|
|
20
32
|
'grid-cols-10',
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
|
|
34
|
+
// === Page container and alignment helpers ===
|
|
35
|
+
'container',
|
|
36
|
+
'mx-auto',
|
|
37
|
+
'align-center',
|
|
38
|
+
'justify-center',
|
|
39
|
+
|
|
40
|
+
// === Flexbox layout utilities ===
|
|
23
41
|
'flex',
|
|
24
|
-
'
|
|
42
|
+
'flex-col',
|
|
43
|
+
'flex-wrap',
|
|
44
|
+
'flex-row-reverse',
|
|
45
|
+
'flex-auto',
|
|
46
|
+
'flex-initial',
|
|
47
|
+
'justify-between',
|
|
48
|
+
'justify-end',
|
|
49
|
+
'space-x-1',
|
|
50
|
+
'items-center',
|
|
51
|
+
|
|
52
|
+
// === Responsive visibility helpers ===
|
|
53
|
+
'sm:hidden',
|
|
54
|
+
'sm:flex',
|
|
55
|
+
|
|
56
|
+
// === Sizing utilities ===
|
|
57
|
+
'w-2/3',
|
|
58
|
+
'w-full',
|
|
59
|
+
'w-fit',
|
|
60
|
+
'w-8',
|
|
61
|
+
'min-w-96',
|
|
62
|
+
'max-w-full',
|
|
63
|
+
|
|
64
|
+
// === Spacing utilities ===
|
|
65
|
+
'p-4',
|
|
25
66
|
'px-6',
|
|
26
|
-
'px-3',
|
|
27
67
|
'px-4',
|
|
68
|
+
'px-3',
|
|
69
|
+
'py-8',
|
|
28
70
|
'py-4',
|
|
29
71
|
'py-2',
|
|
30
72
|
'mb-2',
|
|
31
|
-
'
|
|
73
|
+
'mt-8',
|
|
74
|
+
'mt-2',
|
|
75
|
+
|
|
76
|
+
// === Pagination styles ===
|
|
77
|
+
'bg-white',
|
|
78
|
+
'rounded-md',
|
|
79
|
+
'hover:bg-purple-100',
|
|
80
|
+
'hover:bg-gray-100',
|
|
81
|
+
'hover:bg-gray-300',
|
|
82
|
+
'hover:text-gray-800',
|
|
83
|
+
|
|
84
|
+
// === Dark mode pagination styles ===
|
|
32
85
|
'dark:text-white',
|
|
33
86
|
'dark:bg-gray-800',
|
|
34
|
-
'dark:bg-gray-900',
|
|
35
87
|
'dark:hover:bg-gray-700',
|
|
88
|
+
'dark:bg-gray-900',
|
|
36
89
|
'dark:bg-gray-700',
|
|
37
90
|
'dark:text-gray-400',
|
|
38
|
-
'dark:hover:bg-gray-700',
|
|
39
91
|
'dark:hover:text-gray-400',
|
|
40
|
-
'
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'mt-2',
|
|
92
|
+
'dark:placeholder-gray-400',
|
|
93
|
+
|
|
94
|
+
// === Button and badge helpers ===
|
|
95
|
+
'bg-purple-700',
|
|
96
|
+
'rounded',
|
|
97
|
+
'rounded-full',
|
|
98
|
+
'rounded-t',
|
|
99
|
+
'font-medium',
|
|
100
|
+
'font-normal',
|
|
101
|
+
'text-xs',
|
|
102
|
+
|
|
103
|
+
// === Form state helpers ===
|
|
53
104
|
'disabled:bg-gray-100',
|
|
54
105
|
'disabled:text-gray-400',
|
|
55
106
|
'disabled:cursor-not-allowed',
|
|
56
|
-
|
|
57
|
-
'bg-
|
|
58
|
-
|
|
59
|
-
|
|
107
|
+
'mb-4',
|
|
108
|
+
'bg-red-500',
|
|
109
|
+
'hover:bg-red-700',
|
|
110
|
+
|
|
111
|
+
// === Multiselect dropdown positioning ===
|
|
112
|
+
'absolute',
|
|
113
|
+
'relative',
|
|
114
|
+
'shadow',
|
|
115
|
+
'top-11',
|
|
116
|
+
'z-40',
|
|
117
|
+
'left-0',
|
|
118
|
+
'max-h-select',
|
|
119
|
+
'overflow-y-auto',
|
|
120
|
+
'cursor-pointer',
|
|
121
|
+
|
|
122
|
+
// === Multiselect option styling ===
|
|
123
|
+
'border-b',
|
|
124
|
+
'border',
|
|
125
|
+
'border-l',
|
|
126
|
+
'border-l-2',
|
|
127
|
+
'border-transparent',
|
|
128
|
+
'border-teal-300',
|
|
129
|
+
'hover:border-teal-100',
|
|
130
|
+
'bg-teal-100',
|
|
131
|
+
'bg-transparent',
|
|
132
|
+
'text-teal-700',
|
|
133
|
+
'leading-6',
|
|
134
|
+
'leading-none',
|
|
135
|
+
'appearance-none',
|
|
136
|
+
'outline-none',
|
|
137
|
+
'h-full',
|
|
138
|
+
'm-1',
|
|
139
|
+
'hover:bg-teal-100',
|
|
60
140
|
],
|
|
61
141
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module Tramway
|
|
7
|
+
module Generators
|
|
8
|
+
# Running `rails generate tramway:install` will invoke this generator
|
|
9
|
+
#
|
|
10
|
+
class InstallGenerator < Rails::Generators::Base
|
|
11
|
+
desc 'Installs Tramway dependencies and Tailwind safelist configuration.'
|
|
12
|
+
|
|
13
|
+
def ensure_dependencies
|
|
14
|
+
missing_dependencies = gem_dependencies.reject do |dependency|
|
|
15
|
+
gemfile_contains?(dependency[:name])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return if missing_dependencies.empty?
|
|
19
|
+
|
|
20
|
+
append_to_file 'Gemfile', <<~GEMS
|
|
21
|
+
|
|
22
|
+
# Tramway dependencies
|
|
23
|
+
#{missing_dependencies.pluck(:declaration).join("\n")}
|
|
24
|
+
|
|
25
|
+
GEMS
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ensure_tailwind_safelist
|
|
29
|
+
return create_tailwind_config unless File.exist?(tailwind_config_path)
|
|
30
|
+
|
|
31
|
+
source_entries = extract_safelist_entries(File.read(gem_tailwind_config_path))
|
|
32
|
+
target_content = File.read(tailwind_config_path)
|
|
33
|
+
target_entries = extract_safelist_entries(target_content)
|
|
34
|
+
|
|
35
|
+
missing_entries = source_entries - target_entries
|
|
36
|
+
return if missing_entries.empty?
|
|
37
|
+
|
|
38
|
+
File.write(tailwind_config_path, insert_entries(target_content, missing_entries))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def ensure_tailwind_application_stylesheet
|
|
42
|
+
path = tailwind_application_stylesheet_path
|
|
43
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
44
|
+
|
|
45
|
+
return create_file(path, "#{tailwind_css_import_line}\n") unless File.exist?(path)
|
|
46
|
+
|
|
47
|
+
content = File.read(path)
|
|
48
|
+
return if content.include?(tailwind_css_import_line)
|
|
49
|
+
|
|
50
|
+
File.open(path, 'a') do |file|
|
|
51
|
+
file.write("\n") unless content.empty? || content.end_with?("\n")
|
|
52
|
+
file.write("#{tailwind_css_import_line}\n")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def gem_dependencies
|
|
59
|
+
@gem_dependencies ||= [
|
|
60
|
+
{ name: 'haml-rails', declaration: 'gem "haml-rails"' },
|
|
61
|
+
{ name: 'kaminari', declaration: 'gem "kaminari"' },
|
|
62
|
+
{ name: 'view_component', declaration: 'gem "view_component"' },
|
|
63
|
+
{ name: 'dry-initializer', declaration: "gem 'dry-initializer'" }
|
|
64
|
+
]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def gemfile_path
|
|
68
|
+
@gemfile_path ||= File.join(destination_root, 'Gemfile')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def gemfile_contains?(name)
|
|
72
|
+
return false unless File.exist?(gemfile_path)
|
|
73
|
+
|
|
74
|
+
content = File.read(gemfile_path)
|
|
75
|
+
content.match?(/^\s*gem ['"]#{Regexp.escape(name)}['"]/)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def tailwind_config_path
|
|
79
|
+
@tailwind_config_path ||= File.join(destination_root, 'config/tailwind.config.js')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def gem_tailwind_config_path
|
|
83
|
+
@gem_tailwind_config_path ||= File.expand_path('../../../../config/tailwind.config.js', __dir__)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def tailwind_application_stylesheet_path
|
|
87
|
+
@tailwind_application_stylesheet_path ||= File.join(destination_root, 'app/assets/tailwind/application.css')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def tailwind_css_import_line
|
|
91
|
+
'@import "tailwindcss";'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def create_tailwind_config
|
|
95
|
+
create_file tailwind_config_path, File.read(gem_tailwind_config_path)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def extract_safelist_entries(content)
|
|
99
|
+
section = safelist_section(content)
|
|
100
|
+
return [] if section.nil?
|
|
101
|
+
|
|
102
|
+
section.scan(/'([^']+)'/).flatten.map(&:strip).reject(&:empty?).uniq
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def safelist_section(content)
|
|
106
|
+
match = content.match(/safelist\s*:\s*\[(.*?)\]\s*,?/m)
|
|
107
|
+
match&.[](1)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def insert_entries(content, entries)
|
|
111
|
+
match = content.match(/safelist\s*:\s*\[(.*?)\](\s*,?)/m)
|
|
112
|
+
return content unless match
|
|
113
|
+
|
|
114
|
+
closing_index = match.begin(0) + match[0].rindex(']')
|
|
115
|
+
indentation = closing_indentation(content, closing_index)
|
|
116
|
+
formatted_entries = entries.map { |entry| "#{indentation}'#{entry}',\n" }.join
|
|
117
|
+
|
|
118
|
+
needs_leading_newline = content[closing_index - 1] != "\n"
|
|
119
|
+
insertion = String.new
|
|
120
|
+
insertion << "\n" if needs_leading_newline
|
|
121
|
+
insertion << formatted_entries
|
|
122
|
+
|
|
123
|
+
content.dup.insert(closing_index, insertion)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def closing_indentation(content, index)
|
|
127
|
+
line_start = content.rindex("\n", index - 1) || 0
|
|
128
|
+
line = content[line_start..index]
|
|
129
|
+
line[/^\s*/] || ' '
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -12,6 +12,45 @@ module Tramway
|
|
|
12
12
|
&
|
|
13
13
|
)
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
def tramway_table(**options, &)
|
|
17
|
+
component 'tailwinds/table', options:, &
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tramway_row(**options, &)
|
|
21
|
+
component 'tailwinds/table/row',
|
|
22
|
+
cells: options.delete(:cells),
|
|
23
|
+
href: options.delete(:href),
|
|
24
|
+
options:,
|
|
25
|
+
&
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def tramway_cell(&)
|
|
29
|
+
component 'tailwinds/table/cell', &
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def tramway_button(path:, text: nil, method: :get, **options)
|
|
33
|
+
component 'tailwinds/button',
|
|
34
|
+
text:,
|
|
35
|
+
path:,
|
|
36
|
+
method:,
|
|
37
|
+
color: options.delete(:color),
|
|
38
|
+
type: options.delete(:type),
|
|
39
|
+
size: options.delete(:size),
|
|
40
|
+
options:
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def tramway_back_button
|
|
44
|
+
component 'tailwinds/back_button'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def tramway_container(id: nil, &)
|
|
48
|
+
if id.present?
|
|
49
|
+
component 'tailwinds/containers/narrow', id: id, &
|
|
50
|
+
else
|
|
51
|
+
component 'tailwinds/containers/narrow', &
|
|
52
|
+
end
|
|
53
|
+
end
|
|
15
54
|
end
|
|
16
55
|
end
|
|
17
56
|
end
|
data/lib/tramway/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tramway
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: '0.6'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kalashnikovisme
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2025-10-
|
|
12
|
+
date: 2025-10-25 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: anyway_config
|
|
@@ -143,6 +143,13 @@ files:
|
|
|
143
143
|
- app/assets/javascripts/tramway/table_row_preview_controller.js
|
|
144
144
|
- app/components/tailwind_component.html.haml
|
|
145
145
|
- app/components/tailwind_component.rb
|
|
146
|
+
- app/components/tailwinds/back_button_component.html.haml
|
|
147
|
+
- app/components/tailwinds/back_button_component.rb
|
|
148
|
+
- app/components/tailwinds/base_component.rb
|
|
149
|
+
- app/components/tailwinds/button_component.html.haml
|
|
150
|
+
- app/components/tailwinds/button_component.rb
|
|
151
|
+
- app/components/tailwinds/containers/narrow_component.html.haml
|
|
152
|
+
- app/components/tailwinds/containers/narrow_component.rb
|
|
146
153
|
- app/components/tailwinds/form/builder.rb
|
|
147
154
|
- app/components/tailwinds/form/file_field_component.html.haml
|
|
148
155
|
- app/components/tailwinds/form/file_field_component.rb
|
|
@@ -214,6 +221,7 @@ files:
|
|
|
214
221
|
- app/views/tramway/layouts/application.html.haml
|
|
215
222
|
- config/routes.rb
|
|
216
223
|
- config/tailwind.config.js
|
|
224
|
+
- lib/generators/tramway/install/install_generator.rb
|
|
217
225
|
- lib/kaminari/helpers/tag.rb
|
|
218
226
|
- lib/rules/turbo_html_attributes_rules.rb
|
|
219
227
|
- lib/tasks/tramway_tasks.rake
|