glib-web 4.3.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/glib/glib_direct_uploads_controller.rb +1 -1
  3. data/app/controllers/glib/home_controller.rb +5 -0
  4. data/app/helpers/glib/json_ui/abstract_builder.rb +1 -1
  5. data/app/helpers/glib/json_ui/action_builder/browsers.rb +12 -0
  6. data/app/helpers/glib/json_ui/action_builder/fields.rb +6 -0
  7. data/app/helpers/glib/json_ui/action_builder/logics.rb +9 -0
  8. data/app/helpers/glib/json_ui/action_builder/storage_items.rb +23 -0
  9. data/app/helpers/glib/json_ui/action_builder.rb +0 -18
  10. data/app/helpers/glib/json_ui/menu_builder.rb +1 -1
  11. data/app/helpers/glib/json_ui/view_builder/charts.rb +3 -2
  12. data/app/helpers/glib/json_ui/view_builder/fields.rb +20 -22
  13. data/app/helpers/glib/json_ui/view_builder/panels.rb +23 -11
  14. data/app/helpers/glib/json_ui/view_builder.rb +20 -8
  15. data/app/models/glib/dummy_job_application.rb +3 -2
  16. data/app/views/json_ui/garage/_nav_menu.json.jbuilder +1 -1
  17. data/app/views/json_ui/garage/forms/file_upload.json.jbuilder +2 -2
  18. data/app/views/json_ui/garage/forms/show_hide.json.jbuilder +16 -5
  19. data/app/views/json_ui/garage/forms/submit_on_change.json.jbuilder +1 -1
  20. data/app/views/json_ui/garage/forms/text_validation.json.jbuilder +13 -8
  21. data/app/views/json_ui/garage/test_page/_header.json.jbuilder +14 -0
  22. data/app/views/json_ui/garage/test_page/auto_validate.json.jbuilder +77 -0
  23. data/app/views/json_ui/garage/test_page/dialog.json.jbuilder +38 -0
  24. data/app/views/json_ui/garage/test_page/dialog_open.json.jbuilder +14 -0
  25. data/app/views/json_ui/garage/test_page/form.json.jbuilder +111 -0
  26. data/app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder +63 -0
  27. data/app/views/json_ui/garage/test_page/multiupload.json.jbuilder +65 -0
  28. data/lib/glib/mailer_tester.rb +1 -1
  29. metadata +10 -2
  30. 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: 414da1c14521998494a3274989ed77b5db19a4bea80d386aee773aefb3af390d
4
- data.tar.gz: a1b803901c4c2da2c7dc618905c3b5d05c995dffc02cfd84f69be2826d0326af
3
+ metadata.gz: 73e075cfe5041ccb4a1f0f6836a9fb5fe294fff00698ad0974a1ef70e34d8bb8
4
+ data.tar.gz: 5ebb6ca2859d77329531ebec64019f655da409adc0a617c6567be6ef3acf9c88
5
5
  SHA512:
6
- metadata.gz: 17ca569d203c3b13bcae9f666e04693e0dd0a38f31c785be4d9d62d405568db41532c1a5229ef5892d37a1e9d7f21047593798b396d8f3286584d88e5f9e5edf
7
- data.tar.gz: b9a6f5a64dac552532c2152ce28d1db7365e8a32bd71a6d1547f2d712baa84975fa9a594227f0874d0cc8bde6d1f4bc03d23ac72bf8558a3d790ea287379c948
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
 
@@ -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
@@ -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,8 @@ class Glib::JsonUi::ActionBuilder
7
7
  hash :data
8
8
  hash :variables
9
9
  action :onSet
10
+ bool :cacheData
11
+ bool :debug
10
12
 
11
13
  def dataBuilder(block)
12
14
  json.data do
@@ -15,6 +17,13 @@ class Glib::JsonUi::ActionBuilder
15
17
  end
16
18
  end
17
19
 
20
+ class Run < Action
21
+ hash :condition
22
+ hash :variables
23
+ action :onTrue
24
+ action :onFalse
25
+ end
26
+
18
27
  # Under consideration, likely not a good idea.
19
28
  # class Append < Action
20
29
  # string :targetId
@@ -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
@@ -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
 
@@ -371,11 +383,11 @@ class Glib::JsonUi::ViewBuilder
371
383
  string :distribution
372
384
  string :align
373
385
  action :onClick
374
- hash :dragSupport
386
+ hash :dragSupport, optional: [:onDrop, :paramNameForFormData, :paramNameForNewIndex]
375
387
  end
376
388
 
377
389
  class Flow < View
