glib-web 4.42.0 → 4.42.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fd0f52f48d55fef27f49ae004e3f2c59e730963cecaddd6af5cb5fe962b6323
4
- data.tar.gz: aebd337997b0b656fac64f72a0c53322bd58815bdf2052aef4b15243d1e06e96
3
+ metadata.gz: 10cf86096f7289ec5330a4945455aa966fffa5ec6d25e9eed14a9d1cc05324e5
4
+ data.tar.gz: 3e5c534eb6a24b7643c2ba053a92dbc85854242af338489244d8f229ee8ea86f
5
5
  SHA512:
6
- metadata.gz: 7dfa32067ee2a2be6dd8095bf8fce2b5da301cff44fdc93f1e80eba7fe9562ae12bae8f7edab9902c16e6ace86af262d028052fb30b644f2bb54957d61302571
7
- data.tar.gz: e5d068af18a7f8908ae82aac1d5f74225aacdbd58eac2db8b0b78b07f9a5cb4e037d49f32aa11fb2278bc5341ce8c374a89fac4650f5b97f4227eb618606c136
6
+ metadata.gz: 6f4b740caa11bfc6f41686c03c84fcc507ef0de4ae17d8b317b98fcd301c311bfc72eb9eaeb5ac04fe847828e00826234bc3087f72571143fb653b5e0cd92465
7
+ data.tar.gz: c99131d247a25df28948a755718a63d0d1548eb1d5b149917156340798115a67ab587ceb9c282bbccaeb1e4ddd31e552a9f034c3fa54ca246654c3cbb3f660bc
@@ -131,9 +131,10 @@ module Glib::Json::Libs
131
131
  redirect_to sign_in_url, **redirect_options
132
132
  end
133
133
  format.json do
134
- render \
134
+ render(
135
135
  json: json_ui_401_response(sign_in_url, **redirect_options),
136
136
  status: Rails.env.test? ? :unauthorized : :ok
137
+ )
137
138
  end
138
139
  format.csv do
139
140
  if Rails.env.test?
@@ -230,7 +231,9 @@ module Glib::Json::Libs
230
231
  view: 'label',
231
232
  text: message
232
233
  },
233
- ].concat(bottom_views),
234
+ ].concat(
235
+ bottom_views
236
+ ),
234
237
  }
235
238
  ]
236
239
  # Set this in the project's custom CSS.
@@ -288,6 +291,17 @@ module Glib::Json::Libs
288
291
 
289
292
  module ClassMethods
290
293
 
294
+ # This usually resides in a base controller class, e.g. ApplicationController.
295
+ def __json_libs_init_options
296
+ if instance_variable_defined?(:@__json_libs_init_options)
297
+ @__json_libs_init_options
298
+ elsif superclass.respond_to?(:__json_libs_init_options)
299
+ superclass.__json_libs_init_options
300
+ else
301
+ nil
302
+ end
303
+ end
304
+
291
305
  def json_libs_init(options)
292
306
  include Glib::Json::Transformation
293
307
  include Glib::Json::Validation
@@ -296,6 +310,10 @@ module Glib::Json::Libs
296
310
  include Glib::Json::Traversal
297
311
  include Glib::Json::NewDynamicText
298
312
 
313
+ @__json_libs_init_options = options
314
+
315
+ puts "*********** __json_libs_init_options: #{@__json_libs_init_options}"
316
+
299
317
  before_action do
300
318
  __json_ui_start(options)
301
319
  end
@@ -350,30 +368,37 @@ module Glib::Json::Libs
350
368
  redirect_to instance_exec(&sign_in_url_proc)
351
369
  end
352
370
  format.json do
