glib-web 4.2.0 → 4.4.0

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/glib/json/new_dynamic_text.rb +0 -0
  3. data/app/controllers/glib/glib_direct_uploads_controller.rb +1 -1
  4. data/app/controllers/glib/home_controller.rb +5 -0
  5. data/app/helpers/glib/app_feature_support_helper.rb +0 -0
  6. data/app/helpers/glib/dynamic_texts_helper.rb +0 -0
  7. data/app/helpers/glib/json_ui/abstract_builder.rb +1 -1
  8. data/app/helpers/glib/json_ui/action_builder/browsers.rb +12 -0
  9. data/app/helpers/glib/json_ui/action_builder/components.rb +7 -1
  10. data/app/helpers/glib/json_ui/action_builder/fields.rb +6 -0
  11. data/app/helpers/glib/json_ui/action_builder/logics.rb +30 -0
  12. data/app/helpers/glib/json_ui/action_builder/sheets.rb +0 -0
  13. data/app/helpers/glib/json_ui/action_builder/storage_items.rb +23 -0
  14. data/app/helpers/glib/json_ui/action_builder.rb +0 -18
  15. data/app/helpers/glib/json_ui/analytics_helper.rb +0 -0
  16. data/app/helpers/glib/json_ui/generic_builders.rb +0 -0
  17. data/app/helpers/glib/json_ui/menu_builder.rb +1 -1
  18. data/app/helpers/glib/json_ui/view_builder/charts.rb +3 -2
  19. data/app/helpers/glib/json_ui/view_builder/fields.rb +20 -22
  20. data/app/helpers/glib/json_ui/view_builder/panels.rb +26 -11
  21. data/app/helpers/glib/json_ui/view_builder.rb +20 -8
  22. data/app/models/glib/active_storage/attachment.rb +0 -0
  23. data/app/models/glib/active_storage/blob.rb +0 -0
  24. data/app/models/glib/dummy_job_application.rb +3 -2
  25. data/app/models/glib/dynamic_text_record.rb +0 -0
  26. data/app/models/glib/text.rb +0 -0
  27. data/app/validators/email_typo_validator.rb +0 -0
  28. data/app/validators/email_validator.rb +0 -0
  29. data/app/validators/url_validator.rb +0 -0
  30. data/app/views/json_ui/garage/_nav_menu.json.jbuilder +1 -1
  31. data/app/views/json_ui/garage/actions/dialogs_oauth_post.json.jbuilder +0 -0
  32. data/app/views/json_ui/garage/forms/dynamic_group.json.jbuilder +35 -13
  33. data/app/views/json_ui/garage/forms/dynamic_select_data.json.jbuilder +0 -0
  34. data/app/views/json_ui/garage/forms/file_upload.json.jbuilder +2 -2
  35. data/app/views/json_ui/garage/forms/generic_post.json.jbuilder +0 -0
  36. data/app/views/json_ui/garage/forms/selects.json.jbuilder +39 -5
  37. data/app/views/json_ui/garage/forms/show_hide.json.jbuilder +16 -5
  38. data/app/views/json_ui/garage/forms/submission_flow.json.jbuilder +0 -0
  39. data/app/views/json_ui/garage/forms/submission_flow_post.json.jbuilder +0 -0
  40. data/app/views/json_ui/garage/forms/submission_indicator.json.jbuilder +0 -0
  41. data/app/views/json_ui/garage/forms/submission_indicator_post.json.jbuilder +0 -0
  42. data/app/views/json_ui/garage/forms/submit_on_change.json.jbuilder +1 -1
  43. data/app/views/json_ui/garage/forms/text_validation.json.jbuilder +13 -8
  44. data/app/views/json_ui/garage/home/blank.json.jbuilder +0 -0
  45. data/app/views/json_ui/garage/home/slow.json.jbuilder +0 -0
  46. data/app/views/json_ui/garage/lists/autoload_all.json.jbuilder +0 -0
  47. data/app/views/json_ui/garage/lists/autoload_as_needed.json.jbuilder +0 -0
  48. data/app/views/json_ui/garage/lists/chat_ui.json.jbuilder +0 -0
  49. data/app/views/json_ui/garage/lists/fab.json.jbuilder +0 -0
  50. data/app/views/json_ui/garage/notifications/web_socket.json.jbuilder +0 -0
  51. data/app/views/json_ui/garage/pages/custom_style_class.json.jbuilder +0 -0
  52. data/app/views/json_ui/garage/pages/full_width.json.jbuilder +0 -0
  53. data/app/views/json_ui/garage/pages/full_width_height.json.jbuilder +0 -0
  54. data/app/views/json_ui/garage/pages/layout.json.jbuilder +0 -0
  55. data/app/views/json_ui/garage/pages/loading_indicator.json.jbuilder +0 -0
  56. data/app/views/json_ui/garage/pages/nested_scroll.json.jbuilder +0 -0
  57. data/app/views/json_ui/garage/pages/redirect_onload.json.jbuilder +0 -0
  58. data/app/views/json_ui/garage/panels/_styled.json.jbuilder +0 -0
  59. data/app/views/json_ui/garage/panels/card.json.jbuilder +0 -0
  60. data/app/views/json_ui/garage/panels/custom.json.jbuilder +0 -0
  61. data/app/views/json_ui/garage/panels/outlined.json.jbuilder +0 -0
  62. data/app/views/json_ui/garage/services/dynamic_text.json.jbuilder +0 -0
  63. data/app/views/json_ui/garage/services/image.json.jbuilder +0 -0
  64. data/app/views/json_ui/garage/services/index.json.jbuilder +0 -0
  65. data/app/views/json_ui/garage/tables/_autoload_section.json.jbuilder +0 -0
  66. data/app/views/json_ui/garage/tables/autoload_all.json.jbuilder +0 -0
  67. data/app/views/json_ui/garage/tables/autoload_as_needed.json.jbuilder +0 -0
  68. data/app/views/json_ui/garage/tables/export_import.json.jbuilder +0 -0
  69. data/app/views/json_ui/garage/tables/index.json.jbuilder +0 -0
  70. data/app/views/json_ui/garage/test_page/_header.json.jbuilder +14 -0
  71. data/app/views/json_ui/garage/test_page/auto_validate.json.jbuilder +77 -0
  72. data/app/views/json_ui/garage/test_page/dialog.json.jbuilder +38 -0
  73. data/app/views/json_ui/garage/test_page/dialog_open.json.jbuilder +14 -0
  74. data/app/views/json_ui/garage/test_page/form.json.jbuilder +111 -0
  75. data/app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder +63 -0
  76. data/app/views/json_ui/garage/test_page/multiupload.json.jbuilder +65 -0
  77. data/app/views/json_ui/garage/views/calendar_data.json.jbuilder +0 -0
  78. data/app/views/json_ui/garage/views/map_data.json.jbuilder +0 -0
  79. data/app/views/json_ui/garage/views/multimedia.json.jbuilder +0 -0
  80. data/app/views/layouts/json_ui/no_custom.html.erb +0 -0
  81. data/lib/generators/glib/install_generator.rb +0 -0
  82. data/lib/generators/templates/20191017062519_create_texts.rb +0 -0
  83. data/lib/generators/templates/20191024063257_add_scope_to_texts.rb +0 -0
  84. data/lib/generators/templates/20191112095018_add_lang_to_texts.rb +0 -0
  85. data/lib/generators/templates/20191126071051_create_active_storage_tables.active_storage.rb +0 -0
  86. data/lib/generators/templates/database.yml +0 -0
  87. data/lib/generators/templates/dynamic_text.rb +0 -0
  88. data/lib/glib/dynamic_text/config.rb +0 -0
  89. data/lib/glib/dynamic_text.rb +0 -0
  90. data/lib/glib/json_crawler/action_crawler.rb +0 -0
  91. data/lib/glib/json_crawler/action_crawlers/action_http.rb +0 -0
  92. data/lib/glib/json_crawler/action_crawlers/dialogs_alert.rb +0 -0
  93. data/lib/glib/json_crawler/action_crawlers/menu.rb +0 -0
  94. data/lib/glib/json_crawler/action_crawlers/run_multiple.rb +0 -0
  95. data/lib/glib/json_crawler/action_crawlers/windows_open.rb +0 -0
  96. data/lib/glib/json_crawler/coverage.rb +0 -0
  97. data/lib/glib/mailer_tester.rb +1 -1
  98. data/lib/glib/value.rb +0 -0
  99. data/lib/glib/version.rb +0 -0
  100. metadata +12 -3
  101. data/app/views/json_ui/garage/test_page/index.json.jbuilder +0 -120
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: faf0d291c51c0fa43711bc1e563a85693087ac7979784ef50144838227b36f51
4
- data.tar.gz: 99cb79b91991380f89387958d6f7378a5e9dd18db2517b3ee24888369e09d01a
3
+ metadata.gz: 73e075cfe5041ccb4a1f0f6836a9fb5fe294fff00698ad0974a1ef70e34d8bb8
4
+ data.tar.gz: 5ebb6ca2859d77329531ebec64019f655da409adc0a617c6567be6ef3acf9c88
5
5
  SHA512:
6
- metadata.gz: 0cc7697604c728d1999a4da910991c75e6a9750e9492e8c4489971c60e91f0b8233276a9b6b806737229e7b7950bf14609b89d04845d8b5ef0c6f25d966dd19e
7
- data.tar.gz: 925654476843e66088de357fe3c6f94b5f16411a8e6e70552dcb0c0199198dd607d519c117d079543d8678c86731f11beb9b28c5760548760d3a162445ad0a85
6
+ metadata.gz: 6dc692a9e877c4686fb64600813c1b14e6c9926a69a2d9ffaf0c65cb7ac7a48976ad727eea965c024694e79f5b2af9ae945bb013e02f68d0d434fe57f1bb2640
7
+ data.tar.gz: 6e8a5f227bc198f957372c7de511f8e5b65d197419f3bc6a2e461cf894f6c26e3dbfab25859b0b76b22775186f8ed2106d58d309bfc1118ee68c9f33186389e0
@@ -1,5 +1,5 @@
1
1
  module Glib
2
- class GlibDirectUploadsController < ::ActiveStorage::DirectUploadsController
2
+ class GlibDirectUploadsController < ::ActionController::Base
3
3
  before_action :authenticate_user!
4
4
 
5
5
  rescue_from StandardError do |e|
@@ -35,6 +35,11 @@ module Glib
35
35
  .ma-2 {
36
36
  margin: 8px;
37
37
  }
