brick 1.0.213 → 1.0.215
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/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
|