353
- render json: {
354
- glib_json_dialog_mode? ? :onLoad : :onResponse => __glib_error_dialog(
355
- 'Your session has expired', 'Please refresh the page and retry.', ['dialog-csrf-error'], [
356
- {
357
- view: 'button',
358
- text: 'Refresh',
359
- onClick: {
360
- action: 'auth/saveCsrfToken',
361
- token: form_authenticity_token,
362
- onSave: {
363
- action: 'dialogs/close',
364
- onClose: {
365
- action: 'windows/reload',
366
- onReload: {
367
- action: 'snackbars/alert',
368
- message: 'Refreshed. You can try again now.'
371
+ raise exception if Rails.env.test? || !json_ui_should_render?(self.class.__json_libs_init_options)
372
+
373
+ render(
374
+ json: {
375
+ glib_json_dialog_mode? ? :onLoad : :onResponse => __glib_error_dialog(
376
+ 'Your session has expired',
377
+ 'Please refresh the page and retry.',
378
+ ['dialog-csrf-error'],
379
+ [
380
+ {
381
+ view: 'button',
382
+ text: 'Refresh',
383
+ onClick: {
384
+ action: 'auth/saveCsrfToken',
385
+ token: form_authenticity_token,
386
+ onSave: {
387
+ action: 'dialogs/close',
388
+ onClose: {
389
+ action: 'windows/reload',
390
+ onReload: {
391
+ action: 'snackbars/alert',
392
+ message: 'Refreshed. You can try again now.'
393
+ }
369
394
  }
370
395
  }
371
396
  }
372
- }
373
- },
374
- ]
375
- )
376
- }
397
+ },
398
+ ]
399
+ )
400
+ }
401
+ )
377
402
  end
378
403
  end
379
404
  end
@@ -36,6 +36,16 @@ module Glib::Json::Ui
36
36
  @__json_ui_rendering != nil
37
37
  end
38
38
 
39
+ def json_ui_should_render?(options)
40
+ when_option = options[:when]
41
+ if when_option.is_a?(Proc)
42
+ # should_render = when_option.call(self)
43
+ instance_exec(&when_option)
44
+ else
45
+ when_option == :always
46
+ end
47
+ end
48
+
39
49
  def json_ui_page_lifecycle_prop(name)
40
50
  if (hash = json_transformation_start).is_a?(Hash) && hash['body'] # A valid page
41
51
  original_action = hash[name]
@@ -70,14 +80,7 @@ module Glib::Json::Ui
70
80
  @__json_ui_activated = false
71
81
  @__json_ui_rendering = nil
72
82
 
73
- when_option = options[:when]
74
- if when_option.is_a?(Proc)
75
- # should_render = when_option.call(self)
76
- should_render = instance_exec(&when_option)
77
- else
78
- should_render = when_option == :always
79
- end
80
-
83
+ should_render = json_ui_should_render?(options)
81
84
  if should_render || params[:_render] == 'v1'
82
85
  @__json_ui_activated = true
83
86
  request.variant = :ui
@@ -114,12 +117,14 @@ module Glib::Json::Ui
114
117
 
115
118
  renderer_path = params[:_skip_custom_render] == 'true' ? 'layouts/json_ui/no_custom' : options[:renderer_path]
116
119
  @__json_ui_orig_page = response.body
117
- response.body = render_to_string(
118
- template: renderer_path,
119
- layout: 'json_ui/renderer',
120
- content_type: 'text/html',
121
- formats: [:html],
122
- locals: { page: hash, options: options }
120
+ response.body(
121
+ render_to_string(
122
+ template: renderer_path,
123
+ layout: 'json_ui/renderer',
124
+ content_type: 'text/html',
125
+ formats: [:html],
126
+ locals: { page: hash, options: options }
127
+ )
123
128
  )
124
129
  end
125
130
  end
@@ -269,6 +269,14 @@ class Glib::JsonUi::ViewBuilder
269
269
  #
270
270
  # @see app/views/json_ui/garage/forms/text_validation.json.jbuilder Garage example
271
271
  class Email < Text
272
+ def created
273
+ super
274
+ # Assume format validation handled by frontend
275
+ if @validation.present? && @validation[:format].present?
276
+ @validation.delete(:format)
277
+ json.validation @validation
278
+ end
279
+ end
272
280
  end
273
281
 
274
282
  # URL input field with URL format validation.
@@ -4,7 +4,7 @@ module Glib
4
4
  include ActiveModel::Validations
5
5
  include ActiveModel::AttributeMethods
6
6
 
7
- attr_accessor :name, :age, :position, :words, :accept, :pet_you_have
7
+ attr_accessor :email, :name, :age, :position, :words, :accept, :pet_you_have
8
8
 
9
9
  validates :name, :position, presence: true
10
10
  validates :accept, acceptance: {}, allow_nil: false
@@ -13,5 +13,6 @@ module Glib
13
13
  validates :name, format: { with: /Doe\z/, message: 'This job is for person end with Doe' }
14
14
  validates :position, inclusion: { in: ['programmer', 'devops', 'designer'] }, allow_blank: true
15
15
  validates :pet_you_have, exclusion: { in: ['crocodile', 'shark' ], message: 'We dont accept that kind of animal!' }, allow_blank: true
16
+ validates :email, format: { with: /\.doe\z/, message: "This message is never shown because it's handled by frontend" }
16
17
  end
17
18
  end
@@ -1,14 +1,24 @@
1
- sleep 3
1
+ sleep 0.5
2
2
  page = json_ui_page json
3
3
 
4
- page.body childViews: ->(body) do
5
- body.panels_responsive padding: { all: 16 }, childViews: ->(res) do
6
- res.fields_richText value: "<p>#{DateTime.current.to_fs(:rfc822)}</p><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>"
7
- res.spacer height: 8
8
- res.button text: 'close', onClick: ->(action) do
9
- action.sheets_close onClose: ->(saction) do
10
- saction.snackbars_alert message: 'sheet closed'
4
+ page.body(
5
+ childViews: ->(body) do
6
+ body.panels_responsive(
7
+ padding: { all: 16 },
8
+ childViews: ->(res) do
9
+ res.fields_richText value: "<p>#{DateTime.current.to_fs(:rfc822)}</p><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>"
10
+ res.spacer height: 8
11
+ res.button(
12
+ text: 'close',
13
+ onClick: ->(action) do
14
+ action.sheets_close(
15
+ onClose: ->(saction) do
16
+ saction.snackbars_alert message: 'sheet closed'
17
+ end
18
+ )
19
+ end
20
+ )
11
21
  end
12
- end
22
+ )
13
23
  end
14
- end
24
+ )
@@ -5,6 +5,10 @@ json.title 'Lists'
5
5
  render "#{@path_prefix}/nav_menu", json: json, page: page
6
6
 
7
7
  tab_index = params[:tab].to_i
8
+ batch_count = 20
9
+ editable_ids = (0...batch_count).map do |index|
10
+ "PK_#{(batch_count * tab_index) + index}"
11
+ end
8
12
 
9
13
  page.header(
10
14
  childViews: ->(header) do
@@ -42,7 +46,25 @@ page.form(
42
46
  childViews: ->(header) do
43
47
  header.panels_horizontal(
44
48
  childViews: ->(horizontal) do
45
- horizontal.fields_check name: 'user[check_all]', label: 'All', checkValue: true
49
+ horizontal.fields_check(
50
+ name: 'user[check_all]',
51
+ label: 'All',
52
+ checkValue: true,
53
+ onChange: ->(action) do
54
+ action.runMultiple(
55
+ childActions: ->(multiple) do
56
+ editable_ids.each do |editable_id|
57
+ multiple.logics_set(
58
+ targetId: editable_id,
59
+ conditionalData: {
60
+ value: { "if": [{ "var": 'user[check_all]' }, true, false] }
61
+ }
62
+ )
63
+ end
64
+ end
65
+ )
66
+ end
67
+ )
46
68
  horizontal.spacer width: 20
47
69
  # header.fields_text width: 'matchParent', styleClass: 'outlined', name: 'user[new_name]', label: 'Item name'
48
70
  statuses = [:pending, :active]
@@ -61,23 +83,14 @@ page.form(
61
83
  )
62
84
  section.rows(
63
85
  builder: ->(row) do
64
- batch_count = 20
65
86
  batch_count.times do |index|
66
87
  id = (batch_count * tab_index) + index
67
- row.editable title: "Item #{id}", recordId: "PK_#{id}"
88
+ row.editable id: "PK_#{id}", title: "Item #{id}", recordId: "PK_#{id}"
68
89
  end
69
90
  end
70
91
  )
71
92
  end
72
- ],
73
- fieldCheckValueIf: {
74
- "==": [
75
- {
76
- "var": 'user[check_all]'
77
- },
78
- true
79
93
  ]
80
- }
81
94
  )
82
95
  end
83
96
  )
@@ -1,4 +1,4 @@
1
- navs = ['form', 'dialog', 'form_dynamic', 'auto_validate', 'multiupload', 'dirty_state', 'window', 'selectable', 'lifecycle', 'file_upload_new']
1
+ navs = ['form', 'dialog', 'form_dynamic', 'auto_validate', 'multiupload', 'dirty_state', 'window', 'selectable', 'list_editable', 'lifecycle', 'file_upload_new', 'logics_set', 'http', 'sheets', 'snackbars']
2
2
 
3
3
  view.panels_flow(
4
4
  innerPadding: { bottom: 0 },
@@ -2,76 +2,75 @@ json.title 'Test Page (Form)'
2
2
 
3
3
  page = json_ui_page json
4
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
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
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
+ body.panels_responsive(
17
+ padding: glib_json_padding_body,
18
+ childViews: ->(res) do
19
+ res.h2 text: 'Job Application'
20
+ res.spacer height: 8
21
+ res.panels_form(
22
+ url: json_ui_garage_url(path: 'forms/generic_post'),
23
+ method: 'post',
24
+ model: model,
25
+ autoValidate: true,
26
+ childViews: ->(form) do
27
+ form.fields_text prop: :name, label: 'name', name: 'user[name]', width: 'matchParent'
28
+ form.spacer height: 4
29
+ form.fields_email prop: :email, label: 'email', name: 'user[email]', width: 'matchParent'
30
+ form.fields_text label: 'say hello', name: 'user[say]', width: 'matchParent', validation: { format: { regex: 'hello', message: 'Say hello!' } }, value: 'sup'
31
+ form.spacer height: 4
32
+ form.fields_number prop: :age, label: 'age', name: 'user[age]', width: 'matchParent'
33
+ form.spacer height: 4
34
+ form.fields_number(
35
+ name: 'user[height]',
36
+ label: 'height',
37
+ width: 'matchParent',
38
+ 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' } } },
39
+ value: 150
40
+ )
41
+ form.spacer height: 4
42
+ form.fields_textarea prop: :words, label: 'words', name: 'user[words]', width: 'matchParent'
43
+ form.spacer height: 4
44
+ 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' } } } }
45
+ form.spacer height: 4
46
+ options = ['programmer', 'devops', 'designer', 'ceo', 'office_boy'].map { |v| { text: v.humanize, value: v } }
47
+ form.fields_select prop: :position, label: 'postion', name: 'user[position]', width: 'matchParent', options: options
48
+ form.spacer height: 4
49
+ options2 = ['crocodile', 'cat', 'dog', 'bird', 'shark'].map { |v| { text: v.humanize, value: v } }
50
+ form.fields_select prop: :pet_you_have, label: 'Pet you have', name: 'user[pet_you_have]', width: 'matchParent', options: options2
51
+ form.spacer height: 4
52
+ form.label text: 'Decision'
53
+ options3 = ['choose me', 'dont choose me', 'whatever']
54
+ form.fields_radioGroup(
55
+ value: 'dont choose me',
56
+ validation: { exclusion: { in: ['dont choose me'], message: 'dont choose me' } },
57
+ childViews: ->(radio) do
58
+ options3.each do |option|
59
+ radio.fields_radio label: option, value: option
60
+ end
61
+ end
62
+ )
63
+ form.spacer height: 4
64
+ form.fields_check prop: :accept, label: 'accept', name: 'user[accept]', width: 'matchParent', checkValue: '1'
65
+ form.spacer height: 4
66
+ form.spacer height: 4
67
+ form.fields_submit text: 'submit'
68
+ form.spacer height: 2
69
+ form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
62
70
  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
71
+ )
72
+ res.spacer height: 16
73
73
  end
74
-
75
- res.spacer height: 16
74
+ )
76
75
  end
77
- end
76
+ )
@@ -0,0 +1,70 @@
1
+ json.title 'Test Page (HTTP)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
8
+ body.panels_responsive(
9
+ padding: glib_json_padding_body,
10
+ childViews: ->(res) do
11
+ res.h2 text: 'HTTP'
12
+ res.spacer height: 8
13
+ res.button text: 'http/post', onClick: ->(action) do
14
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
15
+ subaction.http_post(
16
+ url: json_ui_garage_url(path: 'forms/basic_post'),
17
+ formData: { user: { name: { first: 'New', last: 'Joe' } } }
18
+ )
19
+ end
20
+ end
21
+ res.spacer height: 4
22
+ res.button text: 'http/post (error page)', onClick: ->(action) do
23
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
24
+ subaction.http_post(
25
+ retryLimit: 5,
26
+ url: json_ui_garage_url(path: SecureRandom.hex(3)),
27
+ formData: { user: { name: { first: 'New', last: 'Joe' } } }
28
+ )
29
+ end
30
+ end
31
+ res.spacer height: 4
32
+ res.button text: 'http/patch', onClick: ->(action) do
33
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
34
+ subaction.http_patch(
35
+ url: json_ui_garage_url(path: 'forms/basic_post'),
36
+ formData: { 'user[name]' => 'Edit Joe' }
37
+ )
38
+ end
39
+ end
40
+ res.spacer height: 4
41
+ res.button text: 'http/patch (error page)', onClick: ->(action) do
42
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
43
+ subaction.http_patch(
44
+ url: json_ui_garage_url(path: SecureRandom.hex(3)),
45
+ formData: { 'user[name]' => 'Edit Joe' }
46
+ )
47
+ end
48
+ end
49
+ res.spacer height: 4
50
+ res.button text: 'http/delete', onClick: ->(action) do
51
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
52
+ subaction.http_delete(
53
+ url: json_ui_garage_url(path: 'forms/basic_post'),
54
+ formData: { 'user[name]' => 'Delete Joe' }
55
+ )
56
+ end
57
+ end
58
+ res.spacer height: 4
59
+ res.button text: 'http/delete (error page)', onClick: ->(action) do
60
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
61
+ subaction.http_delete(
62
+ url: json_ui_garage_url(path: SecureRandom.hex(3)),
63
+ formData: { 'user[name]' => 'Delete Joe' }
64
+ )
65
+ end
66
+ end
67
+ end
68
+ )
69
+ end
70
+ )
@@ -0,0 +1,96 @@
1
+ json.title 'Test Page (List Editable)'
2
+
3
+ page = json_ui_page json
4
+
5
+ tab_index = params[:tab].to_i
6
+ batch_count = 20
7
+ editable_ids = (0...batch_count).map do |index|
8
+ "PK_#{(batch_count * tab_index) + index}"
9
+ end
10
+
11
+ page.header(
12
+ childViews: ->(header) do
13
+ # Allow navigating to another "edit mode" page to test reuse issues.
14
+ header.tabBar(
15
+ buttons: ->(menu) do
16
+ ['FIRST', 'SECOND'].each_with_index do |text, index|
17
+ menu.button(
18
+ text: text,
19
+ disabled: tab_index == index,
20
+ onClick: ->(action) do
21
+ action.windows_reload url: json_ui_garage_url(path: 'test_page/list_editable', tab: index)
22
+ end
23
+ )
24
+ end
25
+ end
26
+ )
27
+ end
28
+ )
29
+
30
+ page.body childViews: ->(body) do
31
+ render 'json_ui/garage/test_page/header', view: body
32
+
33
+ body.panels_form \
34
+ width: 'matchParent',
35
+ url: json_ui_garage_url(path: 'forms/generic_post'),
36
+ method: 'post',
37
+ padding: glib_json_padding_body,
38
+ childViews: ->(form) do
39
+ form.panels_list(
40
+ fieldPrefix: 'user[items]',
41
+ fieldTitleName: 'name',
42
+ width: 'matchParent',
43
+ sections: [
44
+ ->(section) do
45
+ section.header(
46
+ padding: glib_json_padding_list,
47
+ childViews: ->(header) do
48
+ header.panels_horizontal(
49
+ childViews: ->(horizontal) do
50
+ horizontal.fields_check(
51
+ name: 'user[check_all]',
52
+ label: 'All',
53
+ checkValue: true,
54
+ onChange: ->(action) do
55
+ action.runMultiple(
56
+ childActions: ->(multiple) do
57
+ editable_ids.each do |editable_id|
58
+ multiple.logics_set(
59
+ targetId: editable_id,
60
+ conditionalData: {
61
+ value: { "if": [{ "var": 'user[check_all]' }, true, false] }
62
+ }
63
+ )
64
+ end
65
+ end
66
+ )
67
+ end
68
+ )
69
+ horizontal.spacer width: 20
70
+ statuses = [:pending, :active]
71
+ horizontal.fields_select(
72
+ styleClass: 'outlined',
73
+ name: 'user[status]',
74
+ width: 'matchParent',
75
+ label: 'Status',
76
+ options: statuses.map { |status| { value: status, text: status.to_s.humanize } }
77
+ )
78
+ horizontal.spacer width: 20
79
+ horizontal.fields_submit text: 'Update'
80
+ end
81
+ )
82
+ end
83
+ )
84
+ section.rows(
85
+ builder: ->(row) do
86
+ batch_count.times do |index|
87
+ id = (batch_count * tab_index) + index
88
+ row.editable id: "PK_#{id}", title: "Item #{id}", recordId: "PK_#{id}"
89
+ end
90
+ end
91
+ )
92
+ end
93
+ ]
94
+ )
95
+ end
96
+ end
@@ -2,93 +2,139 @@ json.title 'Test Page (Logics Set)'
2
2
 
3
3
  page = json_ui_page json
4
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: 'Logics Set - Icon Badge'
10
- res.spacer height: 8
11
-
12
- res.panels_flow innerPadding: { bottom: 0 }, width: 'matchParent', childViews: ->(flow) do
13
- flow.button text: 'Set badge to "5"', onClick: ->(action) do
14
- action.logics_set targetId: 'test_icon', conditionalData: {
15
- 'badge.text': '5',
16
- 'badge.backgroundColor': 'primary'
17
- }
18
- end
19
-
20
- flow.spacer width: 8
21
-
22
- flow.button text: 'Set badge to "99"', onClick: ->(action) do
23
- action.logics_set targetId: 'test_icon', conditionalData: {
24
- 'badge.text': '99',
25
- 'badge.backgroundColor': 'warning'
26
- }
27
- end
28
-
29
- flow.spacer width: 8
30
-
31
- flow.button text: 'Clear badge', onClick: ->(action) do
32
- action.logics_set targetId: 'test_icon', data: {
33
- badge: nil
34
- }
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
8
+ body.panels_responsive(
9
+ padding: glib_json_padding_body,
10
+ childViews: ->(res) do
11
+ res.h2 text: 'Logics Set - Icon Badge'
12
+ res.spacer height: 8
13
+ res.panels_flow(
14
+ innerPadding: { bottom: 0 },
15
+ width: 'matchParent',
16
+ childViews: ->(flow) do
17
+ flow.button(
18
+ text: 'Set badge to "5"',
19
+ onClick: ->(action) do
20
+ action.logics_set(
21
+ targetId: 'test_icon',
22
+ conditionalData: {
23
+ 'badge.text': '5',
24
+ 'badge.backgroundColor': 'primary'
25
+ }
26
+ )
27
+ end
28
+ )
29
+ flow.spacer width: 8
30
+ flow.button(
31
+ text: 'Set badge to "99"',
32
+ onClick: ->(action) do
33
+ action.logics_set(
34
+ targetId: 'test_icon',
35
+ conditionalData: {
36
+ 'badge.text': '99',
37
+ 'badge.backgroundColor': 'warning'
38
+ }
39
+ )
40
+ end
41
+ )
42
+ flow.spacer width: 8
43
+ flow.button(
44
+ text: 'Clear badge',
45
+ onClick: ->(action) do
46
+ action.logics_set(
47
+ targetId: 'test_icon',
48
+ data: {
49
+ badge: nil
50
+ }
51
+ )
52
+ end
53
+ )
54
+ end
55
+ )
56
+ res.spacer height: 16
57
+ res.hr width: 'matchParent'
58
+ res.spacer height: 16
59
+ res.icon(
60
+ id: 'test_icon',
61
+ name: 'notifications',
62
+ badge: { text: '1', backgroundColor: 'error' }
63
+ )
64
+ res.spacer height: 24
65
+ res.h2 text: 'Button with Icon Badge'
66
+ res.spacer height: 8
67
+ res.panels_flow(
68
+ innerPadding: { bottom: 0 },
69
+ width: 'matchParent',
70
+ childViews: ->(flow) do
71
+ flow.button(
72
+ text: 'Set button badge to "3"',
73
+ onClick: ->(action) do
74
+ action.logics_set(
75
+ targetId: 'test_button',
76
+ conditionalData: {
77
+ 'icon.badge.text': '3',
78
+ 'icon.badge.backgroundColor': 'success'
79
+ }
80
+ )
81
+ end
82
+ )
83
+ flow.spacer width: 8
84
+ flow.button(
85
+ text: 'Set button badge to "10"',
86
+ onClick: ->(action) do
87
+ action.logics_set(
88
+ targetId: 'test_button',
89
+ conditionalData: {
90
+ 'icon.badge.text': '10',
91
+ 'icon.badge.backgroundColor': 'error'
92
+ }
93
+ )
94
+ end
95
+ )
96
+ flow.spacer width: 8
97
+ flow.button(
98
+ text: 'Clear button badge',
99
+ onClick: ->(action) do
100
+ action.logics_set(
101
+ targetId: 'test_button',
102
+ data: {
103
+ 'icon.badge': nil
104
+ }
105
+ )
106
+ end
107
+ )
108
+ end
109
+ )
110
+ res.spacer height: 16
111
+ res.hr width: 'matchParent'
112
+ res.spacer height: 16
113
+ res.button(
114
+ id: 'test_button',
115
+ text: 'Notifications',
116
+ icon: {
117
+ name: 'notifications',
118
+ badge: { text: '5', backgroundColor: 'warning' }
119
+ },
120
+ onClick: ->(action) do
121
+ action.snackbars_alert message: 'Button clicked!'
122
+ end
123
+ )
124
+ res.spacer height: 16
125
+ res.button(
126
+ text: 'Update duplicate labels',
127
+ onClick: ->(action) do
128
+ action.logics_set(
129
+ targetId: 'duplicate_label',
130
+ data: { text: 'Updated label text' }
131
+ )
132
+ end
133
+ )
134
+ res.spacer height: 8
135
+ res.label id: 'duplicate_label', text: 'Visible label text'
136
+ res.label id: 'duplicate_label', text: 'Hidden label text', displayed: false
35
137
  end
36
- end
37
-
38
- res.spacer height: 16
39
- res.hr width: 'matchParent'
40
- res.spacer height: 16
41
-
42
- res.icon \
43
- id: 'test_icon',
44
- name: 'notifications',
45
- badge: { text: '1', backgroundColor: 'error' }
46
-
47
- res.spacer height: 24
48
- res.h2 text: 'Button with Icon Badge'
49
- res.spacer height: 8
50
-
51
- res.panels_flow innerPadding: { bottom: 0 }, width: 'matchParent', childViews: ->(flow) do
52
- flow.button text: 'Set button badge to "3"', onClick: ->(action) do
53
- action.logics_set targetId: 'test_button', conditionalData: {
54
- 'icon.badge.text': '3',
55
- 'icon.badge.backgroundColor': 'success'
56
- }
57
- end
58
-
59
- flow.spacer width: 8
60
-
61
- flow.button text: 'Set button badge to "10"', onClick: ->(action) do
62
- action.logics_set targetId: 'test_button', conditionalData: {
63
- 'icon.badge.text': '10',
64
- 'icon.badge.backgroundColor': 'error'
65
- }
66
- end
67
-
68
- flow.spacer width: 8
69
-
70
- flow.button text: 'Clear button badge', onClick: ->(action) do
71
- action.logics_set targetId: 'test_button', data: {
72
- 'icon.badge': nil
73
- }
74
- end
75
- end
76
-
77
- res.spacer height: 16
78
- res.hr width: 'matchParent'
79
- res.spacer height: 16
80
-
81
- res.button \
82
- id: 'test_button',
83
- text: 'Notifications',
84
- icon: {
85
- name: 'notifications',
86
- badge: { text: '5', backgroundColor: 'warning' }
87
- },
88
- onClick: ->(action) do
89
- action.snackbars_alert message: 'Button clicked!'
90
- end
91
-
92
- res.spacer height: 16
138
+ )
93
139
  end
94
- end
140
+ )
@@ -0,0 +1,105 @@
1
+ json.title 'Test Page (Sheets)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
8
+ body.panels_responsive(
9
+ padding: glib_json_padding_body,
10
+ childViews: ->(res) do
11
+ res.h2 text: 'Sheets'
12
+ res.spacer height: 8
13
+ res.button text: 'sheets/select', onClick: ->(action) do
14
+ action.sheets_select(
15
+ message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rutrum vel nisi in pharetra. Aenean mollis feugiat elementum. Donec tempor arcu fringilla risus finibus cursus. Donec eleifend ex sit amet odio rutrum, vitae ornare nunc congue. In hac habitasse platea dictumst. Curabitur at velit et odio ornare vestibulum sit amet vitae ex. Proin tincidunt rutrum libero vitae tempus.',
16
+ buttons: ->(menu) do
17
+ menu.button text: 'Option1 Aenean mollis feugiat elementum. Donec tempor arcu fringilla risus finibus cursus.', onClick: ->(subaction) do
18
+ subaction.dialogs_alert message: 'Option 1'
19
+ end
20
+ menu.button text: 'Option2', onClick: ->(subaction) do
21
+ subaction.dialogs_alert message: 'Option 2'
22
+ end
23
+ end
24
+ )
25
+ end
26
+ res.spacer height: 4
27
+ res.button text: "sheets/open placement: 'top'", onClick: ->(action) do
28
+ action.sheets_open placement: 'top', url: json_ui_garage_url(path: 'actions/sheet_content')
29
+ end
30
+ res.spacer height: 4
31
+ res.button text: "sheets/open placement: 'right' with loaderViews", onClick: ->(action) do
32
+ action.sheets_open(
33
+ placement: 'right',
34
+ url: json_ui_garage_url(path: 'actions/sheet_content'),
35
+ loaderViews: ->(sview) do
36
+ sview.panels_responsive padding: { all: 16 }, childViews: ->(sres) do
37
+ sres.skeleton template: 'textArea'
38
+ sres.skeleton template: 'commentList'
39
+ end
40
+ end
41
+ )
42
+ end
43
+ res.spacer height: 4
44
+ res.button text: "sheets/open placement: 'bottom'", onClick: ->(action) do
45
+ action.sheets_open placement: 'bottom', url: json_ui_garage_url(path: 'actions/sheet_content')
46
+ end
47
+ res.spacer height: 4
48
+ res.button text: "sheets/open placement: 'left'", onClick: ->(action) do
49
+ action.sheets_open placement: 'left', url: json_ui_garage_url(path: 'actions/sheet_content')
50
+ end
51
+ res.spacer height: 4
52
+ res.button text: 'sheets/open wrong url', onClick: ->(action) do
53
+ action.sheets_open placement: 'left', url: json_ui_garage_url(path: 'wrongurl')
54
+ end
55
+ res.spacer height: 4
56
+ res.button text: 'sheets/show', onClick: ->(action) do
57
+ action.sheets_show placement: 'right', content: ->(sheet) do
58
+ sheet.body childViews: ->(sbody) do
59
+ sbody.panels_responsive width: 'matchParent', padding: { all: 16 }, childViews: ->(sres) do
60
+ sres.markdown text: %{
61
+ 1. First ordered list item
62
+ 2. Another item
63
+ * Unordered sub-list.
64
+ 1. Actual numbers don't matter, just that it's a number
65
+ 1. Ordered sub-list
66
+ 4. And another item.
67
+
68
+ You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown).
69
+
70
+ To have a line break without a paragraph, you will need to use two trailing spaces.
71
+ Note that this line is separate, but within the same paragraph.
72
+ (This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.)
73
+
74
+ * Unordered list can use asterisks
75
+ - Or minuses
76
+ + Or pluses
77
+
78
+ 1. Make my changes
79
+ 1. Fix bug
80
+ 2. Improve formatting
81
+ - Make the headings bigger
82
+ 2. Push my commits to GitHub
83
+ 3. Open a pull request
84
+ * Describe my changes
85
+ * Mention all the members of my team
86
+ * Ask for feedback
87
+
88
+ + Create a list by starting a line with `+`, `-`, or `*`
89
+ + Sub-lists are made by indenting 2 spaces:
90
+ - Marker character change forces new list start:
91
+ * Ac tristique libero volutpat at
92
+ + Facilisis in pretium nisl aliquet
93
+ - Nulla volutpat aliquam velit
94
+ + Very easy!
95
+ }
96
+ sres.spacer height: 8
97
+ sres.button text: 'close', onClick: ->(saction) { saction.sheets_close }
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ )
104
+ end
105
+ )
@@ -0,0 +1,73 @@
1
+ json.title 'Test Page (Snackbars)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
8
+ body.panels_responsive(
9
+ padding: glib_json_padding_body,
10
+ childViews: ->(res) do
11
+ res.h2 text: 'Snackbars'
12
+ res.spacer height: 8
13
+ res.button(
14
+ text: 'snackbars/alert',
15
+ onClick: ->(action) do
16
+ action.snackbars_alert(
17
+ message: 'This is an alert snackbar. It closes automatically, so it should not display a close button.',
18
+ onClose: ->(subaction) do
19
+ subaction.dialogs_alert message: 'Closed'
20
+ end
21
+ )
22
+ end
23
+ )
24
+ res.spacer height: 4
25
+ res.button(
26
+ text: 'snackbars/alert with no timeout',
27
+ onClick: ->(action) do
28
+ action.snackbars_alert(
29
+ timeout: -1,
30
+ message: 'This is a persistent alert snackbar',
31
+ onClose: ->(subaction) do
32
+ subaction.dialogs_alert message: 'Closed'
33
+ end
34
+ )
35
+ end
36
+ )
37
+ res.spacer height: 4
38
+ res.button(
39
+ text: 'snackbars/select',
40
+ onClick: ->(action) do
41
+ action.snackbars_select(
42
+ styleClass: 'vertical',
43
+ message: 'This is a select snackbar with a really really really really long text',
44
+ onClose: ->(saction) { saction.dialogs_alert message: 'Closed' },
45
+ buttons: ->(menu) do
46
+ menu.button(
47
+ text: 'Option1',
48
+ onClick: ->(subaction) do
49
+ subaction.dialogs_alert message: 'Option 1'
50
+ end
51
+ )
52
+ menu.button(
53
+ text: 'Option2',
54
+ onClick: ->(subaction) do
55
+ subaction.dialogs_alert message: 'Option 2'
56
+ end
57
+ )
58
+ menu.button text: 'Cancel'
59
+ end
60
+ )
61
+ end
62
+ )
63
+ res.spacer height: 4
64
+ res.button(
65
+ text: 'snackbars with styling',
66
+ onClick: ->(action) do
67
+ action.snackbars_alert message: 'This is a styled snackbar', location: 'top', styleClass: 'success'
68
+ end
69
+ )
70
+ end
71
+ )
72
+ end
73
+ )
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.42.0
4
+ version: 4.42.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
@@ -317,7 +317,6 @@ files:
317
317
  - app/views/json_ui/garage/lists/edit_mode.json.jbuilder