38
+
39
+ .align-right {
40
+ display: flex;
41
+ flex-direction: row-reverse;
42
+ }
38
43
  </style>
39
44
  eos
40
45
 
File without changes
File without changes
@@ -287,7 +287,7 @@ module Glib
287
287
  optional = options[:optional] || []
288
288
 
289
289
  required.each do |req|
290
- raise ArgumentError, "Hash property: '#{req}' is requred" if value[req.to_s].blank?
290
+ raise ArgumentError, "Hash property: '#{req}' is required" if value[req.to_s].blank?
291
291
  end
292
292
 
293
293
  if optional.present?
@@ -0,0 +1,12 @@
1
+ class Glib::JsonUi::ActionBuilder
2
+ # FUTURE
3
+ module Browsers
4
+ class DetectCountry < Action
5
+ action :onDetect
6
+ end
7
+
8
+ class DetectTimeZoe < Action
9
+ action :onDetect
10
+ end
11
+ end
12
+ end
@@ -16,7 +16,7 @@ class Glib::JsonUi::ActionBuilder
16
16
  views :childViews
17
17
  end
18
18
 
19
- # Experimental. Can this be merged with `components_set` ?
19
+ # Consider deprecating this. See `components_set` example in app/views/json_ui/garage/forms/selects.json.jbuilder
20
20
  class Replace < Action
21
21
  string :targetId
22
22
  action :onReplace
@@ -32,6 +32,12 @@ class Glib::JsonUi::ActionBuilder
32
32
  string :targetId
