headmin 0.1.2 → 0.2.3
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/.gitignore +1 -15
- data/Gemfile +0 -1
- data/Gemfile.lock +24 -2
- data/README.md +15 -43
- data/app/controllers/concerns/headmin/authentication.rb +0 -8
- data/app/controllers/concerns/headmin/pagination.rb +1 -1
- data/app/helpers/headmin/admin_helper.rb +4 -4
- data/app/models/concerns/headmin/block.rb +11 -0
- data/app/models/concerns/headmin/blockable.rb +10 -0
- data/app/models/concerns/headmin/field.rb +17 -0
- data/app/models/concerns/headmin/fieldable.rb +44 -0
- data/app/services/block_service.rb +68 -0
- data/app/views/{layouts → examples}/admin.html.erb +0 -0
- data/app/views/{layouts/admin → examples}/auth.html.erb +0 -0
- data/app/views/headmin/_blocks.html.erb +24 -0
- data/app/views/headmin/_breadcrumbs.html.erb +9 -5
- data/app/views/headmin/_card.html.erb +48 -0
- data/app/views/headmin/_dropdown.html.erb +18 -0
- data/app/views/headmin/_filters.html.erb +56 -34
- data/app/views/headmin/_form.html.erb +60 -6
- data/app/views/headmin/_heading.html.erb +8 -4
- data/app/views/headmin/_index.html.erb +9 -8
- data/app/views/headmin/_notifications.html.erb +8 -0
- data/app/views/headmin/_pagination.html.erb +11 -7
- data/app/views/headmin/_popup.html.erb +26 -0
- data/app/views/headmin/_table.html.erb +11 -4
- data/app/views/headmin/dropdown/_button.html.erb +13 -0
- data/app/views/headmin/dropdown/_devise.html.erb +21 -0
- data/app/views/headmin/{layout/dropdown → dropdown}/_divider.html.erb +1 -1
- data/app/views/headmin/dropdown/_item.html.erb +18 -0
- data/app/views/headmin/dropdown/_list.html.erb +11 -0
- data/app/views/headmin/dropdown/_locale.html.erb +17 -0
- data/app/views/headmin/filters/_date.html.erb +21 -16
- data/app/views/headmin/filters/_search.html.erb +18 -18
- data/app/views/headmin/filters/_select.html.erb +28 -23
- data/app/views/headmin/filters/filter/_button.html.erb +15 -6
- data/app/views/headmin/filters/filter/_menu_item.html.erb +2 -2
- data/app/views/headmin/filters/filter/_template.html.erb +2 -2
- data/app/views/headmin/forms/_actions.html.erb +4 -8
- data/app/views/headmin/forms/_base.html.erb +60 -0
- data/app/views/headmin/forms/_blocks.html.erb +32 -0
- data/app/views/headmin/forms/_checkbox.html.erb +39 -0
- data/app/views/headmin/forms/_ckeditor.html.erb +42 -0
- data/app/views/headmin/forms/_date.html.erb +45 -0
- data/app/views/headmin/forms/_email.html.erb +39 -0
- data/app/views/headmin/forms/_file.html.erb +40 -0
- data/app/views/headmin/forms/_image.html.erb +55 -0
- data/app/views/headmin/forms/_label.html.erb +24 -0
- data/app/views/headmin/forms/_number.html.erb +50 -0
- data/app/views/headmin/forms/_password.html.erb +69 -0
- data/app/views/headmin/forms/_redactorx.html.erb +49 -0
- data/app/views/headmin/forms/_repeater.html.erb +133 -0
- data/app/views/headmin/forms/_select.html.erb +70 -0
- data/app/views/headmin/forms/_text.html.erb +62 -0
- data/app/views/headmin/forms/_textarea.html.erb +39 -0
- data/app/views/headmin/forms/_url.html.erb +39 -0
- data/app/views/headmin/forms/_validation.html.erb +21 -0
- data/app/views/headmin/forms/actions/_destroy.html.erb +13 -0
- data/app/views/headmin/forms/actions/_save.html.erb +12 -0
- data/app/views/headmin/forms/actions/_view.html.erb +15 -0
- data/app/views/headmin/forms/fields/_base.html.erb +25 -0
- data/app/views/headmin/forms/fields/_file.html.erb +15 -22
- data/app/views/headmin/forms/fields/_group.html.erb +38 -0
- data/app/views/headmin/forms/fields/_image.html.erb +15 -35
- data/app/views/headmin/forms/fields/_list.html.erb +26 -0
- data/app/views/headmin/forms/fields/_text.html.erb +15 -37
- data/app/views/headmin/forms/repeater/_row.html.erb +51 -0
- data/app/views/headmin/heading/_title.html.erb +1 -1
- data/app/views/headmin/layout/_body.html.erb +1 -1
- data/app/views/headmin/layout/_content.html.erb +1 -1
- data/app/views/headmin/layout/_footer.html.erb +1 -1
- data/app/views/headmin/layout/_header.html.erb +1 -1
- data/app/views/headmin/layout/_main.html.erb +2 -2
- data/app/views/headmin/layout/_sidebar.html.erb +1 -1
- data/app/views/headmin/layout/sidebar/{_menu.html.erb → _nav.html.erb} +1 -1
- data/app/views/headmin/nav/_item.html.erb +21 -0
- data/app/views/headmin/nav/item/_devise.html.erb +21 -0
- data/app/views/headmin/nav/item/_locale.html.erb +17 -0
- data/app/views/headmin/pagination/_per_page.html.erb +18 -0
- data/app/views/headmin/{kaminari → pagination/kaminari}/_first_page.html.erb +0 -0
- data/app/views/headmin/{kaminari → pagination/kaminari}/_gap.html.erb +0 -0
- data/app/views/headmin/{kaminari → pagination/kaminari}/_last_page.html.erb +0 -0
- data/app/views/headmin/{kaminari → pagination/kaminari}/_next_page.html.erb +0 -0
- data/app/views/headmin/{kaminari → pagination/kaminari}/_page.html.erb +1 -1
- data/app/views/headmin/{kaminari → pagination/kaminari}/_paginator.html.erb +0 -0
- data/app/views/headmin/{kaminari → pagination/kaminari}/_prev_page.html.erb +0 -0
- data/app/views/headmin/table/_actions.html.erb +24 -17
- data/app/views/headmin/table/_body.html.erb +12 -10
- data/app/views/headmin/table/_foot.html.erb +8 -4
- data/app/views/headmin/table/_footer.html.erb +3 -7
- data/app/views/headmin/table/_head.html.erb +3 -1
- data/app/views/headmin/table/_header.html.erb +3 -7
- data/app/views/headmin/table/actions/_action.html.erb +26 -9
- data/app/views/headmin/table/actions/_delete.html.erb +11 -7
- data/app/views/headmin/table/actions/_export.html.erb +11 -7
- data/app/views/headmin/table/body/_association.html.erb +1 -1
- data/app/views/headmin/table/body/_boolean.erb +1 -1
- data/app/views/headmin/table/body/_currency.html.erb +1 -1
- data/app/views/headmin/table/body/_date.html.erb +1 -1
- data/app/views/headmin/table/body/_id.html.erb +2 -2
- data/app/views/headmin/table/body/_row.html.erb +1 -1
- data/app/views/headmin/table/body/_string.html.erb +1 -1
- data/app/views/headmin/table/body/_text.html.erb +1 -1
- data/app/views/headmin/table/foot/_cell.html.erb +1 -1
- data/app/views/headmin/table/foot/_id.html.erb +2 -2
- data/app/views/headmin/table/head/_cell.html.erb +1 -1
- data/app/views/headmin/table/head/_id.html.erb +3 -3
- data/app/views/headmin/views/devise/confirmations/_new.html.erb +9 -0
- data/app/views/headmin/views/devise/passwords/_edit.html.erb +12 -0
- data/app/views/headmin/views/devise/passwords/_new.html.erb +9 -0
- data/app/views/{admin/users/registrations/edit.html.erb → headmin/views/devise/registrations/_edit.html.erb} +5 -5
- data/app/views/headmin/views/devise/registrations/_new.html.erb +11 -0
- data/app/views/headmin/views/devise/sessions/_new.html.erb +13 -0
- data/app/views/{admin/users → headmin/views/devise}/shared/_error_messages.html.erb +0 -0
- data/app/views/{admin/users → headmin/views/devise}/shared/_links.html.erb +0 -0
- data/app/views/headmin/views/devise/unlocks/_new.html.erb +10 -0
- data/config/initializers/customize_input_error.rb +9 -0
- data/config/locales/devise/en.yml +65 -0
- data/config/locales/en.yml +2 -134
- data/config/locales/headmin/filters/en.yml +13 -0
- data/config/locales/headmin/filters/nl.yml +13 -0
- data/config/locales/headmin/forms/en.yml +34 -0
- data/config/locales/headmin/forms/nl.yml +33 -0
- data/config/locales/headmin/heading/en.yml +5 -0
- data/config/locales/headmin/heading/nl.yml +5 -0
- data/config/locales/headmin/layout/en.yml +14 -0
- data/config/locales/headmin/layout/nl.yml +14 -0
- data/config/locales/headmin/pagination/en.yml +21 -0
- data/config/locales/headmin/pagination/nl.yml +21 -0
- data/config/locales/headmin/table/en.yml +23 -0
- data/config/locales/headmin/table/nl.yml +23 -0
- data/config/locales/headmin/views/en.yml +58 -0
- data/config/locales/headmin/views/nl.yml +58 -0
- data/config/locales/nl.yml +2 -135
- data/dist/css/headmin.css +3182 -743
- data/dist/js/headmin.js +729 -33
- data/docs/README.md +2 -1
- data/docs/blocks.md +70 -85
- data/docs/devise.md +1 -60
- data/docs/fields.md +57 -0
- data/headmin.gemspec +1 -0
- data/lib/generators/headmin/blocks_generator.rb +19 -0
- data/lib/generators/headmin/fields_generator.rb +19 -0
- data/lib/generators/templates/migrations/create_blocks.rb +10 -0
- data/lib/generators/templates/migrations/create_fields.rb +13 -0
- data/lib/generators/templates/models/block.rb +3 -0
- data/lib/generators/templates/models/field.rb +3 -0
- data/lib/headmin/engine.rb +2 -0
- data/lib/headmin/version.rb +1 -1
- data/package.json +6 -6
- data/src/js/headmin/controllers/blocks_controller.js +103 -0
- data/src/js/headmin/controllers/filters_controller.js +23 -12
- data/src/js/headmin/controllers/popup_controller.js +68 -0
- data/src/js/headmin/controllers/repeater_controller.js +117 -11
- data/src/js/headmin/controllers/table_actions_controller.js +16 -5
- data/src/js/headmin/controllers/table_controller.js +84 -1
- data/src/js/headmin/headmin.js +29 -56
- data/src/scss/headmin/filters.scss +0 -14
- data/src/scss/headmin/form.scss +43 -3
- data/src/scss/headmin/popup.scss +17 -0
- data/src/scss/headmin/table.scss +9 -6
- data/src/scss/headmin.scss +7 -4
- data/src/scss/vendor/redactorx/override.css +3 -0
- data/src/scss/vendor/redactorx/redactorx.css +1460 -0
- data/yarn.lock +105 -2210
- metadata +108 -62
- data/app/controllers/admin/users/confirmations_controller.rb +0 -31
- data/app/controllers/admin/users/omniauth_callbacks_controller.rb +0 -31
- data/app/controllers/admin/users/passwords_controller.rb +0 -35
- data/app/controllers/admin/users/registrations_controller.rb +0 -63
- data/app/controllers/admin/users/sessions_controller.rb +0 -28
- data/app/controllers/admin/users/unlocks_controller.rb +0 -31
- data/app/views/admin/users/confirmations/new.html.erb +0 -9
- data/app/views/admin/users/mailer/confirmation_instructions.html.erb +0 -5
- data/app/views/admin/users/mailer/email_changed.html.erb +0 -7
- data/app/views/admin/users/mailer/password_change.html.erb +0 -3
- data/app/views/admin/users/mailer/reset_password_instructions.html.erb +0 -8
- data/app/views/admin/users/mailer/unlock_instructions.html.erb +0 -7
- data/app/views/admin/users/passwords/edit.html.erb +0 -12
- data/app/views/admin/users/passwords/new.html.erb +0 -9
- data/app/views/admin/users/registrations/new.html.erb +0 -11
- data/app/views/admin/users/sessions/new.html.erb +0 -13
- data/app/views/admin/users/unlocks/new.html.erb +0 -10
- data/app/views/headmin/forms/_group.html.erb +0 -36
- data/app/views/headmin/forms/fields/_checkbox.html.erb +0 -23
- data/app/views/headmin/forms/fields/_ckeditor.html.erb +0 -28
- data/app/views/headmin/forms/fields/_currency.html.erb +0 -24
- data/app/views/headmin/forms/fields/_date.html.erb +0 -36
- data/app/views/headmin/forms/fields/_email.html.erb +0 -39
- data/app/views/headmin/forms/fields/_label.html.erb +0 -9
- data/app/views/headmin/forms/fields/_multiple_select.html.erb +0 -37
- data/app/views/headmin/forms/fields/_password.html.erb +0 -39
- data/app/views/headmin/forms/fields/_repeater.html.erb +0 -48
- data/app/views/headmin/forms/fields/_select.html.erb +0 -36
- data/app/views/headmin/forms/fields/_select_tags.html.erb +0 -32
- data/app/views/headmin/forms/fields/_textarea.html.erb +0 -29
- data/app/views/headmin/forms/fields/_url.html.erb +0 -38
- data/app/views/headmin/forms/fields/_validation.html.erb +0 -12
- data/app/views/headmin/forms/fields/repeater/_row.html.erb +0 -16
- data/app/views/headmin/layout/dropdown/_item.html.erb +0 -17
- data/app/views/headmin/layout/header/_account.html.erb +0 -25
- data/app/views/headmin/layout/header/_locale.html.erb +0 -19
- data/app/views/headmin/layout/sidebar/menu/_account.html.erb +0 -25
- data/app/views/headmin/layout/sidebar/menu/_item.html.erb +0 -16
- data/app/views/headmin/layout/sidebar/menu/_locale.html.erb +0 -18
- data/src/js/headmin/controllers/index_controller.js +0 -79
- data/src/js/headmin/controllers/repeater_row_controller.js +0 -54
- data/src/scss/vendor/choices/cross-inverse.svg +0 -6
- data/src/scss/vendor/choices/cross.svg +0 -6
- data/src/scss/vendor/choices/custom.scss +0 -28
- data/src/scss/vendor/choices/variables.scss +0 -16
@@ -0,0 +1,68 @@
|
|
1
|
+
import {Controller} from "stimulus"
|
2
|
+
import Sortable from "sortablejs";
|
3
|
+
import {createPopper} from '@popperjs/core';
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static get targets() {
|
7
|
+
return ["popup", "button"]
|
8
|
+
}
|
9
|
+
|
10
|
+
static get values() {
|
11
|
+
return {id: String}
|
12
|
+
}
|
13
|
+
|
14
|
+
connect() {
|
15
|
+
// Clicked outside popup
|
16
|
+
document.addEventListener('click', (event) => {
|
17
|
+
this.handleOutsideClick(event)
|
18
|
+
})
|
19
|
+
}
|
20
|
+
|
21
|
+
handleOutsideClick(event) {
|
22
|
+
const inPopup = event.target.closest('[data-popup-target="popup"]') !== null
|
23
|
+
const inButton = event.target.closest('[data-popup-target="button"]') !== null
|
24
|
+
const openPopup = document.querySelector('[data-popup-target="popup"]:not(.closed)')
|
25
|
+
|
26
|
+
if(!inButton && !inPopup && openPopup) {
|
27
|
+
this.closePopup(openPopup)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
open(event) {
|
32
|
+
const button = event.target.closest('[data-popup-target="button"]')
|
33
|
+
const popup = this.popupById(button.dataset['popupId'])
|
34
|
+
const passThru = button.dataset['popupPassThru']
|
35
|
+
|
36
|
+
if (passThru) {
|
37
|
+
// Pass click event to an element inside the popup
|
38
|
+
const passThruElement = popup.querySelector(passThru)
|
39
|
+
passThruElement.click()
|
40
|
+
|
41
|
+
} else {
|
42
|
+
// Open the popup
|
43
|
+
createPopper(button, popup)
|
44
|
+
this.openPopup(popup)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
close(event) {
|
49
|
+
const button = event.target.closest('[data-popup-target="button"]')
|
50
|
+
const popup = this.popupById(button.dataset['popupId'])
|
51
|
+
|
52
|
+
this.closePopup(popup)
|
53
|
+
}
|
54
|
+
|
55
|
+
popupById(id) {
|
56
|
+
return this.popupTargets.find((popup) => {
|
57
|
+
return popup.dataset['popupId'] === id
|
58
|
+
})
|
59
|
+
}
|
60
|
+
|
61
|
+
openPopup(popup) {
|
62
|
+
popup.classList.remove('closed')
|
63
|
+
}
|
64
|
+
|
65
|
+
closePopup(popup) {
|
66
|
+
popup.classList.add('closed')
|
67
|
+
}
|
68
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import {Controller} from "stimulus"
|
2
|
-
import
|
2
|
+
import Sortable from "sortablejs";
|
3
3
|
|
4
4
|
export default class extends Controller {
|
5
5
|
static get values() {
|
@@ -7,29 +7,135 @@ export default class extends Controller {
|
|
7
7
|
id: String
|
8
8
|
}
|
9
9
|
}
|
10
|
+
|
10
11
|
static get targets() {
|
11
|
-
return ["
|
12
|
+
return ["repeater", "footer", "template", "row", "list", "empty", "addButton"]
|
13
|
+
}
|
14
|
+
|
15
|
+
connect() {
|
16
|
+
new Sortable(this.listTarget, {
|
17
|
+
animation: 150,
|
18
|
+
ghostClass: 'list-group-item-dark',
|
19
|
+
draggable: '.repeater-row',
|
20
|
+
handle: '.repeater-row-handle',
|
21
|
+
onEnd: () => {
|
22
|
+
this.resetIndices()
|
23
|
+
this.resetPositions()
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
this.toggleEmpty()
|
28
|
+
}
|
29
|
+
|
30
|
+
resetButtonIndices(event) {
|
31
|
+
const row = event.target.closest('.repeater-row')
|
32
|
+
const index = this.containsRow(row) ? row.dataset.rowIndex : ''
|
33
|
+
this.updatePopupButtonIndices(index)
|
34
|
+
}
|
35
|
+
|
36
|
+
containsRow(row) {
|
37
|
+
return this.rowTargets.includes(row)
|
12
38
|
}
|
13
39
|
|
14
|
-
|
40
|
+
updatePopupButtonIndices(index) {
|
41
|
+
console.log(index)
|
42
|
+
const popup = document.querySelector(`[data-popup-target="popup"][data-popup-id="repeater-buttons-${this.idValue}"]`)
|
43
|
+
const buttons = popup.querySelectorAll('a')
|
44
|
+
buttons.forEach((button) => {
|
45
|
+
button.dataset.rowIndex = index
|
46
|
+
})
|
47
|
+
}
|
48
|
+
|
49
|
+
addRow(event) {
|
15
50
|
event.preventDefault()
|
51
|
+
const button = event.target
|
52
|
+
const templateName = button.dataset.templateName
|
53
|
+
let rowIndex = button.dataset.rowIndex
|
16
54
|
|
17
|
-
|
55
|
+
// Prepare html from template
|
56
|
+
let html = this.getTemplateHTML(templateName)
|
18
57
|
html = this.replaceIdsWithTimestamps(html)
|
19
|
-
|
58
|
+
|
59
|
+
// Fallback to last row if no index is set
|
60
|
+
if (rowIndex) {
|
61
|
+
// Insert new row after defined row
|
62
|
+
const row = this.rowTargets[rowIndex]
|
63
|
+
row.insertAdjacentHTML('afterend', html)
|
64
|
+
} else {
|
65
|
+
// Insert before footer
|
66
|
+
this.footerTarget.insertAdjacentHTML('beforebegin', html)
|
67
|
+
}
|
68
|
+
|
69
|
+
// Dispatch an event
|
70
|
+
document.dispatchEvent(new CustomEvent('headmin:reinit', {bubbles: true}))
|
71
|
+
|
72
|
+
this.resetIndices()
|
73
|
+
this.resetPositions()
|
74
|
+
this.toggleEmpty()
|
75
|
+
}
|
76
|
+
|
77
|
+
removeRow(event) {
|
78
|
+
event.preventDefault()
|
79
|
+
|
80
|
+
const row = event.target.closest(".repeater-row")
|
81
|
+
|
82
|
+
if (row.dataset.newRecord === "true") {
|
83
|
+
// New records are simply removed from the page
|
84
|
+
row.remove()
|
85
|
+
} else {
|
86
|
+
// Existing records are hidden and flagged for deletion
|
87
|
+
row.querySelector("input[name*='_destroy']").value = 1
|
88
|
+
row.style.display = 'none'
|
89
|
+
}
|
90
|
+
|
91
|
+
this.resetIndices()
|
92
|
+
this.resetPositions()
|
93
|
+
this.toggleEmpty()
|
20
94
|
}
|
21
95
|
|
22
|
-
getTemplateHTML() {
|
23
|
-
|
96
|
+
getTemplateHTML(name) {
|
97
|
+
const template = this.templateTargets.filter((template) => {
|
98
|
+
return template.dataset.templateName === name
|
99
|
+
})[0]
|
100
|
+
return template.innerHTML
|
24
101
|
}
|
25
102
|
|
26
103
|
replaceIdsWithTimestamps(html) {
|
27
|
-
const regex = new RegExp(
|
104
|
+
const regex = new RegExp('template_id', "g");
|
28
105
|
return html.replace(regex, new Date().getTime())
|
29
106
|
}
|
30
107
|
|
31
|
-
|
32
|
-
this.
|
33
|
-
|
108
|
+
visibleRowsCount() {
|
109
|
+
return this.visibleRows().length
|
110
|
+
}
|
111
|
+
|
112
|
+
visibleRows() {
|
113
|
+
const rows = this.rowTargets
|
114
|
+
return rows.filter((row) => {
|
115
|
+
return row.querySelector("input[name*='_destroy']").value !== '1'
|
116
|
+
})
|
117
|
+
}
|
118
|
+
|
119
|
+
toggleEmpty() {
|
120
|
+
if (this.visibleRowsCount() > 0) {
|
121
|
+
this.emptyTarget.classList.add('invisible')
|
122
|
+
} else {
|
123
|
+
this.emptyTarget.classList.remove('invisible')
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
resetPositions() {
|
128
|
+
this.visibleRows().forEach((row, index) => {
|
129
|
+
const positionInput = row.querySelector("input[name*='position']")
|
130
|
+
if (positionInput) {
|
131
|
+
positionInput.value = index
|
132
|
+
}
|
133
|
+
})
|
134
|
+
}
|
135
|
+
|
136
|
+
resetIndices() {
|
137
|
+
this.visibleRows().forEach((row, index) => {
|
138
|
+
row.dataset.rowIndex = index
|
139
|
+
})
|
34
140
|
}
|
35
141
|
}
|
@@ -2,7 +2,7 @@ import {Controller} from "stimulus"
|
|
2
2
|
|
3
3
|
export default class extends Controller {
|
4
4
|
static get targets() {
|
5
|
-
return ["form", "select", "method"]
|
5
|
+
return ["form", "select", "method", "button"]
|
6
6
|
}
|
7
7
|
|
8
8
|
connect() {
|
@@ -12,11 +12,22 @@ export default class extends Controller {
|
|
12
12
|
update(event) {
|
13
13
|
event.preventDefault()
|
14
14
|
const option = this.selectTarget.options[this.selectTarget.selectedIndex]
|
15
|
-
const action = this.selectTarget.value
|
16
|
-
const method = option.dataset.method
|
17
15
|
|
18
16
|
// Replace form action
|
19
|
-
this.formTarget.action =
|
20
|
-
|
17
|
+
this.formTarget.action = this.selectTarget.value
|
18
|
+
|
19
|
+
// Replace form method
|
20
|
+
this.methodTarget.value = option.dataset.method
|
21
|
+
|
22
|
+
// Set confirm on form button
|
23
|
+
const confirm = option.dataset.confirm
|
24
|
+
if(confirm) {
|
25
|
+
this.buttonTarget.dataset.confirm = confirm
|
26
|
+
} else {
|
27
|
+
this.buttonTarget.removeAttribute('data-confirm')
|
28
|
+
}
|
29
|
+
|
30
|
+
// Enable button
|
31
|
+
this.buttonTarget.removeAttribute('disabled')
|
21
32
|
}
|
22
33
|
}
|
@@ -8,7 +8,7 @@ export default class extends Controller {
|
|
8
8
|
}
|
9
9
|
|
10
10
|
static get targets() {
|
11
|
-
return ["table", "body"]
|
11
|
+
return ["table", "body", "actions", "idCheckbox", "idsCheckbox"]
|
12
12
|
}
|
13
13
|
|
14
14
|
connect() {
|
@@ -33,4 +33,87 @@ export default class extends Controller {
|
|
33
33
|
})
|
34
34
|
return data
|
35
35
|
}
|
36
|
+
|
37
|
+
toggleIds(event) {
|
38
|
+
const checkbox = event.target
|
39
|
+
this.toggleIdsCheckboxes(checkbox.checked)
|
40
|
+
this.toggleIdCheckboxes(checkbox.checked)
|
41
|
+
this.syncFields()
|
42
|
+
this.toggleActions()
|
43
|
+
}
|
44
|
+
|
45
|
+
toggleId(event) {
|
46
|
+
this.toggleIdsCheckboxes(false)
|
47
|
+
this.syncFields()
|
48
|
+
this.toggleActions()
|
49
|
+
}
|
50
|
+
|
51
|
+
toggleActions() {
|
52
|
+
const idFields = this.getIdFields()
|
53
|
+
if(idFields.length > 0) {
|
54
|
+
this.actionsTarget.classList.remove('d-none')
|
55
|
+
} else {
|
56
|
+
this.actionsTarget.classList.add('d-none')
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
toggleIdsCheckboxes(checked) {
|
61
|
+
this.idsCheckboxTargets.forEach((checkbox) => {
|
62
|
+
checkbox.checked = checked
|
63
|
+
});
|
64
|
+
}
|
65
|
+
|
66
|
+
toggleIdCheckboxes(checked) {
|
67
|
+
this.idCheckboxTargets.forEach((checkbox) => {
|
68
|
+
checkbox.checked = checked
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
syncFields() {
|
73
|
+
this.removeIds()
|
74
|
+
if(this.idsCheckboxTarget.checked) {
|
75
|
+
this.addId('')
|
76
|
+
} else {
|
77
|
+
this.idCheckboxTargets.forEach((checkbox) => {
|
78
|
+
if(checkbox.checked) {
|
79
|
+
this.addId(checkbox.value)
|
80
|
+
}
|
81
|
+
});
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
addId(id) {
|
86
|
+
let field = this.getIdField(id)
|
87
|
+
if (!field) {
|
88
|
+
field = this.newIdField(id)
|
89
|
+
this.actionsTarget.querySelector('form').insertAdjacentHTML('afterbegin', field)
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
removeIds() {
|
94
|
+
const fields = this.getIdFields()
|
95
|
+
fields.forEach((field) => {
|
96
|
+
this.actionsTarget.querySelector('form').removeChild(field)
|
97
|
+
});
|
98
|
+
}
|
99
|
+
|
100
|
+
removeId(id) {
|
101
|
+
const field = this.getIdField(id)
|
102
|
+
if (field) {
|
103
|
+
this.actionsTarget.querySelector('form').removeChild(field)
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
newIdField(id) {
|
108
|
+
const template = this.actionsTarget.querySelector('[data-table-target="idFieldTemplate"]')
|
109
|
+
return template.innerHTML.replace(/ID/g, id)
|
110
|
+
}
|
111
|
+
|
112
|
+
getIdFields() {
|
113
|
+
return this.actionsTarget.querySelectorAll(`input[name="ids[]"]`);
|
114
|
+
}
|
115
|
+
|
116
|
+
getIdField(id) {
|
117
|
+
return this.actionsTarget.querySelector(`input[name="ids[]"][value="${id}"]`);
|
118
|
+
}
|
36
119
|
}
|
data/src/js/headmin/headmin.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import 'ckeditor5-build-classic-simple-upload-adapter-image-resize';
|
2
|
-
import
|
3
|
-
import Sortable from 'sortablejs';
|
2
|
+
import TomSelect from "tom-select";
|
4
3
|
import flatpickr from "flatpickr";
|
5
4
|
import bootstrap from "bootstrap/dist/js/bootstrap.bundle";
|
6
5
|
import Rails from "@rails/ujs";
|
@@ -20,14 +19,33 @@ export class Headmin {
|
|
20
19
|
|
21
20
|
// Init Plugins
|
22
21
|
this.initPlugins()
|
22
|
+
|
23
|
+
// Listen for a headmin:reinit event
|
24
|
+
document.addEventListener('headmin:reinit', (e) => {
|
25
|
+
this.initPlugins()
|
26
|
+
})
|
27
|
+
|
23
28
|
}
|
24
29
|
|
25
30
|
static initPlugins() {
|
26
|
-
this.
|
31
|
+
this.initTomSelects()
|
27
32
|
this.initFlatpickrs()
|
28
33
|
this.initToasts()
|
29
34
|
this.initPopovers()
|
30
35
|
this.initCKEditors()
|
36
|
+
this.initRedactorX()
|
37
|
+
}
|
38
|
+
|
39
|
+
static initRedactorX() {
|
40
|
+
document.querySelectorAll('.redactorx').forEach((element) => {
|
41
|
+
if (typeof RedactorX == 'undefined') {
|
42
|
+
console.error("RedactorX is a paid module and is not included in Headmin. Please purchase it and import it as a JS module")
|
43
|
+
return false;
|
44
|
+
}
|
45
|
+
|
46
|
+
const options = JSON.parse(element.getAttribute('data-redactor-options'))
|
47
|
+
RedactorX(element, options);
|
48
|
+
})
|
31
49
|
}
|
32
50
|
|
33
51
|
static initFlatpickrs() {
|
@@ -55,6 +73,10 @@ export class Headmin {
|
|
55
73
|
}
|
56
74
|
|
57
75
|
static initCKEditor(element) {
|
76
|
+
|
77
|
+
// Check if CKEditor is already initialized
|
78
|
+
if (element.nextElementSibling && element.nextElementSibling.classList.contains('ck-editor')) return
|
79
|
+
|
58
80
|
const defaultToolbarItems = [
|
59
81
|
'heading', '|', 'bold', 'italic', 'bulletedList', 'numberedList', '|', 'outdent',
|
60
82
|
'indent', '|', 'link', 'imageUpload', 'blockQuote', 'insertTable', 'mediaEmbed', '|', 'undo', 'redo'
|
@@ -96,59 +118,10 @@ export class Headmin {
|
|
96
118
|
})
|
97
119
|
}
|
98
120
|
|
99
|
-
static
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
static initChoicesSelectTags() {
|
105
|
-
document.querySelectorAll('.select-tags').forEach((select) => {
|
106
|
-
|
107
|
-
// Skip if already initialized
|
108
|
-
if (select.dataset.choice === 'active') {
|
109
|
-
return
|
110
|
-
}
|
111
|
-
|
112
|
-
// Create a new instance
|
113
|
-
new Choices(select, {
|
114
|
-
removeItemButton: true,
|
115
|
-
loadingText: 'Loading...',
|
116
|
-
noResultsText: 'No results found',
|
117
|
-
noChoicesText: 'No choices to choose from',
|
118
|
-
itemSelectText: 'Press to select',
|
119
|
-
addItems: true,
|
120
|
-
addItemText: (value) => {
|
121
|
-
return `Press Enter to add <b>"${value}"</b>`;
|
122
|
-
},
|
123
|
-
maxItemText: (maxItemCount) => {
|
124
|
-
return `Only ${maxItemCount} values can be added`;
|
125
|
-
},
|
126
|
-
});
|
127
|
-
})
|
128
|
-
}
|
129
|
-
|
130
|
-
static initChoicesMultipleSelect() {
|
131
|
-
document.querySelectorAll('.multiple-select').forEach((select) => {
|
132
|
-
|
133
|
-
// Skip if already initialized
|
134
|
-
if (select.dataset.choice === 'active') {
|
135
|
-
return
|
136
|
-
}
|
137
|
-
|
138
|
-
// Create a new instance
|
139
|
-
new Choices(select, {
|
140
|
-
removeItemButton: true,
|
141
|
-
loadingText: 'Loading...',
|
142
|
-
noResultsText: 'No results found',
|
143
|
-
noChoicesText: 'No choices to choose from',
|
144
|
-
itemSelectText: 'Press to select',
|
145
|
-
addItems: true,
|
146
|
-
addItemText: (value) => {
|
147
|
-
return `Press Enter to add <b>"${value}"</b>`;
|
148
|
-
},
|
149
|
-
maxItemText: (maxItemCount) => {
|
150
|
-
return `Only ${maxItemCount} values can be added`;
|
151
|
-
},
|
121
|
+
static initTomSelects() {
|
122
|
+
document.querySelectorAll('select[multiple]').forEach((select) => {
|
123
|
+
new TomSelect(select, {
|
124
|
+
create: select.dataset['tags'] === "true"
|
152
125
|
});
|
153
126
|
})
|
154
127
|
}
|
data/src/scss/headmin/form.scss
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
input[type="search"] {
|
2
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='%236c757d' viewBox='0 0 16 16'%3E%3Cpath d='M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z'/%3E%3C/svg%3E%0A");
|
3
|
+
background-repeat: no-repeat;
|
4
|
+
background-position: 10px center;
|
5
|
+
padding-left: 35px
|
6
|
+
}
|
7
|
+
|
8
|
+
.form-label[required="required"]:after {
|
9
|
+
content: '*';
|
10
|
+
margin-left: 1px;
|
11
|
+
}
|
12
|
+
|
1
13
|
.forms-group {
|
2
14
|
.card-footer {
|
3
15
|
border-top: none;
|
@@ -19,21 +31,49 @@
|
|
19
31
|
& > .repeater-row-add {
|
20
32
|
visibility: visible;
|
21
33
|
}
|
34
|
+
|
35
|
+
& > .repeater-row-handle {
|
36
|
+
visibility: visible;
|
37
|
+
}
|
22
38
|
}
|
23
39
|
}
|
24
40
|
|
25
41
|
.repeater-row-remove {
|
26
42
|
position: absolute;
|
27
|
-
top: calc(50% -
|
43
|
+
top: calc(50% - 17px);
|
28
44
|
right: -22px;
|
29
45
|
z-index: 2;
|
30
46
|
visibility: hidden;
|
47
|
+
|
48
|
+
i.bi {
|
49
|
+
background: white;
|
50
|
+
border-radius: 50%;
|
51
|
+
}
|
31
52
|
}
|
32
53
|
|
33
54
|
.repeater-row-add {
|
34
55
|
position: absolute;
|
35
|
-
top: calc(100% -
|
36
|
-
right: calc(50% -
|
56
|
+
top: calc(100% - 18px);
|
57
|
+
right: calc(50% - 17px);
|
37
58
|
z-index: 2;
|
38
59
|
visibility: hidden;
|
60
|
+
|
61
|
+
i.bi {
|
62
|
+
background: white;
|
63
|
+
border-radius: 50%;
|
64
|
+
}
|
39
65
|
}
|
66
|
+
|
67
|
+
.repeater-row-handle {
|
68
|
+
position: absolute;
|
69
|
+
top: 0;
|
70
|
+
left: 0;
|
71
|
+
height: 100%;
|
72
|
+
width: 18px;
|
73
|
+
display: flex;
|
74
|
+
align-items: center;
|
75
|
+
justify-content: center;
|
76
|
+
z-index: 2;
|
77
|
+
visibility: hidden;
|
78
|
+
cursor: move;
|
79
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
.h-popup {
|
2
|
+
padding: 10px;
|
3
|
+
background: $white;
|
4
|
+
z-index: $zindex-popover;
|
5
|
+
width: $popover-max-width;
|
6
|
+
@include reset-text();
|
7
|
+
@include font-size($popover-font-size);
|
8
|
+
word-wrap: break-word;
|
9
|
+
background-color: $popover-bg;
|
10
|
+
background-clip: padding-box;
|
11
|
+
@include border-radius($popover-border-radius);
|
12
|
+
@include box-shadow($popover-box-shadow);
|
13
|
+
|
14
|
+
&.closed {
|
15
|
+
display: none;
|
16
|
+
}
|
17
|
+
}
|
data/src/scss/headmin/table.scss
CHANGED
@@ -21,12 +21,15 @@
|
|
21
21
|
}
|
22
22
|
|
23
23
|
.h-table-actions {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
position: absolute;
|
25
|
+
top: 0;
|
26
|
+
right: 0;
|
27
|
+
width: calc(100% - 54px);
|
28
|
+
background: $table-striped-bg;
|
29
|
+
padding: 0.3rem 0.3rem 0.26rem 0.3rem !important;
|
28
30
|
|
29
|
-
select {
|
30
|
-
|
31
|
+
.form-select {
|
32
|
+
width: 300px;
|
33
|
+
max-width: 100%;
|
31
34
|
}
|
32
35
|
}
|
data/src/scss/headmin.scss
CHANGED
@@ -39,14 +39,16 @@
|
|
39
39
|
@import "~bootstrap/scss/helpers.scss";
|
40
40
|
@import "~bootstrap/scss/utilities/api.scss";
|
41
41
|
|
42
|
-
//
|
43
|
-
@import "
|
44
|
-
@import "~choices.js/src/styles/choices.scss";
|
45
|
-
@import "vendor/choices/custom"; // Choices custom styling
|
42
|
+
// Tom select
|
43
|
+
@import "~tom-select/dist/scss/tom-select.bootstrap5";
|
46
44
|
|
47
45
|
// Flatpickr
|
48
46
|
@import "~flatpickr/dist/flatpickr";
|
49
47
|
|
48
|
+
// Redactor
|
49
|
+
@import "vendor/redactorx/redactorx";
|
50
|
+
@import "vendor/redactorx/override";
|
51
|
+
|
50
52
|
// Headmin
|
51
53
|
@import "headmin/filters";
|
52
54
|
@import "headmin/form";
|
@@ -56,3 +58,4 @@
|
|
56
58
|
@import "headmin/table";
|
57
59
|
@import "headmin/utilities";
|
58
60
|
@import "headmin/filter";
|
61
|
+
@import "headmin/popup";
|