318
318
  - app/views/json_ui/garage/lists/fab.json.jbuilder
319
319
  - app/views/json_ui/garage/lists/index.json.jbuilder
320
- - app/views/json_ui/garage/lists/old_edit_mode.json.jbuilder
321
320
  - app/views/json_ui/garage/lists/reordering.json.jbuilder
322
321
  - app/views/json_ui/garage/lists/templating.json.jbuilder
323
322
  - app/views/json_ui/garage/multistep_form/step1.json.jbuilder
@@ -380,10 +379,14 @@ files:
380
379
  - app/views/json_ui/garage/test_page/file_upload_new.json.jbuilder
381
380
  - app/views/json_ui/garage/test_page/form.json.jbuilder
382
381
  - app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder
382
+ - app/views/json_ui/garage/test_page/http.json.jbuilder
383
383
  - app/views/json_ui/garage/test_page/lifecycle.json.jbuilder
384
+ - app/views/json_ui/garage/test_page/list_editable.json.jbuilder
384
385
  - app/views/json_ui/garage/test_page/logics_set.json.jbuilder
385
386
  - app/views/json_ui/garage/test_page/multiupload.json.jbuilder
386
387
  - app/views/json_ui/garage/test_page/selectable.json.jbuilder
388
+ - app/views/json_ui/garage/test_page/sheets.json.jbuilder
389
+ - app/views/json_ui/garage/test_page/snackbars.json.jbuilder
387
390
  - app/views/json_ui/garage/test_page/window.json.jbuilder
