brick 1.0.213 → 1.0.214
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +0 -13
- data/lib/brick/extensions.rb +37 -50
- data/lib/brick/frameworks/rails/engine.rb +17 -7
- data/lib/brick/frameworks/rails/form_tags.rb +268 -229
- data/lib/brick/reflect_tables.rb +1 -1
- data/lib/brick/version_number.rb +1 -1
- 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: afed5350a655b7eca570609ff4d15f998ecfe746345c4771b7eb910274df14ae
|
4
|
+
data.tar.gz: 45d5cccb07240fe0995bf812dc4b0cc34d21edd1bb0ab3cf212cd255c8a0f6a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca420002527953167f6ba1112e43d197bea259bf360c89ff92a01c9753c53da1b7060c15cd021c27e6106c4e77e7d3c30f589c89509ae21c2a2fe69d42e6f079
|
7
|
+
data.tar.gz: e6b1fd3745b5c69bfaa9486a4158b7a8efc03c7c43aa884a13f630e9b023e40e0893e1862e6e7fee545049efb46c5b3686cd4f05363d19eb3dfcf0d8f6b1cef3
|
data/lib/brick/config.rb
CHANGED
@@ -429,19 +429,6 @@ module Brick
|
|
429
429
|
end
|
430
430
|
end
|
431
431
|
|
432
|
-
def acts_as_list_cols
|
433
|
-
@mutex.synchronize { @acts_as_list || {} }
|
434
|
-
end
|
435
|
-
|
436
|
-
# Get something like:
|
437
|
-
# { 'on_call_list' => { _brick_default: [:last_name, :first_name] } }
|
438
|
-
# { 'on_call_list' => { _brick_default: :sequence } }
|
439
|
-
def acts_as_list_cols=(position_cols)
|
440
|
-
@mutex.synchronize do
|
441
|
-
@acts_as_list ||= position_cols
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
432
|
def metadata_columns
|
446
433
|
@mutex.synchronize { @metadata_columns ||= ['created_at', 'updated_at', 'deleted_at'] }
|
447
434
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -71,15 +71,24 @@ module ActiveRecord
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def real_model(params)
|
74
|
-
if params && (
|
75
|
-
|
74
|
+
if params && ((sub_name = params.fetch(inheritance_column, nil)).present? ||
|
75
|
+
(sub_name = params[name.underscore]&.fetch(inheritance_column, nil)))
|
76
|
+
sub_name = sub_name.first if sub_name.is_a?(Array) # Support the params style that gets returned from #_brick_querying
|
76
77
|
# Make sure the chosen model is really the same or a subclass of this model
|
77
|
-
|
78
|
+
return self if sub_name.blank?
|
79
|
+
|
80
|
+
(possible_model = sub_name.constantize) <= self ? possible_model : self
|
78
81
|
else
|
79
82
|
self
|
80
83
|
end
|
81
84
|
end
|
82
85
|
|
86
|
+
# Accommodate STI
|
87
|
+
def real_singular(params)
|
88
|
+
real_model = real_model(params)
|
89
|
+
[real_model, real_model.name.underscore.split('/').last]
|
90
|
+
end
|
91
|
+
|
83
92
|
def json_column?(col)
|
84
93
|
col.type == :json || ::Brick.config.json_columns[table_name]&.include?(col.name) ||
|
85
94
|
(
|
@@ -196,33 +205,6 @@ module ActiveRecord
|
|
196
205
|
def _brick_monetized_attributes
|
197
206
|
@_brick_monetized_attributes ||= respond_to?(:monetized_attributes) ? monetized_attributes.values : {}
|
198
207
|
end
|
199
|
-
|
200
|
-
# def acts_as_list(aal_cols = nil)
|
201
|
-
# if aal_cols
|
202
|
-
# aal_cols = [aal_cols] unless aal_cols.is_a?(Array)
|
203
|
-
# @acts_as_list_cols = aal_cols.each_with_object([]) do |aal_col, s|
|
204
|
-
# if column_names.include?(aal_col = aal_col.to_s) && !s.include?(aal_col)
|
205
|
-
# s << aal_col
|
206
|
-
# end
|
207
|
-
# end
|
208
|
-
# else
|
209
|
-
# if [:integer, :bigint].include?(columns_hash['position']&.type)
|
210
|
-
# @acts_as_list_cols = ['position']
|
211
|
-
# else
|
212
|
-
# return
|
213
|
-
# end
|
214
|
-
# end
|
215
|
-
# # Override save in order to update neighbours when necessary
|
216
|
-
# alias _brick_save save
|
217
|
-
# def save
|
218
|
-
# # @acts_as_list_cols
|
219
|
-
# # -1
|
220
|
-
# @acts_as_list_cols.each do |aal_col|
|
221
|
-
# binding.pry if (aal_change = changes[aal_col])
|
222
|
-
# end
|
223
|
-
# _brick_save
|
224
|
-
# end
|
225
|
-
# end
|
226
208
|
end
|
227
209
|
|
228
210
|
def self.brick_parse_dsl(join_array = nil, prefix = [], translations = {}, is_polymorphic = false, dsl = nil, emit_dsl = false)
|
@@ -1765,7 +1747,9 @@ class Object
|
|
1765
1747
|
end
|
1766
1748
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
1767
1749
|
code << " self.table_name = '#{self.table_name = matching}'\n" if (inheritable_name || model_name).underscore.pluralize != matching
|
1768
|
-
|
1750
|
+
|
1751
|
+
if (inh_col = relation.fetch(:sti_col, nil) ||
|
1752
|
+
::Brick.config.sti_type_column.find { |_k, v| v.include?(matching) }&.first)
|
1769
1753
|
new_model_class.inheritance_column = inh_col
|
1770
1754
|
code << " self.inheritance_column = '#{inh_col}'\n"
|
1771
1755
|
end
|
@@ -1816,11 +1800,6 @@ class Object
|
|
1816
1800
|
end
|
1817
1801
|
end
|
1818
1802
|
|
1819
|
-
if (sti_col = relation.fetch(:sti_col, nil))
|
1820
|
-
new_model_class.send(:'inheritance_column=', sti_col)
|
1821
|
-
code << " self.inheritance_column = #{sti_col.inspect}\n"
|
1822
|
-
end
|
1823
|
-
|
1824
1803
|
unless is_sti
|
1825
1804
|
fks = relation[:fks] || {}
|
1826
1805
|
# Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
|
@@ -1902,12 +1881,6 @@ class Object
|
|
1902
1881
|
end
|
1903
1882
|
end
|
1904
1883
|
|
1905
|
-
# Apply any acts_as_list things
|
1906
|
-
if (aal_col = ::Brick.config.acts_as_list_cols.fetch(matching, nil))
|
1907
|
-
new_model_class.send(:acts_as_list, aal_col.to_sym)
|
1908
|
-
code << " acts_as_list :#{aal_col}\n"
|
1909
|
-
end
|
1910
|
-
|
1911
1884
|
# Auto-support Ransack if it's present
|
1912
1885
|
if self.respond_to?(:ransackable_attributes)
|
1913
1886
|
def self.ransackable_attributes(auth_object = nil)
|
@@ -2480,13 +2453,15 @@ class Object
|
|
2480
2453
|
code << " @#{plural_table_name}._brick_querying(params, brick_col_names: true)\n"
|
2481
2454
|
code << " end\n"
|
2482
2455
|
|
2483
|
-
|
2456
|
+
# ----------------------------------------------------------------------------------
|
2457
|
+
|
2484
2458
|
if pk.present?
|
2485
2459
|
code << " def show\n"
|
2486
2460
|
code << " #{find_by_name = "find_#{singular_table_name}"}\n"
|
2487
2461
|
code << " end\n"
|
2488
2462
|
self.define_method :show do
|
2489
2463
|
_schema, @_is_show_schema_list = ::Brick.set_db_schema(params)
|
2464
|
+
_, singular_table_name = model.real_singular(params)
|
2490
2465
|
instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
|
2491
2466
|
add_csp_hash("'unsafe-inline'")
|
2492
2467
|
end
|
@@ -2505,7 +2480,8 @@ class Object
|
|
2505
2480
|
send(params_name_sym)
|
2506
2481
|
rescue
|
2507
2482
|
end
|
2508
|
-
|
2483
|
+
real_model, singular_table_name = model.real_singular(params)
|
2484
|
+
new_params ||= real_model.attribute_names.each_with_object({}) do |a, s|
|
2509
2485
|
if (val = params["__#{a}"])
|
2510
2486
|
# val = case new_obj.class.column_for_attribute(a).type
|
2511
2487
|
# when :datetime, :date, :time, :timestamp
|
@@ -2516,7 +2492,7 @@ class Object
|
|
2516
2492
|
s[a] = val
|
2517
2493
|
end
|
2518
2494
|
end
|
2519
|
-
if (new_obj =
|
2495
|
+
if (new_obj = real_model.new(new_params)).respond_to?(:serializable_hash)
|
2520
2496
|
# Convert any Filename objects with nil into an empty string so that #encode can be called on them
|
2521
2497
|
new_obj.serializable_hash.each do |k, v|
|
2522
2498
|
new_obj.send("#{k}=", ::ActiveStorage::Filename.new('')) if v.is_a?(::ActiveStorage::Filename) && !v.instance_variable_get(:@filename)
|
@@ -2542,12 +2518,20 @@ class Object
|
|
2542
2518
|
end
|
2543
2519
|
render json: { result: ::Brick.unexclude_column(table_name, col) }
|
2544
2520
|
else
|
2545
|
-
|
2546
|
-
|
2521
|
+
real_model = model.real_model(params)
|
2522
|
+
singular_table_name = real_model.name.underscore.split('/').last
|
2523
|
+
created_obj = model.send(:new, send(params_name_sym))
|
2524
|
+
if created_obj.respond_to?(inh_col = model.inheritance_column) && created_obj.send(inh_col) == ''
|
2525
|
+
created_obj.send("#{inh_col}=", model.name)
|
2526
|
+
end
|
2527
|
+
created_obj.save
|
2528
|
+
@_lookup_context.instance_variable_set(:@_brick_model, real_model)
|
2547
2529
|
if created_obj.errors.empty?
|
2530
|
+
instance_variable_set("@#{singular_table_name}".to_sym, created_obj)
|
2548
2531
|
index
|
2549
2532
|
render :index
|
2550
2533
|
else # Surface errors to the user in a flash message
|
2534
|
+
instance_variable_set("@#{singular_table_name}".to_sym, created_obj)
|
2551
2535
|
flash.now.alert = (created_obj.errors.errors.map { |err| "<b>#{err.attribute}</b> #{err.message}" }.join(', '))
|
2552
2536
|
new
|
2553
2537
|
render :new
|
@@ -2566,6 +2550,7 @@ class Object
|
|
2566
2550
|
code << " end\n"
|
2567
2551
|
self.define_method :edit do
|
2568
2552
|
_schema, @_is_show_schema_list = ::Brick.set_db_schema(params)
|
2553
|
+
_, singular_table_name = model.real_singular(params)
|
2569
2554
|
instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
|
2570
2555
|
add_csp_hash
|
2571
2556
|
end
|
@@ -2593,6 +2578,7 @@ class Object
|
|
2593
2578
|
# return
|
2594
2579
|
end
|
2595
2580
|
|
2581
|
+
_, singular_table_name = model.real_singular(params)
|
2596
2582
|
instance_variable_set("@#{singular_table_name}".to_sym, (obj = find_obj))
|
2597
2583
|
upd_params = send(params_name_sym)
|
2598
2584
|
json_overrides = ::Brick.config.json_columns&.fetch(table_name, nil)
|
@@ -2679,11 +2665,11 @@ class Object
|
|
2679
2665
|
if is_need_params
|
2680
2666
|
code << " def #{params_name}\n"
|
2681
2667
|
permits_txt = model._brick_find_permits(model, permits = model._brick_all_fields(true))
|
2682
|
-
code << " params.require(:#{
|
2668
|
+
code << " params.require(:#{model.base_class.name.underscore.tr('/', '_')
|
2683
2669
|
}).permit(#{permits_txt.map(&:inspect).join(', ')})\n"
|
2684
2670
|
code << " end\n"
|
2685
2671
|
self.define_method(params_name) do
|
2686
|
-
params.require(
|
2672
|
+
params.require(model.base_class.name.underscore.tr('/', '_').to_sym).permit(permits)
|
2687
2673
|
end
|
2688
2674
|
private params_name
|
2689
2675
|
# Get column names for params from relations[model.table_name][:cols].keys
|
@@ -2978,7 +2964,8 @@ module Brick
|
|
2978
2964
|
else
|
2979
2965
|
inverse_table = [primary_table] if polymorphic_class
|
2980
2966
|
assoc_bt = bts[cnstr_name] = { is_bt: true, fk: fk[2], assoc_name: bt_assoc_name, inverse_table: inverse_table || primary_table }
|
2981
|
-
assoc_bt[:optional] = true if is_optional
|
2967
|
+
assoc_bt[:optional] = true if is_optional ||
|
2968
|
+
(is_optional.nil? && !relations[fk[1]][:cols][fk[2]][3])
|
2982
2969
|
assoc_bt[:polymorphic] = [polymorphic_class] if polymorphic_class
|
2983
2970
|
end
|
2984
2971
|
if is_class
|
@@ -809,6 +809,7 @@ h1, h3 {
|
|
809
809
|
background-repeat: no-repeat;
|
810
810
|
background-size: 100% 100%;
|
811
811
|
width: 28px;
|
812
|
+
height: 32px;
|
812
813
|
cursor: pointer;
|
813
814
|
}
|
814
815
|
#mermaidErd {
|
@@ -942,10 +943,10 @@ table.shadow > tbody > tr {
|
|
942
943
|
table tbody tr:nth-of-type(even) {
|
943
944
|
background-color: #f3f3f3;
|
944
945
|
}
|
945
|
-
table tbody tr:nth-of-type(even) .
|
946
|
+
table tbody tr:nth-of-type(even) .alternating-gray {
|
946
947
|
background-color: #fff;
|
947
948
|
}
|
948
|
-
table tbody tr:nth-of-type(odd) .
|
949
|
+
table tbody tr:nth-of-type(odd) .alternating-gray {
|
949
950
|
background-color: #f3f3f3;
|
950
951
|
}
|
951
952
|
|
@@ -991,6 +992,11 @@ a.big-arrow {
|
|
991
992
|
background-color: red;
|
992
993
|
color: white;
|
993
994
|
}
|
995
|
+
.brick-note {
|
996
|
+
font-size: 0.7em;
|
997
|
+
color: #A0FFA0;
|
998
|
+
max-width: 0;
|
999
|
+
}
|
994
1000
|
|
995
1001
|
#revertTemplate {
|
996
1002
|
display: none;
|
@@ -1336,8 +1342,7 @@ end
|
|
1336
1342
|
end}
|
1337
1343
|
%>
|
1338
1344
|
|
1339
|
-
#{"<hr><%=
|
1340
|
-
obj_name}\", #{new_path_name}, { class: '__brick' }) if respond_to?(:#{new_path_name}) %>" unless @_brick_model.is_view?}
|
1345
|
+
#{"<hr><%= link_to_brick(model, new: true, class: '__brick') %>" unless @_brick_model.is_view?}
|
1341
1346
|
#{script}
|
1342
1347
|
</body>
|
1343
1348
|
</html>
|
@@ -1437,7 +1442,7 @@ end
|
|
1437
1442
|
<head>
|
1438
1443
|
#{css}
|
1439
1444
|
<title><%=
|
1440
|
-
base_model = (model = (obj = @#{obj_name})
|
1445
|
+
base_model = (model = (obj = @#{obj_name}).class).base_class
|
1441
1446
|
see_all_path = send(\"#\{base_model._brick_index}_path\")
|
1442
1447
|
#{(inh_col = @_brick_model.inheritance_column).present? &&
|
1443
1448
|
" if obj.respond_to?(:#{inh_col}) && (model_name = @#{obj_name}.#{inh_col}) &&
|
@@ -1445,6 +1450,7 @@ end
|
|
1445
1450
|
see_all_path << \"?#{inh_col}=#\{model_name}\"
|
1446
1451
|
end
|
1447
1452
|
model_name = base_model.name if model_name.is_a?(Numeric)"}
|
1453
|
+
model_name = nil if model_name == ''
|
1448
1454
|
page_title = (\"#\{model_name ||= model.name}: #\{obj&.brick_descrip || controller_name}\")
|
1449
1455
|
%></title>
|
1450
1456
|
</head>
|
@@ -1510,8 +1516,12 @@ end
|
|
1510
1516
|
# path_options = [obj.#{pk}]
|
1511
1517
|
# path_options << { '_brick_schema': } if
|
1512
1518
|
options = {}
|
1513
|
-
|
1514
|
-
|
1519
|
+
options[:url] = if obj.new_record?
|
1520
|
+
link_to_brick(obj.class, path_only: true) # Properly supports STI, but only works for :new
|
1521
|
+
else
|
1522
|
+
path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
|
1523
|
+
options[:url] = send(\"#\{path_helper}_path\".to_sym, obj) if ::Brick.config.path_prefix || (path_helper != obj.class.table_name)
|
1524
|
+
end
|
1515
1525
|
%>
|
1516
1526
|
<br><br>
|
1517
1527
|
|
@@ -6,70 +6,20 @@ module Brick::Rails::FormTags
|
|
6
6
|
# When a relation is not provided, first see if one exists which matches the controller name or
|
7
7
|
# something has turned up in the instance variables.
|
8
8
|
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
9
|
+
klass = relation.klass
|
9
10
|
|
10
11
|
nfc = Brick.config.sidescroll.fetch(relation.table_name, nil)&.fetch(:num_frozen_columns, nil) ||
|
11
12
|
Brick.config.sidescroll.fetch(:num_frozen_columns, nil) ||
|
12
13
|
0
|
13
14
|
|
14
|
-
|
15
|
-
out = +"<div id=\"headerTopContainer\"><table id=\"headerTop\"></table>
|
16
|
-
"
|
17
|
-
klass = relation.klass
|
15
|
+
out = +''
|
18
16
|
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
19
|
-
|
20
|
-
out <<
|
21
|
-
<div id=\"headerButtonBox\">
|
22
|
-
"
|
23
|
-
unless show_row_count == false
|
24
|
-
out << " <div id=\"rowCount\"></div>
|
25
|
-
"
|
26
|
-
end
|
27
|
-
unless show_erd_button == false
|
28
|
-
out << " <div id=\"imgErd\" title=\"Show ERD\"></div>
|
29
|
-
"
|
30
|
-
end
|
31
|
-
if rel && show_in_app_button != false && (in_app = rel.fetch(:existing, nil)&.fetch(:index, nil))
|
32
|
-
begin
|
33
|
-
in_app = send("#{in_app}_path") if in_app.is_a?(Symbol)
|
34
|
-
out << " <td title=\"Show in app\">#{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>
|
35
|
-
"
|
36
|
-
rescue ActionController::UrlGenerationError # Avoid snags like "No route matches {:action=>"index", :controller=>"categories/products"}, missing required keys: [:category_id]"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
if show_avo_button != false && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && klass.name.exclude?('::')
|
40
|
-
out << "
|
41
|
-
<td>#{link_to_brick(
|
42
|
-
::Brick::Rails::AVO_SVG.html_safe,
|
43
|
-
{ index_proc: Proc.new do |_avo_model, relation|
|
44
|
-
path_helper = "resources_#{relation.fetch(:auto_prefixed_schema, nil)}#{klass.model_name.route_key}_path".to_sym
|
45
|
-
::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
|
46
|
-
end,
|
47
|
-
title: "#{klass.name} in Avo" }
|
48
|
-
)}</td>
|
49
|
-
"
|
50
|
-
end
|
51
|
-
|
52
|
-
if show_aa_button != false && Object.const_defined?('ActiveAdmin')
|
53
|
-
ActiveAdmin.application.namespaces.names.each do |ns|
|
54
|
-
out << "
|
55
|
-
<td>#{link_to_brick(
|
56
|
-
::Brick::Rails::AA_PNG.html_safe,
|
57
|
-
{ index_proc: Proc.new do |aa_model, relation|
|
58
|
-
path_helper = "#{ns}_#{relation.fetch(:auto_prefixed_schema, nil)}#{aa_model.model_name.route_key}_path".to_sym
|
59
|
-
send(path_helper) if respond_to?(path_helper)
|
60
|
-
end,
|
61
|
-
title: "#{klass.name} in ActiveAdmin" }
|
62
|
-
)}</td>
|
63
|
-
"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
out << " </div>
|
67
|
-
</div>
|
68
|
-
"
|
17
|
+
if show_header != false
|
18
|
+
out << brick_header(rel, klass, show_row_count, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
|
69
19
|
end
|
70
20
|
|
71
|
-
|
72
|
-
<table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\"#{ " x-num-frozen=\"#{nfc}\"" if nfc.positive? }>
|
21
|
+
# HTML for brick_grid
|
22
|
+
out << "<table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\"#{ " x-num-frozen=\"#{nfc}\"" if nfc.positive? }>
|
73
23
|
<thead><tr>"
|
74
24
|
pk = klass.primary_key || []
|
75
25
|
pk = [pk] unless pk.is_a?(Array)
|
@@ -145,13 +95,13 @@ module Brick::Rails::FormTags
|
|
145
95
|
row_count = 0
|
146
96
|
relation.each do |obj|
|
147
97
|
out << "<tr>\n"
|
148
|
-
out << "<td class=\"col-sticky\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
|
98
|
+
out << "<td class=\"col-sticky alternating-gray\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
|
149
99
|
pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
|
150
100
|
sequence.each_with_index do |col_name, idx|
|
151
101
|
val = obj.attributes[col_name]
|
152
102
|
bt = bts[col_name] || composite_bt_names[col_name]
|
153
103
|
out << '<td'
|
154
|
-
(classes ||= []) << 'col-sticky' if idx < nfc
|
104
|
+
(classes ||= []) << 'col-sticky alternating-gray' if idx < nfc
|
155
105
|
(classes ||= []) << 'dimmed' unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
|
156
106
|
(col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
|
157
107
|
(classes ||= []) << 'right' if val.is_a?(Numeric) && !bt
|
@@ -255,161 +205,14 @@ module Brick::Rails::FormTags
|
|
255
205
|
out << '</tr>'
|
256
206
|
row_count += 1
|
257
207
|
end
|
258
|
-
if rel && (total_row_count = rel.fetch(:rowcount, nil))
|
259
|
-
total_row_count = total_row_count > row_count ? " (out of #{total_row_count})" : nil
|
260
|
-
end
|
261
208
|
out << " </tbody>
|
262
209
|
</table>
|
263
|
-
<script>
|
264
|
-
var rowCount = document.getElementById(\"rowCount\");
|
265
|
-
if (rowCount) rowCount.innerHTML = \"#{pluralize(row_count, "row")}#{total_row_count} \";
|
266
|
-
</script>
|
267
|
-
"
|
268
|
-
|
269
|
-
# Javascript for brick_grid
|
270
|
-
(@_brick_javascripts ||= {})[:grid_scripts] = "
|
271
|
-
var #{table_name}HtColumns;
|
272
|
-
|
273
|
-
// Snag first TR for sticky header
|
274
|
-
var grid = document.getElementById(\"#{table_name}\");
|
275
|
-
#{table_name}HtColumns = grid && [grid.getElementsByTagName(\"TR\")[0]];
|
276
|
-
var headerTop = document.getElementById(\"headerTop\");
|
277
|
-
var headerCols;
|
278
|
-
if (grid) {
|
279
|
-
// COLUMN HEADER AND TABLE CELL HIGHLIGHTING
|
280
|
-
var gridHighHeader = null,
|
281
|
-
gridHighCell = null;
|
282
|
-
grid.addEventListener(\"mouseenter\", gridMove);
|
283
|
-
grid.addEventListener(\"mousemove\", gridMove);
|
284
|
-
grid.addEventListener(\"mouseleave\", function (evt) {
|
285
|
-
if (gridHighCell) gridHighCell.classList.remove(\"highlight\");
|
286
|
-
gridHighCell = null;
|
287
|
-
if (gridHighHeader) gridHighHeader.classList.remove(\"highlight\");
|
288
|
-
gridHighHeader = null;
|
289
|
-
});
|
290
|
-
function gridMove(evt) {
|
291
|
-
var lastHighCell = gridHighCell;
|
292
|
-
gridHighCell = document.elementFromPoint(evt.x, evt.y);
|
293
|
-
while (gridHighCell && gridHighCell.tagName !== \"TD\" && gridHighCell.tagName !== \"TH\")
|
294
|
-
gridHighCell = gridHighCell.parentElement;
|
295
|
-
if (gridHighCell) {
|
296
|
-
if (lastHighCell !== gridHighCell) {
|
297
|
-
gridHighCell.classList.add(\"highlight\");
|
298
|
-
if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
|
299
|
-
}
|
300
|
-
var lastHighHeader = gridHighHeader;
|
301
|
-
if ((gridHighHeader = headerCols[gridHighCell.cellIndex]) && lastHighHeader !== gridHighHeader) {
|
302
|
-
if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
|
303
|
-
if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
|
304
|
-
}
|
305
|
-
}
|
306
|
-
}
|
307
|
-
// // LESS TOUCHY NAVIGATION BACK OR FORWARD IN HISTORY WHEN USING MOUSE WHEEL
|
308
|
-
// grid.addEventListener(\"wheel\", function (evt) {
|
309
|
-
// grid.scrollLeft += evt.deltaX;
|
310
|
-
// document.body.scrollTop += (evt.deltaY * 0.6);
|
311
|
-
// evt.preventDefault();
|
312
|
-
// return false;
|
313
|
-
// });
|
314
|
-
}
|
315
|
-
function setHeaderSizes() {
|
316
|
-
if (grid.clientWidth > window.outerWidth)
|
317
|
-
document.getElementById(\"titleBox\").style.width = grid.clientWidth;
|
318
|
-
// console.log(\"start\");
|
319
|
-
// See if the headerTop is already populated
|
320
|
-
// %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
|
321
|
-
headerTop.innerHTML = \"\"; // %%% Would love to not have to clear it out like this every time! (Currently doing this to support resize events.)
|
322
|
-
var isEmpty = headerTop.childElementCount === 0;
|
323
|
-
var numFixed = parseInt(grid.getAttribute(\"x-num-frozen\")) || 0;
|
324
|
-
var fixedColLefts = [0];
|
325
|
-
|
326
|
-
// Set up proper sizings of sticky column header
|
327
|
-
var node;
|
328
|
-
for (var j = 0; j < #{table_name}HtColumns.length; ++j) {
|
329
|
-
var row = #{table_name}HtColumns[j];
|
330
|
-
var tr = isEmpty ? document.createElement(\"TR\") : headerTop.childNodes[j];
|
331
|
-
tr.innerHTML = row.innerHTML.trim();
|
332
|
-
var curLeft = 0.0;
|
333
|
-
// Match up widths from the original column headers
|
334
|
-
for (var i = 0; i < row.childNodes.length; ++i) {
|
335
|
-
node = row.childNodes[i];
|
336
|
-
if (node.nodeType === 1) {
|
337
|
-
var th = tr.childNodes[i];
|
338
|
-
th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
|
339
|
-
// Add \"left: __px\" style to the fixed-width column THs
|
340
|
-
if (i <= numFixed) {
|
341
|
-
th.style.position = \"sticky\";
|
342
|
-
th.style.backgroundColor = \"#008061\";
|
343
|
-
th.style.zIndex = \"1\";
|
344
|
-
th.style.left = curLeft + \"px\";
|
345
|
-
fixedColLefts.push(curLeft += node.clientWidth);
|
346
|
-
}
|
347
|
-
if (#{pk&.present? ? 'i > 0' : 'true'}) {
|
348
|
-
// Add <span> at the end
|
349
|
-
var span = document.createElement(\"SPAN\");
|
350
|
-
span.className = \"exclude\";
|
351
|
-
span.innerHTML = \"X\";
|
352
|
-
span.addEventListener(\"click\", function (e) {
|
353
|
-
e.stopPropagation();
|
354
|
-
doFetch(\"POST\", {_brick_exclude: this.parentElement.getAttribute(\"x-order\")});
|
355
|
-
});
|
356
|
-
th.appendChild(span);
|
357
|
-
}
|
358
|
-
}
|
359
|
-
}
|
360
|
-
headerCols = tr.childNodes;
|
361
|
-
if (isEmpty) headerTop.appendChild(tr);
|
362
|
-
}
|
363
|
-
// Add \"left: __px\" style to all fixed-width column TDs
|
364
|
-
[...grid.children[1].children].forEach(function (row) {
|
365
|
-
for (var j = 1; j <= numFixed; ++j) {
|
366
|
-
row.children[j].style.left = fixedColLefts[j] + 'px';
|
367
|
-
}
|
368
|
-
});
|
369
|
-
grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
|
370
|
-
// console.log(\"end\");
|
371
|
-
}
|
372
|
-
|
373
|
-
if (headerTop) {
|
374
|
-
onImagesLoaded(function() {
|
375
|
-
setHeaderSizes();
|
376
|
-
});
|
377
|
-
window.addEventListener(\"resize\", function(event) {
|
378
|
-
setHeaderSizes();
|
379
|
-
}, true);#{
|
380
|
-
if !klass.is_view? && respond_to?(new_path_name = "new_#{klass._brick_index(:singular)}_path")
|
381
|
-
"
|
382
|
-
var headerButtonBox = document.getElementById(\"headerButtonBox\");
|
383
|
-
if (headerButtonBox) {
|
384
|
-
var addNew = document.createElement(\"A\");
|
385
|
-
addNew.id = \"addNew\";
|
386
|
-
addNew.href = \"#{send(new_path_name)}\";
|
387
|
-
addNew.title = \"New #{table_name.singularize}\";
|
388
|
-
addNew.innerHTML = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"#fff\" d=\"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\"/></svg>';
|
389
|
-
headerButtonBox.append(addNew);
|
390
|
-
}
|
391
|
-
"
|
392
|
-
end}
|
393
|
-
}
|
394
|
-
|
395
|
-
function onImagesLoaded(event) {
|
396
|
-
var images = document.getElementsByTagName(\"IMG\");
|
397
|
-
var numLoaded = images.length;
|
398
|
-
for (var i = 0; i < images.length; ++i) {
|
399
|
-
if (images[i].complete)
|
400
|
-
--numLoaded;
|
401
|
-
else {
|
402
|
-
images[i].addEventListener(\"load\", function() {
|
403
|
-
if (--numLoaded <= 0)
|
404
|
-
event();
|
405
|
-
});
|
406
|
-
}
|
407
|
-
}
|
408
|
-
if (numLoaded <= 0)
|
409
|
-
event();
|
410
|
-
}
|
411
210
|
"
|
211
|
+
if rel && (total_row_count = rel.fetch(:rowcount, nil))
|
212
|
+
total_row_count = total_row_count > row_count ? " (out of #{total_row_count})" : nil
|
213
|
+
end
|
412
214
|
|
215
|
+
set_grid_javascript(klass, pk, show_new_button, row_count, total_row_count)
|
413
216
|
out.html_safe
|
414
217
|
end # brick_grid
|
415
218
|
|
@@ -418,7 +221,7 @@ function onImagesLoaded(event) {
|
|
418
221
|
def brick_form_for(obj, options = {}, model = obj.class, bts = {}, pk = (obj.class.primary_key || []))
|
419
222
|
pk = [pk] unless pk.is_a?(Array)
|
420
223
|
pk.map!(&:to_s)
|
421
|
-
form_for(obj.becomes(model), options) do |f|
|
224
|
+
form_for(obj.becomes(model.base_class), options) do |f|
|
422
225
|
out = +'<table class="shadow">'
|
423
226
|
has_fields = false
|
424
227
|
# If it's a new record, set any default polymorphic types
|
@@ -525,28 +328,37 @@ function onImagesLoaded(event) {
|
|
525
328
|
|
526
329
|
# ------------------------------------------
|
527
330
|
# Our cool N:M checkbox constellation editor
|
528
|
-
def brick_constellation(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {}
|
331
|
+
def brick_constellation(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {},
|
332
|
+
show_header: nil, show_erd_button: nil, show_in_app_button: nil, show_new_button: nil, show_avo_button: nil, show_aa_button: nil)
|
333
|
+
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
334
|
+
klass = relation.klass
|
529
335
|
x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
|
530
336
|
|
337
|
+
out = +''
|
338
|
+
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
339
|
+
if show_header != false
|
340
|
+
out << brick_header(rel, klass, false, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
|
341
|
+
end
|
342
|
+
|
531
343
|
# HTML for constellation
|
532
344
|
prefix = options[:prefix]
|
533
|
-
out
|
345
|
+
out << "<form action=\"#{"#{prefix}/" if prefix}brick_constellation\">
|
534
346
|
<table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\">
|
535
|
-
<thead><tr><td
|
536
|
-
"
|
347
|
+
<thead><tr><td class=\"brick-note\">Checkbox changes are saved immediately</td>"
|
537
348
|
# Header row with X axis values
|
349
|
+
# (In order for grid highlighting to function, these TH elements must have no whitespace between them.
|
350
|
+
# In this way the Javascript headerCols array will be set properly.
|
538
351
|
x_list.each do |x_item|
|
539
|
-
out << "
|
540
|
-
"
|
352
|
+
out << "<th>#{x_item.first}</th>"
|
541
353
|
end
|
542
|
-
out << "
|
354
|
+
out << "</tr></thead>
|
543
355
|
<tbody>
|
544
356
|
"
|
545
|
-
obj_path = "#{
|
357
|
+
obj_path = "#{klass._brick_index(:singular)}_path".to_sym
|
546
358
|
link_arrow = link_to('⇛', send(obj_path, '____'), { class: 'big-arrow' })
|
547
|
-
pk_as_array =
|
359
|
+
pk_as_array = klass._pk_as_array
|
548
360
|
y_list.each do |y_item|
|
549
|
-
out << " <tr><th>#{y_item.first}</th>
|
361
|
+
out << " <tr><th class=\"col-sticky\">#{y_item.first}</th>
|
550
362
|
"
|
551
363
|
x_list.each do |x_item|
|
552
364
|
checked = existing.find { |e| e[1] == x_item.last && e[2] == y_item.last }
|
@@ -570,7 +382,7 @@ function onImagesLoaded(event) {
|
|
570
382
|
_this = this;
|
571
383
|
if (this.checked) {
|
572
384
|
var ids = this.value.split(\"_\");
|
573
|
-
doFetch(\"POST\", {modelName: \"#{
|
385
|
+
doFetch(\"POST\", {modelName: \"#{klass.name}\",
|
574
386
|
args: [#{x_axis[1].inspect}, ids[0], #{y_axis[1].inspect}, ids[1]],
|
575
387
|
_brick_action: \"/#{prefix}brick_associate\"},
|
576
388
|
function (p) { // If it returns successfully, create an <a> element
|
@@ -586,7 +398,7 @@ function onImagesLoaded(event) {
|
|
586
398
|
}
|
587
399
|
);
|
588
400
|
} else if (nextSib = this.nextElementSibling) {
|
589
|
-
doFetch(\"DELETE\", {modelName: \"#{
|
401
|
+
doFetch(\"DELETE\", {modelName: \"#{klass.name}\",
|
590
402
|
id: this.getAttribute(\"x-id\"),
|
591
403
|
_brick_action: \"/#{prefix}brick_associate\"},
|
592
404
|
function (p) { // If it returns successfully, remove the an <a> element
|
@@ -599,6 +411,7 @@ function onImagesLoaded(event) {
|
|
599
411
|
</script>
|
600
412
|
</form>
|
601
413
|
"
|
414
|
+
set_grid_javascript(klass, pk_as_array, false)
|
602
415
|
out.html_safe
|
603
416
|
end # brick_constellation
|
604
417
|
|
@@ -606,7 +419,9 @@ function onImagesLoaded(event) {
|
|
606
419
|
# Our cool N:M bezier visualisation
|
607
420
|
# (...... work in progress .......)
|
608
421
|
def brick_bezier(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {})
|
422
|
+
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
609
423
|
x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
|
424
|
+
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
610
425
|
# HTML for constellation
|
611
426
|
# X axis (List on left side)
|
612
427
|
out = +"<table id=\"#{x_axis.first}\" class=\"shadow\">
|
@@ -629,13 +444,222 @@ function onImagesLoaded(event) {
|
|
629
444
|
out.html_safe
|
630
445
|
end # brick_bezier
|
631
446
|
|
447
|
+
# ---------------------------------------------------------------------------------------------------------
|
448
|
+
def brick_header(rel, klass, show_row_count, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
|
449
|
+
out = +"<div id=\"headerTopContainer\"><table id=\"headerTop\"></table>
|
450
|
+
<div id=\"headerTopAddNew\">
|
451
|
+
<div id=\"headerButtonBox\">
|
452
|
+
"
|
453
|
+
unless show_row_count == false
|
454
|
+
out << " <div id=\"rowCount\"></div>
|
455
|
+
"
|
456
|
+
end
|
457
|
+
unless show_erd_button == false
|
458
|
+
out << " <div id=\"imgErd\" title=\"Show ERD\"></div>
|
459
|
+
"
|
460
|
+
end
|
461
|
+
if rel && show_in_app_button != false && (in_app = rel.fetch(:existing, nil)&.fetch(:index, nil))
|
462
|
+
begin
|
463
|
+
in_app = send("#{in_app}_path") if in_app.is_a?(Symbol)
|
464
|
+
out << " <td title=\"Show in app\">#{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>
|
465
|
+
"
|
466
|
+
rescue ActionController::UrlGenerationError # Avoid snags like "No route matches {:action=>"index", :controller=>"categories/products"}, missing required keys: [:category_id]"
|
467
|
+
end
|
468
|
+
end
|
469
|
+
if show_avo_button != false && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && klass.name.exclude?('::')
|
470
|
+
out << "
|
471
|
+
<td>#{link_to_brick(
|
472
|
+
::Brick::Rails::AVO_SVG.html_safe,
|
473
|
+
{ index_proc: Proc.new do |_avo_model, relation|
|
474
|
+
path_helper = "resources_#{relation.fetch(:auto_prefixed_schema, nil)}#{klass.model_name.route_key}_path".to_sym
|
475
|
+
::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
|
476
|
+
end,
|
477
|
+
title: "#{klass.name} in Avo" }
|
478
|
+
)}</td>
|
479
|
+
"
|
480
|
+
end
|
481
|
+
|
482
|
+
if show_aa_button != false && Object.const_defined?('ActiveAdmin')
|
483
|
+
ActiveAdmin.application.namespaces.names.each do |ns|
|
484
|
+
out << "
|
485
|
+
<td>#{link_to_brick(
|
486
|
+
::Brick::Rails::AA_PNG.html_safe,
|
487
|
+
{ index_proc: Proc.new do |aa_model, relation|
|
488
|
+
path_helper = "#{ns}_#{relation.fetch(:auto_prefixed_schema, nil)}#{aa_model.model_name.route_key}_path".to_sym
|
489
|
+
send(path_helper) if respond_to?(path_helper)
|
490
|
+
end,
|
491
|
+
title: "#{rel[:class_name]} in ActiveAdmin" }
|
492
|
+
)}</td>
|
493
|
+
"
|
494
|
+
end
|
495
|
+
end
|
496
|
+
out << " </div>
|
497
|
+
</div>
|
498
|
+
</div>
|
499
|
+
"
|
500
|
+
out
|
501
|
+
end # brick_header
|
502
|
+
|
503
|
+
# -----------------------------------------------------------------------------------------------
|
504
|
+
def set_grid_javascript(klass, pk, show_new_button = nil, row_count = nil, total_row_count = nil)
|
505
|
+
table_name = klass.table_name.split('.').last
|
506
|
+
|
507
|
+
# Javascript for brick_grid and brick_constellation
|
508
|
+
grid_scripts = (@_brick_javascripts ||= {})[:grid_scripts] = +''
|
509
|
+
|
510
|
+
grid_scripts << "
|
511
|
+
// Plunk the row count in now that we know it
|
512
|
+
var rowCount = document.getElementById(\"rowCount\");
|
513
|
+
if (rowCount) rowCount.innerHTML = \"#{pluralize(row_count, "row")}#{total_row_count} \";
|
514
|
+
var #{table_name}HtColumns;
|
515
|
+
" unless row_count.nil?
|
516
|
+
|
517
|
+
grid_scripts << "
|
518
|
+
// Snag first TR for sticky header
|
519
|
+
var grid = document.getElementById(\"#{table_name}\");
|
520
|
+
#{table_name}HtColumns = grid && [grid.getElementsByTagName(\"TR\")[0]];
|
521
|
+
var headerTop = document.getElementById(\"headerTop\");
|
522
|
+
var headerCols;
|
523
|
+
if (grid) {
|
524
|
+
// COLUMN HEADER AND TABLE CELL HIGHLIGHTING
|
525
|
+
var gridHighHeader = null,
|
526
|
+
gridHighCell = null;
|
527
|
+
grid.addEventListener(\"mouseenter\", gridMove);
|
528
|
+
grid.addEventListener(\"mousemove\", gridMove);
|
529
|
+
grid.addEventListener(\"mouseleave\", function (evt) {
|
530
|
+
if (gridHighCell) gridHighCell.classList.remove(\"highlight\");
|
531
|
+
gridHighCell = null;
|
532
|
+
if (gridHighHeader) gridHighHeader.classList.remove(\"highlight\");
|
533
|
+
gridHighHeader = null;
|
534
|
+
});
|
535
|
+
function gridMove(evt) {
|
536
|
+
var lastHighCell = gridHighCell;
|
537
|
+
gridHighCell = document.elementFromPoint(evt.x, evt.y);
|
538
|
+
while (gridHighCell && gridHighCell.tagName !== \"TD\" && gridHighCell.tagName !== \"TH\")
|
539
|
+
gridHighCell = gridHighCell.parentElement;
|
540
|
+
if (gridHighCell) {
|
541
|
+
if (lastHighCell !== gridHighCell) {
|
542
|
+
gridHighCell.classList.add(\"highlight\");
|
543
|
+
if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
|
544
|
+
}
|
545
|
+
var lastHighHeader = gridHighHeader;
|
546
|
+
if ((gridHighHeader = headerCols[gridHighCell.cellIndex]) && lastHighHeader !== gridHighHeader) {
|
547
|
+
if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
|
548
|
+
if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
|
549
|
+
}
|
550
|
+
}
|
551
|
+
}
|
552
|
+
// // Less touchy navigation back or forward in history when using mouse wheel
|
553
|
+
// grid.addEventListener(\"wheel\", function (evt) {
|
554
|
+
// grid.scrollLeft += evt.deltaX;
|
555
|
+
// document.body.scrollTop += (evt.deltaY * 0.6);
|
556
|
+
// evt.preventDefault();
|
557
|
+
// return false;
|
558
|
+
// });
|
559
|
+
}
|
560
|
+
function setHeaderSizes() {
|
561
|
+
if (grid.clientWidth > window.outerWidth)
|
562
|
+
document.getElementById(\"titleBox\").style.width = grid.clientWidth;
|
563
|
+
// console.log(\"start\");
|
564
|
+
// See if the headerTop is already populated
|
565
|
+
// %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
|
566
|
+
headerTop.innerHTML = \"\"; // %%% Would love to not have to clear it out like this every time! (Currently doing this to support resize events.)
|
567
|
+
var isEmpty = headerTop.childElementCount === 0;
|
568
|
+
var numFixed = parseInt(grid.getAttribute(\"x-num-frozen\")) || 0;
|
569
|
+
var fixedColLefts = [0];
|
570
|
+
|
571
|
+
// Set up proper sizings of sticky column header
|
572
|
+
var node;
|
573
|
+
for (var j = 0; j < #{table_name}HtColumns.length; ++j) {
|
574
|
+
var row = #{table_name}HtColumns[j];
|
575
|
+
var tr = isEmpty ? document.createElement(\"TR\") : headerTop.childNodes[j];
|
576
|
+
tr.innerHTML = row.innerHTML.trim();
|
577
|
+
var curLeft = 0.0;
|
578
|
+
// Match up widths from the original column headers
|
579
|
+
for (var i = 0; i < row.childNodes.length; ++i) {
|
580
|
+
node = row.childNodes[i];
|
581
|
+
if (node.nodeType === 1) {
|
582
|
+
var th = tr.childNodes[i];
|
583
|
+
th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
|
584
|
+
// Add \"left: __px\" style to the fixed-width column THs
|
585
|
+
if (i <= numFixed) {
|
586
|
+
th.style.position = \"sticky\";
|
587
|
+
th.style.backgroundColor = \"#008061\";
|
588
|
+
th.style.zIndex = \"1\";
|
589
|
+
th.style.left = curLeft + \"px\";
|
590
|
+
fixedColLefts.push(curLeft += node.clientWidth);
|
591
|
+
}
|
592
|
+
if (#{pk&.present? ? 'i > 0' : 'true'}) {
|
593
|
+
// Add <span> at the end
|
594
|
+
var span = document.createElement(\"SPAN\");
|
595
|
+
span.className = \"exclude\";
|
596
|
+
span.innerHTML = \"X\";
|
597
|
+
span.addEventListener(\"click\", function (e) {
|
598
|
+
e.stopPropagation();
|
599
|
+
doFetch(\"POST\", {_brick_exclude: this.parentElement.getAttribute(\"x-order\")});
|
600
|
+
});
|
601
|
+
th.appendChild(span);
|
602
|
+
}
|
603
|
+
}
|
604
|
+
}
|
605
|
+
headerCols = tr.childNodes;
|
606
|
+
if (isEmpty) headerTop.appendChild(tr);
|
607
|
+
}
|
608
|
+
// Add \"left: __px\" style to all fixed-width column TDs
|
609
|
+
[...grid.children[1].children].forEach(function (row) {
|
610
|
+
for (var j = 1; j <= numFixed; ++j) {
|
611
|
+
row.children[j].style.left = fixedColLefts[j] + 'px';
|
612
|
+
}
|
613
|
+
});
|
614
|
+
grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
|
615
|
+
// console.log(\"end\");
|
616
|
+
}
|
617
|
+
|
618
|
+
if (headerTop) {
|
619
|
+
onImagesLoaded(function() {
|
620
|
+
setHeaderSizes();
|
621
|
+
});
|
622
|
+
window.addEventListener(\"resize\", function(event) {
|
623
|
+
setHeaderSizes();
|
624
|
+
}, true);#{
|
625
|
+
"
|
626
|
+
var headerButtonBox = document.getElementById(\"headerButtonBox\");
|
627
|
+
if (headerButtonBox) {
|
628
|
+
var addNew = document.createElement(\"A\");
|
629
|
+
addNew.id = \"addNew\";
|
630
|
+
addNew.href = \"#{link_to_brick(klass, new: true, path_only: true)}\";
|
631
|
+
addNew.title = \"New #{table_name.singularize}\";
|
632
|
+
addNew.innerHTML = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"#fff\" d=\"M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z\"/></svg>';
|
633
|
+
headerButtonBox.append(addNew);
|
634
|
+
}
|
635
|
+
" unless klass.is_view? || show_new_button == false
|
636
|
+
}
|
637
|
+
}
|
638
|
+
|
639
|
+
function onImagesLoaded(event) {
|
640
|
+
var images = document.getElementsByTagName(\"IMG\");
|
641
|
+
var numLoaded = images.length;
|
642
|
+
for (var i = 0; i < images.length; ++i) {
|
643
|
+
if (images[i].complete)
|
644
|
+
--numLoaded;
|
645
|
+
else {
|
646
|
+
images[i].addEventListener(\"load\", function() {
|
647
|
+
if (--numLoaded <= 0)
|
648
|
+
event();
|
649
|
+
});
|
650
|
+
}
|
651
|
+
}
|
652
|
+
if (numLoaded <= 0)
|
653
|
+
event();
|
654
|
+
}
|
655
|
+
"
|
656
|
+
end
|
657
|
+
|
658
|
+
# -------------------------------------
|
632
659
|
def _n_m_prep(relation, x_axis, y_axis)
|
633
|
-
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
634
660
|
# Just find the first two BT things at this point
|
635
661
|
|
636
662
|
klass = relation.klass
|
637
|
-
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
638
|
-
# fk_assocs = rel[:fks].map { |k, fk| [fk[:assoc_name], fk[:fk]] }
|
639
663
|
fk_assocs = klass.reflect_on_all_associations.each_with_object([]) do |assoc, s|
|
640
664
|
s << [assoc.name.to_s, assoc.foreign_key, assoc.klass] if assoc.belongs_to?
|
641
665
|
end
|
@@ -690,8 +714,10 @@ function onImagesLoaded(event) {
|
|
690
714
|
kwargs[:visited] = {}
|
691
715
|
end
|
692
716
|
|
693
|
-
|
694
|
-
|
717
|
+
unless (is_path_only = kwargs.delete(:path_only))
|
718
|
+
text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
|
719
|
+
text = text.call if text.is_a?(Proc)
|
720
|
+
end
|
695
721
|
klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
|
696
722
|
args.first.is_a?(ActiveRecord::Base) ||
|
697
723
|
args.first.is_a?(Class)) &&
|
@@ -739,6 +765,8 @@ function onImagesLoaded(event) {
|
|
739
765
|
filter_parts << "#{whr.first}=#{whr.last}" if whr.last && !whr.last.is_a?(Array)
|
740
766
|
end
|
741
767
|
klass_or_obj = klass_or_obj.klass
|
768
|
+
end
|
769
|
+
if klass_or_obj.is_a?(Class) && klass_or_obj <= ActiveRecord::Base
|
742
770
|
type_col = klass_or_obj.inheritance_column
|
743
771
|
if klass_or_obj.column_names.include?(type_col) && klass_or_obj.name != klass_or_obj.base_class.name
|
744
772
|
filter_parts << "#{type_col}=#{klass_or_obj.name}"
|
@@ -750,11 +778,22 @@ function onImagesLoaded(event) {
|
|
750
778
|
relation = ::Brick.relations.fetch(rel_name || klass.table_name, nil)
|
751
779
|
if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
|
752
780
|
(klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
|
753
|
-
|
754
|
-
|
781
|
+
if kwargs.delete(:new)
|
782
|
+
path = (proc = kwargs[:new_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(:singular, '/', relation, true), action: :new)}#{filter}"
|
783
|
+
return path if is_path_only
|
784
|
+
|
785
|
+
lt_args = [text || "New #{klass_or_obj.name}", path]
|
786
|
+
else
|
787
|
+
path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/', relation, true), action: :index)}#{filter}"
|
788
|
+
return path if is_path_only
|
789
|
+
|
790
|
+
lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
|
791
|
+
end
|
755
792
|
else
|
756
793
|
# If there are multiple incoming parameters then last one is probably the actual ID, and first few might be some nested tree of stuff leading up to it
|
757
794
|
path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.class.base_class._brick_index(nil, '/', relation, true), action: :show, id: klass_or_obj)}#{filter}"
|
795
|
+
return path if is_path_only
|
796
|
+
|
758
797
|
lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
|
759
798
|
end
|
760
799
|
kwargs.delete(:visited)
|
data/lib/brick/reflect_tables.rb
CHANGED
@@ -341,7 +341,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
341
341
|
fk[4].downcase! if fk[4] =~ /^[A-Z0-9_]+$/
|
342
342
|
fk[2] = connection.send(:oracle_downcase, fk[2])
|
343
343
|
end
|
344
|
-
::Brick._add_bt_and_hm(fk, relations)
|
344
|
+
::Brick._add_bt_and_hm(fk, relations, nil, nil)
|
345
345
|
end
|
346
346
|
kcus = nil # Allow this large item to be garbage collected
|
347
347
|
end
|
data/lib/brick/version_number.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.214
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|