378
- hash :innerPadding
390
+ hash :innerPadding, optional: [:top, :right, :bottom, :left, :x, :y]
379
391
  views :childViews
380
392
 
381
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
@@ -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
@@ -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
@@ -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
 
@@ -349,12 +349,14 @@ page.form \
349
349
  form.spacer height: 20
350
350
  options = [
351
351
  { text: 'Option 1', value: 'option1' },
352
- { text: 'Option 2', value: 'option2' }
352
+ { text: 'Option 2', value: 'option2' },
353
+ { text: 'Option 3', value: 'option3' }
353
354
  ]
354
355
  form.fields_select name: 'user[loadif7]', width: 'matchParent', label: 'Select "show"', options: options, value: '', onChangeAndLoad: ->(action) do
355
356
  action.runMultiple childActions: ->(saction) do
356
357
  saction.logics_set targetId: 'panel7a', conditionalData: { displayed: { "==": [{ "var": 'user[loadif7]' }, 'option1'] } }
357
358
  saction.logics_set targetId: 'panel7b', conditionalData: { displayed: { "==": [{ "var": 'user[loadif7]' }, 'option2'] } }
359
+ saction.logics_set targetId: 'panel7c', conditionalData: { displayed: { "==": [{ "var": 'user[loadif7]' }, 'option3'] } }
358
360
  end
359
361
  end
360
362
 
@@ -363,8 +365,8 @@ page.form \
363
365
  styleClass: 'border-2',
364
366
  width: 'matchParent',
365
367
  padding: { top: 8, right: 8, bottom: 8, left: 8 },
366
- childViews: ->(res) do
367
- res.fields_text name: 'user[loadif_target9]', width: 'matchParent', value: 'Value 7a'
368
+ childViews: ->(vertical) do
369
+ vertical.fields_text name: 'user[0][loadif_target9]', width: 'matchParent', value: 'Value 7a'
368
370
  end
369
371
 
370
372
  form.panels_vertical \
@@ -372,8 +374,17 @@ page.form \
372
374
  styleClass: 'border-2',
373
375
  width: 'matchParent',
374
376
  padding: { top: 8, right: 8, bottom: 8, left: 8 },
375
- childViews: ->(res) do
376
- res.fields_text name: 'user[loadif_target9]', width: 'matchParent', value: 'Value 7b'
377
+ childViews: ->(vertical) do
378
+ vertical.fields_text name: 'user[1][loadif_target9]', width: 'matchParent', value: 'Value 7b'
379
+ end
380
+
381
+ form.panels_vertical \
382
+ id: 'panel7c',
383
+ styleClass: 'border-2',
384
+ width: 'matchParent',
385
+ padding: { top: 8, right: 8, bottom: 8, left: 8 },
386
+ childViews: ->(vertical) do
387
+ vertical.fields_text id: 'target_panel_3', name: 'user[2][loadif_target9]', width: 'matchParent', value: 'Value 7c'
377
388
  end
378
389
 
379
390
  form.spacer height: 20
@@ -107,7 +107,7 @@ page.scroll childViews: ->(scroll) do
107
107
  end
108
108
 
109
109
  form.spacer height: 20
110
- rules = { fileType: 'image', maxFileSize: 5000, fileTypeErrorText: 'Invalid!', maxFileSizeErrorText: 'Too big!' }
110
+ rules = { fileType: 'image', maxFileSize: 5000, maxFileSizeErrorText: 'Too big!' }
111
111
  form.fields_file \
112
112
  name: 'user[photo][]',
113
113
  width: 'matchParent',
@@ -15,29 +15,32 @@ page.form \
15
15
  name: 'user[name]',
16
16
  width: 'matchParent',
17
17
  label: 'Name'
18
-
18
+ form.spacer height: 8
19
19
 
20
20
  form.fields_email \
21
21
  name: 'user[email]',
22
22
  width: 'matchParent',
23
23
  label: 'Email'
24
+ form.spacer height: 8
24
25
 
25
26
  form.fields_url \
26
27
  name: 'user[url]',
27
28
  width: 'matchParent',
28
29
  label: 'URL'
30
+ form.spacer height: 8
29
31
 
30
32
  form.fields_number \
31
33
  prop: :age,
32
34
  name: 'user[age]',
33
35
  width: 'matchParent',
34
36
  label: 'Age'
35
-
37
+ form.spacer height: 8
36
38
 
37
39
  form.fields_phone \
38
40
  name: 'user[phone1]',
39
41
  width: 'matchParent',
40
42
  label: 'Phone field'
43
+ form.spacer height: 8
41
44
 
42
45
  form.fields_phone \
43
46
  name: 'user[phone2]',