33
33
  hash :data
34
34
  action :onSet
35
+
36
+ def dataBuilder(block)
37
+ json.data do
38
+ block.call(page.sview_builder)
39
+ end
40
+ end
35
41
  end
36
42
  end
37
43
  end
@@ -13,5 +13,11 @@ class Glib::JsonUi::ActionBuilder
13
13
  string :targetId
14
14
  action :onReset
15
15
  end
16
+
17
+
18
+ class GetDynamicGroupEntryValues < Action
19
+ array :names
20
+ action :onGet
21
+ end
16
22
  end
17
23
  end
@@ -7,6 +7,36 @@ class Glib::JsonUi::ActionBuilder
7
7
  hash :data
8
8
  hash :variables
9
9
  action :onSet
10
+ bool :cacheData
11
+ bool :debug
12
+
13
+ def dataBuilder(block)
14
+ json.data do
15
+ block.call(page.sview_builder)
16
+ end
17
+ end
10
18
  end
19
+
20
+ class Run < Action
21
+ hash :condition
22
+ hash :variables
23
+ action :onTrue
24
+ action :onFalse
25
+ end
26
+
27
+ # Under consideration, likely not a good idea.
28
+ # class Append < Action
29
+ # string :targetId
30
+ # hash :condition
31
+ # string :prop # Property name, e.g. rows, childViews
32
+ # array :data
33
+ # action :onAppend
34
+
35
+ # def dataBuilder(block)
36
+ # json.data do
37
+ # block.call(page.view_builder)
38
+ # end
39
+ # end
40
+ # end
11
41
  end
12
42
  end
File without changes
@@ -0,0 +1,23 @@
1
+ class Glib::JsonUi::ActionBuilder
2
+ # FUTURE
3
+ module StorageItems
4
+ class Set < Action
5
+ string :key
6
+ string :value
7
+ action :onSet
8
+ end
9
+
10
+ class Get < Action
11
+ string :key
12
+ action :onGet
13
+ end
14
+
15
+ class Remove < Action
16
+ action :onRemove
17
+ end
18
+
19
+ class Clear < Action
20
+ action :onClear
21
+ end
22
+ end
23
+ end
@@ -157,24 +157,6 @@ module Glib
157
157
  # class Clear < Action
158
158
  # end
159
159
  end
160
-
161
- # FUTURE
162
-
163
- module Data
164
- class Save < Action
165
- string :key
166
- string :value
167
- action :onSave
168
- end
169
-
170
- class Remove < Action
171
- end
172
-
173
- class Clear < Action
174
- end
175
- end
176
-
177
- ###
178
160
  end
179
161
  end
180
162
  end
File without changes
File without changes
@@ -8,7 +8,7 @@ module Glib
8
8
  class MenuItem < JsonUiElement
9
9
  include Builder::MouseEvents
10
10
 
11
- hash :analytics
11
+ hash :analytics, optional: [:featured, :disabled]
12
12
  end
13
13
 
14
14
  class Button < MenuItem
@@ -43,8 +43,9 @@ class Glib::JsonUi::ViewBuilder
43
43
  string :suffix
44
44
  int :min
45
45
  int :max
46
- hash :legend
47
- hash :plugins
46
+ # https://www.chartjs.org/docs/latest/configuration/legend.html
47
+ hash :legend, optional: [:position, :align, :display, :maxHeight, :maxWidth, :fullSize, :reverse, :labels, :rtl, :title]
48
+ hash :plugins, optional: [:datalabels, :centerLabel, :customTooltip]
48
49
  end
49
50
  end
50
51
 
@@ -6,7 +6,6 @@ class Glib::JsonUi::ViewBuilder
6
6
 
7
7
  bool :readOnly
8
8
  bool :disabled
9
- hash :validation
10
9
  bool :disableDirtyCheck
11
10
  action :onChange
12
11
  action :onChangeAndLoad
@@ -17,6 +16,16 @@ class Glib::JsonUi::ViewBuilder
17
16
  { only_path: true }
18
17
  end
19
18
 
