brick 1.0.213 → 1.0.215
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 +38 -50
- data/lib/brick/frameworks/rails/engine.rb +26 -14
- data/lib/brick/frameworks/rails/form_tags.rb +275 -233
- data/lib/brick/reflect_tables.rb +1 -1
- data/lib/brick/route_mapper.rb +8 -1
- data/lib/brick/version_number.rb +1 -1
- data/lib/generators/brick/install_generator.rb +2 -2
- metadata +2 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53a4013353cb1918fd78cd37988cb934a83be74305a3351ede0082e95ce971db
|
4
|
+
data.tar.gz: 8cee272dceee0c0938675e999f36ad3df9d833d0b11a62c07a595cd1664dd21b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99ad38c47011412ed7d145f4315c8e971ceab2d301d2a84be35471313fcc73686dec7c105aede9ef33b7202c16e50878139e753dd0cc3b0359cf84db5d8ad033
|
7
|
+
data.tar.gz: f3b9d54daeb4d49d3abddf50a4d267a315ea280a4b3f59d812107d25b46d5c13be0fe1f83d518cd6f91e2a248e87bc65dd35d18e3430fc5a44f2fd1bf2e32f4e
|
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,9 @@ 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])
|
2969
|
+
) && ActiveRecord.version >= ::Gem::Version.new('5.0')
|
2982
2970
|
assoc_bt[:polymorphic] = [polymorphic_class] if polymorphic_class
|
2983
2971
|
end
|
2984
2972
|
if is_class
|
@@ -292,9 +292,10 @@ function linkSchemas() {
|
|
292
292
|
if is_2x # Avo 2.x?
|
293
293
|
"::#{class_name}Resource".constantize
|
294
294
|
else # Avo 3.x
|
295
|
-
|
296
|
-
|
297
|
-
|
295
|
+
if ::Avo::BaseResource.constants.exclude?(class_name.to_sym) &&
|
296
|
+
::Avo::Resources.constants.exclude?(class_name.to_sym) &&
|
297
|
+
(klass = Object.const_get(class_name)).is_a?(Class)
|
298
|
+
::Brick.avo_3x_resource(klass, class_name)
|
298
299
|
end
|
299
300
|
end
|
300
301
|
end
|
@@ -809,6 +810,7 @@ h1, h3 {
|
|
809
810
|
background-repeat: no-repeat;
|
810
811
|
background-size: 100% 100%;
|
811
812
|
width: 28px;
|
813
|
+
height: 32px;
|
812
814
|
cursor: pointer;
|
813
815
|
}
|
814
816
|
#mermaidErd {
|
@@ -942,10 +944,10 @@ table.shadow > tbody > tr {
|
|
942
944
|
table tbody tr:nth-of-type(even) {
|
943
945
|
background-color: #f3f3f3;
|
944
946
|
}
|
945
|
-
table tbody tr:nth-of-type(even) .
|
947
|
+
table tbody tr:nth-of-type(even) .alternating-gray {
|
946
948
|
background-color: #fff;
|
947
949
|
}
|
948
|
-
table tbody tr:nth-of-type(odd) .
|
950
|
+
table tbody tr:nth-of-type(odd) .alternating-gray {
|
949
951
|
background-color: #f3f3f3;
|
950
952
|
}
|
951
953
|
|
@@ -991,6 +993,11 @@ a.big-arrow {
|
|
991
993
|
background-color: red;
|
992
994
|
color: white;
|
993
995
|
}
|
996
|
+
.brick-note {
|
997
|
+
font-size: 0.7em;
|
998
|
+
color: #A0FFA0;
|
999
|
+
max-width: 0;
|
1000
|
+
}
|
994
1001
|
|
995
1002
|
#revertTemplate {
|
996
1003
|
display: none;
|
@@ -1060,7 +1067,7 @@ document.querySelectorAll(\"input[type=submit][data-confirm]\").forEach(function
|
|
1060
1067
|
"\nbrickTestSchema = \"#{::Brick.test_schema}\";" if ::Brick.test_schema
|
1061
1068
|
}
|
1062
1069
|
function doFetch(method, payload, success) {
|
1063
|
-
payload.authenticity_token = <%= session[:_csrf_token].inspect.html_safe %>;
|
1070
|
+
payload.authenticity_token = <%= (session[:_csrf_token] || form_authenticity_token).inspect.html_safe %>;
|
1064
1071
|
var action = payload._brick_action || location.href;
|
1065
1072
|
delete payload._brick_action;
|
1066
1073
|
if (!success) {
|
@@ -1317,10 +1324,11 @@ end
|
|
1317
1324
|
self.class.class_exec { include ::Brick::Rails::FormTags } unless respond_to?(:brick_grid)
|
1318
1325
|
|
1319
1326
|
#{# Determine if we should render an N:M representation or the standard "mega_grid"
|
1320
|
-
taa = ::Brick.config.treat_as_associative&.fetch(
|
1327
|
+
taa = ::Brick.config.treat_as_associative&.fetch(table_name, nil)
|
1321
1328
|
options = {}
|
1322
1329
|
options[:prefix] = prefix unless prefix.blank?
|
1323
|
-
if taa.is_a?(String) # Write out a constellation
|
1330
|
+
if taa.is_a?(String) || # Write out a constellation
|
1331
|
+
(taa.is_a?(Array) && (options[:axes] = taa[0..-2]) && (options[:dsl] = taa.last))
|
1324
1332
|
representation = :constellation
|
1325
1333
|
"
|
1326
1334
|
brick_constellation(@#{res_name}, #{options.inspect}, bt_descrip: @_brick_bt_descrip, bts: bts)"
|
@@ -1336,8 +1344,7 @@ end
|
|
1336
1344
|
end}
|
1337
1345
|
%>
|
1338
1346
|
|
1339
|
-
#{"<hr><%=
|
1340
|
-
obj_name}\", #{new_path_name}, { class: '__brick' }) if respond_to?(:#{new_path_name}) %>" unless @_brick_model.is_view?}
|
1347
|
+
#{"<hr><%= link_to_brick(model, new: true, class: '__brick') %>" unless @_brick_model.is_view?}
|
1341
1348
|
#{script}
|
1342
1349
|
</body>
|
1343
1350
|
</html>
|
@@ -1437,7 +1444,7 @@ end
|
|
1437
1444
|
<head>
|
1438
1445
|
#{css}
|
1439
1446
|
<title><%=
|
1440
|
-
base_model = (model = (obj = @#{obj_name})
|
1447
|
+
base_model = (model = (obj = @#{obj_name}).class).base_class
|
1441
1448
|
see_all_path = send(\"#\{base_model._brick_index}_path\")
|
1442
1449
|
#{(inh_col = @_brick_model.inheritance_column).present? &&
|
1443
1450
|
" if obj.respond_to?(:#{inh_col}) && (model_name = @#{obj_name}.#{inh_col}) &&
|
@@ -1445,6 +1452,7 @@ end
|
|
1445
1452
|
see_all_path << \"?#{inh_col}=#\{model_name}\"
|
1446
1453
|
end
|
1447
1454
|
model_name = base_model.name if model_name.is_a?(Numeric)"}
|
1455
|
+
model_name = nil if model_name == ''
|
1448
1456
|
page_title = (\"#\{model_name ||= model.name}: #\{obj&.brick_descrip || controller_name}\")
|
1449
1457
|
%></title>
|
1450
1458
|
</head>
|
@@ -1510,8 +1518,12 @@ end
|
|
1510
1518
|
# path_options = [obj.#{pk}]
|
1511
1519
|
# path_options << { '_brick_schema': } if
|
1512
1520
|
options = {}
|
1513
|
-
|
1514
|
-
|
1521
|
+
options[:url] = if obj.new_record?
|
1522
|
+
link_to_brick(obj.class, path_only: true) # Properly supports STI, but only works for :new
|
1523
|
+
else
|
1524
|
+
path_helper = obj.new_record? ? #{model_name}._brick_index : #{model_name}._brick_index(:singular)
|
1525
|
+
options[:url] = send(\"#\{path_helper}_path\".to_sym, obj) if ::Brick.config.path_prefix || (path_helper != obj.class.table_name)
|
1526
|
+
end
|
1515
1527
|
%>
|
1516
1528
|
<br><br>
|
1517
1529
|
|
@@ -1748,7 +1760,7 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
|
|
1748
1760
|
"
|
1749
1761
|
end
|
1750
1762
|
if representation == :grid
|
1751
|
-
"<script>
|
1763
|
+
inline << "<script>
|
1752
1764
|
<% # Make column headers sort when clicked
|
1753
1765
|
# %%% Create a smart javascript routine which can do this client-side %>
|
1754
1766
|
[... document.getElementsByTagName(\"TH\")].forEach(function (th) {
|
@@ -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,
|
149
|
-
|
98
|
+
out << "<td class=\"col-sticky alternating-gray\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
|
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,32 +328,43 @@ 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
|
335
|
+
if (axes = options[:axes])
|
336
|
+
x_axis, y_axis = axes
|
337
|
+
end
|
529
338
|
x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
|
530
339
|
|
340
|
+
out = +''
|
341
|
+
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
342
|
+
if show_header != false
|
343
|
+
out << brick_header(rel, klass, false, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
|
344
|
+
end
|
345
|
+
|
531
346
|
# HTML for constellation
|
532
347
|
prefix = options[:prefix]
|
533
|
-
out
|
348
|
+
out << "<form action=\"#{"#{prefix}/" if prefix}brick_constellation\">
|
534
349
|
<table id=\"#{table_name = relation.table_name.split('.').last}\" class=\"shadow\">
|
535
|
-
<thead><tr><td
|
536
|
-
"
|
350
|
+
<thead><tr><td class=\"brick-note\">Checkbox changes are saved immediately</td>"
|
537
351
|
# Header row with X axis values
|
352
|
+
# (In order for grid highlighting to function, these TH elements must have no whitespace between them.
|
353
|
+
# In this way the Javascript headerCols array will be set properly.
|
538
354
|
x_list.each do |x_item|
|
539
|
-
out << "
|
540
|
-
"
|
355
|
+
out << "<th>#{x_item.first}</th>"
|
541
356
|
end
|
542
|
-
out << "
|
357
|
+
out << "</tr></thead>
|
543
358
|
<tbody>
|
544
359
|
"
|
545
|
-
obj_path = "#{
|
360
|
+
obj_path = "#{klass._brick_index(:singular)}_path".to_sym
|
546
361
|
link_arrow = link_to('⇛', send(obj_path, '____'), { class: 'big-arrow' })
|
547
|
-
pk_as_array = relation.klass._pk_as_array
|
548
362
|
y_list.each do |y_item|
|
549
|
-
out << " <tr><th>#{y_item.first}</th>
|
363
|
+
out << " <tr><th class=\"col-sticky\">#{y_item.first}</th>
|
550
364
|
"
|
551
365
|
x_list.each do |x_item|
|
552
366
|
checked = existing.find { |e| e[1] == x_item.last && e[2] == y_item.last }
|
553
|
-
item_id =
|
367
|
+
item_id = checked.first.join('%2F') if checked
|
554
368
|
out << " <td><input type=\"checkbox\" name=\"#{table_name}\" #{"x-id=\"#{item_id}\" " if checked
|
555
369
|
}\" value=\"#{x_item.last}_#{y_item.last}\"#{' checked' if checked}>
|
556
370
|
#{link_arrow.gsub('____', item_id) if checked}</td>
|
@@ -570,14 +384,14 @@ function onImagesLoaded(event) {
|
|
570
384
|
_this = this;
|
571
385
|
if (this.checked) {
|
572
386
|
var ids = this.value.split(\"_\");
|
573
|
-
doFetch(\"POST\", {modelName: \"#{
|
387
|
+
doFetch(\"POST\", {modelName: \"#{klass.name}\",
|
574
388
|
args: [#{x_axis[1].inspect}, ids[0], #{y_axis[1].inspect}, ids[1]],
|
575
389
|
_brick_action: \"/#{prefix}brick_associate\"},
|
576
390
|
function (p) { // If it returns successfully, create an <a> element
|
577
391
|
p.text().then(function (response) {
|
578
392
|
var recordId = JSON.parse(response).data;
|
579
393
|
if (recordId) {
|
580
|
-
console.log(_this.getAttribute(\"x-id\"));
|
394
|
+
// console.log(_this.getAttribute(\"x-id\"));
|
581
395
|
var tmp = document.createElement(\"DIV\");
|
582
396
|
tmp.innerHTML = \"#{link_arrow.gsub('"', '\"')}\".replace(\"____\", recordId);
|
583
397
|
_this.parentElement.append(tmp.firstChild);
|
@@ -586,7 +400,7 @@ function onImagesLoaded(event) {
|
|
586
400
|
}
|
587
401
|
);
|
588
402
|
} else if (nextSib = this.nextElementSibling) {
|
589
|
-
doFetch(\"DELETE\", {modelName: \"#{
|
403
|
+
doFetch(\"DELETE\", {modelName: \"#{klass.name}\",
|
590
404
|
id: this.getAttribute(\"x-id\"),
|
591
405
|
_brick_action: \"/#{prefix}brick_associate\"},
|
592
406
|
function (p) { // If it returns successfully, remove the an <a> element
|
@@ -599,6 +413,7 @@ function onImagesLoaded(event) {
|
|
599
413
|
</script>
|
600
414
|
</form>
|
601
415
|
"
|
416
|
+
set_grid_javascript(klass, klass._pk_as_array, false)
|
602
417
|
out.html_safe
|
603
418
|
end # brick_constellation
|
604
419
|
|
@@ -606,7 +421,9 @@ function onImagesLoaded(event) {
|
|
606
421
|
# Our cool N:M bezier visualisation
|
607
422
|
# (...... work in progress .......)
|
608
423
|
def brick_bezier(relation = nil, options = {}, x_axis: nil, y_axis: nil, bt_descrip: nil, bts: {})
|
424
|
+
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
609
425
|
x_axis, x_list, y_axis, y_list, existing = _n_m_prep(relation, x_axis, y_axis)
|
426
|
+
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
610
427
|
# HTML for constellation
|
611
428
|
# X axis (List on left side)
|
612
429
|
out = +"<table id=\"#{x_axis.first}\" class=\"shadow\">
|
@@ -629,13 +446,222 @@ function onImagesLoaded(event) {
|
|
629
446
|
out.html_safe
|
630
447
|
end # brick_bezier
|
631
448
|
|
449
|
+
# ---------------------------------------------------------------------------------------------------------
|
450
|
+
def brick_header(rel, klass, show_row_count, show_erd_button, show_in_app_button, show_avo_button, show_aa_button)
|
451
|
+
out = +"<div id=\"headerTopContainer\"><table id=\"headerTop\"></table>
|
452
|
+
<div id=\"headerTopAddNew\">
|
453
|
+
<div id=\"headerButtonBox\">
|
454
|
+
"
|
455
|
+
unless show_row_count == false
|
456
|
+
out << " <div id=\"rowCount\"></div>
|
457
|
+
"
|
458
|
+
end
|
459
|
+
unless show_erd_button == false
|
460
|
+
out << " <div id=\"imgErd\" title=\"Show ERD\"></div>
|
461
|
+
"
|
462
|
+
end
|
463
|
+
if rel && show_in_app_button != false && (in_app = rel.fetch(:existing, nil)&.fetch(:index, nil))
|
464
|
+
begin
|
465
|
+
in_app = send("#{in_app}_path") if in_app.is_a?(Symbol)
|
466
|
+
out << " <td title=\"Show in app\">#{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>
|
467
|
+
"
|
468
|
+
rescue ActionController::UrlGenerationError # Avoid snags like "No route matches {:action=>"index", :controller=>"categories/products"}, missing required keys: [:category_id]"
|
469
|
+
end
|
470
|
+
end
|
471
|
+
if show_avo_button != false && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && klass.name.exclude?('::')
|
472
|
+
out << "
|
473
|
+
<td>#{link_to_brick(
|
474
|
+
::Brick::Rails::AVO_SVG.html_safe,
|
475
|
+
{ index_proc: Proc.new do |_avo_model, relation|
|
476
|
+
path_helper = "resources_#{relation.fetch(:auto_prefixed_schema, nil)}#{klass.model_name.route_key}_path".to_sym
|
477
|
+
::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
|
478
|
+
end,
|
479
|
+
title: "#{klass.name} in Avo" }
|
480
|
+
)}</td>
|
481
|
+
"
|
482
|
+
end
|
483
|
+
|
484
|
+
if show_aa_button != false && Object.const_defined?('ActiveAdmin')
|
485
|
+
ActiveAdmin.application.namespaces.names.each do |ns|
|
486
|
+
out << "
|
487
|
+
<td>#{link_to_brick(
|
488
|
+
::Brick::Rails::AA_PNG.html_safe,
|
489
|
+
{ index_proc: Proc.new do |aa_model, relation|
|
490
|
+
path_helper = "#{ns}_#{relation.fetch(:auto_prefixed_schema, nil)}#{aa_model.model_name.route_key}_path".to_sym
|
491
|
+
send(path_helper) if respond_to?(path_helper)
|
492
|
+
end,
|
493
|
+
title: "#{rel[:class_name]} in ActiveAdmin" }
|
494
|
+
)}</td>
|
495
|
+
"
|
496
|
+
end
|
497
|
+
end
|
498
|
+
out << " </div>
|
499
|
+
</div>
|
500
|
+
</div>
|
501
|
+
"
|
502
|
+
out
|
503
|
+
end # brick_header
|
504
|
+
|
505
|
+
# -----------------------------------------------------------------------------------------------
|
506
|
+
def set_grid_javascript(klass, pk, show_new_button = nil, row_count = nil, total_row_count = nil)
|
507
|
+
table_name = klass.table_name.split('.').last
|
508
|
+
|
509
|
+
# Javascript for brick_grid and brick_constellation
|
510
|
+
grid_scripts = (@_brick_javascripts ||= {})[:grid_scripts] = +''
|
511
|
+
|
512
|
+
grid_scripts << "
|
513
|
+
// Plunk the row count in now that we know it
|
514
|
+
var rowCount = document.getElementById(\"rowCount\");
|
515
|
+
if (rowCount) rowCount.innerHTML = \"#{pluralize(row_count, "row")}#{total_row_count} \";
|
516
|
+
var #{table_name}HtColumns;
|
517
|
+
" unless row_count.nil?
|
518
|
+
|
519
|
+
grid_scripts << "
|
520
|
+
// Snag first TR for sticky header
|
521
|
+
var grid = document.getElementById(\"#{table_name}\");
|
522
|
+
#{table_name}HtColumns = grid && [grid.getElementsByTagName(\"TR\")[0]];
|
523
|
+
var headerTop = document.getElementById(\"headerTop\");
|
524
|
+
var headerCols;
|
525
|
+
if (grid) {
|
526
|
+
// COLUMN HEADER AND TABLE CELL HIGHLIGHTING
|
527
|
+
var gridHighHeader = null,
|
528
|
+
gridHighCell = null;
|
529
|
+
grid.addEventListener(\"mouseenter\", gridMove);
|
530
|
+
grid.addEventListener(\"mousemove\", gridMove);
|
531
|
+
grid.addEventListener(\"mouseleave\", function (evt) {
|
532
|
+
if (gridHighCell) gridHighCell.classList.remove(\"highlight\");
|
533
|
+
gridHighCell = null;
|
534
|
+
if (gridHighHeader) gridHighHeader.classList.remove(\"highlight\");
|
535
|
+
gridHighHeader = null;
|
536
|
+
});
|
537
|
+
function gridMove(evt) {
|
538
|
+
var lastHighCell = gridHighCell;
|
539
|
+
gridHighCell = document.elementFromPoint(evt.x, evt.y);
|
540
|
+
while (gridHighCell && gridHighCell.tagName !== \"TD\" && gridHighCell.tagName !== \"TH\")
|
541
|
+
gridHighCell = gridHighCell.parentElement;
|
542
|
+
if (gridHighCell) {
|
543
|
+
if (lastHighCell !== gridHighCell) {
|
544
|
+
gridHighCell.classList.add(\"highlight\");
|
545
|
+
if (lastHighCell) lastHighCell.classList.remove(\"highlight\");
|
546
|
+
}
|
547
|
+
var lastHighHeader = gridHighHeader;
|
548
|
+
if ((gridHighHeader = headerCols[gridHighCell.cellIndex]) && lastHighHeader !== gridHighHeader) {
|
549
|
+
if (gridHighHeader) gridHighHeader.classList.add(\"highlight\");
|
550
|
+
if (lastHighHeader) lastHighHeader.classList.remove(\"highlight\");
|
551
|
+
}
|
552
|
+
}
|
553
|
+
}
|
554
|
+
// // Less touchy navigation back or forward in history when using mouse wheel
|
555
|
+
// grid.addEventListener(\"wheel\", function (evt) {
|
556
|
+
// grid.scrollLeft += evt.deltaX;
|
557
|
+
// document.body.scrollTop += (evt.deltaY * 0.6);
|
558
|
+
// evt.preventDefault();
|
559
|
+
// return false;
|
560
|
+
// });
|
561
|
+
}
|
562
|
+
function setHeaderSizes() {
|
563
|
+
if (grid.clientWidth > window.outerWidth)
|
564
|
+
document.getElementById(\"titleBox\").style.width = grid.clientWidth;
|
565
|
+
// console.log(\"start\");
|
566
|
+
// See if the headerTop is already populated
|
567
|
+
// %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
|
568
|
+
headerTop.innerHTML = \"\"; // %%% Would love to not have to clear it out like this every time! (Currently doing this to support resize events.)
|
569
|
+
var isEmpty = headerTop.childElementCount === 0;
|
570
|
+
var numFixed = parseInt(grid.getAttribute(\"x-num-frozen\")) || 0;
|
571
|
+
var fixedColLefts = [0];
|
572
|
+
|
573
|
+
// Set up proper sizings of sticky column header
|
574
|
+
var node;
|
575
|
+
for (var j = 0; j < #{table_name}HtColumns.length; ++j) {
|
576
|
+
var row = #{table_name}HtColumns[j];
|
577
|
+
var tr = isEmpty ? document.createElement(\"TR\") : headerTop.childNodes[j];
|
578
|
+
tr.innerHTML = row.innerHTML.trim();
|
579
|
+
var curLeft = 0.0;
|
580
|
+
// Match up widths from the original column headers
|
581
|
+
for (var i = 0; i < row.childNodes.length; ++i) {
|
582
|
+
node = row.childNodes[i];
|
583
|
+
if (node.nodeType === 1) {
|
584
|
+
var th = tr.childNodes[i];
|
585
|
+
th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
|
586
|
+
// Add \"left: __px\" style to the fixed-width column THs
|
587
|
+
if (i <= numFixed) {
|
588
|
+
th.style.position = \"sticky\";
|
589
|
+
th.style.backgroundColor = \"#008061\";
|
590
|
+
th.style.zIndex = \"1\";
|
591
|
+
th.style.left = curLeft + \"px\";
|
592
|
+
fixedColLefts.push(curLeft += node.clientWidth);
|
593
|
+
}
|
594
|
+
if (#{pk&.present? ? 'i > 0' : 'true'}) {
|
595
|
+
// Add <span> at the end
|
596
|
+
var span = document.createElement(\"SPAN\");
|
597
|
+
span.className = \"exclude\";
|
598
|
+
span.innerHTML = \"X\";
|
599
|
+
span.addEventListener(\"click\", function (e) {
|
600
|
+
e.stopPropagation();
|
601
|
+
doFetch(\"POST\", {_brick_exclude: this.parentElement.getAttribute(\"x-order\")});
|
602
|
+
});
|
603
|
+
th.appendChild(span);
|
604
|
+
}
|
605
|
+
}
|
606
|
+
}
|
607
|
+
headerCols = tr.childNodes;
|
608
|
+
if (isEmpty) headerTop.appendChild(tr);
|
609
|
+
}
|
610
|
+
// Add \"left: __px\" style to all fixed-width column TDs
|
611
|
+
[...grid.children[1].children].forEach(function (row) {
|
612
|
+
for (var j = 1; j <= numFixed; ++j) {
|
613
|
+
row.children[j].style.left = fixedColLefts[j] + 'px';
|
614
|
+
}
|
615
|
+
});
|
616
|
+
grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
|
617
|
+
// console.log(\"end\");
|
618
|
+
}
|
619
|
+
|
620
|
+
if (headerTop) {
|
621
|
+
onImagesLoaded(function() {
|
622
|
+
setHeaderSizes();
|
623
|
+
});
|
624
|
+
window.addEventListener(\"resize\", function(event) {
|
625
|
+
setHeaderSizes();
|
626
|
+
}, true);#{
|
627
|
+
"
|
628
|
+
var headerButtonBox = document.getElementById(\"headerButtonBox\");
|
629
|
+
if (headerButtonBox) {
|
630
|
+
var addNew = document.createElement(\"A\");
|
631
|
+
addNew.id = \"addNew\";
|
632
|
+
addNew.href = \"#{link_to_brick(klass, new: true, path_only: true)}\";
|
633
|
+
addNew.title = \"New #{table_name.singularize}\";
|
634
|
+
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>';
|
635
|
+
headerButtonBox.append(addNew);
|
636
|
+
}
|
637
|
+
" unless klass.is_view? || show_new_button == false
|
638
|
+
}
|
639
|
+
}
|
640
|
+
|
641
|
+
function onImagesLoaded(event) {
|
642
|
+
var images = document.getElementsByTagName(\"IMG\");
|
643
|
+
var numLoaded = images.length;
|
644
|
+
for (var i = 0; i < images.length; ++i) {
|
645
|
+
if (images[i].complete)
|
646
|
+
--numLoaded;
|
647
|
+
else {
|
648
|
+
images[i].addEventListener(\"load\", function() {
|
649
|
+
if (--numLoaded <= 0)
|
650
|
+
event();
|
651
|
+
});
|
652
|
+
}
|
653
|
+
}
|
654
|
+
if (numLoaded <= 0)
|
655
|
+
event();
|
656
|
+
}
|
657
|
+
"
|
658
|
+
end
|
659
|
+
|
660
|
+
# -------------------------------------
|
632
661
|
def _n_m_prep(relation, x_axis, y_axis)
|
633
|
-
relation ||= (instance_variable_get("@#{controller_name}".to_sym) || _brick_resource_from_iv)
|
634
662
|
# Just find the first two BT things at this point
|
635
663
|
|
636
664
|
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
665
|
fk_assocs = klass.reflect_on_all_associations.each_with_object([]) do |assoc, s|
|
640
666
|
s << [assoc.name.to_s, assoc.foreign_key, assoc.klass] if assoc.belongs_to?
|
641
667
|
end
|
@@ -650,8 +676,9 @@ function onImagesLoaded(event) {
|
|
650
676
|
x_axis = fk_assocs.shift unless x_axis
|
651
677
|
puts "FK Leftovers: #{fk_assocs.join(', ')}" unless fk_assocs.empty?
|
652
678
|
|
679
|
+
pk_as_array = klass._pk_as_array
|
653
680
|
existing = relation.each_with_object([]) do |row, s|
|
654
|
-
row_id = row.send(
|
681
|
+
row_id = pk_as_array.map { |pk_part| row.send(pk_part) }
|
655
682
|
if (x_id = row.send(x_axis[1])) && (y_id = row.send(y_axis[1]))
|
656
683
|
s << [row_id, x_id, y_id]
|
657
684
|
end
|
@@ -690,8 +717,10 @@ function onImagesLoaded(event) {
|
|
690
717
|
kwargs[:visited] = {}
|
691
718
|
end
|
692
719
|
|
693
|
-
|
694
|
-
|
720
|
+
unless (is_path_only = kwargs.delete(:path_only))
|
721
|
+
text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
|
722
|
+
text = text.call if text.is_a?(Proc)
|
723
|
+
end
|
695
724
|
klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
|
696
725
|
args.first.is_a?(ActiveRecord::Base) ||
|
697
726
|
args.first.is_a?(Class)) &&
|
@@ -739,6 +768,8 @@ function onImagesLoaded(event) {
|
|
739
768
|
filter_parts << "#{whr.first}=#{whr.last}" if whr.last && !whr.last.is_a?(Array)
|
740
769
|
end
|
741
770
|
klass_or_obj = klass_or_obj.klass
|
771
|
+
end
|
772
|
+
if klass_or_obj.is_a?(Class) && klass_or_obj <= ActiveRecord::Base
|
742
773
|
type_col = klass_or_obj.inheritance_column
|
743
774
|
if klass_or_obj.column_names.include?(type_col) && klass_or_obj.name != klass_or_obj.base_class.name
|
744
775
|
filter_parts << "#{type_col}=#{klass_or_obj.name}"
|
@@ -750,11 +781,22 @@ function onImagesLoaded(event) {
|
|
750
781
|
relation = ::Brick.relations.fetch(rel_name || klass.table_name, nil)
|
751
782
|
if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
|
752
783
|
(klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
|
753
|
-
|
754
|
-
|
784
|
+
if kwargs.delete(:new)
|
785
|
+
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}"
|
786
|
+
return path if is_path_only
|
787
|
+
|
788
|
+
lt_args = [text || "New #{klass_or_obj.name}", path]
|
789
|
+
else
|
790
|
+
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}"
|
791
|
+
return path if is_path_only
|
792
|
+
|
793
|
+
lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
|
794
|
+
end
|
755
795
|
else
|
756
796
|
# 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
797
|
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}"
|
798
|
+
return path if is_path_only
|
799
|
+
|
758
800
|
lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
|
759
801
|
end
|
760
802
|
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/route_mapper.rb
CHANGED
@@ -62,7 +62,14 @@ module Brick
|
|
62
62
|
end
|
63
63
|
else
|
64
64
|
# puts "#{' ' * ind}resources :#{res_name} #{options.inspect unless options.blank?}"
|
65
|
-
|
65
|
+
if options.key(:only) || !Object.const_defined?('Rails') ||
|
66
|
+
!::Rails.application.config.respond_to?(:api_only) || !::Rails.application.config.api_only
|
67
|
+
resources_options = options
|
68
|
+
else # If it's a Rails API app and no :only options are defined, normally routes for :new and :edit
|
69
|
+
# are not supplied. But by default we may want for them to be shown.
|
70
|
+
resources_options = options.merge(only: [:index, :show, :create, :update, :destroy, :new, :edit])
|
71
|
+
end
|
72
|
+
send(:resources, res_name.to_sym, **resources_options)
|
66
73
|
end
|
67
74
|
end
|
68
75
|
|
data/lib/brick/version_number.rb
CHANGED
@@ -315,8 +315,8 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
|
|
315
315
|
# # the right. Indicating just :bezier is the same as :bezier_full, which shows the full list of all possible
|
316
316
|
# # things that can be associated. :bezier_union shows just the ones that are currently wired up, and
|
317
317
|
# # :bezier_excluded, :bezier_excluded_left, or :bezier_excluded_right shows the ones not yet wired up.
|
318
|
-
# Brick.treat_as_associative = { 'flights' =>
|
319
|
-
# 'crew' =>
|
318
|
+
# Brick.treat_as_associative = { 'flights' => { bezier: ['departure.code', 'arrival.code'] },
|
319
|
+
# 'crew' => { constellation: ['flight', 'personnel', '[used ? [used it!] : []]'] } }
|
320
320
|
|
321
321
|
# # We normally don't show the timestamp columns \"created_at\", \"updated_at\", and \"deleted_at\", and also do
|
322
322
|
# # not consider them when finding associative tables to support an N:M association. (That is, ones that can be a
|
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.215
|
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-
|
11
|
+
date: 2024-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -178,20 +178,6 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: mysql2
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - "~>"
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: '0.5'
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - "~>"
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '0.5'
|
195
181
|
- !ruby/object:Gem::Dependency
|
196
182
|
name: pg
|
197
183
|
requirement: !ruby/object:Gem::Requirement
|