headmin 0.1.1
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 +7 -0
- data/.gitignore +13 -0
- data/.nvmrc +1 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +43 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/Rakefile +16 -0
- data/app/assets/images/avatar.jpg +0 -0
- data/app/controllers/admin/users/confirmations_controller.rb +31 -0
- data/app/controllers/admin/users/omniauth_callbacks_controller.rb +31 -0
- data/app/controllers/admin/users/passwords_controller.rb +35 -0
- data/app/controllers/admin/users/registrations_controller.rb +63 -0
- data/app/controllers/admin/users/sessions_controller.rb +28 -0
- data/app/controllers/admin/users/unlocks_controller.rb +31 -0
- data/app/controllers/concerns/headmin/acts_as_list.rb +16 -0
- data/app/controllers/concerns/headmin/authentication.rb +17 -0
- data/app/controllers/concerns/headmin/ckeditor.rb +27 -0
- data/app/controllers/concerns/headmin/filter.rb +5 -0
- data/app/controllers/concerns/headmin/pagination.rb +23 -0
- data/app/controllers/concerns/headmin/searchable.rb +15 -0
- data/app/controllers/concerns/headmin/sortable.rb +44 -0
- data/app/helpers/headmin/admin_helper.rb +65 -0
- data/app/helpers/headmin/filter_helper.rb +12 -0
- data/app/helpers/headmin/notification_helper.rb +31 -0
- data/app/views/admin/users/confirmations/new.html.erb +9 -0
- data/app/views/admin/users/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/admin/users/mailer/email_changed.html.erb +7 -0
- data/app/views/admin/users/mailer/password_change.html.erb +3 -0
- data/app/views/admin/users/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/admin/users/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/admin/users/passwords/edit.html.erb +12 -0
- data/app/views/admin/users/passwords/new.html.erb +9 -0
- data/app/views/admin/users/registrations/edit.html.erb +24 -0
- data/app/views/admin/users/registrations/new.html.erb +11 -0
- data/app/views/admin/users/sessions/new.html.erb +13 -0
- data/app/views/admin/users/shared/_error_messages.html.erb +7 -0
- data/app/views/admin/users/shared/_links.html.erb +27 -0
- data/app/views/admin/users/unlocks/new.html.erb +10 -0
- data/app/views/headmin/_breadcrumbs.html.erb +23 -0
- data/app/views/headmin/_filters.html.erb +47 -0
- data/app/views/headmin/_form.html.erb +11 -0
- data/app/views/headmin/_heading.html.erb +9 -0
- data/app/views/headmin/_index.html.erb +12 -0
- data/app/views/headmin/_notifications.html.erb +12 -0
- data/app/views/headmin/_pagination.html.erb +13 -0
- data/app/views/headmin/_table.html.erb +13 -0
- data/app/views/headmin/filters/_date.html.erb +46 -0
- data/app/views/headmin/filters/_search.html.erb +22 -0
- data/app/views/headmin/filters/_select.html.erb +39 -0
- data/app/views/headmin/filters/filter/_button.html.erb +22 -0
- data/app/views/headmin/filters/filter/_menu_item.html.erb +12 -0
- data/app/views/headmin/filters/filter/_template.html.erb +13 -0
- data/app/views/headmin/forms/_actions.html.erb +32 -0
- data/app/views/headmin/forms/_errors.html.erb +20 -0
- data/app/views/headmin/forms/_group.html.erb +36 -0
- data/app/views/headmin/forms/fields/_checkbox.html.erb +23 -0
- data/app/views/headmin/forms/fields/_ckeditor.html.erb +28 -0
- data/app/views/headmin/forms/fields/_currency.html.erb +24 -0
- data/app/views/headmin/forms/fields/_date.html.erb +36 -0
- data/app/views/headmin/forms/fields/_email.html.erb +39 -0
- data/app/views/headmin/forms/fields/_file.html.erb +24 -0
- data/app/views/headmin/forms/fields/_image.html.erb +37 -0
- data/app/views/headmin/forms/fields/_label.html.erb +9 -0
- data/app/views/headmin/forms/fields/_multiple_select.html.erb +37 -0
- data/app/views/headmin/forms/fields/_password.html.erb +39 -0
- data/app/views/headmin/forms/fields/_repeater.html.erb +48 -0
- data/app/views/headmin/forms/fields/_select.html.erb +36 -0
- data/app/views/headmin/forms/fields/_select_tags.html.erb +32 -0
- data/app/views/headmin/forms/fields/_text.html.erb +39 -0
- data/app/views/headmin/forms/fields/_textarea.html.erb +29 -0
- data/app/views/headmin/forms/fields/_url.html.erb +38 -0
- data/app/views/headmin/forms/fields/_validation.html.erb +12 -0
- data/app/views/headmin/forms/fields/repeater/_row.html.erb +16 -0
- data/app/views/headmin/heading/_title.html.erb +24 -0
- data/app/views/headmin/kaminari/_first_page.html.erb +11 -0
- data/app/views/headmin/kaminari/_gap.html.erb +12 -0
- data/app/views/headmin/kaminari/_last_page.html.erb +12 -0
- data/app/views/headmin/kaminari/_next_page.html.erb +13 -0
- data/app/views/headmin/kaminari/_page.html.erb +12 -0
- data/app/views/headmin/kaminari/_paginator.html.erb +25 -0
- data/app/views/headmin/kaminari/_prev_page.html.erb +11 -0
- data/app/views/headmin/layout/_body.html.erb +9 -0
- data/app/views/headmin/layout/_content.html.erb +9 -0
- data/app/views/headmin/layout/_footer.html.erb +17 -0
- data/app/views/headmin/layout/_header.html.erb +13 -0
- data/app/views/headmin/layout/_main.html.erb +13 -0
- data/app/views/headmin/layout/_sidebar.html.erb +20 -0
- data/app/views/headmin/layout/dropdown/_divider.html.erb +9 -0
- data/app/views/headmin/layout/dropdown/_item.html.erb +17 -0
- data/app/views/headmin/layout/header/_account.html.erb +25 -0
- data/app/views/headmin/layout/header/_locale.html.erb +19 -0
- data/app/views/headmin/layout/sidebar/_bottom.html.erb +4 -0
- data/app/views/headmin/layout/sidebar/_menu.html.erb +9 -0
- data/app/views/headmin/layout/sidebar/menu/_account.html.erb +25 -0
- data/app/views/headmin/layout/sidebar/menu/_item.html.erb +16 -0
- data/app/views/headmin/layout/sidebar/menu/_locale.html.erb +18 -0
- data/app/views/headmin/table/_actions.html.erb +19 -0
- data/app/views/headmin/table/_body.html.erb +19 -0
- data/app/views/headmin/table/_foot.html.erb +15 -0
- data/app/views/headmin/table/_footer.html.erb +13 -0
- data/app/views/headmin/table/_head.html.erb +15 -0
- data/app/views/headmin/table/_header.html.erb +13 -0
- data/app/views/headmin/table/actions/_action.html.erb +10 -0
- data/app/views/headmin/table/actions/_delete.html.erb +8 -0
- data/app/views/headmin/table/actions/_export.html.erb +8 -0
- data/app/views/headmin/table/body/_association.html.erb +11 -0
- data/app/views/headmin/table/body/_boolean.erb +16 -0
- data/app/views/headmin/table/body/_currency.html.erb +10 -0
- data/app/views/headmin/table/body/_date.html.erb +11 -0
- data/app/views/headmin/table/body/_id.html.erb +3 -0
- data/app/views/headmin/table/body/_row.html.erb +9 -0
- data/app/views/headmin/table/body/_sort.html.erb +3 -0
- data/app/views/headmin/table/body/_string.html.erb +16 -0
- data/app/views/headmin/table/body/_text.html.erb +10 -0
- data/app/views/headmin/table/foot/_cell.html.erb +10 -0
- data/app/views/headmin/table/foot/_id.html.erb +9 -0
- data/app/views/headmin/table/head/_cell.html.erb +13 -0
- data/app/views/headmin/table/head/_empty.html.erb +1 -0
- data/app/views/headmin/table/head/_id.html.erb +9 -0
- data/app/views/headmin/table/head/_sort.html.erb +3 -0
- data/app/views/headmin/table/head/cell/_asc.html.erb +4 -0
- data/app/views/headmin/table/head/cell/_default.html.erb +7 -0
- data/app/views/headmin/table/head/cell/_desc.html.erb +4 -0
- data/app/views/layouts/admin/auth.html.erb +20 -0
- data/app/views/layouts/admin.html.erb +42 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/locales/defaults/en.yml +215 -0
- data/config/locales/defaults/nl.yml +213 -0
- data/config/locales/devise/nl.yml +85 -0
- data/config/locales/en.yml +137 -0
- data/config/locales/nl.yml +138 -0
- data/dist/css/headmin.css +9874 -0
- data/dist/js/headmin.js +852 -0
- data/docs/README.md +4 -0
- data/docs/blocks.md +116 -0
- data/docs/devise.md +62 -0
- data/headmin.gemspec +35 -0
- data/lib/headmin/engine.rb +10 -0
- data/lib/headmin/version.rb +5 -0
- data/lib/headmin.rb +4 -0
- data/package.json +62 -0
- data/src/js/headmin/controllers/filter_controller.js +47 -0
- data/src/js/headmin/controllers/filters_controller.js +53 -0
- data/src/js/headmin/controllers/index_controller.js +79 -0
- data/src/js/headmin/controllers/repeater_controller.js +35 -0
- data/src/js/headmin/controllers/repeater_row_controller.js +54 -0
- data/src/js/headmin/controllers/table_actions_controller.js +22 -0
- data/src/js/headmin/controllers/table_controller.js +36 -0
- data/src/js/headmin/headmin.js +168 -0
- data/src/js/headmin.js +1 -0
- data/src/scss/headmin/filter.scss +33 -0
- data/src/scss/headmin/filters.scss +14 -0
- data/src/scss/headmin/form.scss +39 -0
- data/src/scss/headmin/general.scss +3 -0
- data/src/scss/headmin/layout/body.scss +6 -0
- data/src/scss/headmin/layout/sidebar.scss +22 -0
- data/src/scss/headmin/layout.scss +2 -0
- data/src/scss/headmin/login.scss +35 -0
- data/src/scss/headmin/table.scss +32 -0
- data/src/scss/headmin/utilities.scss +19 -0
- data/src/scss/headmin.scss +58 -0
- data/src/scss/vendor/bootstrap/variables.scss +71 -0
- data/src/scss/vendor/choices/cross-inverse.svg +6 -0
- data/src/scss/vendor/choices/cross.svg +6 -0
- data/src/scss/vendor/choices/custom.scss +28 -0
- data/src/scss/vendor/choices/variables.scss +16 -0
- data/webpack.config.js +30 -0
- data/yarn.lock +7512 -0
- metadata +220 -0
data/docs/README.md
ADDED
data/docs/blocks.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Blocks & Fields
|
2
|
+
|
3
|
+
## Database
|
4
|
+
blocks
|
5
|
+
```
|
6
|
+
id = 1 resource_type="Page" resource_id="1" name = 'people', position = 1
|
7
|
+
```
|
8
|
+
|
9
|
+
fields
|
10
|
+
```
|
11
|
+
id = 1 block_id="1" name = 'people_title' type = 'string' value = 'test'
|
12
|
+
id = 2 block_id="1" name = 'people_people' type = 'list'
|
13
|
+
id = 3 block_id="1" name = 'people_people_1_person' type = 'group' parent_id = 2
|
14
|
+
id = 4 block_id="1" name = 'people_people_1_person_name' type = 'string' parent_id = 3 value = 'Jef'
|
15
|
+
id = 5 block_id="1" name = 'people_people_2_person' type = 'group' parent_id = 2
|
16
|
+
id = 6 block_id="1" name = 'people_people_2_person_name' type = 'string' parent_id = 6 value = 'Gert-Jan'
|
17
|
+
```
|
18
|
+
|
19
|
+
## Models
|
20
|
+
|
21
|
+
|
22
|
+
```rb
|
23
|
+
# models/page.rb
|
24
|
+
|
25
|
+
class Page < ActiveRecord::Model
|
26
|
+
has_many :blocks, as: :blockable
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
```rb
|
31
|
+
# models/block.rb
|
32
|
+
|
33
|
+
class Block < ActiveRecord::Model
|
34
|
+
belongs_to :blockable, polymorphic: true
|
35
|
+
has_many :fields
|
36
|
+
|
37
|
+
# db to hash
|
38
|
+
def fields_hash
|
39
|
+
{
|
40
|
+
title: 'test',
|
41
|
+
people: [
|
42
|
+
{name: 'Jef'},
|
43
|
+
{name: 'Gert-Jan'}
|
44
|
+
]
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
```rb
|
51
|
+
# models/block/fields.rb
|
52
|
+
|
53
|
+
class Block::Field < ActiveRecord::Model
|
54
|
+
self.table_name = 'block_fields'
|
55
|
+
|
56
|
+
belongs_to :block
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
## Controllers
|
61
|
+
```ruby
|
62
|
+
# website/pages_controller.rb
|
63
|
+
|
64
|
+
class PagesController < ApplicationController
|
65
|
+
def show
|
66
|
+
@page = Page.friendly.find(params[:slug])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
## Views
|
72
|
+
```erb
|
73
|
+
# admin/pages/_form.html.erb
|
74
|
+
|
75
|
+
<input type="text" name="title" value="test">
|
76
|
+
|
77
|
+
<%= render 'blocks/people', required: true, moveable: false %>
|
78
|
+
<%= render 'blocks/menu', required: true, moveable: false %>
|
79
|
+
<%= render 'blocks/contact_form', required: true, moveable: false %>
|
80
|
+
<%= render 'blocks/map', required: true, moveable: false %>
|
81
|
+
|
82
|
+
<%= render 'blocks/heading', required: false, moveable: true %>
|
83
|
+
<%= render 'blocks/text', required: false, moveable: true %>
|
84
|
+
<%= render 'blocks/image', required: false, moveable: true %>
|
85
|
+
<%= render 'blocks/video', required: false, moveable: true %>
|
86
|
+
```
|
87
|
+
|
88
|
+
```erb
|
89
|
+
# admin/blocks/_people.html.erb
|
90
|
+
|
91
|
+
<input type="text" name="people_title" value="title" value="test">
|
92
|
+
<div class="repeater" data-min="2" data-max="3">
|
93
|
+
<div class="group">
|
94
|
+
<input type="text" name="people_people_1_person_name" value="name" value="Jef">
|
95
|
+
<input type="text" name="people_people_2_person_name" value="name" value="Gert-Jan">
|
96
|
+
</div>
|
97
|
+
</div>
|
98
|
+
```
|
99
|
+
|
100
|
+
```erb
|
101
|
+
# website/pages/show.html.erb
|
102
|
+
|
103
|
+
<% @page.blocks.each do |block| %>
|
104
|
+
<%= render "block_#{block.name}", block: block %>
|
105
|
+
<% end %>
|
106
|
+
```
|
107
|
+
|
108
|
+
```erb
|
109
|
+
# website/blocks/_people.html.erb
|
110
|
+
|
111
|
+
<% fields = block.fields_hash %>
|
112
|
+
<%= fields[:title] %>
|
113
|
+
<% fields[:people].each do |person| %>
|
114
|
+
<%= person[:name] %>
|
115
|
+
<% end %>
|
116
|
+
```
|
data/docs/devise.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Authentication
|
2
|
+
|
3
|
+
We use the gem Devise for authentication. In this example, two different user types will be set up:
|
4
|
+
Admin and normal User. Only admins have access to the headmin back office. It is optional if normal users
|
5
|
+
are allowed to login/register from the website.
|
6
|
+
|
7
|
+
##Installation
|
8
|
+
|
9
|
+
#### Setup devise
|
10
|
+
Add the gem devise and bundle install. Create a user model:
|
11
|
+
|
12
|
+
```
|
13
|
+
rails generate devise User
|
14
|
+
```
|
15
|
+
|
16
|
+
Add the field "type" to the user model
|
17
|
+
```
|
18
|
+
rails g migration AddTypeToUsers type:string
|
19
|
+
rails db:migrate
|
20
|
+
```
|
21
|
+
|
22
|
+
In models/ add the file admin.rb
|
23
|
+
```
|
24
|
+
class Admin < User
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
#### Setup routes
|
29
|
+
In routes.rb
|
30
|
+
```
|
31
|
+
namespace(:admin) do
|
32
|
+
devise_for :admins, path: 'users', singular: 'admin', controllers: {
|
33
|
+
sessions: 'admin/users/sessions',
|
34
|
+
registrations: 'admin/users/registrations',
|
35
|
+
passwords: 'admin/users/passwords',
|
36
|
+
unlocks: 'admin/users/unlocks',
|
37
|
+
confirmations: 'admin/users/confirmations',
|
38
|
+
}
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
In case, no normal users are necessary, you can discard the next step:
|
43
|
+
```
|
44
|
+
scope module: 'website' do
|
45
|
+
devise_for :users, controllers: {
|
46
|
+
sessions: 'website/users/sessions',
|
47
|
+
registrations: 'website/users/registrations',
|
48
|
+
passwords: 'website/users/passwords',
|
49
|
+
unlocks: 'website/users/unlocks',
|
50
|
+
confirmations: 'website/users/confirmations',
|
51
|
+
}
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
#### Setup protected routes and configuration for headmin
|
56
|
+
In controllers/admin_controller.rb
|
57
|
+
```
|
58
|
+
class AdminController < ApplicationController
|
59
|
+
alias_method :devise_current_user, :current_user
|
60
|
+
include Headmin::Authentication
|
61
|
+
end
|
62
|
+
```
|
data/headmin.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/headmin/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "headmin"
|
7
|
+
spec.version = Headmin::VERSION
|
8
|
+
spec.authors = ["Jef Vlamings"]
|
9
|
+
spec.email = ["vlamingsjef@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Admin component library"
|
12
|
+
spec.description = "A complete library of commonly used components to build an admin interface in your Ruby on Rails project."
|
13
|
+
spec.homepage = "https://github.com/insiting/headmin"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/insiting/headmin"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/insiting/headmin/releases"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
# Uncomment to register a new dependency of your gem
|
31
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
32
|
+
|
33
|
+
# For more information and examples about making a new gem, checkout our
|
34
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
35
|
+
end
|
data/lib/headmin.rb
ADDED
data/package.json
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
{
|
2
|
+
"name": "headmin",
|
3
|
+
"version": "0.1.1",
|
4
|
+
"description": "A complete library of commonly used components to build an admin interface in your Ruby on Rails project.",
|
5
|
+
"main": "src/js/headmin.js",
|
6
|
+
"sass": "src/scss/headmin.scss",
|
7
|
+
"style": "src/css/headmin.css",
|
8
|
+
"repository": {
|
9
|
+
"type": "git",
|
10
|
+
"url": "git+https://github.com/insiting/headmin.git"
|
11
|
+
},
|
12
|
+
"keywords": [
|
13
|
+
"headmin",
|
14
|
+
"insiting"
|
15
|
+
],
|
16
|
+
"author": "Jef Vlamings <vlamingsjef@gmail.com>",
|
17
|
+
"license": "MIT",
|
18
|
+
"bugs": {
|
19
|
+
"url": "https://github.com/insiting/headmin/issues"
|
20
|
+
},
|
21
|
+
"homepage": "https://github.com/insiting/headmin#readme",
|
22
|
+
"dependencies": {
|
23
|
+
"@rails/ujs": "^6.0.0",
|
24
|
+
"bootstrap": "^5.0.0-beta2",
|
25
|
+
"choices.js": "^9.0.1",
|
26
|
+
"ckeditor5-build-classic-simple-upload-adapter-image-resize": "^1.0.4",
|
27
|
+
"flatpickr": "^4.6.9",
|
28
|
+
"popper": "^1.0.1",
|
29
|
+
"sortablejs": "^1.13.0",
|
30
|
+
"stimulus": "^2.0.0"
|
31
|
+
},
|
32
|
+
"devDependencies": {
|
33
|
+
"@babel/core": "^7.12.10",
|
34
|
+
"@babel/preset-env": "^7.12.11",
|
35
|
+
"autoprefixer": "^10.2.3",
|
36
|
+
"babel-loader": "^8.2.2",
|
37
|
+
"node-sass": "^5.0.0",
|
38
|
+
"node-sass-tilde-importer": "^1.0.2",
|
39
|
+
"onchange": "^7.1.0",
|
40
|
+
"path": "^0.12.7",
|
41
|
+
"postcss": "^8.2.4",
|
42
|
+
"postcss-cli": "^8.3.1",
|
43
|
+
"watch": "^1.0.2",
|
44
|
+
"webpack": "4.44.2",
|
45
|
+
"webpack-cli": "^4.4.0"
|
46
|
+
},
|
47
|
+
"browserslist": [
|
48
|
+
"last 2 versions",
|
49
|
+
"ie >= 11"
|
50
|
+
],
|
51
|
+
"scripts": {
|
52
|
+
"autoprefixer": "postcss -u autoprefixer -b 'last 2 versions' -r dist/css/headmin.css",
|
53
|
+
"compile:sass": "node-sass --importer=node_modules/node-sass-tilde-importer src/scss/headmin.scss dist/css/headmin.css",
|
54
|
+
"build:css": "yarn compile:sass && yarn autoprefixer",
|
55
|
+
"build:js": "webpack",
|
56
|
+
"build": "yarn build:css & yarn build:js",
|
57
|
+
"watch:css": "onchange src/scss/**/*.scss -- yarn build:css",
|
58
|
+
"watch:js": "onchange src/js/**/*.js -- yarn build:js",
|
59
|
+
"watch": "yarn watch:css & yarn watch:js",
|
60
|
+
"dev": "yarn build & yarn watch"
|
61
|
+
}
|
62
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["button", "popup"]
|
6
|
+
}
|
7
|
+
|
8
|
+
// Attaches controller logic to the element itself
|
9
|
+
// This allows calling controller methods from the element in other controllers
|
10
|
+
connect () {
|
11
|
+
this.element['controller'] = this
|
12
|
+
}
|
13
|
+
|
14
|
+
toggle(event) {
|
15
|
+
event.preventDefault()
|
16
|
+
const expanded = this.buttonTarget.getAttribute('aria-expanded') === 'true'
|
17
|
+
if (expanded) {
|
18
|
+
this.close(null)
|
19
|
+
} else {
|
20
|
+
this.open()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
open() {
|
25
|
+
this.buttonTarget.setAttribute('aria-expanded', 'true')
|
26
|
+
this.popupTarget.classList.remove('closed')
|
27
|
+
}
|
28
|
+
|
29
|
+
close(event) {
|
30
|
+
if (this.isClickedInside(event)) {
|
31
|
+
event.preventDefault()
|
32
|
+
return
|
33
|
+
}
|
34
|
+
this.buttonTarget.setAttribute('aria-expanded', 'false')
|
35
|
+
this.popupTarget.classList.add('closed')
|
36
|
+
}
|
37
|
+
|
38
|
+
isClickedInside(event) {
|
39
|
+
if (!event) {
|
40
|
+
return false
|
41
|
+
}
|
42
|
+
const inPopup = this.popupTarget.contains(event.target)
|
43
|
+
const inButton = this.buttonTarget.contains(event.target)
|
44
|
+
const inAddButton = event.target.dataset.action === "click->filters#add"
|
45
|
+
return (inPopup || inButton || inAddButton)
|
46
|
+
}
|
47
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
import {Headmin} from "../headmin";
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get targets() {
|
6
|
+
return ["form", "list", "input", "template", "button"]
|
7
|
+
}
|
8
|
+
|
9
|
+
add(event) {
|
10
|
+
event.preventDefault()
|
11
|
+
const name = event.target.dataset.filterName
|
12
|
+
const button = this.getButtonByName(name)
|
13
|
+
if (button) {
|
14
|
+
this.openFilter(button)
|
15
|
+
} else {
|
16
|
+
const template = this.getTemplateByName(name)
|
17
|
+
this.listTarget.insertAdjacentHTML('beforeend', template.innerHTML)
|
18
|
+
const button = this.getButtonByName(name)
|
19
|
+
Headmin.initPlugins()
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
remove(event) {
|
24
|
+
const filter = event.currentTarget.closest('.h-filter')
|
25
|
+
filter.remove()
|
26
|
+
this.formTarget.submit()
|
27
|
+
}
|
28
|
+
|
29
|
+
removeAll(event) {
|
30
|
+
this.listTarget.innerHTML = ""
|
31
|
+
this.formTarget.submit()
|
32
|
+
}
|
33
|
+
|
34
|
+
update(event) {
|
35
|
+
this.formTarget.submit()
|
36
|
+
}
|
37
|
+
|
38
|
+
getTemplateByName(name) {
|
39
|
+
return this.templateTargets.find(function (element) {
|
40
|
+
return element.dataset.filterName === name
|
41
|
+
})
|
42
|
+
}
|
43
|
+
|
44
|
+
getButtonByName(name) {
|
45
|
+
return this.buttonTargets.find(function (element) {
|
46
|
+
return element.dataset.filterName === name
|
47
|
+
})
|
48
|
+
}
|
49
|
+
|
50
|
+
openFilter(button) {
|
51
|
+
button.controller.open()
|
52
|
+
}
|
53
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["actions", "idCheckbox", "idsCheckbox"]
|
6
|
+
}
|
7
|
+
|
8
|
+
toggleIds(event) {
|
9
|
+
const checkbox = event.target
|
10
|
+
this.toggleIdsCheckboxes(checkbox.checked)
|
11
|
+
this.toggleIdCheckboxes(checkbox.checked)
|
12
|
+
this.syncFields()
|
13
|
+
}
|
14
|
+
|
15
|
+
toggleId(event) {
|
16
|
+
this.toggleIdsCheckboxes(false)
|
17
|
+
this.syncFields()
|
18
|
+
}
|
19
|
+
|
20
|
+
toggleIdsCheckboxes(checked) {
|
21
|
+
this.idsCheckboxTargets.forEach((checkbox) => {
|
22
|
+
checkbox.checked = checked
|
23
|
+
});
|
24
|
+
}
|
25
|
+
|
26
|
+
toggleIdCheckboxes(checked) {
|
27
|
+
this.idCheckboxTargets.forEach((checkbox) => {
|
28
|
+
checkbox.checked = checked
|
29
|
+
});
|
30
|
+
}
|
31
|
+
|
32
|
+
syncFields() {
|
33
|
+
this.removeIds()
|
34
|
+
if(this.idsCheckboxTarget.checked) {
|
35
|
+
this.addId('')
|
36
|
+
} else {
|
37
|
+
this.idCheckboxTargets.forEach((checkbox) => {
|
38
|
+
if(checkbox.checked) {
|
39
|
+
this.addId(checkbox.value)
|
40
|
+
}
|
41
|
+
});
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
addId(id) {
|
46
|
+
let field = this.getIdField(id)
|
47
|
+
if (!field) {
|
48
|
+
field = this.newIdField(id)
|
49
|
+
this.actionsTarget.insertAdjacentHTML('afterbegin', field)
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
removeIds() {
|
54
|
+
const fields = this.getIdFields()
|
55
|
+
fields.forEach((field) => {
|
56
|
+
this.actionsTarget.removeChild(field)
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
removeId(id) {
|
61
|
+
const field = this.getIdField(id)
|
62
|
+
if (field) {
|
63
|
+
this.actionsTarget.removeChild(field)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
newIdField(id) {
|
68
|
+
const template = this.actionsTarget.querySelector('[data-index-target="idFieldTemplate"]')
|
69
|
+
return template.innerHTML.replace(/ID/g, id)
|
70
|
+
}
|
71
|
+
|
72
|
+
getIdFields() {
|
73
|
+
return this.actionsTarget.querySelectorAll(`input[name="ids[]"]`);
|
74
|
+
}
|
75
|
+
|
76
|
+
getIdField(id) {
|
77
|
+
return this.actionsTarget.querySelector(`input[name="ids[]"][value="${id}"]`);
|
78
|
+
}
|
79
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
import {Headmin} from "../headmin";
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get values() {
|
6
|
+
return {
|
7
|
+
id: String
|
8
|
+
}
|
9
|
+
}
|
10
|
+
static get targets() {
|
11
|
+
return ["links", "template"]
|
12
|
+
}
|
13
|
+
|
14
|
+
add_association(event) {
|
15
|
+
event.preventDefault()
|
16
|
+
|
17
|
+
let html = this.getTemplateHTML()
|
18
|
+
html = this.replaceIdsWithTimestamps(html)
|
19
|
+
this.addNewRow(html)
|
20
|
+
}
|
21
|
+
|
22
|
+
getTemplateHTML() {
|
23
|
+
return this.templateTarget.innerHTML
|
24
|
+
}
|
25
|
+
|
26
|
+
replaceIdsWithTimestamps(html) {
|
27
|
+
const regex = new RegExp(this.idValue, "g");
|
28
|
+
return html.replace(regex, new Date().getTime())
|
29
|
+
}
|
30
|
+
|
31
|
+
addNewRow(html) {
|
32
|
+
this.linksTarget.insertAdjacentHTML('beforebegin', html)
|
33
|
+
Headmin.initPlugins()
|
34
|
+
}
|
35
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
import {Headmin} from '../headmin'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get targets() {
|
6
|
+
return ["row"]
|
7
|
+
}
|
8
|
+
|
9
|
+
add_association(event) {
|
10
|
+
event.preventDefault()
|
11
|
+
|
12
|
+
let html = this.getTemplateHTML()
|
13
|
+
html = this.replaceIdsWithTimestamps(html)
|
14
|
+
this.addRow(html)
|
15
|
+
}
|
16
|
+
|
17
|
+
remove_association(event) {
|
18
|
+
event.preventDefault()
|
19
|
+
|
20
|
+
let row = event.target.closest(".repeater-row")
|
21
|
+
|
22
|
+
if (row.dataset.newRecord === "true") {
|
23
|
+
// New records are simply removed from the page
|
24
|
+
row.remove()
|
25
|
+
} else {
|
26
|
+
// Existing records are hidden and flagged for deletion
|
27
|
+
row.querySelector("input[name*='_destroy']").value = 1
|
28
|
+
row.style.display = 'none'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
addRow(html) {
|
33
|
+
this.rowTarget.insertAdjacentHTML('afterend', html)
|
34
|
+
Headmin.initPlugins()
|
35
|
+
}
|
36
|
+
|
37
|
+
replaceIdsWithTimestamps(html) {
|
38
|
+
const regex = new RegExp(this.idValue(), "g");
|
39
|
+
return html.replace(regex, new Date().getTime())
|
40
|
+
}
|
41
|
+
|
42
|
+
getTemplateHTML() {
|
43
|
+
const templateTarget = this.templateTarget()
|
44
|
+
return templateTarget.innerHTML
|
45
|
+
}
|
46
|
+
|
47
|
+
templateTarget() {
|
48
|
+
return this.rowTarget.closest('.repeater').querySelector('[data-repeater-target="template"]')
|
49
|
+
}
|
50
|
+
|
51
|
+
idValue() {
|
52
|
+
return this.rowTarget.closest('.repeater').dataset.repeaterIdValue
|
53
|
+
}
|
54
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return ["form", "select", "method"]
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.wrapperClass = "table-actions"
|
10
|
+
}
|
11
|
+
|
12
|
+
update(event) {
|
13
|
+
event.preventDefault()
|
14
|
+
const option = this.selectTarget.options[this.selectTarget.selectedIndex]
|
15
|
+
const action = this.selectTarget.value
|
16
|
+
const method = option.dataset.method
|
17
|
+
|
18
|
+
// Replace form action
|
19
|
+
this.formTarget.action = action
|
20
|
+
this.methodTarget.value = method
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
import Sortable from "sortablejs";
|
3
|
+
import Rails from "@rails/ujs";
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static get values() {
|
7
|
+
return {url: String}
|
8
|
+
}
|
9
|
+
|
10
|
+
static get targets() {
|
11
|
+
return ["table", "body"]
|
12
|
+
}
|
13
|
+
|
14
|
+
connect() {
|
15
|
+
new Sortable(this.bodyTarget, {
|
16
|
+
handle: '.table-drag-sort-handle',
|
17
|
+
onEnd: (event) => {
|
18
|
+
Rails.ajax({
|
19
|
+
url: this.urlValue,
|
20
|
+
type: "PATCH",
|
21
|
+
data: this.getIdsDataString(),
|
22
|
+
});
|
23
|
+
}
|
24
|
+
})
|
25
|
+
}
|
26
|
+
|
27
|
+
getIdsDataString() {
|
28
|
+
const table = this.tableTarget
|
29
|
+
let data = ""
|
30
|
+
const handles = [...table.querySelectorAll(".table-drag-sort-handle")]
|
31
|
+
handles.map(handle => {
|
32
|
+
data += `ids[]=${handle.dataset.id}&`
|
33
|
+
})
|
34
|
+
return data
|
35
|
+
}
|
36
|
+
}
|