19
+ def validation(validation)
20
+ return if validation.blank?
21
+
22
+ if validation[:format].present?
23
+ context.cast_to_js_regex(validation[:format])
24
+ end
25
+
26
+ json.validation validation
27
+ end
28
+
20
29
  def label(label)
21
30
  @label = label
22
31
  end
@@ -127,7 +136,7 @@ class Glib::JsonUi::ViewBuilder
127
136
  end
128
137
 
129
138
  class Timer < Text
130
- hash :actionCable
139
+ # hash :actionCable
131
140
  int :min
132
141
  int :max
133
142
  bool :forward
@@ -168,7 +177,7 @@ class Glib::JsonUi::ViewBuilder
168
177
 
169
178
  class RichText < Text
170
179
  array :images
171
- hash :imageUploader
180
+ hash :imageUploader, required: [:name], optional: [:accepts, :directUploadUrl]
172
181
  # `html` or `markdown`
173
182
  string :produce
174
183
  string :accept
@@ -180,7 +189,7 @@ class Glib::JsonUi::ViewBuilder
180
189
  # bool :readOnly
181
190
  bool :multiple, cache: true
182
191
  # bool :manualEntry
183
- hash :append
192
+ hash :append, optional: [:icon]
184
193
 
185
194
  panels_builder :accessory, :header, :footer
186
195
  end
@@ -269,16 +278,14 @@ class Glib::JsonUi::ViewBuilder
269
278
  # file_rules = { fileType: 'image/*', maxFileSize: 5000 }
270
279
  # file_rules = { fileType: 'video/*', maxFileSize: 50000 }
271
280
  # file_rules = { fileType: 'application/pdf', maxFileSize: 5000 }
272
- hash :accepts
273
281
 
274
- string :directUploadUrl
282
+ include Glib::JsonUi::Upload
283
+
275
284
  string :fileUrl
276
285
  string :fileTitle
277
286
  string :uploadText
278
- hash :placeholderView
279
- hash :infoSpec
280
- string :storagePrefix
281
- hash :metadata
287
+ hash :placeholderView # deprecated
288
+ hash :infoSpec # deprecated
282
289
 
283
290
  def buttonLabels(obj)
284
291
  @buttonLabels = ActiveSupport::HashWithIndifferentAccess.new(obj)
@@ -314,23 +321,14 @@ class Glib::JsonUi::ViewBuilder
314
321
 
315
322
  class MultiUpload < AbstractField
316
323
  include Glib::JsonUi::Default
324
+ include Glib::JsonUi::Upload
317
325
 
318
- # hash :accepts
319
326
  array :files
320
- string :directUploadUrl
321
327
  string :uploadTitle
322
328
  action :onFinishUpload
323
- string :strategy # can be "delegate" or "dropUpload"
324
329
  string :url # http post end point if you don't want to use onFinishUpload
325
-
326
- string :storagePrefix
327
- hash :metadata
328
- string :tagging
329
- hash :tags
330
-
331
330
  required :accepts, :directUploadUrl
332
331
 
333
-
334
332
  def accepts(value)
335
333
  @accepts = value
336
334
  end
@@ -378,7 +376,6 @@ class Glib::JsonUi::ViewBuilder
378
376
 
379
377
  class Sign < AbstractField
380
378
  string :directUploadUrl
381
-
382
379
  required :directUploadUrl
383
380
 
384
381
  # Override
@@ -422,7 +419,8 @@ class Glib::JsonUi::ViewBuilder
422
419
  hash :latitudeField
423
420
  hash :longitudeField
424
421
  hash :zoomField
425
- hash :autocompleteOptions
422
+ # https://developers.google.com/maps/documentation/javascript/reference/places-widget#AutocompleteOptions
423
+ hash :autocompleteOptions, optional: [:bounds, :componentRestrictions, :fields, :strictBounds, :types]
426
424
  end
427
425
 
428
426
  class StripeToken < AbstractField
@@ -125,6 +125,10 @@ class Glib::JsonUi::ViewBuilder
125
125
  self.class.field_validation(@model, prop)
126
126
  end
127
127
 