@@ -45,6 +48,7 @@ page.form \
45
48
  label: 'Phone field with Australia as the default country',
46
49
  disableAutoDetect: true, # Disable country auto detect by user IP
47
50
  defaultCountry: 'AU' # ISO Country code, see: https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
51
+ form.spacer height: 8
48
52
 
49
53
  form.fields_password \
50
54
  name: 'user[password]',
@@ -52,6 +56,7 @@ page.form \
52
56
  label: 'Password',
53
57
  hint: 'Should contain at least 6 characters',
54
58
  leftIcon: 'lock'
59
+ form.spacer height: 8
55
60
 
56
61
  form.fields_textarea \
57
62
  prop: :words,
@@ -59,6 +64,7 @@ page.form \
59
64
  width: 'matchParent',
60
65
  label: 'Textarea with maxLength',
61
66
  maxLength: 1000
67
+ form.spacer height: 8
62
68
 
63
69
 
64
70
  options = ['programmer', 'devops', 'designer', 'ceo', 'office_boy'].map { |v| { text: v.humanize, value: v } }
@@ -68,11 +74,10 @@ page.form \
68
74
  width: 'matchParent',
69
75
  label: 'Position',
70
76
  options: options
77
+ form.spacer height: 8
71
78
 
72
79
 
73
- form.spacer height: 10
74
80
  form.h4 text: 'Gender'
75
- form.spacer height: 4
76
81
  form.fields_radioGroup \
77
82
  name: 'user[gender]',
78
83
  validation: { required: { message: 'Required' } },
@@ -82,9 +87,9 @@ page.form \
82
87
  group.fields_radio value: 'F', label: 'Female'
83
88
  end
84
89
 
85
- form.spacer height: 10
90
+ form.spacer height: 8
91
+
86
92
  form.h4 text: 'Skills'
87
- form.spacer height: 4
88
93
  form.fields_checkGroup \
89
94
  name: 'user[skills][]',
90
95
  uncheckValue: 1,
@@ -95,10 +100,10 @@ page.form \
95
100
  group.fields_check checkValue: 4, label: 'Mobile Development'
96
101
  end
97
102
 
98
- form.spacer height: 10
103
+ form.spacer height: 8
99
104
  form.fields_check prop: :accept, label: 'Accept terms & condition', name: 'user[accept]'
100
105
 
101
- form.spacer height: 30
106
+ form.spacer height: 8
102
107
  form.fields_submit text: 'Submit'
103
108
  form.fields_submit text: 'Submit (disable if form invalid)', disableIfFormInvalid: true
104
109
  end
