b4um_generators 0.2.11 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/README.de.md +165 -8
- data/README.md +162 -8
- data/lib/b4um_generators/version.rb +1 -1
- data/lib/generators/b4um/attachment/attachment_generator.rb +83 -23
- data/lib/generators/b4um/authentication/authentication_generator.rb +63 -0
- data/lib/generators/b4um/bento/bento_generator.rb +24 -0
- data/lib/generators/b4um/help/help_generator.rb +148 -11
- data/lib/generators/b4um/in_place/in_place_generator.rb +1231 -0
- data/lib/generators/b4um/in_place/templates/_flash.html.erb +15 -0
- data/lib/generators/b4um/in_place/templates/b4um_in_place_helper.rb +7 -0
- data/lib/generators/b4um/in_place/templates/display_attachment.html.erb.tt +154 -0
- data/lib/generators/b4um/in_place/templates/display_attachments.html.erb.tt +165 -0
- data/lib/generators/b4um/in_place/templates/display_boolean.html.erb.tt +18 -0
- data/lib/generators/b4um/in_place/templates/display_number.html.erb.tt +18 -0
- data/lib/generators/b4um/in_place/templates/display_radio.html.erb.tt +23 -0
- data/lib/generators/b4um/in_place/templates/display_rich_text.html.erb.tt +123 -0
- data/lib/generators/b4um/in_place/templates/display_select.html.erb.tt +23 -0
- data/lib/generators/b4um/in_place/templates/display_text.html.erb.tt +18 -0
- data/lib/generators/b4um/in_place/templates/edit_attachment.html.erb.tt +83 -0
- data/lib/generators/b4um/in_place/templates/edit_attachments.html.erb.tt +65 -0
- data/lib/generators/b4um/in_place/templates/edit_boolean.html.erb.tt +32 -0
- data/lib/generators/b4um/in_place/templates/edit_number.html.erb.tt +26 -0
- data/lib/generators/b4um/in_place/templates/edit_radio.html.erb.tt +32 -0
- data/lib/generators/b4um/in_place/templates/edit_rich_text.html.erb.tt +25 -0
- data/lib/generators/b4um/in_place/templates/edit_select.html.erb.tt +28 -0
- data/lib/generators/b4um/in_place/templates/edit_text.html.erb.tt +25 -0
- data/lib/generators/b4um/in_place/templates/in_place.css +935 -0
- data/lib/generators/b4um/in_place/templates/in_place_controller.js +115 -0
- data/lib/generators/b4um/install/install_generator.rb +18 -13
- data/lib/generators/b4um/install/templates/_flash.html.erb +15 -0
- data/lib/generators/b4um/install/templates/_footer.html.erb +6 -1
- data/lib/generators/b4um/install/templates/b4um/forms.css +127 -0
- data/lib/generators/b4um/install/templates/b4um/tables.css +49 -0
- data/lib/generators/b4um/install/templates/image_lightbox_controller.js +34 -1
- data/lib/generators/b4um/install/templates/rich_text_controller.js +22 -0
- data/lib/generators/b4um/install/templates/scroll_to_top_controller.js +12 -0
- data/lib/generators/b4um/scaffold/scaffold_generator.rb +106 -0
- data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +29 -0
- data/lib/generators/b4um/scaffold/templates/_resource.html.erb.tt +35 -0
- data/lib/generators/b4um/scaffold/templates/_table.html.erb.tt +31 -0
- data/lib/generators/b4um/table/table_generator.rb +187 -5
- data/lib/generators/b4um/table/templates/_table.html.erb.tt +286 -0
- data/lib/generators/b4um/trix/trix_generator.rb +104 -6
- metadata +27 -3
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static values = {
|
|
5
|
+
url: String,
|
|
6
|
+
outsideSave: { type: Boolean, default: true },
|
|
7
|
+
cancelUrl: String
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
this.handlePointerDown = this.handlePointerDown.bind(this)
|
|
12
|
+
this.handleKeyDown = this.handleKeyDown.bind(this)
|
|
13
|
+
|
|
14
|
+
if (this.element.querySelector('form')) {
|
|
15
|
+
this.rememberFormState()
|
|
16
|
+
|
|
17
|
+
document.addEventListener('pointerdown', this.handlePointerDown)
|
|
18
|
+
document.addEventListener('keydown', this.handleKeyDown)
|
|
19
|
+
this.focusEditor()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
disconnect() {
|
|
24
|
+
document.removeEventListener('pointerdown', this.handlePointerDown)
|
|
25
|
+
document.removeEventListener('keydown', this.handleKeyDown)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
edit(event) {
|
|
29
|
+
if (event?.type === 'keydown') {
|
|
30
|
+
event.preventDefault()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!this.hasUrlValue) return
|
|
34
|
+
|
|
35
|
+
const frame = this.element.closest('turbo-frame')
|
|
36
|
+
if (!frame) return
|
|
37
|
+
|
|
38
|
+
frame.src = this.urlValue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
handlePointerDown(event) {
|
|
42
|
+
if (this.element.contains(event.target)) return
|
|
43
|
+
|
|
44
|
+
if (this.outsideSaveValue) {
|
|
45
|
+
this.save()
|
|
46
|
+
} else {
|
|
47
|
+
this.cancel()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handleKeyDown(event) {
|
|
52
|
+
if (event.key !== 'Escape') return
|
|
53
|
+
|
|
54
|
+
event.preventDefault()
|
|
55
|
+
this.cancel()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
focusEditor() {
|
|
59
|
+
const control = this.element.querySelector(
|
|
60
|
+
'input:not([type="hidden"]):not([type="file"]), select, textarea, trix-editor'
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if (!control) return
|
|
64
|
+
|
|
65
|
+
requestAnimationFrame(() => {
|
|
66
|
+
control.focus()
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
rememberFormState() {
|
|
71
|
+
this.initialFormStates = new WeakMap()
|
|
72
|
+
|
|
73
|
+
this.element.querySelectorAll('form').forEach((form) => {
|
|
74
|
+
this.initialFormStates.set(form, new URLSearchParams(new FormData(form)).toString())
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
formChanged(form) {
|
|
79
|
+
if (!this.initialFormStates?.has(form)) return true
|
|
80
|
+
|
|
81
|
+
const initialState = this.initialFormStates.get(form)
|
|
82
|
+
const currentState = new URLSearchParams(new FormData(form)).toString()
|
|
83
|
+
|
|
84
|
+
return initialState !== currentState
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
save(event) {
|
|
88
|
+
const form = event?.target?.closest('form') || this.element.querySelector('form')
|
|
89
|
+
|
|
90
|
+
if (!form) return
|
|
91
|
+
|
|
92
|
+
const fileInput = event?.target instanceof HTMLInputElement && event.target.type === 'file' ? event.target : null
|
|
93
|
+
|
|
94
|
+
if (fileInput?.files?.length) {
|
|
95
|
+
form.requestSubmit()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!this.formChanged(form)) {
|
|
100
|
+
this.cancel()
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
form.requestSubmit()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
cancel() {
|
|
108
|
+
if (!this.hasCancelUrlValue) return
|
|
109
|
+
|
|
110
|
+
const frame = this.element.closest('turbo-frame')
|
|
111
|
+
if (!frame) return
|
|
112
|
+
|
|
113
|
+
frame.src = this.cancelUrlValue
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -333,6 +333,13 @@ module B4um
|
|
|
333
333
|
)
|
|
334
334
|
end
|
|
335
335
|
|
|
336
|
+
def copy_flash
|
|
337
|
+
copy_file(
|
|
338
|
+
"_flash.html.erb",
|
|
339
|
+
"app/views/shared/_flash.html.erb"
|
|
340
|
+
)
|
|
341
|
+
end
|
|
342
|
+
|
|
336
343
|
def copy_navigation_controller
|
|
337
344
|
copy_file "navigation_controller.js",
|
|
338
345
|
"app/javascript/controllers/navigation_controller.js"
|
|
@@ -385,6 +392,16 @@ module B4um
|
|
|
385
392
|
"app/javascript/controllers/image_lightbox_controller.js"
|
|
386
393
|
end
|
|
387
394
|
|
|
395
|
+
def copy_scroll_to_top_controller
|
|
396
|
+
copy_file "scroll_to_top_controller.js",
|
|
397
|
+
"app/javascript/controllers/scroll_to_top_controller.js"
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def copy_rich_text_controller
|
|
401
|
+
copy_file "rich_text_controller.js",
|
|
402
|
+
"app/javascript/controllers/rich_text_controller.js"
|
|
403
|
+
end
|
|
404
|
+
|
|
388
405
|
def copy_dismissible_controller
|
|
389
406
|
copy_file "dismissible_controller.js",
|
|
390
407
|
"app/javascript/controllers/dismissible_controller.js"
|
|
@@ -470,19 +487,7 @@ module B4um
|
|
|
470
487
|
<%= render "shared/navigation" %>
|
|
471
488
|
|
|
472
489
|
<main class="container">
|
|
473
|
-
|
|
474
|
-
<div class="flash <%= "flash--" + type.to_s %>"
|
|
475
|
-
data-controller="dismissible">
|
|
476
|
-
<%= message %>
|
|
477
|
-
|
|
478
|
-
<button type="button"
|
|
479
|
-
class="flash__close"
|
|
480
|
-
data-action="dismissible#dismiss"
|
|
481
|
-
aria-label="Close">
|
|
482
|
-
×
|
|
483
|
-
</button>
|
|
484
|
-
</div>
|
|
485
|
-
<% end %>
|
|
490
|
+
<%= render "shared/flash" %>
|
|
486
491
|
ERB
|
|
487
492
|
)
|
|
488
493
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<div id="flash-messages">
|
|
2
|
+
<% flash.each do |type, message| %>
|
|
3
|
+
<div class="flash <%= "flash--" + type.to_s %>"
|
|
4
|
+
data-controller="dismissible">
|
|
5
|
+
<%= message %>
|
|
6
|
+
|
|
7
|
+
<button type="button"
|
|
8
|
+
class="flash__close"
|
|
9
|
+
data-action="dismissible#dismiss"
|
|
10
|
+
aria-label="Close">
|
|
11
|
+
×
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<footer class="b4um-footer">
|
|
2
|
-
<a
|
|
2
|
+
<a
|
|
3
|
+
class="b4um-footer__back-to-top"
|
|
4
|
+
href="#b4um-page-top"
|
|
5
|
+
data-controller="scroll-to-top"
|
|
6
|
+
data-action="click->scroll-to-top#scroll"
|
|
7
|
+
>
|
|
3
8
|
Zurück zum Seitenanfang
|
|
4
9
|
</a>
|
|
5
10
|
|
|
@@ -275,6 +275,133 @@
|
|
|
275
275
|
display: block;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
/* -------------------------------------------------------
|
|
279
|
+
Selects
|
|
280
|
+
------------------------------------------------------- */
|
|
281
|
+
|
|
282
|
+
.form-field--select .form-label {
|
|
283
|
+
top: 0;
|
|
284
|
+
|
|
285
|
+
color: var(--b4um-primary);
|
|
286
|
+
|
|
287
|
+
transform: translateY(-50%) scale(0.82);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.form-select {
|
|
291
|
+
width: 100%;
|
|
292
|
+
margin: 0;
|
|
293
|
+
padding: 1rem 3rem 1rem 1.15rem;
|
|
294
|
+
|
|
295
|
+
box-sizing: border-box;
|
|
296
|
+
|
|
297
|
+
border: 1px solid var(--b4um-border);
|
|
298
|
+
border-radius: 8px;
|
|
299
|
+
|
|
300
|
+
background-color: var(--b4um-surface);
|
|
301
|
+
color: var(--b4um-text);
|
|
302
|
+
|
|
303
|
+
font: inherit;
|
|
304
|
+
|
|
305
|
+
cursor: pointer;
|
|
306
|
+
|
|
307
|
+
transition:
|
|
308
|
+
border-color 0.18s ease,
|
|
309
|
+
box-shadow 0.18s ease,
|
|
310
|
+
background-color 0.18s ease;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.form-select:hover {
|
|
314
|
+
border-color: var(--b4um-border-hover);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.form-select:focus {
|
|
318
|
+
outline: none;
|
|
319
|
+
|
|
320
|
+
border-color: var(--b4um-primary);
|
|
321
|
+
box-shadow: 0 0 0 3px var(--b4um-primary-soft);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* -------------------------------------------------------
|
|
325
|
+
Radio buttons
|
|
326
|
+
------------------------------------------------------- */
|
|
327
|
+
|
|
328
|
+
.form-field--radio {
|
|
329
|
+
padding: 0.75rem 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.form-field--radio > .form-label {
|
|
333
|
+
position: static;
|
|
334
|
+
|
|
335
|
+
display: block;
|
|
336
|
+
|
|
337
|
+
margin-bottom: 0.75rem;
|
|
338
|
+
padding: 0;
|
|
339
|
+
|
|
340
|
+
background: none;
|
|
341
|
+
color: var(--b4um-text-secondary);
|
|
342
|
+
|
|
343
|
+
transform: none;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.form-radio-options {
|
|
347
|
+
display: flex;
|
|
348
|
+
flex-wrap: wrap;
|
|
349
|
+
align-items: center;
|
|
350
|
+
gap: 0.75rem 1.25rem;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.form-radio-option {
|
|
354
|
+
display: inline-flex;
|
|
355
|
+
align-items: center;
|
|
356
|
+
gap: 0.5rem;
|
|
357
|
+
|
|
358
|
+
color: var(--b4um-text);
|
|
359
|
+
|
|
360
|
+
cursor: pointer;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.form-radio {
|
|
364
|
+
appearance: none;
|
|
365
|
+
-webkit-appearance: none;
|
|
366
|
+
|
|
367
|
+
width: 18px;
|
|
368
|
+
height: 18px;
|
|
369
|
+
margin: 0;
|
|
370
|
+
|
|
371
|
+
flex: 0 0 18px;
|
|
372
|
+
|
|
373
|
+
border: 1px solid var(--b4um-border-checkbox);
|
|
374
|
+
border-radius: 50%;
|
|
375
|
+
|
|
376
|
+
background-color: var(--b4um-surface);
|
|
377
|
+
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
|
|
380
|
+
transition:
|
|
381
|
+
border-color 0.18s ease,
|
|
382
|
+
box-shadow 0.18s ease,
|
|
383
|
+
transform 0.12s ease;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.form-radio:hover {
|
|
387
|
+
border-color: var(--b4um-primary);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.form-radio:checked {
|
|
391
|
+
border: 5px solid var(--b4um-primary);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.form-radio:focus-visible {
|
|
395
|
+
outline: none;
|
|
396
|
+
|
|
397
|
+
border-color: var(--b4um-primary);
|
|
398
|
+
box-shadow: 0 0 0 3px var(--b4um-primary-soft);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.form-radio:active {
|
|
402
|
+
transform: scale(0.94);
|
|
403
|
+
}
|
|
404
|
+
|
|
278
405
|
/* -------------------------------------------------------
|
|
279
406
|
Checkboxes
|
|
280
407
|
------------------------------------------------------- */
|
|
@@ -65,3 +65,52 @@
|
|
|
65
65
|
.b4um-table td:last-child {
|
|
66
66
|
padding-right: 1.25rem;
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
/* -------------------------------------------------------
|
|
70
|
+
Table media
|
|
71
|
+
------------------------------------------------------- */
|
|
72
|
+
|
|
73
|
+
.b4um-table__media {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
gap: 0.5rem;
|
|
77
|
+
|
|
78
|
+
min-width: 100px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.b4um-table__image-button {
|
|
82
|
+
display: block;
|
|
83
|
+
|
|
84
|
+
width: 100px;
|
|
85
|
+
height: 75px;
|
|
86
|
+
|
|
87
|
+
flex: 0 0 100px;
|
|
88
|
+
|
|
89
|
+
padding: 0;
|
|
90
|
+
|
|
91
|
+
border: 0;
|
|
92
|
+
border-radius: 8px;
|
|
93
|
+
|
|
94
|
+
overflow: hidden;
|
|
95
|
+
|
|
96
|
+
background: transparent;
|
|
97
|
+
|
|
98
|
+
cursor: zoom-in;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.b4um-table__image {
|
|
102
|
+
display: block;
|
|
103
|
+
|
|
104
|
+
width: 100%;
|
|
105
|
+
height: 100%;
|
|
106
|
+
|
|
107
|
+
border: 0;
|
|
108
|
+
border-radius: 8px;
|
|
109
|
+
|
|
110
|
+
object-fit: cover;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.b4um-table__media-empty {
|
|
114
|
+
color: var(--b4um-text-muted);
|
|
115
|
+
font-style: italic;
|
|
116
|
+
}
|
|
@@ -21,6 +21,39 @@ export default class extends Controller {
|
|
|
21
21
|
this.dragStartX = 0
|
|
22
22
|
this.dragCurrentX = 0
|
|
23
23
|
this.dragPointerId = null
|
|
24
|
+
|
|
25
|
+
this.prepareActionTextImages()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
prepareActionTextImages() {
|
|
29
|
+
const images = this.element.querySelectorAll('.trix-content figure.attachment img')
|
|
30
|
+
|
|
31
|
+
images.forEach((image) => {
|
|
32
|
+
if (image.closest('[data-image-lightbox-target="item"]')) return
|
|
33
|
+
|
|
34
|
+
const item = image.closest('a') || image
|
|
35
|
+
const figure = image.closest('figure.attachment')
|
|
36
|
+
const figcaption = figure?.querySelector('figcaption')
|
|
37
|
+
const caption = figcaption?.textContent?.trim() || ''
|
|
38
|
+
|
|
39
|
+
item.dataset.imageLightboxTarget = 'item'
|
|
40
|
+
item.dataset.imageLightboxUrl = image.currentSrc || image.src
|
|
41
|
+
item.dataset.imageLightboxAlt = image.alt || ''
|
|
42
|
+
item.dataset.imageLightboxCaption = caption
|
|
43
|
+
|
|
44
|
+
const actions = [item.dataset.action, 'click->image-lightbox#open']
|
|
45
|
+
|
|
46
|
+
if (item.tagName !== 'A') {
|
|
47
|
+
actions.push('keydown.enter->image-lightbox#open', 'keydown.space->image-lightbox#open')
|
|
48
|
+
|
|
49
|
+
item.setAttribute('role', 'button')
|
|
50
|
+
item.setAttribute('tabindex', '0')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
item.dataset.action = actions.filter(Boolean).join(' ')
|
|
54
|
+
|
|
55
|
+
item.classList.add('in-place-rich-text__lightbox-image')
|
|
56
|
+
})
|
|
24
57
|
}
|
|
25
58
|
|
|
26
59
|
open(event) {
|
|
@@ -458,7 +491,7 @@ export default class extends Controller {
|
|
|
458
491
|
if (!item) return
|
|
459
492
|
|
|
460
493
|
if (this.hasCaptionTarget) {
|
|
461
|
-
this.captionTarget.textContent = item.dataset.imageLightboxAlt || ''
|
|
494
|
+
this.captionTarget.textContent = item.dataset.imageLightboxCaption || item.dataset.imageLightboxAlt || ''
|
|
462
495
|
}
|
|
463
496
|
|
|
464
497
|
if (this.hasCounterTarget) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
connect() {
|
|
5
|
+
this.prepareLinks()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
prepareLinks() {
|
|
9
|
+
const links = this.element.querySelectorAll('a[href]')
|
|
10
|
+
|
|
11
|
+
links.forEach((link) => {
|
|
12
|
+
link.setAttribute('target', '_blank')
|
|
13
|
+
|
|
14
|
+
const rel = new Set((link.getAttribute('rel') || '').split(/\s+/).filter(Boolean))
|
|
15
|
+
|
|
16
|
+
rel.add('noopener')
|
|
17
|
+
rel.add('noreferrer')
|
|
18
|
+
|
|
19
|
+
link.setAttribute('rel', Array.from(rel).join(' '))
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -25,6 +25,27 @@ module B4um
|
|
|
25
25
|
enum: %w[bento table],
|
|
26
26
|
desc: "Index layout: bento or table"
|
|
27
27
|
|
|
28
|
+
class_option :select,
|
|
29
|
+
type: :string,
|
|
30
|
+
desc: "Renders a field as a select list. Example: status:Active=Aktiv,Inactive=Inaktiv"
|
|
31
|
+
|
|
32
|
+
class_option :radio,
|
|
33
|
+
type: :string,
|
|
34
|
+
desc: "Renders a field as radio buttons. Example: condition:new=Neu,used=Gebraucht"
|
|
35
|
+
|
|
36
|
+
def validate_editor_options
|
|
37
|
+
configurations = {
|
|
38
|
+
select: select_configuration,
|
|
39
|
+
radio: radio_configuration
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
configurations.each do |option_name, configuration|
|
|
43
|
+
validate_editor_configuration(option_name, configuration)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
validate_editor_configuration_conflict(configurations)
|
|
47
|
+
end
|
|
48
|
+
|
|
28
49
|
def create_b4um_form
|
|
29
50
|
form_path = File.join(
|
|
30
51
|
"app/views",
|
|
@@ -328,6 +349,91 @@ module B4um
|
|
|
328
349
|
navigation_link.chomp
|
|
329
350
|
)
|
|
330
351
|
end
|
|
352
|
+
|
|
353
|
+
private
|
|
354
|
+
|
|
355
|
+
def validate_editor_configuration(option_name, configuration)
|
|
356
|
+
return unless options[option_name].present?
|
|
357
|
+
|
|
358
|
+
unless configuration
|
|
359
|
+
raise Thor::Error,
|
|
360
|
+
"Invalid --#{option_name} configuration. " \
|
|
361
|
+
"Expected FIELD:VALUE[,VALUE...]"
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
field_names = attributes.map(&:name)
|
|
365
|
+
|
|
366
|
+
return if field_names.include?(configuration[:field])
|
|
367
|
+
|
|
368
|
+
raise Thor::Error,
|
|
369
|
+
"--#{option_name} field #{configuration[:field]} " \
|
|
370
|
+
"must be one of the generated fields: #{field_names.join(", ")}"
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def validate_editor_configuration_conflict(configurations)
|
|
374
|
+
select = configurations[:select]
|
|
375
|
+
radio = configurations[:radio]
|
|
376
|
+
|
|
377
|
+
return unless select && radio
|
|
378
|
+
return unless select[:field] == radio[:field]
|
|
379
|
+
|
|
380
|
+
raise Thor::Error,
|
|
381
|
+
"Field #{select[:field]} cannot use both --select and --radio"
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def select_configuration
|
|
385
|
+
editor_configuration(:select)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def radio_configuration
|
|
389
|
+
editor_configuration(:radio)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def editor_configuration(option_name)
|
|
393
|
+
value = options[option_name]
|
|
394
|
+
return unless value.present?
|
|
395
|
+
|
|
396
|
+
field, values = value.split(":", 2)
|
|
397
|
+
|
|
398
|
+
return unless field.present? && values.present?
|
|
399
|
+
|
|
400
|
+
choices = editor_choices(values)
|
|
401
|
+
return if choices.empty?
|
|
402
|
+
|
|
403
|
+
{
|
|
404
|
+
field: field,
|
|
405
|
+
values: choices
|
|
406
|
+
}
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def editor_choices(values)
|
|
410
|
+
values.split(",").filter_map do |choice|
|
|
411
|
+
value, label = choice.strip.split("=", 2)
|
|
412
|
+
|
|
413
|
+
next if value.blank?
|
|
414
|
+
|
|
415
|
+
{
|
|
416
|
+
value: value,
|
|
417
|
+
label: label.presence || value
|
|
418
|
+
}
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def select_field?(field)
|
|
423
|
+
configuration = select_configuration
|
|
424
|
+
|
|
425
|
+
configuration &&
|
|
426
|
+
configuration[:field] == field &&
|
|
427
|
+
configuration[:values].any?
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def radio_field?(field)
|
|
431
|
+
configuration = radio_configuration
|
|
432
|
+
|
|
433
|
+
configuration &&
|
|
434
|
+
configuration[:field] == field &&
|
|
435
|
+
configuration[:values].any?
|
|
436
|
+
end
|
|
331
437
|
end
|
|
332
438
|
end
|
|
333
439
|
end
|
|
@@ -62,6 +62,35 @@
|
|
|
62
62
|
<%%= form.label :<%= attribute.name %>, class: "form-label" %>
|
|
63
63
|
</div>
|
|
64
64
|
|
|
65
|
+
<% elsif select_field?(attribute.name) -%>
|
|
66
|
+
<div class="form-field form-field--select">
|
|
67
|
+
<%%= form.label :<%= attribute.name %>, class: "form-label" %>
|
|
68
|
+
|
|
69
|
+
<%%= form.select :<%= attribute.name %>,
|
|
70
|
+
<%= select_configuration[:values].inspect %>.map { |choice|
|
|
71
|
+
[choice[:label], choice[:value]]
|
|
72
|
+
},
|
|
73
|
+
{},
|
|
74
|
+
class: "form-select" %>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<% elsif radio_field?(attribute.name) -%>
|
|
78
|
+
<div class="form-field form-field--radio">
|
|
79
|
+
<%%= form.label :<%= attribute.name %>, class: "form-label" %>
|
|
80
|
+
|
|
81
|
+
<div class="form-radio-options">
|
|
82
|
+
<%% <%= radio_configuration[:values].inspect %>.each do |choice| %>
|
|
83
|
+
<label class="form-radio-option">
|
|
84
|
+
<%%= form.radio_button :<%= attribute.name %>,
|
|
85
|
+
choice[:value],
|
|
86
|
+
class: "form-radio" %>
|
|
87
|
+
|
|
88
|
+
<span><%%= choice[:label] %></span>
|
|
89
|
+
</label>
|
|
90
|
+
<%% end %>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
65
94
|
<% elsif attribute.type == :boolean -%>
|
|
66
95
|
<div class="form-field form-field--checkbox">
|
|
67
96
|
<%%= form.check_box :<%= attribute.name %>, class: "form-checkbox" %>
|
|
@@ -130,6 +130,41 @@
|
|
|
130
130
|
<span class="resource-value resource-value--empty">No images</span>
|
|
131
131
|
<%% end %>
|
|
132
132
|
</div>
|
|
133
|
+
<% elsif select_field?(attribute.name) -%>
|
|
134
|
+
<% if attribute.name == "status" -%>
|
|
135
|
+
<span class="b4um-badge">
|
|
136
|
+
<%%=
|
|
137
|
+
<%= select_configuration[:values].inspect %>.to_h {
|
|
138
|
+
|choice| [choice[:value], choice[:label]]
|
|
139
|
+
}.fetch(
|
|
140
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
141
|
+
<%= singular_table_name %>.<%= attribute.name %>
|
|
142
|
+
)
|
|
143
|
+
%>
|
|
144
|
+
</span>
|
|
145
|
+
<% else -%>
|
|
146
|
+
<span class="resource-value">
|
|
147
|
+
<%%=
|
|
148
|
+
<%= select_configuration[:values].inspect %>.to_h {
|
|
149
|
+
|choice| [choice[:value], choice[:label]]
|
|
150
|
+
}.fetch(
|
|
151
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
152
|
+
<%= singular_table_name %>.<%= attribute.name %>
|
|
153
|
+
)
|
|
154
|
+
%>
|
|
155
|
+
</span>
|
|
156
|
+
<% end -%>
|
|
157
|
+
<% elsif radio_field?(attribute.name) -%>
|
|
158
|
+
<span class="resource-value">
|
|
159
|
+
<%%=
|
|
160
|
+
<%= radio_configuration[:values].inspect %>.to_h {
|
|
161
|
+
|choice| [choice[:value], choice[:label]]
|
|
162
|
+
}.fetch(
|
|
163
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
164
|
+
<%= singular_table_name %>.<%= attribute.name %>
|
|
165
|
+
)
|
|
166
|
+
%>
|
|
167
|
+
</span>
|
|
133
168
|
<% elsif attribute.name == "status" -%>
|
|
134
169
|
<span class="b4um-badge"><%%= <%= singular_table_name %>.<%= attribute.name %> %></span>
|
|
135
170
|
<% elsif attribute.type == :text -%>
|
|
@@ -14,7 +14,38 @@
|
|
|
14
14
|
<tr>
|
|
15
15
|
<% attributes.each do |attribute| -%>
|
|
16
16
|
<td>
|
|
17
|
+
<% if select_field?(attribute.name) -%>
|
|
17
18
|
<% if attribute.name == "status" -%>
|
|
19
|
+
<span class="b4um-badge">
|
|
20
|
+
<%%=
|
|
21
|
+
<%= select_configuration[:values].inspect %>.to_h {
|
|
22
|
+
|choice| [choice[:value], choice[:label]]
|
|
23
|
+
}.fetch(
|
|
24
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
25
|
+
<%= singular_table_name %>.<%= attribute.name %>
|
|
26
|
+
)
|
|
27
|
+
%>
|
|
28
|
+
</span>
|
|
29
|
+
<% else -%>
|
|
30
|
+
<%%=
|
|
31
|
+
<%= select_configuration[:values].inspect %>.to_h {
|
|
32
|
+
|choice| [choice[:value], choice[:label]]
|
|
33
|
+
}.fetch(
|
|
34
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
35
|
+
<%= singular_table_name %>.<%= attribute.name %>
|
|
36
|
+
)
|
|
37
|
+
%>
|
|
38
|
+
<% end -%>
|
|
39
|
+
<% elsif radio_field?(attribute.name) -%>
|
|
40
|
+
<%%=
|
|
41
|
+
<%= radio_configuration[:values].inspect %>.to_h {
|
|
42
|
+
|choice| [choice[:value], choice[:label]]
|
|
43
|
+
}.fetch(
|
|
44
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
45
|
+
<%= singular_table_name %>.<%= attribute.name %>
|
|
46
|
+
)
|
|
47
|
+
%>
|
|
48
|
+
<% elsif attribute.name == "status" -%>
|
|
18
49
|
<span class="b4um-badge">
|
|
19
50
|
<%%= <%= singular_table_name %>.<%= attribute.name %> %>
|
|
20
51
|
</span>
|