388
391
  - app/views/json_ui/garage/views/_chart_data.json.jbuilder
389
392
  - app/views/json_ui/garage/views/_checkbox_expand.json.jbuilder
@@ -1,84 +0,0 @@
1
- page = json_ui_page json
2
-
3
- json.title 'Lists'
4
-
5
- render "#{@path_prefix}/nav_menu", json: json, page: page
6
-
7
- tab_index = params[:tab].to_i
8
-
9
- page.header(
10
- childViews: ->(header) do
11
- # Allow navigating to another "edit mode" page to test reuse issues.
12
- header.tabBar(
13
- buttons: ->(menu) do
14
- ['FIRST', 'SECOND'].each_with_index do |text, index|
15
- menu.button(
16
- text: text,
17
- disabled: tab_index == index,
18
- onClick: ->(action) do
19
- action.windows_reload url: json_ui_garage_url(path: 'lists/edit_mode', tab: index)
20
- end
21
- )
22
- end
23
- end
24
- )
25
- end
26
- )
27
-
28
- page.form(
29
- width: 'matchParent',
30
- url: json_ui_garage_url(path: 'forms/generic_post'),
31
- method: 'post',
32
- padding: glib_json_padding_body,
33
- childViews: ->(form) do
34
- form.panels_list(
35
- fieldPrefix: 'user[items]',
36
- fieldTitleName: 'name',
37
- width: 'matchParent',
38
- sections: [
39
- ->(section) do
40
- section.header(
41
- padding: glib_json_padding_list,
42
- childViews: ->(header) do
43
- header.panels_horizontal(
44
- childViews: ->(horizontal) do
45
- horizontal.fields_check name: 'user[check_all]', label: 'All', checkValue: true
46
- horizontal.spacer width: 20
47
- # header.fields_text width: 'matchParent', styleClass: 'outlined', name: 'user[new_name]', label: 'Item name'
48
- statuses = [:pending, :active]
49
- horizontal.fields_select(
50
- styleClass: 'outlined',
51
- name: 'user[status]',
52
- width: 'matchParent',
53
- label: 'Status',
54
- options: statuses.map { |status| { value: status, text: status.to_s.humanize } }
55
- )
56
- horizontal.spacer width: 20
57
- horizontal.fields_submit text: 'Update'
58
- end
59
- )
60
- end
61
- )
62
- section.rows(
63
- builder: ->(row) do
64
- batch_count = 20
65
- batch_count.times do |index|
66
- id = (batch_count * tab_index) + index
67
- # row.editable title: "Item #{id}", recordId: "PK_#{id}"
68
- row.thumbnail title: "Item #{id}", recordId: "PK_#{id}"
69
- end
70
- end
71
- )
72
- end
73
- ],
74
- fieldCheckValueIf: {
75
- "==": [
76
- {
77
- "var": 'user[check_all]'
78
- },
79
- true
80
- ]
81
- }
82
- )
83
- end
84
- )