@@ -0,0 +1,14 @@
1
+ navs = ['form', 'dialog', 'form_dynamic', 'auto_validate', 'multiupload']
2
+
3
+ view.panels_flow innerPadding: { bottom: 0 }, styleClass: 'align-right', width: 'matchParent', childViews: ->(res) do
4
+ navs.each_with_index do |nav, index|
5
+ if index < navs.size && index != 0
6
+ res.spacer width: 4
7
+ res.label text: '|'
8
+ res.spacer width: 4
9
+ end
10
+ res.label text: nav, onClick: ->(action) do
11
+ action.windows_open url: json_ui_garage_url(path: "test_page/#{nav}")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,77 @@
1
+ json.title 'Test Page (Form)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body childViews: ->(body) do
6
+ render 'json_ui/garage/test_page/header', view: body
7
+
8
+ model = Glib::DummyJobApplication.new(
9
+ name: 'John Deo',
10
+ accept: '0',
11
+ words: '',
12
+ age: 17,
13
+ position: 'ceo',
14
+ pet_you_have: 'crocodile'
15
+ )
16
+
17
+ body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
18
+ res.h2 text: 'Job Application'
19
+ res.spacer height: 8
20
+ res.panels_form \
21
+ url: json_ui_garage_url(path: 'forms/generic_post'),
22
+ method: 'post',
23
+ model: model,
24
+ autoValidate: true,
25
+ childViews: ->(form) do
26
+ form.fields_text prop: :name, label: 'name', name: 'user[name]', width: 'matchParent'
27
+ form.spacer height: 4
28
+
29
+ form.fields_text label: 'say hello', name: 'user[say]', width: 'matchParent', validation: { format: { regex: 'hello', message: 'Say hello!' } }, value: 'sup'
30
+ form.spacer height: 4
31
+
32
+ form.fields_number prop: :age, label: 'age', name: 'user[age]', width: 'matchParent'
33
+ form.spacer height: 4
34
+
35
+ form.fields_number \
36
+ name: 'user[height]',
37
+ label: 'height',
38
+ width: 'matchParent',
39
+ validation: { numericality: { greater_than_or_equal_to: 155, less_than_or_equal_to: 190, message: { greater_than_or_equal_to: 'Too short', less_than_or_equal_to: 'Too long' } } },
40
+ value: 150
41
+ form.spacer height: 4
42
+
43
+ form.fields_textarea prop: :words, label: 'words', name: 'user[words]', width: 'matchParent'
44
+ form.spacer height: 4
45
+
46
+ form.fields_textarea label: 'Tell me story', name: 'user[story]', width: 'matchParent', validation: { length: { minimum: 1, message: { too_short: { one: 'Min %{count} word', other: 'Min %{count} words' } } } }
47
+ form.spacer height: 4
48
+
49
+ options = ['programmer', 'devops', 'designer', 'ceo', 'office_boy'].map { |v| { text: v.humanize, value: v } }
50
+ form.fields_select prop: :position, label: 'postion', name: 'user[position]', width: 'matchParent', options: options
51
+ form.spacer height: 4
52
+
53
+ options2 = ['crocodile', 'cat', 'dog', 'bird', 'shark'].map { |v| { text: v.humanize, value: v } }
54
+ form.fields_select prop: :pet_you_have, label: 'Pet you have', name: 'user[pet_you_have]', width: 'matchParent', options: options2
55
+ form.spacer height: 4
56
+
57
+ form.label text: 'Decision'
58
+ options3 = ['choose me', 'dont choose me', 'whatever']
59
+ form.fields_radioGroup value: 'dont choose me', validation: { exclusion: { in: ['dont choose me'], message: 'dont choose me' } }, childViews: ->(radio) do
60
+ options3.each do |option|
61
+ radio.fields_radio label: option, value: option
62
+ end
63
+ end
64
+ form.spacer height: 4
65
+
66
+ form.fields_check prop: :accept, label: 'accept', name: 'user[accept]', width: 'matchParent', checkValue: '1'
67
+ form.spacer height: 4
68
+
69
+ form.spacer height: 4
70
+ form.fields_submit text: 'submit'
71
+ form.spacer height: 2
72
+ form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
73
+ end
74
+
75
+ res.spacer height: 16
76
+ end
77
+ end
@@ -0,0 +1,38 @@
1
+
2
+
3
+ json.title 'Test Page (Form)'
4
+
5
+ page = json_ui_page json
6
+
7
+ page.body childViews: ->(body) do
8
+ render 'json_ui/garage/test_page/header', view: body
9
+
10
+ body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
11
+ res.h2 text: 'Dialog'
12
+ res.spacer height: 8
13
+ res.button text: 'Dialog updateExisting', onClick: ->(action) do
14
+ action.runMultiple childActions: ->(saction) do
15
+ saction.dialogs_show content: ->(dialog) do
16
+ dialog.body padding: glib_json_padding_body, childViews: ->(sbody) do
17
+ sbody.h1 text: 'Hello world'
18
+ sbody.button text: 'change dialog content', onClick: ->(ssaction) do
19
+ ssaction.dialogs_show updateExisting: true, disableCloseButton: true, content: ->(sdialog) do
20
+ sdialog.body padding: glib_json_padding_body, childViews: ->(ssbody) do
21
+ ssbody.h1 text: 'Hello world (updated)'
22
+ ssbody.spacer height: 8
23
+ ssbody.button text: 'close', onClick: ->(xaction) do
24
+ xaction.dialogs_close
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ res.spacer height: 4
34
+ res.button text: 'Dialog open', onClick: ->(action) do
35
+ action.dialogs_open url: json_ui_garage_url(path: 'test_page/dialog_open')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ page = json_ui_page json
2
+
3
+ page.body padding: glib_json_padding_body, childViews: ->(body) do
4
+ body.panels_responsive width: 'matchParent', childViews: ->(res) do
5
+ res.h2 text: 'Title'
6
+ res.spacer height: 16
7
+ res.button text: 'open', onClick: ->(action) do
8
+ action.dialogs_open url: json_ui_garage_url(path: 'test_page/dialog_open')
9
+ end
10
+ res.button text: 'close', onClick: ->(action) do
11
+ action.dialogs_close
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,111 @@
1
+ json.title 'Test Page (Form)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body childViews: ->(body) do
6
+ render 'json_ui/garage/test_page/header', view: body
7
+
8
+ body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
9
+ res.h2 text: 'Form'
10
+ res.spacer height: 8
11
+ res.panels_form \
12
+ url: json_ui_garage_url(path: 'forms/generic_post'),
13
+ method: 'post',
14
+ childViews: ->(form) do
15
+ form.panels_flow innerPadding: { bottom: 0 }, width: 'matchParent', childViews: ->(hori) do
16
+ hori.button text: 'components/set', onClick: ->(action) do
17
+ action.runMultiple childActions: ->(saction) do
18
+ saction.components_set targetId: 'textarea', data: { value: 'The quick brown fox jumps over the lazy dog' }
19
+ new_options = [{ text: 'Option99', value: 'option99' }]
20
+ ['select', 'chip_group'].each do |id|
21
+ saction.components_set targetId: id, data: { options: new_options, value: ['option99'] }
22
+ end
23
+ end
24
+ end
25
+ hori.spacer width: 4
26
+ hori.button text: 'logics/set', onClick: ->(action) do
27
+ action.logics_set targetId: 'date', conditionalData: { value: { "+": [{ "var": ['user[date]'] }, 60 * 60 * 24 * 3] } }
28
+ end
29
+ hori.spacer width: 4
30
+ hori.button text: 'components/replace', onClick: ->(action) do
31
+ action.runMultiple childActions: ->(saction) do
32
+ saction.components_replace targetId: 'radio_group', newView: ->(view) do
33
+ view.fields_radioGroup value: '', childViews: ->(sview) do
34
+ sview.fields_radio label: 'Option99', value: 'option99'
35
+ end
36
+ end
37
+
38
+ saction.components_replace targetId: 'check_group', newView: ->(view) do
39
+ view.fields_checkGroup value: '', childViews: ->(sview) do
40
+ sview.fields_check label: 'Option99', value: nil, checkValue: 'option99'
41
+ end
42
+ end
43
+ end
44
+ end
45
+ hori.spacer width: 4
46
+ hori.button text: 'hide select', onClick: ->(action) do
47
+ action.components_set targetId: 'select', data: { displayed: false }
48
+ end
49
+ hori.spacer height: 4
50
+ hori.button text: 'hide jack', onClick: ->(action) do
51
+ action.runMultiple childActions: ->(saction) do
52
+ saction.components_set targetId: 'ver', data: { displayed: true }
53
+ saction.components_set targetId: 'ver1', data: { displayed: false }
54
+ end
55
+ end
56
+ hori.spacer height: 4
57
+ hori.button text: 'show jack', onClick: ->(action) do
58
+ action.runMultiple childActions: ->(saction) do
59
+ saction.components_set targetId: 'ver', data: { displayed: false }
60
+ saction.components_set targetId: 'ver1', data: { displayed: true }
61
+ end
62
+ end
63
+ end
64
+
65
+ form.spacer height: 8
66
+ form.hr width: 'matchParent'
67
+ form.spacer height: 8
68
+
69
+ validation = { required: { message: 'Required' } }
70
+ options = ['option1', 'option2', 'option3', 'option4'].map { |option| { 'text'=> option.humanize, 'value' => option } }
71
+
72
+ form.fields_date width: 'matchParent', name: 'user[date]', id: 'date', value: Date.new(2024, 7, 24), validation: validation
73
+ form.hr width: 'matchParent'
74
+ form.fields_select multiple: true, width: 'matchParent', name: 'user[select][]', id: 'select', options: options, value: ['option1', 'option2'], validation: validation
75
+ form.hr width: 'matchParent'
76
+ form.fields_chipGroup width: 'matchParent', name: 'user[chip_group]', id: 'chip_group', options: options, value: ['option2'], validation: validation
77
+ form.hr width: 'matchParent'
78
+ form.fields_radioGroup width: 'matchParent', name: 'user[radio_group]', id: 'radio_group', value: 'option3', validation: validation, childViews: ->(radio) do
79
+ options.each do |option|
80
+ radio.fields_radio label: option['text'], value: option['value']
81
+ end
82
+ end
83
+ form.hr width: 'matchParent'
84
+ check_group_value = ['option3', 'option1']
85
+ form.fields_checkGroup width: 'matchParent', name: 'user[check_group]', id: 'check_group', value: check_group_value, validation: validation, childViews: ->(radio) do
86
+ options.each do |option|
87
+ radio.fields_check label: option['text'], checkValue: option['value']
88
+ end
89
+ end
90
+ form.hr width: 'matchParent'
91
+ form.panels_vertical width: 'matchParent', id: 'ver', childViews: ->(ver) do
92
+ form.fields_text width: 'matchParent', name: 'user[text]', id: 'text', value: 'John Doe', validation: validation.merge(format: { regex: /Doe\z/, message: 'Must end with Doe' })
93
+ end
94
+ form.panels_vertical width: 'matchParent', id: 'ver1', displayed: false, childViews: ->(ver) do
95
+ form.fields_text width: 'matchParent', name: 'user[text]', value: 'Jack Doe', validation: validation.merge(format: { regex: /Doe\z/, message: 'Must end with Doe' })
96
+ end
97
+
98
+ form.hr width: 'matchParent'
99
+
100
+ form.fields_textarea width: 'matchParent', name: 'user[textarea]', id: 'textarea', value: 'Lorem ipsum et dumet bla bla bla...'
101
+
102
+ form.hr width: 'matchParent'
103
+ form.fields_submit text: 'submit'
104
+ form.spacer height: 2
105
+ form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
106
+ end
107
+
108
+
109
+ res.spacer height: 16
110
+ end
111
+ end
@@ -0,0 +1,63 @@
1
+ json.title 'Test Page (Form)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body childViews: ->(body) do
6
+ render 'json_ui/garage/test_page/header', view: body
7
+
8
+ body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
9
+ res.h2 text: 'Form'
10
+ res.spacer height: 8
11
+ res.panels_form \
12
+ url: json_ui_garage_url(path: 'forms/generic_post'),
13
+ method: 'post',
14
+ childViews: ->(form) do
15
+
16
+ properties = [
17
+ [
18
+ { name: 'question', value: 'Punctuality' },
19
+ { name: 'type', value: 'rating' },
20
+ ],
21
+ [
22
+ { name: 'question', value: 'Quality of work' },
23
+ { name: 'type', value: 'rating' },
24
+ { name: 'enabled', value: '1' },
25
+ ],
26
+ [
27
+ { name: 'question', value: 'Satisfied?' },
28
+ { name: 'type', value: 'yes_no' },
29
+ ]
30
+ ]
31
+ validation = { required: { message: 'Required' } }
32
+ form.fields_dynamicGroup width: 'matchParent', name: 'user[evaluation]', groupFieldProperties: properties, titlePrefix: 'Entry', content: ->(group) do
33
+ group.template padding: { left: 32 }, childViews: ->(template) do
34
+ template.spacer height: 10
35
+ template.fields_text width: 'matchParent', name: 'question', label: 'Question', placeholder: 'Question', validation: validation
36
+
37
+ options = [ :rating, :yes_no ]
38
+ template.fields_select \
39
+ width: 'matchParent',
40
+ name: 'type',
41
+ label: 'Answer Type',
42
+ placeholder: 'Answer Type',
43
+ options: options.map { |o| { text: o.to_s.humanize, value: o } },
44
+ validation: validation
45
+
46
+ template.fields_check \
47
+ width: 'matchParent',
48
+ name: 'enabled',
49
+ label: 'Enable',
50
+ checkValue: '1',
51
+ showIf: { "==": [{ "var": 'user[evaluation][{{index}}][type]' }, 'rating'] }
52
+
53
+ template.spacer height: 14
54
+ end
55
+ end
56
+ form.fields_submit text: 'submit'
57
+ form.spacer height: 2
58
+ form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
59
+ end
60
+
61
+ res.spacer height: 16
62
+ end
63
+ end
@@ -0,0 +1,65 @@
1
+ json.title 'Test Page (Form)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body childViews: ->(body) do
6
+ render 'json_ui/garage/test_page/header', view: body
7
+
8
+ body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
9
+ res.h2 text: 'MultiUpload'
10
+ res.spacer height: 8
11
+ res.panels_form \
12
+ url: json_ui_garage_url(path: 'forms/generic_post'),
13
+ method: 'post',
14
+ childViews: ->(form) do
15
+
16
+ form.spacer height: 16
17
+ form.label text: 'Simple file upload'
18
+ form.spacer height: 8
19
+ form.fields_multiUpload \
20
+ name: 'user[multi][]',
21
+ id: 'upload_1',
22
+ width: 360,
23
+ accepts: { fileType: 'image', maxFileSize: 10 },
24
+ directUploadUrl: glib_direct_uploads_url,
25
+ uploadTitle: 'Files uploaded:',
26
+ storagePrefix: 'glib',
27
+ metadata: {
28
+ foo: 'bar',
29
+ zoo: 'baz'
30
+ },
31
+ # tagging: 'key=value&key1=value1',
32
+ tags: { key: 'value', key1: 'value1' },
33
+ files: [
34
+ { name: 'File (Example)', signed_id: ActiveStorage::Attachment.first&.signed_id }
35
+ ]
36
+ form.button text: 'clear files', onClick: ->(action) do
37
+ action.components_set targetId: 'upload_1', data: { files: [], value: nil }
38
+ end
39
+
40
+ form.spacer height: 16
41
+ form.label text: 'File upload with onFinishUpload'
42
+ form.spacer height: 8
43
+ form.fields_multiUpload \
44
+ name: 'user[multi2][]',
45
+ id: 'upload_2',
46
+ width: 360,
47
+ accepts: { fileType: 'image', maxFileSize: 5000 },
48
+ directUploadUrl: glib_direct_uploads_url,
49
+ uploadTitle: 'Files uploaded:',
50
+ onFinishUpload: ->(action) { action.forms_submit }
51
+ form.button text: 'populate files', onClick: ->(action) do
52
+ action.components_set targetId: 'upload_2', data: {
53
+ files: [
54
+ { name: 'File (Example)', signed_id: ActiveStorage::Attachment.last&.signed_id }
55
+ ],
56
+ value: [1]
57
+ }
58
+ end
59
+
60
+ form.fields_submit text: 'submit'
61
+ end
62
+
63
+ res.spacer height: 16
64
+ end
65
+ end
@@ -61,7 +61,7 @@ module Glib
61
61
  end