128
+ def cast_to_js_regex(format_validation)
129
+ self.class.cast_to_js_regex(format_validation)
130
+ end
131
+
128
132
  def self.lookup_error_message(model_name, attribute_name, key)
129
133
  message = I18n.t("activerecord.errors.models.#{model_name}.attributes.#{attribute_name}.#{key}", default: nil) if model_name.present? && attribute_name.present?
130
134
  message ||= I18n.t("activerecord.errors.models.#{model_name}.#{key}", default: nil) if model_name.present?
@@ -134,6 +138,18 @@ class Glib::JsonUi::ViewBuilder
134
138
  message
135
139
  end
136
140
 
141
+ def self.cast_to_js_regex(format_validation)
142
+ if format_validation[:with].instance_of?(Regexp)
143
+ format_validation[:with] = JsRegex.new(format_validation[:with]).source
144
+ end
145
+ if format_validation[:without].instance_of?(Regexp)
146
+ format_validation[:without] = JsRegex.new(format_validation[:without]).source
147
+ end
148
+ if format_validation[:regex].instance_of?(Regexp)
149
+ format_validation[:regex] = JsRegex.new(format_validation[:regex]).source
150
+ end
151
+ end
152
+
137
153
  def self.field_validation(model, prop)
138
154
  validations = {}
139
155
  ignored = ['confirmation', 'comparison', 'uniqueness', 'validates_associated', 'validates_each', 'validates_with']
@@ -157,12 +173,7 @@ class Glib::JsonUi::ViewBuilder
157
173
  'less_than_or_equal_to', 'other_than', 'in', 'odd', 'even'
158
174
  ].inject({}) { |prev, curr| prev.merge(curr => lookup_error_message(model.to_s.underscore, prop, curr)) }
159
175
  when :format
160
- if validations[validator.kind][:with].instance_of?(Regexp)
161
- validations[validator.kind][:with] = JsRegex.new(validations[validator.kind][:with]).source
162
- end
163
- if validations[validator.kind][:without].instance_of?(Regexp)
164
- validations[validator.kind][:without] = JsRegex.new(validations[validator.kind][:without]).source
165
- end
176
+ cast_to_js_regex(validations[validator.kind])
166
177
  validations[validator.kind][:message] ||= lookup_error_message(model.to_s.underscore, prop, 'invalid')
167
178
  when :inclusion
168
179
  validations[validator.kind][:message] ||= lookup_error_message(model.to_s.underscore, prop, 'inclusion')
@@ -250,10 +261,11 @@ class Glib::JsonUi::ViewBuilder
250
261
  string :fieldSubsubtitleName
251
262
 
252
263
  # This can be used to implement "check all" and "uncheck all".
264
+ # deprecated?
253
265
  hash :fieldCheckValueIf
254
266
 
255
267
  hash :phoenixSocket
256
- hash :actionCable
268
+ # hash :actionCable
257
269
 
258
270
  hash :nextPage
259
271
  hash :prevPage
@@ -289,8 +301,8 @@ class Glib::JsonUi::ViewBuilder
289
301
 
290
302
  class Table < View
291
303
  hash :nextPage
292
- hash :export
293
- hash :import
304
+ hash :export, required: [:label, :fileName]
305
+ hash :import, required: [:submitUrl, :paramName]
294
306
  action :onScrollToTop
295
307
  action :onScrollToBottom
296
308
 
@@ -328,6 +340,9 @@ class Glib::JsonUi::ViewBuilder
328
340
  string :align
329
341
  action :onClick
330
342
  views :hoverViews
343
+
344
+ # Experimental
345
+ panels_builder :accessory, :header, :footer
331
346
  end
332
347
 
333
348
  class Column < View
@@ -368,11 +383,11 @@ class Glib::JsonUi::ViewBuilder
368
383
  string :distribution
369
384
  string :align
370
385
  action :onClick
371
- hash :dragSupport
386
+ hash :dragSupport, optional: [:onDrop, :paramNameForFormData, :paramNameForNewIndex]
372
387
  end
373
388
 
374
389
  class Flow < View
