b4um_generators 0.2.7 → 0.2.9
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/lib/b4um_generators/version.rb +1 -1
- data/lib/generators/b4um/authentication/authentication_generator.rb +356 -1
- data/lib/generators/b4um/install/templates/b4um/lightbox.css +92 -10
- data/lib/generators/b4um/install/templates/b4um/theme.css +8 -0
- data/lib/generators/b4um/install/templates/image_lightbox_controller.js +72 -8
- data/lib/generators/b4um/scaffold/templates/_bento.html.erb.tt +15 -13
- data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +12 -2
- data/lib/generators/b4um/scaffold/templates/_resource.html.erb.tt +17 -1
- data/lib/generators/b4um/scaffold/templates/_table.html.erb.tt +7 -0
- data/lib/generators/b4um/trix/templates/b4um_rich_text_helper.rb +2 -1
- data/lib/generators/b4um/trix/templates/b4um_trix.js +73 -34
- data/lib/generators/b4um/trix/trix_generator.rb +41 -7
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4554ba68c9377451369455eee637d0ebf9b05926d17f1cca4e7846ed9a3e7ffe
|
|
4
|
+
data.tar.gz: 52c056de9b177de1f0e790ede1d7c13fcd5888c18eb95e02d5e93895c2827040
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2758a16ff6d6fdfc220c7c49c8c698d09628ba53ecf45054f446bf062d291dfed85c5ca2a31d50e69e3d6a77521500d01805eaa21ff07cc8bf1adabe6ec0566
|
|
7
|
+
data.tar.gz: 22027e9c4911cbc9b16f8ad15132ff9681c32eaa4b53760c4e7c5838f7ff86e609879d94450bf19e975a105a48c196864d7c5bdcbea351266901c109563399e8
|
|
@@ -182,18 +182,32 @@ module B4um
|
|
|
182
182
|
|
|
183
183
|
navigation = File.read(full_navigation_path)
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
if navigation.include?(
|
|
186
186
|
'navigation_button_to "Logout", logout_path, method: :delete'
|
|
187
187
|
)
|
|
188
|
+
add_register_navigation_link(
|
|
189
|
+
navigation_path,
|
|
190
|
+
navigation
|
|
191
|
+
)
|
|
192
|
+
return
|
|
193
|
+
end
|
|
188
194
|
|
|
189
195
|
marker = "<%# B4UM_NAVIGATION_LINKS %>"
|
|
190
196
|
|
|
191
197
|
return unless navigation.include?(marker)
|
|
192
198
|
|
|
199
|
+
controller_name = authentication_model_name.pluralize
|
|
200
|
+
new_resource_path =
|
|
201
|
+
"new_#{authentication_model_name}_path"
|
|
202
|
+
|
|
193
203
|
authentication_links = <<~ERB
|
|
194
204
|
<% if logged_in? %>
|
|
195
205
|
<%= navigation_button_to "Logout", logout_path, method: :delete %>
|
|
196
206
|
<% else %>
|
|
207
|
+
<%= navigation_link_to "Register",
|
|
208
|
+
#{new_resource_path},
|
|
209
|
+
controller: :#{controller_name},
|
|
210
|
+
action: :new %>
|
|
197
211
|
<%= navigation_link_to "Login",
|
|
198
212
|
login_path,
|
|
199
213
|
controller: :sessions,
|
|
@@ -210,6 +224,106 @@ module B4um
|
|
|
210
224
|
)
|
|
211
225
|
end
|
|
212
226
|
|
|
227
|
+
def protect_authentication_resource
|
|
228
|
+
controller_path = authentication_resource_controller_path
|
|
229
|
+
full_controller_path = File.join(
|
|
230
|
+
destination_root,
|
|
231
|
+
controller_path
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return unless File.exist?(full_controller_path)
|
|
235
|
+
|
|
236
|
+
add_authentication_resource_callbacks(controller_path)
|
|
237
|
+
add_authentication_resource_authorization(controller_path)
|
|
238
|
+
redirect_destroyed_authentication_resource_to_root(controller_path)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def sign_in_after_registration
|
|
242
|
+
controller_path = authentication_resource_controller_path
|
|
243
|
+
|
|
244
|
+
full_controller_path = File.join(
|
|
245
|
+
destination_root,
|
|
246
|
+
controller_path
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return unless File.exist?(full_controller_path)
|
|
250
|
+
|
|
251
|
+
controller = File.read(full_controller_path)
|
|
252
|
+
|
|
253
|
+
session_assignment =
|
|
254
|
+
"session[:#{authentication_model_name}_id] = " \
|
|
255
|
+
"@#{authentication_model_name}.id"
|
|
256
|
+
|
|
257
|
+
return if controller.include?(session_assignment)
|
|
258
|
+
|
|
259
|
+
save_pattern =
|
|
260
|
+
/^(\s*)if @#{Regexp.escape(authentication_model_name)}\.save\s*$/
|
|
261
|
+
|
|
262
|
+
return unless controller.match?(save_pattern)
|
|
263
|
+
|
|
264
|
+
inject_into_file(
|
|
265
|
+
controller_path,
|
|
266
|
+
after: save_pattern
|
|
267
|
+
) do
|
|
268
|
+
"\n\\1 #{session_assignment}"
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def add_profile_navigation
|
|
273
|
+
navigation_path = "app/views/shared/_navigation.html.erb"
|
|
274
|
+
|
|
275
|
+
full_navigation_path = File.join(
|
|
276
|
+
destination_root,
|
|
277
|
+
navigation_path
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
return unless File.exist?(full_navigation_path)
|
|
281
|
+
|
|
282
|
+
navigation = File.read(full_navigation_path)
|
|
283
|
+
|
|
284
|
+
resource_name = authentication_class_name.pluralize
|
|
285
|
+
controller_name = authentication_model_name.pluralize
|
|
286
|
+
|
|
287
|
+
resource_pattern = /
|
|
288
|
+
^[ \t]*<%=\s*navigation_link_to\s+"#{Regexp.escape(resource_name)}",
|
|
289
|
+
.*?
|
|
290
|
+
controller:\s*:#{Regexp.escape(controller_name)}
|
|
291
|
+
\s*%>\s*
|
|
292
|
+
/mx
|
|
293
|
+
|
|
294
|
+
if navigation.match?(resource_pattern)
|
|
295
|
+
gsub_file(
|
|
296
|
+
navigation_path,
|
|
297
|
+
resource_pattern,
|
|
298
|
+
""
|
|
299
|
+
)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
navigation = File.read(full_navigation_path)
|
|
303
|
+
|
|
304
|
+
return if navigation.include?(
|
|
305
|
+
'navigation_link_to "Profile"'
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
login_marker = "<% if logged_in? %>"
|
|
309
|
+
|
|
310
|
+
return unless navigation.include?(login_marker)
|
|
311
|
+
|
|
312
|
+
profile_link = <<~ERB
|
|
313
|
+
<% if logged_in? %>
|
|
314
|
+
<%= navigation_link_to "Profile",
|
|
315
|
+
#{current_authentication_method},
|
|
316
|
+
controller: :#{controller_name},
|
|
317
|
+
action: :show %>
|
|
318
|
+
ERB
|
|
319
|
+
|
|
320
|
+
gsub_file(
|
|
321
|
+
navigation_path,
|
|
322
|
+
login_marker,
|
|
323
|
+
profile_link.chomp
|
|
324
|
+
)
|
|
325
|
+
end
|
|
326
|
+
|
|
213
327
|
def protect_controllers
|
|
214
328
|
protected_controller_names.each do |controller_name|
|
|
215
329
|
controller_path = File.join(
|
|
@@ -251,6 +365,247 @@ module B4um
|
|
|
251
365
|
|
|
252
366
|
private
|
|
253
367
|
|
|
368
|
+
def add_register_navigation_link(navigation_path, navigation)
|
|
369
|
+
return if navigation.include?(
|
|
370
|
+
'navigation_link_to "Register",'
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
controller_name = authentication_model_name.pluralize
|
|
374
|
+
new_resource_path =
|
|
375
|
+
"new_#{authentication_model_name}_path"
|
|
376
|
+
|
|
377
|
+
login_pattern = /
|
|
378
|
+
^([ \t]*)<%=\s*navigation_link_to\s+"Login",
|
|
379
|
+
/x
|
|
380
|
+
|
|
381
|
+
return unless navigation.match?(login_pattern)
|
|
382
|
+
|
|
383
|
+
register_link = <<~ERB
|
|
384
|
+
<%= navigation_link_to "Register",
|
|
385
|
+
#{new_resource_path},
|
|
386
|
+
controller: :#{controller_name},
|
|
387
|
+
action: :new %>
|
|
388
|
+
ERB
|
|
389
|
+
|
|
390
|
+
gsub_file(
|
|
391
|
+
navigation_path,
|
|
392
|
+
login_pattern
|
|
393
|
+
) do |match|
|
|
394
|
+
indentation = match[/\A[ \t]*/]
|
|
395
|
+
|
|
396
|
+
indented_register = register_link.lines.map do |line|
|
|
397
|
+
"#{indentation}#{line}"
|
|
398
|
+
end.join
|
|
399
|
+
|
|
400
|
+
"#{indented_register}#{match}"
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def authentication_resource_controller_path
|
|
405
|
+
File.join(
|
|
406
|
+
"app/controllers",
|
|
407
|
+
"#{authentication_model_name.pluralize}_controller.rb"
|
|
408
|
+
)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def redirect_destroyed_authentication_resource_to_root(controller_path)
|
|
412
|
+
resource_path =
|
|
413
|
+
"#{authentication_model_name.pluralize}_path"
|
|
414
|
+
|
|
415
|
+
redirect =
|
|
416
|
+
"redirect_to #{resource_path}, status: :see_other"
|
|
417
|
+
|
|
418
|
+
return unless controller_contains?(controller_path, redirect)
|
|
419
|
+
|
|
420
|
+
gsub_file(
|
|
421
|
+
controller_path,
|
|
422
|
+
redirect,
|
|
423
|
+
"redirect_to root_path, status: :see_other"
|
|
424
|
+
)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def add_authentication_resource_callbacks(controller_path)
|
|
428
|
+
add_require_login_callback(controller_path)
|
|
429
|
+
add_prevent_authentication_index_callback(controller_path)
|
|
430
|
+
add_require_current_authentication_callback(controller_path)
|
|
431
|
+
add_clear_authentication_session_callback(controller_path)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def add_require_login_callback(controller_path)
|
|
435
|
+
callback = "before_action :require_login, except: %i[ new create ]"
|
|
436
|
+
|
|
437
|
+
return if controller_contains?(controller_path, callback)
|
|
438
|
+
|
|
439
|
+
inject_into_file(
|
|
440
|
+
controller_path,
|
|
441
|
+
after: /^class .*Controller < ApplicationController\s*$/
|
|
442
|
+
) do
|
|
443
|
+
"\n #{callback}"
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def add_prevent_authentication_index_callback(controller_path)
|
|
448
|
+
callback =
|
|
449
|
+
"before_action :prevent_authentication_index, only: :index"
|
|
450
|
+
|
|
451
|
+
return if controller_contains?(controller_path, callback)
|
|
452
|
+
|
|
453
|
+
inject_into_file(
|
|
454
|
+
controller_path,
|
|
455
|
+
after: /^\s*before_action :require_login, except: %i\[ new create \]\s*$/
|
|
456
|
+
) do
|
|
457
|
+
"\n #{callback}"
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def add_require_current_authentication_callback(controller_path)
|
|
462
|
+
callback =
|
|
463
|
+
"before_action :require_current_#{authentication_model_name}, " \
|
|
464
|
+
"only: %i[ show edit update destroy ]"
|
|
465
|
+
|
|
466
|
+
return if controller_contains?(controller_path, callback)
|
|
467
|
+
|
|
468
|
+
set_callback =
|
|
469
|
+
/^\s*before_action :set_#{authentication_model_name}, only: %i\[ show edit update destroy \]\s*$/
|
|
470
|
+
|
|
471
|
+
if controller_matches?(controller_path, set_callback)
|
|
472
|
+
inject_into_file(
|
|
473
|
+
controller_path,
|
|
474
|
+
after: set_callback
|
|
475
|
+
) do
|
|
476
|
+
"\n #{callback}"
|
|
477
|
+
end
|
|
478
|
+
else
|
|
479
|
+
inject_into_file(
|
|
480
|
+
controller_path,
|
|
481
|
+
after: /^class .*Controller < ApplicationController\s*$/
|
|
482
|
+
) do
|
|
483
|
+
"\n #{callback}"
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def add_clear_authentication_session_callback(controller_path)
|
|
489
|
+
callback =
|
|
490
|
+
"after_action :clear_authentication_session, only: :destroy"
|
|
491
|
+
|
|
492
|
+
return if controller_contains?(controller_path, callback)
|
|
493
|
+
|
|
494
|
+
current_user_callback =
|
|
495
|
+
/^\s*before_action :require_current_#{authentication_model_name}, only: %i\[ show edit update destroy \]\s*$/
|
|
496
|
+
|
|
497
|
+
inject_into_file(
|
|
498
|
+
controller_path,
|
|
499
|
+
after: current_user_callback
|
|
500
|
+
) do
|
|
501
|
+
"\n #{callback}"
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def add_authentication_resource_authorization(controller_path)
|
|
506
|
+
add_prevent_authentication_index_method(controller_path)
|
|
507
|
+
add_require_current_authentication_method(controller_path)
|
|
508
|
+
add_clear_authentication_session_method(controller_path)
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def add_prevent_authentication_index_method(controller_path)
|
|
512
|
+
method_name = "def prevent_authentication_index"
|
|
513
|
+
|
|
514
|
+
return if controller_contains?(controller_path, method_name)
|
|
515
|
+
|
|
516
|
+
inject_private_method(
|
|
517
|
+
controller_path,
|
|
518
|
+
<<~RUBY
|
|
519
|
+
def prevent_authentication_index
|
|
520
|
+
redirect_to #{current_authentication_method}
|
|
521
|
+
end
|
|
522
|
+
RUBY
|
|
523
|
+
)
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def add_require_current_authentication_method(controller_path)
|
|
527
|
+
method_name =
|
|
528
|
+
"def require_current_#{authentication_model_name}"
|
|
529
|
+
|
|
530
|
+
return if controller_contains?(controller_path, method_name)
|
|
531
|
+
|
|
532
|
+
inject_private_method(
|
|
533
|
+
controller_path,
|
|
534
|
+
<<~RUBY
|
|
535
|
+
def require_current_#{authentication_model_name}
|
|
536
|
+
return if @#{authentication_model_name} == #{current_authentication_method}
|
|
537
|
+
|
|
538
|
+
redirect_to root_path, alert: "Access denied."
|
|
539
|
+
end
|
|
540
|
+
RUBY
|
|
541
|
+
)
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def add_clear_authentication_session_method(controller_path)
|
|
545
|
+
method_name = "def clear_authentication_session"
|
|
546
|
+
|
|
547
|
+
return if controller_contains?(controller_path, method_name)
|
|
548
|
+
|
|
549
|
+
inject_private_method(
|
|
550
|
+
controller_path,
|
|
551
|
+
<<~RUBY
|
|
552
|
+
def clear_authentication_session
|
|
553
|
+
session.delete(:#{authentication_model_name}_id)
|
|
554
|
+
end
|
|
555
|
+
RUBY
|
|
556
|
+
)
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def inject_private_method(controller_path, method_body)
|
|
560
|
+
full_controller_path = File.join(
|
|
561
|
+
destination_root,
|
|
562
|
+
controller_path
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
content = File.read(full_controller_path)
|
|
566
|
+
|
|
567
|
+
class_end_position = content.rindex(/^end\s*$/)
|
|
568
|
+
|
|
569
|
+
unless class_end_position
|
|
570
|
+
raise Thor::Error,
|
|
571
|
+
"Could not find the closing class end in #{controller_path}"
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
indented_body = method_body.lines.map do |line|
|
|
575
|
+
line.strip.empty? ? "\n" : " #{line}"
|
|
576
|
+
end.join
|
|
577
|
+
|
|
578
|
+
updated_content = content.dup
|
|
579
|
+
|
|
580
|
+
updated_content.insert(
|
|
581
|
+
class_end_position,
|
|
582
|
+
"\n#{indented_body}"
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
File.write(
|
|
586
|
+
full_controller_path,
|
|
587
|
+
updated_content
|
|
588
|
+
)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def controller_contains?(controller_path, content)
|
|
592
|
+
full_controller_path = File.join(
|
|
593
|
+
destination_root,
|
|
594
|
+
controller_path
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
File.read(full_controller_path).include?(content)
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def controller_matches?(controller_path, pattern)
|
|
601
|
+
full_controller_path = File.join(
|
|
602
|
+
destination_root,
|
|
603
|
+
controller_path
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
File.read(full_controller_path).match?(pattern)
|
|
607
|
+
end
|
|
608
|
+
|
|
254
609
|
def protect_index_actions_for(controller_name)
|
|
255
610
|
index_path = File.join(
|
|
256
611
|
"app/views",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/* -------------------------------------------------------
|
|
2
2
|
Image lightbox
|
|
3
3
|
------------------------------------------------------- */
|
|
4
|
-
|
|
4
|
+
.lupe {
|
|
5
|
+
cursor: zoom-in;
|
|
6
|
+
}
|
|
5
7
|
.resource-image__button,
|
|
6
8
|
.resource-images__button {
|
|
7
9
|
display: block;
|
|
@@ -38,7 +40,11 @@
|
|
|
38
40
|
padding: 70px 90px 110px;
|
|
39
41
|
box-sizing: border-box;
|
|
40
42
|
|
|
41
|
-
background
|
|
43
|
+
background: var(--b4um-hero-teaser-background);
|
|
44
|
+
color: var(--b4um-hero-teaser-text);
|
|
45
|
+
|
|
46
|
+
backdrop-filter: blur(16px);
|
|
47
|
+
-webkit-backdrop-filter: blur(16px);
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
.image-lightbox[hidden] {
|
|
@@ -121,7 +127,7 @@ body.image-lightbox-open {
|
|
|
121
127
|
top: 28px;
|
|
122
128
|
left: 28px;
|
|
123
129
|
|
|
124
|
-
color:
|
|
130
|
+
color: var(--b4um-hero-teaser-text);
|
|
125
131
|
|
|
126
132
|
font-size: 0.9rem;
|
|
127
133
|
font-weight: 500;
|
|
@@ -146,7 +152,7 @@ body.image-lightbox-open {
|
|
|
146
152
|
border: 0;
|
|
147
153
|
|
|
148
154
|
background: transparent;
|
|
149
|
-
color:
|
|
155
|
+
color: var(--b4um-hero-teaser-text);
|
|
150
156
|
|
|
151
157
|
font: inherit;
|
|
152
158
|
font-size: 2.5rem;
|
|
@@ -165,11 +171,11 @@ body.image-lightbox-open {
|
|
|
165
171
|
justify-content: center;
|
|
166
172
|
|
|
167
173
|
width: 56px;
|
|
168
|
-
height:
|
|
169
|
-
padding:
|
|
174
|
+
height: 56px;
|
|
175
|
+
padding-bottom: 12px;
|
|
170
176
|
|
|
171
177
|
border: 0;
|
|
172
|
-
border-radius:
|
|
178
|
+
border-radius: 50%;
|
|
173
179
|
|
|
174
180
|
background-color: rgba(0, 0, 0, 0.35);
|
|
175
181
|
color: #ffffff;
|
|
@@ -181,7 +187,7 @@ body.image-lightbox-open {
|
|
|
181
187
|
|
|
182
188
|
cursor: pointer;
|
|
183
189
|
|
|
184
|
-
transform: translateY(-
|
|
190
|
+
transform: translateY(-100%);
|
|
185
191
|
|
|
186
192
|
transition:
|
|
187
193
|
background-color 0.18s ease,
|
|
@@ -189,7 +195,7 @@ body.image-lightbox-open {
|
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
.image-lightbox__navigation:hover {
|
|
192
|
-
background-color: rgba(
|
|
198
|
+
background-color: rgba(0, 0, 0, 0.16);
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
.image-lightbox__navigation--previous {
|
|
@@ -212,7 +218,7 @@ body.image-lightbox-open {
|
|
|
212
218
|
bottom: 24px;
|
|
213
219
|
left: 20px;
|
|
214
220
|
|
|
215
|
-
color:
|
|
221
|
+
color: var(--b4um-hero-teaser-text);
|
|
216
222
|
|
|
217
223
|
font-size: 0.85rem;
|
|
218
224
|
text-align: center;
|
|
@@ -220,6 +226,82 @@ body.image-lightbox-open {
|
|
|
220
226
|
overflow-wrap: anywhere;
|
|
221
227
|
}
|
|
222
228
|
|
|
229
|
+
/* -------------------------------------------------------
|
|
230
|
+
Thumbnails
|
|
231
|
+
------------------------------------------------------- */
|
|
232
|
+
|
|
233
|
+
.image-lightbox__thumbnails {
|
|
234
|
+
position: absolute;
|
|
235
|
+
right: 6rem;
|
|
236
|
+
bottom: 2rem;
|
|
237
|
+
left: 6rem;
|
|
238
|
+
z-index: 3;
|
|
239
|
+
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
justify-content: center;
|
|
243
|
+
|
|
244
|
+
gap: 0.75rem;
|
|
245
|
+
|
|
246
|
+
overflow-x: auto;
|
|
247
|
+
overflow-y: hidden;
|
|
248
|
+
|
|
249
|
+
padding: 0.5rem;
|
|
250
|
+
|
|
251
|
+
scrollbar-width: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.image-lightbox__thumbnails::-webkit-scrollbar {
|
|
255
|
+
display: none;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.image-lightbox__thumbnail {
|
|
259
|
+
flex: 0 0 auto;
|
|
260
|
+
|
|
261
|
+
width: 72px;
|
|
262
|
+
height: 52px;
|
|
263
|
+
|
|
264
|
+
padding: 3px;
|
|
265
|
+
|
|
266
|
+
overflow: hidden;
|
|
267
|
+
|
|
268
|
+
border: 2px solid transparent;
|
|
269
|
+
border-radius: 10px;
|
|
270
|
+
|
|
271
|
+
background: rgba(255, 255, 255, 0.12);
|
|
272
|
+
|
|
273
|
+
opacity: 0.55;
|
|
274
|
+
cursor: pointer;
|
|
275
|
+
|
|
276
|
+
transition:
|
|
277
|
+
opacity 0.2s ease,
|
|
278
|
+
border-color 0.2s ease,
|
|
279
|
+
transform 0.2s ease;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.image-lightbox__thumbnail:hover {
|
|
283
|
+
opacity: 0.85;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.image-lightbox__thumbnail.is-active {
|
|
287
|
+
border-color: var(--b4um-hero-teaser-text);
|
|
288
|
+
|
|
289
|
+
opacity: 1;
|
|
290
|
+
|
|
291
|
+
transform: scale(1.06);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.image-lightbox__thumbnail img {
|
|
295
|
+
display: block;
|
|
296
|
+
|
|
297
|
+
width: 100%;
|
|
298
|
+
height: 100%;
|
|
299
|
+
|
|
300
|
+
border-radius: 6px;
|
|
301
|
+
|
|
302
|
+
object-fit: cover;
|
|
303
|
+
}
|
|
304
|
+
|
|
223
305
|
@media (prefers-reduced-motion: reduce) {
|
|
224
306
|
.image-lightbox__image--sliding {
|
|
225
307
|
transition: none;
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
--b4um-primary-shadow: rgba(46, 71, 210, 0.22);
|
|
63
63
|
--b4um-primary-shadow-soft: rgba(46, 71, 210, 0.12);
|
|
64
64
|
--b4um-danger-shadow: rgba(204, 0, 0, 0.18);
|
|
65
|
+
|
|
66
|
+
--b4um-hero-teaser-background: rgba(255, 255, 255, 0.55);
|
|
67
|
+
--b4um-hero-teaser-text: rgba(20, 20, 20, 0.9);
|
|
68
|
+
--b4um-hero-teaser-border: rgba(255, 255, 255, 0.3);
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
/* -------------------------------------------------------
|
|
@@ -128,5 +132,9 @@
|
|
|
128
132
|
--b4um-primary-shadow-soft: rgba(142, 162, 255, 0.14);
|
|
129
133
|
--b4um-danger-shadow: rgba(255, 133, 133, 0.18);
|
|
130
134
|
|
|
135
|
+
--b4um-hero-teaser-background: rgba(20, 20, 20, 0.55);
|
|
136
|
+
--b4um-hero-teaser-text: rgba(255, 255, 255, 0.85);
|
|
137
|
+
--b4um-hero-teaser-border: rgba(255, 255, 255, 0.18);
|
|
138
|
+
|
|
131
139
|
color-scheme: dark;
|
|
132
140
|
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
|
2
2
|
|
|
3
3
|
export default class extends Controller {
|
|
4
|
-
static targets = [
|
|
4
|
+
static targets = [
|
|
5
|
+
'item',
|
|
6
|
+
'overlay',
|
|
7
|
+
'stage',
|
|
8
|
+
'image',
|
|
9
|
+
'caption',
|
|
10
|
+
'counter',
|
|
11
|
+
'previousButton',
|
|
12
|
+
'nextButton',
|
|
13
|
+
'thumbnails'
|
|
14
|
+
]
|
|
5
15
|
|
|
6
16
|
connect() {
|
|
7
17
|
this.currentIndex = 0
|
|
@@ -22,6 +32,7 @@ export default class extends Controller {
|
|
|
22
32
|
if (index === -1) return
|
|
23
33
|
|
|
24
34
|
this.currentIndex = index
|
|
35
|
+
this.buildThumbnails()
|
|
25
36
|
this.showCurrentImage()
|
|
26
37
|
|
|
27
38
|
this.overlayTarget.hidden = false
|
|
@@ -30,6 +41,64 @@ export default class extends Controller {
|
|
|
30
41
|
this.overlayTarget.focus()
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
buildThumbnails() {
|
|
45
|
+
if (!this.hasThumbnailsTarget) return
|
|
46
|
+
|
|
47
|
+
this.thumbnailsTarget.innerHTML = ''
|
|
48
|
+
|
|
49
|
+
this.itemTargets.forEach((item, index) => {
|
|
50
|
+
const button = document.createElement('button')
|
|
51
|
+
button.type = 'button'
|
|
52
|
+
button.className = 'image-lightbox__thumbnail'
|
|
53
|
+
button.setAttribute('aria-label', `Bild ${index + 1} anzeigen`)
|
|
54
|
+
|
|
55
|
+
const image = document.createElement('img')
|
|
56
|
+
image.src = item.dataset.imageLightboxUrl
|
|
57
|
+
image.alt = item.dataset.imageLightboxAlt || ''
|
|
58
|
+
|
|
59
|
+
button.appendChild(image)
|
|
60
|
+
|
|
61
|
+
button.addEventListener('click', (event) => {
|
|
62
|
+
event.stopPropagation()
|
|
63
|
+
|
|
64
|
+
if (index === this.currentIndex) return
|
|
65
|
+
if (this.isSliding) return
|
|
66
|
+
|
|
67
|
+
const direction = index > this.currentIndex ? 'next' : 'previous'
|
|
68
|
+
|
|
69
|
+
this.updateThumbnails(index)
|
|
70
|
+
this.slideTo(index, direction)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
this.thumbnailsTarget.appendChild(button)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
this.updateThumbnails()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
updateThumbnails(activeIndex = this.currentIndex) {
|
|
80
|
+
if (!this.hasThumbnailsTarget) return
|
|
81
|
+
|
|
82
|
+
const thumbnails = Array.from(this.thumbnailsTarget.querySelectorAll('.image-lightbox__thumbnail'))
|
|
83
|
+
|
|
84
|
+
thumbnails.forEach((thumbnail, index) => {
|
|
85
|
+
const active = index === activeIndex
|
|
86
|
+
|
|
87
|
+
thumbnail.classList.toggle('is-active', active)
|
|
88
|
+
thumbnail.setAttribute('aria-current', active ? 'true' : 'false')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const activeThumbnail = thumbnails[activeIndex]
|
|
92
|
+
|
|
93
|
+
if (activeThumbnail) {
|
|
94
|
+
activeThumbnail.scrollIntoView({
|
|
95
|
+
behavior: this.prefersReducedMotion() ? 'auto' : 'smooth',
|
|
96
|
+
block: 'nearest',
|
|
97
|
+
inline: 'center'
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
33
102
|
close() {
|
|
34
103
|
if (this.isSliding) return
|
|
35
104
|
|
|
@@ -37,13 +106,6 @@ export default class extends Controller {
|
|
|
37
106
|
document.body.classList.remove('image-lightbox-open')
|
|
38
107
|
}
|
|
39
108
|
|
|
40
|
-
/*
|
|
41
|
-
closeOnBackground(event) {
|
|
42
|
-
if (event.target !== this.overlayTarget) return
|
|
43
|
-
|
|
44
|
-
this.close()
|
|
45
|
-
}*/
|
|
46
|
-
|
|
47
109
|
closeOnBackground(event) {
|
|
48
110
|
if (this.isDragging || this.isSliding) return
|
|
49
111
|
|
|
@@ -207,6 +269,7 @@ export default class extends Controller {
|
|
|
207
269
|
|
|
208
270
|
this.currentIndex = newIndex
|
|
209
271
|
this.updateInformation()
|
|
272
|
+
this.updateThumbnails()
|
|
210
273
|
|
|
211
274
|
this.dragPreviousImage = null
|
|
212
275
|
this.dragNextImage = null
|
|
@@ -361,6 +424,7 @@ export default class extends Controller {
|
|
|
361
424
|
|
|
362
425
|
this.currentIndex = index
|
|
363
426
|
this.updateInformation()
|
|
427
|
+
this.updateThumbnails()
|
|
364
428
|
|
|
365
429
|
this.isSliding = false
|
|
366
430
|
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
<div id="<%= plural_table_name %>" class="b4um-grid">
|
|
2
2
|
<%% <%= plural_table_name %>.each_with_index do |<%= singular_table_name %>, index| %>
|
|
3
|
-
<%% card_class =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
<%% card_class, preview_length =
|
|
4
|
+
case index % 5
|
|
5
|
+
when 0
|
|
6
|
+
["b4um-card b4um-card--wide", 240]
|
|
7
|
+
when 1
|
|
8
|
+
["b4um-card b4um-card--soft", 58]
|
|
9
|
+
when 3
|
|
10
|
+
["b4um-card b4um-card--large", 1600]
|
|
11
|
+
else
|
|
12
|
+
["b4um-card", 58]
|
|
13
|
+
end
|
|
14
|
+
%>
|
|
15
15
|
|
|
16
16
|
<article class="<%%= card_class %>">
|
|
17
|
-
<%%= render <%= singular_table_name %>,
|
|
17
|
+
<%%= render <%= singular_table_name %>,
|
|
18
|
+
compact: true,
|
|
19
|
+
preview_length: preview_length %>
|
|
18
20
|
|
|
19
21
|
<div class="b4um-card__actions">
|
|
20
22
|
<%%= link_to "Show",
|
|
@@ -76,11 +76,21 @@
|
|
|
76
76
|
<%%= form.label :<%= attribute.name %>, class: "form-label" %>
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
|
-
<% elsif
|
|
79
|
+
<% elsif attribute.type == :integer -%>
|
|
80
80
|
<div class="form-field">
|
|
81
81
|
<%%= form.number_field :<%= attribute.name %>,
|
|
82
82
|
class: "form-input",
|
|
83
|
-
placeholder: " "
|
|
83
|
+
placeholder: " ",
|
|
84
|
+
step: 1 %>
|
|
85
|
+
<%%= form.label :<%= attribute.name %>, class: "form-label" %>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<% elsif [:float, :decimal].include?(attribute.type) -%>
|
|
89
|
+
<div class="form-field">
|
|
90
|
+
<%%= form.number_field :<%= attribute.name %>,
|
|
91
|
+
class: "form-input",
|
|
92
|
+
placeholder: " ",
|
|
93
|
+
step: "any" %>
|
|
84
94
|
<%%= form.label :<%= attribute.name %>, class: "form-label" %>
|
|
85
95
|
</div>
|
|
86
96
|
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
alt="">
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
|
+
<div class="image-lightbox__thumbnails" data-image-lightbox-target="thumbnails"></div>
|
|
53
|
+
|
|
52
54
|
<div class="image-lightbox__caption"
|
|
53
55
|
data-image-lightbox-target="caption"></div>
|
|
54
56
|
</div>
|
|
@@ -119,6 +121,8 @@
|
|
|
119
121
|
›
|
|
120
122
|
</button>
|
|
121
123
|
|
|
124
|
+
<div class="image-lightbox__thumbnails" data-image-lightbox-target="thumbnails"></div>
|
|
125
|
+
|
|
122
126
|
<div class="image-lightbox__caption"
|
|
123
127
|
data-image-lightbox-target="caption"></div>
|
|
124
128
|
</div>
|
|
@@ -131,11 +135,23 @@
|
|
|
131
135
|
<% elsif attribute.type == :text -%>
|
|
132
136
|
<span class="resource-value">
|
|
133
137
|
<%% if local_assigns[:compact] %>
|
|
134
|
-
<%%= truncate(
|
|
138
|
+
<%%= truncate(
|
|
139
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
140
|
+
length: local_assigns.fetch(:preview_length, 160)
|
|
141
|
+
) %>
|
|
135
142
|
<%% else %>
|
|
136
143
|
<%%= <%= singular_table_name %>.<%= attribute.name %> %>
|
|
137
144
|
<%% end %>
|
|
138
145
|
</span>
|
|
146
|
+
<% elsif attribute.type == :decimal -%>
|
|
147
|
+
<span class="resource-value">
|
|
148
|
+
<%%= number_with_precision(
|
|
149
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
150
|
+
precision: 2,
|
|
151
|
+
delimiter: ".",
|
|
152
|
+
separator: ","
|
|
153
|
+
) %>
|
|
154
|
+
</span>
|
|
139
155
|
<% else -%>
|
|
140
156
|
<span class="resource-value"><%%= <%= singular_table_name %>.<%= attribute.name %> %></span>
|
|
141
157
|
<% end -%>
|
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
</span>
|
|
21
21
|
<% elsif attribute.type == :text -%>
|
|
22
22
|
<%%= truncate(<%= singular_table_name %>.<%= attribute.name %>, length: 100) %>
|
|
23
|
+
<% elsif attribute.type == :decimal -%>
|
|
24
|
+
<%%= number_with_precision(
|
|
25
|
+
<%= singular_table_name %>.<%= attribute.name %>,
|
|
26
|
+
precision: 2,
|
|
27
|
+
delimiter: ".",
|
|
28
|
+
separator: ","
|
|
29
|
+
) %>
|
|
23
30
|
<% else -%>
|
|
24
31
|
<%%= <%= singular_table_name %>.<%= attribute.name %> %>
|
|
25
32
|
<% end -%>
|
|
@@ -10,7 +10,8 @@ module B4umRichTextHelper
|
|
|
10
10
|
first_element = document.element_children.first
|
|
11
11
|
heading = b4um_preview_heading(first_element)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
document.css("action-text-attachment").remove
|
|
14
|
+
plain_text = document.text.strip
|
|
14
15
|
|
|
15
16
|
if heading
|
|
16
17
|
heading_text = heading.text.strip
|
|
@@ -43,6 +43,7 @@ configureHeadingAttributes()
|
|
|
43
43
|
configureAlignmentAttributes()
|
|
44
44
|
configureContainerAttributes()
|
|
45
45
|
configureColorAttributes()
|
|
46
|
+
preserveNestedContainerHtmlAttributes()
|
|
46
47
|
registerAlignmentElements()
|
|
47
48
|
|
|
48
49
|
document.addEventListener('trix-initialize', (event) => {
|
|
@@ -77,6 +78,8 @@ function insertContainer(editor, tagName) {
|
|
|
77
78
|
return
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
const selectedText = editor.getDocument().toString().slice(selectedRange[0], selectedRange[1]).trim()
|
|
82
|
+
|
|
80
83
|
const id = window.prompt('Bitte geben Sie die ID ein:')
|
|
81
84
|
|
|
82
85
|
if (id === null) return
|
|
@@ -91,14 +94,23 @@ function insertContainer(editor, tagName) {
|
|
|
91
94
|
editor.recordUndoEntry(`Insert ${tagName.toUpperCase()} Container`)
|
|
92
95
|
|
|
93
96
|
editor.setSelectedRange(selectedRange)
|
|
97
|
+
editor.activateAttribute(tagName)
|
|
94
98
|
|
|
95
|
-
const
|
|
99
|
+
const documentModel = editor.getDocument()
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
const block = documentModel.getBlocks().find((block) => {
|
|
102
|
+
return block.getAttributes().includes(tagName) && block.toString().includes(selectedText)
|
|
103
|
+
})
|
|
98
104
|
|
|
99
|
-
|
|
105
|
+
if (!block) {
|
|
106
|
+
console.error('b4um Trix: Container-Block wurde nicht gefunden.')
|
|
107
|
+
return
|
|
108
|
+
}
|
|
100
109
|
|
|
101
|
-
|
|
110
|
+
const newBlock = block.addHTMLAttribute('id', trimmedId)
|
|
111
|
+
const newDocument = documentModel.replaceBlock(block, newBlock)
|
|
112
|
+
|
|
113
|
+
editor.loadDocument(newDocument)
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
/* -------------------------------------------------------------------------- */
|
|
@@ -106,8 +118,6 @@ function insertContainer(editor, tagName) {
|
|
|
106
118
|
/* -------------------------------------------------------------------------- */
|
|
107
119
|
|
|
108
120
|
function allowB4umHtmlAttributes() {
|
|
109
|
-
const originalSetHTML = Trix.HTMLSanitizer.setHTML
|
|
110
|
-
|
|
111
121
|
Trix.HTMLSanitizer.setHTML = function (element, html, options = {}) {
|
|
112
122
|
const allowedAttributes = [
|
|
113
123
|
'style',
|
|
@@ -121,10 +131,14 @@ function allowB4umHtmlAttributes() {
|
|
|
121
131
|
...(options.allowedAttributes || [])
|
|
122
132
|
]
|
|
123
133
|
|
|
124
|
-
|
|
134
|
+
const sanitizedElement = this.sanitize(html, {
|
|
125
135
|
...options,
|
|
126
136
|
allowedAttributes: [...new Set(allowedAttributes)]
|
|
127
137
|
})
|
|
138
|
+
|
|
139
|
+
const sanitizedHtml = sanitizedElement.getHTML ? sanitizedElement.getHTML() : sanitizedElement.outerHTML
|
|
140
|
+
|
|
141
|
+
element.innerHTML = sanitizedHtml
|
|
128
142
|
}
|
|
129
143
|
}
|
|
130
144
|
|
|
@@ -152,22 +166,17 @@ function configureAlignmentAttributes() {
|
|
|
152
166
|
}
|
|
153
167
|
|
|
154
168
|
function configureContainerAttributes() {
|
|
155
|
-
Trix.config.blockAttributes.
|
|
156
|
-
|
|
157
|
-
Trix.config.blockAttributes.b4umDiv = {
|
|
158
|
-
tagName: 'div',
|
|
169
|
+
Trix.config.blockAttributes.section = {
|
|
170
|
+
tagName: 'section',
|
|
159
171
|
parse: true,
|
|
160
172
|
nestable: false,
|
|
161
173
|
exclusive: true,
|
|
162
174
|
group: false,
|
|
163
|
-
htmlAttributes: ['id']
|
|
164
|
-
test(element) {
|
|
165
|
-
return element.hasAttribute('id')
|
|
166
|
-
}
|
|
175
|
+
htmlAttributes: ['id']
|
|
167
176
|
}
|
|
168
177
|
|
|
169
|
-
Trix.config.blockAttributes.
|
|
170
|
-
tagName: '
|
|
178
|
+
Trix.config.blockAttributes.article = {
|
|
179
|
+
tagName: 'article',
|
|
171
180
|
parse: true,
|
|
172
181
|
nestable: false,
|
|
173
182
|
exclusive: true,
|
|
@@ -175,8 +184,8 @@ function configureContainerAttributes() {
|
|
|
175
184
|
htmlAttributes: ['id']
|
|
176
185
|
}
|
|
177
186
|
|
|
178
|
-
Trix.config.blockAttributes.
|
|
179
|
-
tagName: '
|
|
187
|
+
Trix.config.blockAttributes.div = {
|
|
188
|
+
tagName: 'div',
|
|
180
189
|
parse: true,
|
|
181
190
|
nestable: false,
|
|
182
191
|
exclusive: true,
|
|
@@ -185,6 +194,40 @@ function configureContainerAttributes() {
|
|
|
185
194
|
}
|
|
186
195
|
}
|
|
187
196
|
|
|
197
|
+
function preserveNestedContainerHtmlAttributes() {
|
|
198
|
+
const prototype = Trix.HTMLParser.prototype
|
|
199
|
+
|
|
200
|
+
if (prototype.b4umPreservesNestedContainerHtmlAttributes) return
|
|
201
|
+
|
|
202
|
+
const originalAppendBlockForAttributesWithElement = prototype.appendBlockForAttributesWithElement
|
|
203
|
+
|
|
204
|
+
prototype.appendBlockForAttributesWithElement = function (attributes, element, htmlAttributes = {}) {
|
|
205
|
+
let mergedHtmlAttributes = { ...htmlAttributes }
|
|
206
|
+
let parentElement = element.parentElement
|
|
207
|
+
|
|
208
|
+
while (parentElement && parentElement !== this.containerElement) {
|
|
209
|
+
const tagName = parentElement.tagName.toLowerCase()
|
|
210
|
+
|
|
211
|
+
const isB4umContainer = ['section', 'article', 'div'].includes(tagName)
|
|
212
|
+
|
|
213
|
+
if (isB4umContainer && parentElement.hasAttribute('id')) {
|
|
214
|
+
mergedHtmlAttributes = {
|
|
215
|
+
id: parentElement.getAttribute('id'),
|
|
216
|
+
...mergedHtmlAttributes
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
break
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
parentElement = parentElement.parentElement
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return originalAppendBlockForAttributesWithElement.call(this, attributes, element, mergedHtmlAttributes)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
prototype.b4umPreservesNestedContainerHtmlAttributes = true
|
|
229
|
+
}
|
|
230
|
+
|
|
188
231
|
function configureColorAttributes() {
|
|
189
232
|
FOREGROUND_COLORS.forEach((color, index) => {
|
|
190
233
|
Trix.config.textAttributes[`fgColor${index + 1}`] = {
|
|
@@ -312,9 +355,7 @@ function installContainerButtonHandlers(editorElement, toolbar) {
|
|
|
312
355
|
if (!editor) return
|
|
313
356
|
|
|
314
357
|
toolbar
|
|
315
|
-
.querySelectorAll(
|
|
316
|
-
'[data-trix-attribute="b4umDiv"], [data-trix-attribute="section"], [data-trix-attribute="article"]'
|
|
317
|
-
)
|
|
358
|
+
.querySelectorAll('[data-trix-attribute="section"], [data-trix-attribute="article"], [data-trix-attribute="div"]')
|
|
318
359
|
.forEach((button) => {
|
|
319
360
|
if (button.dataset.b4umContainerHandler === 'true') return
|
|
320
361
|
|
|
@@ -334,9 +375,7 @@ function installContainerButtonHandlers(editorElement, toolbar) {
|
|
|
334
375
|
event.preventDefault()
|
|
335
376
|
event.stopImmediatePropagation()
|
|
336
377
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
insertContainer(editor, tagName)
|
|
378
|
+
insertContainer(editor, attributeName)
|
|
340
379
|
},
|
|
341
380
|
true
|
|
342
381
|
)
|
|
@@ -447,15 +486,6 @@ function containerButtonsTemplate() {
|
|
|
447
486
|
<span
|
|
448
487
|
class="trix-button-group trix-button-group--container-tools"
|
|
449
488
|
data-trix-button-group="container-tools">
|
|
450
|
-
<button
|
|
451
|
-
type="button"
|
|
452
|
-
class="trix-button"
|
|
453
|
-
data-trix-attribute="b4umDiv"
|
|
454
|
-
title="DIV"
|
|
455
|
-
aria-label="DIV"
|
|
456
|
-
tabindex="-1">
|
|
457
|
-
div
|
|
458
|
-
</button>
|
|
459
489
|
<button
|
|
460
490
|
type="button"
|
|
461
491
|
class="trix-button"
|
|
@@ -474,6 +504,15 @@ function containerButtonsTemplate() {
|
|
|
474
504
|
tabindex="-1">
|
|
475
505
|
article
|
|
476
506
|
</button>
|
|
507
|
+
<button
|
|
508
|
+
type="button"
|
|
509
|
+
class="trix-button"
|
|
510
|
+
data-trix-attribute="div"
|
|
511
|
+
title="DIV"
|
|
512
|
+
aria-label="DIV"
|
|
513
|
+
tabindex="-1">
|
|
514
|
+
div
|
|
515
|
+
</button>
|
|
477
516
|
</span>
|
|
478
517
|
`
|
|
479
518
|
}
|
|
@@ -151,16 +151,49 @@ module B4um
|
|
|
151
151
|
|
|
152
152
|
resource_name = name.underscore
|
|
153
153
|
|
|
154
|
+
preview_length =
|
|
155
|
+
'local_assigns\.fetch\(:preview_length,\s*160\)'
|
|
156
|
+
|
|
157
|
+
resource_attribute =
|
|
158
|
+
"#{Regexp.escape(resource_name)}\\.#{Regexp.escape(attribute)}"
|
|
159
|
+
|
|
160
|
+
rich_text_pattern =
|
|
161
|
+
/truncate\(\s*#{resource_attribute},\s*length:\s*#{preview_length}\s*\)/m
|
|
162
|
+
|
|
163
|
+
plain_text_pattern =
|
|
164
|
+
/truncate\(\s*#{resource_attribute}\.to_plain_text,\s*length:\s*#{preview_length}\s*\)/m
|
|
165
|
+
|
|
166
|
+
replacement =
|
|
167
|
+
"b4um_rich_text_preview(" \
|
|
168
|
+
"#{resource_name}.#{attribute}, " \
|
|
169
|
+
"length: local_assigns.fetch(:preview_length, 160))"
|
|
170
|
+
|
|
171
|
+
legacy_replacement =
|
|
172
|
+
"b4um_rich_text_preview(" \
|
|
173
|
+
"#{resource_name}.#{attribute}, length: 160)"
|
|
174
|
+
|
|
175
|
+
gsub_file(
|
|
176
|
+
resource_partial_path,
|
|
177
|
+
rich_text_pattern,
|
|
178
|
+
replacement
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
gsub_file(
|
|
182
|
+
resource_partial_path,
|
|
183
|
+
plain_text_pattern,
|
|
184
|
+
replacement
|
|
185
|
+
)
|
|
186
|
+
|
|
154
187
|
gsub_file(
|
|
155
188
|
resource_partial_path,
|
|
156
189
|
"truncate(#{resource_name}.#{attribute}, length: 160)",
|
|
157
|
-
|
|
190
|
+
legacy_replacement
|
|
158
191
|
)
|
|
159
192
|
|
|
160
193
|
gsub_file(
|
|
161
194
|
resource_partial_path,
|
|
162
195
|
"truncate(#{resource_name}.#{attribute}.to_plain_text, length: 160)",
|
|
163
|
-
|
|
196
|
+
legacy_replacement
|
|
164
197
|
)
|
|
165
198
|
|
|
166
199
|
partial = File.read(resource_partial_path)
|
|
@@ -243,7 +276,7 @@ module B4um
|
|
|
243
276
|
unless partial.include?("blob.video?")
|
|
244
277
|
gsub_file(
|
|
245
278
|
action_text_blob_partial_path,
|
|
246
|
-
/<% if blob\.representable\? %>\s
|
|
279
|
+
/<% if blob\.representable\? %>\s*<%= image_tag .+? %>\s*<% end %>/m,
|
|
247
280
|
<<~ERB.chomp
|
|
248
281
|
<% if blob.video? %>
|
|
249
282
|
<%= video_tag rails_blob_path(blob, disposition: "inline"),
|
|
@@ -251,8 +284,8 @@ module B4um
|
|
|
251
284
|
playsinline: true,
|
|
252
285
|
preload: "metadata",
|
|
253
286
|
class: "b4um-rich-text-video" %>
|
|
254
|
-
<% elsif blob.
|
|
255
|
-
|
|
287
|
+
<% elsif blob.image? %>
|
|
288
|
+
<%= image_tag rails_blob_path(blob, disposition: "inline") %>
|
|
256
289
|
<% end %>
|
|
257
290
|
ERB
|
|
258
291
|
)
|
|
@@ -266,13 +299,14 @@ module B4um
|
|
|
266
299
|
action_text_blob_partial_path,
|
|
267
300
|
/<%= image_tag (.+?) %>/,
|
|
268
301
|
<<~ERB.chomp
|
|
269
|
-
<%= image_tag
|
|
302
|
+
<%= image_tag rails_blob_path(blob, disposition: "inline"),
|
|
270
303
|
data: {
|
|
271
304
|
image_lightbox_target: "item",
|
|
272
305
|
image_lightbox_url: rails_blob_path(blob, only_path: true),
|
|
273
306
|
image_lightbox_alt: blob.filename.to_s,
|
|
274
307
|
action: "click->image-lightbox#open"
|
|
275
|
-
}
|
|
308
|
+
},
|
|
309
|
+
class: "lupe" %>
|
|
276
310
|
ERB
|
|
277
311
|
)
|
|
278
312
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: b4um_generators
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Baum
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-04 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: railties
|