62
62
 
63
63
  def mails
64
- @mails ||= ActionMailer::Base.deliveries
64
+ ActionMailer::Base.deliveries
65
65
  end
66
66
 
67
67
  def last_mail
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glib-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.0
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
@@ -111,6 +111,7 @@ files:
111
111
  - app/helpers/glib/json_ui/abstract_builder.rb
112
112
  - app/helpers/glib/json_ui/action_builder.rb
113
113
  - app/helpers/glib/json_ui/action_builder/bottom_banners.rb
114
+ - app/helpers/glib/json_ui/action_builder/browsers.rb
114
115
  - app/helpers/glib/json_ui/action_builder/commands.rb
115
116
  - app/helpers/glib/json_ui/action_builder/components.rb
116
117
  - app/helpers/glib/json_ui/action_builder/dialogs.rb
@@ -123,6 +124,7 @@ files:
123
124
  - app/helpers/glib/json_ui/action_builder/popovers.rb
124
125
  - app/helpers/glib/json_ui/action_builder/sheets.rb
125
126
  - app/helpers/glib/json_ui/action_builder/snackbars.rb
127
+ - app/helpers/glib/json_ui/action_builder/storage_items.rb
126
128
  - app/helpers/glib/json_ui/action_builder/tours.rb
127
129
  - app/helpers/glib/json_ui/action_builder/windows.rb