375
- hash :innerPadding
390
+ hash :innerPadding, optional: [:top, :right, :bottom, :left, :x, :y]
376
391
  views :childViews
377
392
 
378
393
  required :innerPadding
@@ -1,5 +1,20 @@
1
1
  module Glib
2
2
  module JsonUi
3
+
4
+ module Upload
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ string :directUploadUrl
9
+ hash :accepts, optional: [:fileType, :maxFileSize, :maxFileLength, :maxFileSizeErrorText, :maxFileLengthErrorText]
10
+ string :strategy # can be "delegate" or "dropUpload"
11
+ string :storagePrefix
12
+ hash :metadata
13
+ string :tagging
14
+ hash :tags
15
+ end
16
+ end
17
+
3
18
  class ViewBuilder < AbstractBuilder
4
19
  def initialize(json, page, multiple)
5
20
  super(json, page)
@@ -38,7 +53,7 @@ module Glib
38
53
  length :width
39
54
  length :height
40
55
  color :backgroundColor
41
- hash :padding
56
+ hash :padding, optional: [:top, :right, :bottom, :left, :x, :y, :all]
42
57
  singleton_array :styleClass, :styleClasses
43
58
 
44
59
  hash :showIf
@@ -49,7 +64,7 @@ module Glib
49
64
  bool :displayed
50
65
  bool :submitWhenNotDisplayed
51
66
 
52
- hash :analytics
67
+ hash :analytics, optional: [:featured, :disabled]
53
68
  hash :dragData
54
69
 
55
70
  def self.component_name
@@ -139,7 +154,7 @@ module Glib
139
154
  end
140
155
 
141
156
  class Label < AbstractText
142
- hash :actionCable
157
+ # hash :actionCable
143
158
  # string :format
144
159
  action :onClick
145
160
  end
@@ -315,16 +330,13 @@ module Glib
315
330
  end
316
331
 
317
332
  class TreeView < View
333
+ include Glib::JsonUi::Upload
334
+
318
335
  string :inputName
319
336
  string :url
320
- string :directUploadUrl
321
- hash :accepts
322
337
  string :selected
323
338
  array :items
324
339
  hash :dropData
325
- string :strategy
326
- string :storagePrefix
327
- hash :metadata
328
340
  end
329
341
  end
330
342
  end
File without changes
File without changes
@@ -1,9 +1,10 @@
1
1
  module Glib
2
2
  class DummyJobApplication
3
+ include ActiveModel::Model
3
4
  include ActiveModel::Validations
4
5
  include ActiveModel::AttributeMethods
5
6
 
6
- attr_accessor :name, :age, :position, :words, :accept
7
+ attr_accessor :name, :age, :position, :words, :accept, :pet_you_have
7
8
 
8
9
  validates :name, :position, presence: true
9
10
  validates :accept, acceptance: {}, allow_nil: false
@@ -11,6 +12,6 @@ module Glib
11
12
  validates :age, numericality: { only_integer: true, less_than_or_equal_to: 30, greater_than_or_equal_to: 18 }, allow_blank: true
12
13
  validates :name, format: { with: /Doe\z/, message: 'This job is for person end with Doe' }
13
14
  validates :position, inclusion: { in: ['programmer', 'devops', 'designer'] }, allow_blank: true
14
-
15
+ validates :pet_you_have, exclusion: { in: ['crocodile', 'shark' ], message: 'We dont accept that kind of animal!' }, allow_blank: true
15
16
  end
16
17
  end
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -52,7 +52,7 @@ if local_assigns[:top_nav] || json_ui_app_is_web?
52
52
  end
53
53
 
54
54
  menu.button text: 'Test Page', onClick: ->(action) do
55
- action.windows_open url: json_ui_garage_url(path: 'test_page/index')
55
+ action.windows_open url: json_ui_garage_url(path: 'test_page/form')
56
56
  end
57
57
 
58
58
  menu.divider
@@ -27,20 +27,42 @@ page.form url: json_ui_garage_url(path: 'forms/generic_post'), method: 'post', p
27
27
  template.spacer height: 10