128
130
  - app/helpers/glib/json_ui/analytics_helper.rb
@@ -287,7 +289,13 @@ files:
287
289
  - app/views/json_ui/garage/tables/index.json.jbuilder
288
290
  - app/views/json_ui/garage/tables/layout.json.jbuilder
289
291
  - app/views/json_ui/garage/tables/panel_content.json.jbuilder
290
- - app/views/json_ui/garage/test_page/index.json.jbuilder
292
+ - app/views/json_ui/garage/test_page/_header.json.jbuilder
293
+ - app/views/json_ui/garage/test_page/auto_validate.json.jbuilder
294
+ - app/views/json_ui/garage/test_page/dialog.json.jbuilder
295
+ - app/views/json_ui/garage/test_page/dialog_open.json.jbuilder
296
+ - app/views/json_ui/garage/test_page/form.json.jbuilder
297
+ - app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder
298
+ - app/views/json_ui/garage/test_page/multiupload.json.jbuilder
291
299
  - app/views/json_ui/garage/views/_chart_data.json.jbuilder
292
300
  - app/views/json_ui/garage/views/banners.json.jbuilder
293
301
  - app/views/json_ui/garage/views/calendar_data.json.jbuilder
@@ -1,120 +0,0 @@
1
- json.title 'Test Page'
2
-
3
- page = json_ui_page json
4
-
5
- page.body childViews: ->(body) do
6
- body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
7
- res.panels_column lg: { cols: 6 }, childViews: ->(col) do
8
- res.h2 text: 'Reactivity & validation'
9
- res.spacer height: 8
10
- res.panels_form \
11
- url: json_ui_garage_url(path: 'forms/generic_post'),
12
- method: 'post',
13
- childViews: ->(form) do
14
- form.panels_flow innerPadding: { bottom: 0 }, width: 'matchParent', childViews: ->(hori) do
15
- hori.button text: 'components/set', onClick: ->(action) do
16
- action.runMultiple childActions: ->(saction) do
17
- saction.components_set targetId: 'text', data: { value: 'Doe John' }
18
- saction.components_set targetId: 'textarea', data: { value: 'The quick brown fox jumps over the lazy dog' }
19
- new_options = [{ text: 'Option99', value: 'option99' }]
20
- ['select', 'chip_group'].each do |id|
21
- saction.components_set targetId: id, data: { options: new_options, value: ['option99'] }
22
- end
23
- end
24
- end
25
- hori.spacer width: 4
26
- hori.button text: 'logics/set', onClick: ->(action) do
27
- action.logics_set targetId: 'date', conditionalData: { value: { "+": [{ "var": ['user[date]'] }, 60 * 60 * 24 * 3] } }
28
- end
29
- hori.spacer width: 4
30
- hori.button text: 'components/replace', onClick: ->(action) do
31
- action.runMultiple childActions: ->(saction) do
32
- saction.components_replace targetId: 'radio_group', newView: ->(view) do
33
- view.fields_radioGroup value: '', childViews: ->(sview) do
34
- sview.fields_radio label: 'Option99', value: 'option99'
35
- end
36
- end
37
-
38
- saction.components_replace targetId: 'check_group', newView: ->(view) do
39
- view.fields_checkGroup value: '', childViews: ->(sview) do
40
- sview.fields_check label: 'Option99', value: nil, checkValue: 'option99'
41
- end
42
- end
43
- end
44
- end
45
- hori.spacer width: 4
46
- hori.button text: 'hide select', onClick: ->(action) do
47
- action.components_set targetId: 'select', data: { displayed: false }
48
- end
49
- end
50
-
51
- form.spacer height: 8
52
- form.hr width: 'matchParent'
53
- form.spacer height: 8
54
-
55
- validation = { required: { message: 'Required' } }
56
- options = ['option1', 'option2', 'option3', 'option4'].map { |option| { 'text'=> option.humanize, 'value' => option } }
57
-
58
- form.fields_date width: 'matchParent', name: 'user[date]', id: 'date', value: Date.new(2024, 7, 24), validation: validation
59
- form.hr width: 'matchParent'
60
- form.fields_select multiple: true, width: 'matchParent', name: 'user[select][]', id: 'select', options: options, value: ['option1', 'option2'], validation: validation
61
- form.hr width: 'matchParent'
62
- form.fields_chipGroup width: 'matchParent', name: 'user[chip_group]', id: 'chip_group', options: options, value: ['option2'], validation: validation
63
- form.hr width: 'matchParent'
64
- form.fields_radioGroup width: 'matchParent', name: 'user[radio_group]', id: 'radio_group', value: 'option3', validation: validation, childViews: ->(radio) do
65
- options.each do |option|
66
- radio.fields_radio label: option['text'], value: option['value']
67
- end
68
- end
69
- form.hr width: 'matchParent'
70
- check_group_value = ['option3', 'option1']
71
- form.fields_checkGroup width: 'matchParent', name: 'user[check_group]', id: 'check_group', value: check_group_value, validation: validation, childViews: ->(radio) do
72
- options.each do |option|
73
- radio.fields_check label: option['text'], checkValue: option['value']
74
- end
75
- end
76
- form.hr width: 'matchParent'
77
- form.fields_text width: 'matchParent', name: 'user[text]', id: 'text', value: 'John Doe', validation: validation
78
-
79
- form.hr width: 'matchParent'
80
-
81
- form.fields_textarea width: 'matchParent', name: 'user[textarea]', id: 'textarea', value: 'Lorem ipsum et dumet bla bla bla...'
82
-
83
- form.hr width: 'matchParent'
84
- form.fields_submit text: 'submit'
85
- form.spacer height: 2
86
- form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
87
- end
88
-
89
-
90
- res.spacer height: 16
91
-
92
- end
93
-
94
-
95
- res.panels_column lg: { cols: 6 }, childViews: ->(col) do
96
- res.h2 text: 'Dialog'
97
- res.spacer height: 8
98
- res.button text: 'Dialog updateExisting', onClick: ->(action) do
99
- action.runMultiple childActions: ->(saction) do
100
- saction.dialogs_show updateExisting: true, content: ->(dialog) do
101
- dialog.body padding: glib_json_padding_body, childViews: ->(sbody) do
102
- sbody.h1 text: 'Hello world'
103
- sbody.button text: 'change dialog content', onClick: ->(ssaction) do
104
- ssaction.dialogs_show updateExisting: true, disableCloseButton: true, content: ->(sdialog) do
105
- sdialog.body padding: glib_json_padding_body, childViews: ->(ssbody) do
106
- ssbody.h1 text: 'Hello world (updated)'
107
- ssbody.spacer height: 8
108
- ssbody.button text: 'close', onClick: ->(xaction) do
109
- xaction.dialogs_close
110
- end
111
- end
112
- end
113
- end
114
- end
115
- end
116
- end
117
- end
118
- end
119
- end
120
- end