28
28
  template.fields_text width: 'matchParent', name: 'question', label: 'Question', placeholder: 'Question'
29
29
 
30
- options = [ :rating, :yes_no ]
31
- template.fields_select \
32
- width: 'matchParent',
33
- name: 'type',
34
- label: 'Answer Type',
35
- placeholder: 'Answer Type',
36
- options: options.map { |o| { text: o.to_s.humanize, value: o } }
30
+ form.panels_responsive childViews: ->(column) do
31
+ options = [:rating, :yes_no]
32
+ template.fields_select \
33
+ width: 'matchParent',
34
+ name: 'type',
35
+ label: 'Answer Type',
36
+ placeholder: 'Answer Type',
37
+ options: options.map { |o| { text: o.to_s.humanize, value: o } },
38
+ onChange: ->(action) do
39
+ action.components_findClosest view: 'panels/responsive', onFind: ->(find) do
40
+ find.logics_set dataBuilder: ->(set) do
41
+ set.panels_responsive accessory: ->(accessory) do
42
+ accessory.footer childViews: ->(footer) do
43
+ # TODO: Is there a way to only show this if the selected option is "rating".
44
+ # I.e. `showIf: { "==": [{ "var": 'user[evaluation][{{index}}][type]' }, 'rating'] }`
45
+ footer.fields_check \
46
+ width: 'matchParent',
47
+ name: 'enabled',
48
+ label: 'Enable',
49
+ checkValue: '1'
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end, accessory: ->(accessory) do
56
+ accessory.footer childViews: ->(footer) do
57
+ end
58
+ end
37
59
 
38
- template.fields_check \
39
- width: 'matchParent',
40
- name: 'enabled',
41
- label: 'Enable',
42
- checkValue: '1',
43
- showIf: { "==": [{ "var": 'user[evaluation][{{index}}][type]' }, 'rating'] }
60
+ # template.fields_check \
61
+ # width: 'matchParent',
62
+ # name: 'enabled',
63
+ # label: 'Enable',
64
+ # checkValue: '1',
65
+ # showIf: { "==": [{ "var": 'user[evaluation][{{index}}][type]' }, 'rating'] }
44
66
 
45
67
  template.spacer height: 14
46
68
  end
@@ -17,7 +17,7 @@ page.form options.merge(childViews: ->(form) do
17
17
  fileTitle: '1 month ago',
18
18
  placeholderView: { type: 'image', width: 100, height: 75, url: 'https://www.atms.com.au/wp-content/uploads/2019/10/placeholder-1-1024x683.png?x93630' }
19
19
 
20
- rules = { fileType: 'image', maxFileSize: 1, fileTypeErrorText: 'Invalid!', maxFileSizeErrorText: 'Too big!' }
20
+ rules = { fileType: 'image', maxFileSize: 1, maxFileSizeErrorText: 'Too big!' }
21
21
  form.fields_file name: 'user[photo][]', width: 'matchParent', label: 'Avatar', accepts: rules, directUploadUrl: glib_direct_uploads_url,
22
22
  placeholderView: { type: 'avatar', width: 100, height: 100, url: 'data:image/jpeg;base64,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' }
23
23
 
@@ -32,7 +32,7 @@ page.form options.merge(childViews: ->(form) do
32
32
  form.fields_file name: 'user[zip][]', width: 'matchParent', label: 'ZIP Document', accepts: rules, directUploadUrl: glib_direct_uploads_url
33
33
 
34
34
  # TODO
35
- # rules = { fileType: 'image/*', maxFileSize: 1, fileTypeErrorText: 'Invalid!', maxFileSizeErrorText: 'Too big!' }
35
+ # rules = { fileType: 'image/*', maxFileSize: 1, maxFileSizeErrorText: 'Too big!' }
36
36
  # form.fields_multiImage name: 'user[photos][]', width: 'matchParent', label: 'Avatar', accepts: rules, directUploadUrl: glib_direct_uploads_url,
37
37
  # placeholderView: { type: 'avatar', width: 100, height: 100, url: 'data:image/jpeg;base64,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' }
38
38