glib-web 6.12.0 → 7.0.0.beta1

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/glib/auth/policy.rb +1 -5
  3. data/app/controllers/concerns/glib/json/libs.rb +0 -33
  4. data/app/helpers/glib/json_ui/action_builder/bottom_banners.rb +0 -1
  5. data/app/helpers/glib/json_ui/action_builder/components.rb +1 -16
  6. data/app/helpers/glib/json_ui/action_builder/logics.rb +1 -2
  7. data/app/helpers/glib/json_ui/action_builder/snackbars.rb +0 -16
  8. data/app/helpers/glib/json_ui/response_helper.rb +0 -12
  9. data/app/helpers/glib/json_ui/view_builder/fields.rb +4 -133
  10. data/app/helpers/glib/json_ui/view_builder/panels.rb +144 -8
  11. data/app/views/json_ui/garage/actions/_components.json.jbuilder +2 -2
  12. data/app/views/json_ui/garage/forms/bulk_edit_post.json.jbuilder +1 -1
  13. data/app/views/json_ui/garage/forms/checkboxes.json.jbuilder +3 -3
  14. data/app/views/json_ui/garage/forms/selects.json.jbuilder +5 -5
  15. data/app/views/json_ui/garage/forms/update_options.json.jbuilder +1 -1
  16. data/app/views/json_ui/garage/panels/flow.json.jbuilder +6 -6
  17. data/app/views/json_ui/garage/panels/horizontal.json.jbuilder +10 -10
  18. data/app/views/json_ui/garage/panels/responsive.json.jbuilder +10 -10
  19. data/app/views/json_ui/garage/panels/ul.json.jbuilder +2 -2
  20. data/app/views/json_ui/garage/panels/vertical.json.jbuilder +10 -10
  21. data/app/views/json_ui/garage/tables/bulk_edit.json.jbuilder +14 -14
  22. data/app/views/json_ui/garage/test_page/_header.json.jbuilder +1 -1
  23. data/app/views/json_ui/garage/test_page/calendar.json.jbuilder +6 -6
  24. data/app/views/json_ui/garage/test_page/carousel.json.jbuilder +4 -4
  25. data/app/views/json_ui/garage/test_page/charts.json.jbuilder +4 -4
  26. data/app/views/json_ui/garage/test_page/column.json.jbuilder +3 -3
  27. data/app/views/json_ui/garage/test_page/components.json.jbuilder +7 -7
  28. data/app/views/json_ui/garage/test_page/custom.json.jbuilder +3 -3
  29. data/app/views/json_ui/garage/test_page/dialog.json.jbuilder +0 -21
  30. data/app/views/json_ui/garage/test_page/fields_creditCard.json.jbuilder +6 -6
  31. data/app/views/json_ui/garage/test_page/fields_date_time.json.jbuilder +8 -8
  32. data/app/views/json_ui/garage/test_page/fields_dynamicSelect.json.jbuilder +8 -8
  33. data/app/views/json_ui/garage/test_page/fields_location.json.jbuilder +6 -6
  34. data/app/views/json_ui/garage/test_page/fields_otp.json.jbuilder +8 -8
  35. data/app/views/json_ui/garage/test_page/fields_phone.json.jbuilder +9 -9
  36. data/app/views/json_ui/garage/test_page/fields_radio.json.jbuilder +4 -4
  37. data/app/views/json_ui/garage/test_page/fields_rating.json.jbuilder +10 -10
  38. data/app/views/json_ui/garage/test_page/fields_richText.json.jbuilder +10 -10
  39. data/app/views/json_ui/garage/test_page/fields_select.json.jbuilder +8 -8
  40. data/app/views/json_ui/garage/test_page/fields_sign.json.jbuilder +8 -56
  41. data/app/views/json_ui/garage/test_page/fields_stripeExternalAccount.json.jbuilder +6 -6
  42. data/app/views/json_ui/garage/test_page/fields_stripeToken.json.jbuilder +6 -6
  43. data/app/views/json_ui/garage/test_page/fields_text.json.jbuilder +5 -5
  44. data/app/views/json_ui/garage/test_page/fields_timer.json.jbuilder +6 -6
  45. data/app/views/json_ui/garage/test_page/flow.json.jbuilder +6 -6
  46. data/app/views/json_ui/garage/test_page/form.json.jbuilder +9 -9
  47. data/app/views/json_ui/garage/test_page/grid.json.jbuilder +4 -4
  48. data/app/views/json_ui/garage/test_page/horizontal.json.jbuilder +5 -5
  49. data/app/views/json_ui/garage/test_page/image.json.jbuilder +9 -9
  50. data/app/views/json_ui/garage/test_page/lifecycle.json.jbuilder +6 -6
  51. data/app/views/json_ui/garage/test_page/list.json.jbuilder +6 -6
  52. data/app/views/json_ui/garage/test_page/multimedia_docx.json.jbuilder +5 -5
  53. data/app/views/json_ui/garage/test_page/multimedia_video.json.jbuilder +6 -6
  54. data/app/views/json_ui/garage/test_page/pagination.json.jbuilder +6 -6
  55. data/app/views/json_ui/garage/test_page/panels_bulkEdit2.json.jbuilder +244 -0
  56. data/app/views/json_ui/garage/test_page/panels_bulkEdit3.json.jbuilder +3 -3
  57. data/app/views/json_ui/garage/test_page/progressCircle.json.jbuilder +8 -8
  58. data/app/views/json_ui/garage/test_page/responsive.json.jbuilder +4 -4
  59. data/app/views/json_ui/garage/test_page/scroll.json.jbuilder +5 -5
  60. data/app/views/json_ui/garage/test_page/split.json.jbuilder +1 -1
  61. data/app/views/json_ui/garage/test_page/table.json.jbuilder +5 -5
  62. data/app/views/json_ui/garage/test_page/timeline.json.jbuilder +5 -5
  63. data/app/views/json_ui/garage/test_page/ul.json.jbuilder +4 -4
  64. data/app/views/json_ui/garage/test_page/vertical.json.jbuilder +5 -5
  65. data/app/views/json_ui/garage/test_page/web.json.jbuilder +5 -5
  66. data/app/views/json_ui/garage/views/_expand_shrink_ex.json.jbuilder +4 -4
  67. data/app/views/json_ui/garage/views/controls.json.jbuilder +4 -4
  68. data/app/views/json_ui/garage/views/{components_set.json.jbuilder → logics_set.json.jbuilder} +2 -2
  69. data/lib/glib/engine.rb +0 -32
  70. data/lib/glib/json_crawler/action_crawlers/forms_submit.rb +1 -1
  71. data/lib/glib/json_crawler/router.rb +0 -24
  72. data/lib/glib/json_ui/config.rb +1 -1
  73. data/lib/glib/test/changed_files_rubocop.rb +55 -0
  74. data/lib/glib/test_helpers.rb +7 -9
  75. data/lib/glib-web.rb +2 -4
  76. metadata +6 -37
  77. data/app/controllers/concerns/glib/auth/token_authenticatable.rb +0 -37
  78. data/app/controllers/glib/api_controller.rb +0 -52
  79. data/app/jobs/glib/purge_unattached_job.rb +0 -32
  80. data/app/models/concerns/glib/snapshot_v2.rb +0 -514
  81. data/app/models/glib/model.rb +0 -40
  82. data/app/models/glib/plain_model.rb +0 -23
  83. data/app/models/snapshot_blob_reference.rb +0 -20
  84. data/app/views/json_ui/garage/test_page/fields_change_trigger.json.jbuilder +0 -128
  85. data/lib/generators/glib/snapshot_v2_generator.rb +0 -54
  86. data/lib/generators/templates/snapshot_v2/add_snapshot_v2_columns.rb +0 -18
  87. data/lib/generators/templates/snapshot_v2/create_snapshot_blob_references.rb +0 -10
  88. data/lib/glib/purge_unattached/config.rb +0 -38
  89. data/lib/glib/test/full_suite_rubocop.rb +0 -87
  90. data/lib/glib/test/rubocop_guards.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 817984b6f3889911d4fbfa93df3612733ccdc32c424e1047d9fe3a578d771057
4
- data.tar.gz: dc7aa2551592c95d7808061645eb2c67c7d1c910e9acd994a4357db824ea33a5
3
+ metadata.gz: 38b655bd80465e224c8ed56c8e63694af4786e89a4028da3915737bf8ac96120
4
+ data.tar.gz: 6baa89e800c1223673fe1d9bbad61694e6a59c46123f42faff344d819e143dbf
5
5
  SHA512:
6
- metadata.gz: 1f271cf7c9b788dbd9c3cd6e7bafe9cbb020156194cd2f52a6db592616691aa80a297b8f214eb2622b7129abc408a876d8335153aef8cc4031a2e168c58257d2
7
- data.tar.gz: 52f8ae7045f853ffe7315eed140613d27080ee68868ce7fe603d43266d3968f600779283d6449fbf246066481eac6078925364a48f06c2438b288de5be44b8ad
6
+ metadata.gz: 30a1adca1ba187b2f23ff52f54b41259742e15582abf95404b07b658e6eb4a408e2a236b8ffe7b4b45459e7ad3abfb9f8017e8701887fe5b4f5028d045834d23
7
+ data.tar.gz: c4645773a9b5aea3d3b3ba0aea2e24c609e09b45bc7b4904b76bad3af3dd486ed9b4b1b38dba4b956da4620656127462970a1b73c960c231a21da42685fd6f71
@@ -22,11 +22,7 @@ module Glib::Auth
22
22
  # - Need to find a solution where we can reuse a single public policy
23
23
  # after_action :verify_authorized
24
24
 
25
- # Guarded because not every includer has `helper_method`: ActionController::Base
26
- # does, but bare ActionController::API does not (gems like jbuilder may include
27
- # ActionController::Helpers into it in a booted app, and a plain non-controller
28
- # class never has it). Same guard Pundit's own included block uses.
29
- helper_method :policy, :can?, :cannot? if respond_to?(:helper_method)
25
+ helper_method :policy, :can?, :cannot?
30
26
  end
31
27
 
32
28
  def assert_current_user_present
@@ -96,18 +96,6 @@ module Glib::Json::Libs
96
96
 
97
97
  json_ui_redirect_to url, status: status
98
98
  end
99
- # Any other request format (e.g. a browser tab opening a protected file link
100
- # `/temp_blobs/:id/report.pdf`, where the filename sets the format) used to raise
101
- # ActionController::UnknownFormat -> a bare 406 with an empty body. Mirror the html
102
- # branch instead: whatever opened the URL (browser tab, iframe, img) is redirected
103
- # to the target, which the browser follows natively.
104
- #
105
- # MUST stay the LAST block. `negotiate_mime` answers `Accept: */*` requests with
106
- # the FIRST declared format, so an `any` block placed before html would capture
107
- # ordinary browser page loads and leave request.format as `*/*` for the action.
108
- format.any do
109
- redirect_to url
110
- end
111
99
  end
112
100
  end
113
101
 
@@ -154,17 +142,6 @@ module Glib::Json::Libs
154
142
  redirect_to sign_in_url, **redirect_options
155
143
  end
156
144
  end
157
- # Other non-HTML/JSON formats (a protected file link whose filename sets the
158
- # request format, an XML export, ...) used to raise UnknownFormat -> a bare 406.
159
- # Mirrors the csv branch: a clean gate status in test, a redirect in production.
160
- # MUST stay the LAST block -- see glib_redirect_to.
161
- format.any do
162
- if Rails.env.test?
163
- head :unauthorized
164
- else
165
- redirect_to sign_in_url, **redirect_options
166
- end
167
- end
168
145
  end
169
146
  end
170
147
 
@@ -208,16 +185,6 @@ module Glib::Json::Libs
208
185
  redirect_to user_default_url
209
186
  end
210
187
  end
211
- # Other non-HTML/JSON formats -- mirrors the csv branch (clean gate status in
212
- # test, redirect in production); previously raised UnknownFormat -> a bare 406.
213
- # MUST stay the LAST block -- see glib_redirect_to.
214
- format.any do
215
- if Rails.env.test?
216
- head :forbidden
217
- else
218
- redirect_to user_default_url
219
- end
220
- end
221
188
  end
222
189
  end
223
190
 
@@ -1,4 +1,3 @@
1
- # deprecated
2
1
  class Glib::JsonUi::ActionBuilder
3
2
  module BottomBanners
4
3
  class Open < Action
@@ -25,7 +25,7 @@ class Glib::JsonUi::ActionBuilder
25
25
  views :childViews
26
26
  end
27
27
 
28
- # Consider deprecating this. See `components_set` example in app/views/json_ui/garage/forms/selects.json.jbuilder
28
+ # Consider deprecating this. See `logics_set` example in app/views/json_ui/garage/forms/selects.json.jbuilder
29
29
  class Replace < Action
30
30
  string :targetId
31
31
  action :onReplace
@@ -37,20 +37,5 @@ class Glib::JsonUi::ActionBuilder
37
37
  end
38
38
  end
39
39
 
40
- # Deprecated: use `logics_set` instead. `logics_set` is a superset of this
41
- # action (same `targetId`/`data`/`onSet`/`skipOnChange`, plus
42
- # `conditionalData`, `targetIds`, `variables` and `cacheData`).
43
- class Set < Action
44
- string :targetId
45
- hash :data
46
- action :onSet
47
- bool :skipOnChange
48
-
49
- def dataBuilder(block)
50
- json.data do
51
- block.call(page.sview_builder)
52
- end
53
- end
54
- end
55
40
  end
56
41
  end
@@ -1,7 +1,6 @@
1
1
  class Glib::JsonUi::ActionBuilder
2
2
  module Logics
3
- # The canonical setter action. Prefer this over the deprecated
4
- # `components_set`: it supports plain static `data` as well as
3
+ # The canonical setter action. It supports plain static `data` as well as
5
4
  # `conditionalData` (JsonLogic), `targetIds`, `variables` and `cacheData`.
6
5
  class Set < Action
7
6
  string :targetId
@@ -7,31 +7,15 @@ class Glib::JsonUi::ActionBuilder
7
7
  int :timeout
8
8
  action :onClose
9
9
 
10
- string :verticalPosition # Deprecated
11
- string :horizontalPosition # Deprecated
12
-
13
10
  string :location
14
11
  singleton_array :styleClass, :styleClasses
15
12
  end
16
13
 
17
- # class Confirm < Action
18
- # string :message
19
- # action :onConfirm
20
- # action :onCancel
21
-
22
- # string :verticalPosition
23
- # string :horizontalPosition
24
- # singleton_array :styleClass, :styleClasses
25
- # end
26
-
27
14
  class Select < Action
28
15
  string :message
29
16
  int :timeout
30
17
  action :onClose
31
18
 
32
- string :verticalPosition # Deprecated
33
- string :horizontalPosition # Deprecated
34
-
35
19
  string :location
36
20
  singleton_array :styleClass, :styleClasses
37
21
 
@@ -10,18 +10,6 @@ module Glib
10
10
  end
11
11
  end
12
12
 
13
- # Deprecated
14
- def json_ui_response_with_view(json)
15
- json.analytics do
16
- json.disabled true
17
- end
18
-
19
- json.onResponse do
20
- response = Glib::JsonUi::PageHelper::Page.new(json, self)
21
- yield response.action_builder
22
- end
23
- end
24
-
25
13
  class Response
26
14
  attr_reader :action_builder
27
15
 
@@ -211,51 +211,6 @@ class Glib::JsonUi::ViewBuilder
211
211
  end
212
212
  end
213
213
 
214
- # Timing options for the `onChange` / `onChangeAndLoad` actions of fields
215
- # whose value changes while the user is still typing.
216
- #
217
- # Honoured by the single-line text family (text, number, email, url,
218
- # password -- all one Vue component) and by textarea. Other fields ignore
219
- # them: the discrete ones (check, radio, select, chip group, date pickers)
220
- # commit a value the moment it is picked, and richText/phone/file keep
221
- # their own reporting. The options are declared on `Text`, so its other
222
- # subclasses accept them without effect -- same as `leftIcon` and
223
- # `onTypeStart` there.
224
- #
225
- # In both modes the change is also reported when focus leaves the field.
226
- # Under `input` that only flushes a change already pending in the debounce
227
- # window -- it cannot produce a second run, because a value that was
228
- # already reported is not reported again.
229
- module ChangeTrigger
230
- def self.included(base)
231
- # When the `onChange` / `onChangeAndLoad` actions fire.
232
- #
233
- # - `input` (default): after the user pauses typing for `changeDelay`.
234
- # - `blur`: only once focus leaves the field. Use for actions too
235
- # expensive to run mid-word -- a server round trip, a recalculation
236
- # that rewrites other fields under the user's cursor.
237
- #
238
- # @example Round-trip only once the user is done with the field
239
- # form.fields_text \
240
- # name: 'user[promo_code]',
241
- # label: 'Promo code',
242
- # changeOn: 'blur',
243
- # onChange: ->(action) { action.http_get url: verify_promo_path }
244
- base.enum :changeOn, options: %w[input blur]
245
-
246
- # Milliseconds of no typing before `changeOn: 'input'` fires.
247
- # Defaults to 300. Ignored under `changeOn: 'blur'`.
248
- #
249
- # @example Slow down a search-as-you-type round trip
250
- # form.fields_text \
251
- # name: 'q',
252
- # label: 'Search',
253
- # changeDelay: 800,
254
- # onChange: ->(action) { action.http_get url: search_path }
255
- base.int :changeDelay
256
- end
257
- end
258
-
259
214
  # Basic text input field for single-line text entry.
260
215
  #
261
216
  # The most common field type, supporting various customizations like
@@ -273,8 +228,6 @@ class Glib::JsonUi::ViewBuilder
273
228
  #
274
229
  # @see app/views/json_ui/garage/forms/basic.json.jbuilder Garage example
275
230
  class Text < AbstractField
276
- include ChangeTrigger
277
-
278
231
  # Maximum number of characters allowed.
279
232
  int :maxLength
280
233
 
@@ -706,10 +659,7 @@ class Glib::JsonUi::ViewBuilder
706
659
  class ChipGroup < AbstractField
707
660
  color :color
708
661
  array :options
709
- # cache: true for the same reason as Upload's — without it a
710
- # multiple-selection ChipGroup submits under a single key and only the
711
- # last selected value survives server-side parsing.
712
- bool :multiple, cache: true
662
+ bool :multiple
713
663
  end
714
664
 
715
665
  class TimeZone < AbstractField
@@ -847,11 +797,7 @@ class Glib::JsonUi::ViewBuilder
847
797
  include Glib::JsonUi::FileUploadErrorHandler
848
798
 
849
799
  action :onFinishUpload
850
- # cache: true so `@multiple` is set before AbstractField#created computes
851
- # the submit key — without it the `[]` array suffix is dropped and a
852
- # multi-file submit collapses to the last file server-side (silently
853
- # saves one Document per drop instead of one per file).
854
- bool :multiple, cache: true
800
+ bool :multiple
855
801
  hash :placeholderView, required: [:type, :width, :height], optional: [:url]
856
802
  hash :inputView, optional: [:files, :variant]
857
803
  hash :clipboardView, optional: [:icon]
@@ -909,88 +855,13 @@ class Glib::JsonUi::ViewBuilder
909
855
  end
910
856
  end
911
857
 
912
- # Signature field that lets the user draw a signature on a canvas and
913
- # upload it as an image via ActiveStorage direct upload.
914
- #
915
- # The form shows a placeholder sized by `width`/`height`; tapping it opens
916
- # a dialog with the drawing canvas, and Confirm uploads the drawing as a
917
- # PNG to `directUploadUrl` and submits the resulting signed id under
918
- # `name`. The placeholder then shows the signature, and reopening the
919
- # dialog lets the user edit it. Because the canvas is not in the form,
920
- # the placeholder can be given the same height as a neighbouring input
921
- # (e.g. a typed-name alternative) without losing drawing space.
922
- #
923
- # `label` titles the dialog; `placeholder` replaces the "Tap to sign"
924
- # prompt.
925
- #
926
- # With `typedName` the dialog gains Draw/Type tabs: the user can type
927
- # their name instead of drawing, and the placeholder then shows the name
928
- # in a handwriting style (the `glib-sign-typed` hook carries the font).
929
- # The typed name is submitted under `typedName[:name]`; exactly one of the
930
- # signed id and the typed name is ever non-blank. `typedName[:value]`
931
- # pre-populates an existing name.
932
- #
933
- # @example Basic signature field
934
- # form.fields_sign \
935
- # name: 'user[signature]',
936
- # label: 'Your signature',
937
- # directUploadUrl: glib_direct_uploads_url,
938
- # width: 320,
939
- # height: 160,
940
- # validation: { required: { message: 'add your signature!' } }
941
- #
942
- # @example Drawn or typed
943
- # form.fields_sign \
944
- # name: 'user[signature]',
945
- # label: 'Your signature',
946
- # typedName: { name: 'user[signature_name]', label: 'Type your full name', placeholder: 'Full name' },
947
- # directUploadUrl: glib_direct_uploads_url,
948
- # width: 320,
949
- # height: 160
950
- #
951
- # @example Compact signature without validation
952
- # form.fields_sign \
953
- # id: 'signature_compact',
954
- # name: 'user[signature_compact]',
955
- # directUploadUrl: glib_direct_uploads_url,
956
- # width: 220,
957
- # height: 100
958
- #
959
- # @note A saved signature is a signed id that cannot be redrawn from the
960
- # canvas: when a record is re-edited the field pre-populates `value`
961
- # with the existing attachment's signed id (the component renders a
962
- # read-only preview instead of ink). An untouched field therefore
963
- # re-submits that same signed id, so consumers should treat a
964
- # same-signed-id re-attach as "no change". Resize it via
965
- # `components_set`/`logics_set` with `width`/`height`.
966
- #
967
- # @see app/views/json_ui/garage/test_page/fields_sign.json.jbuilder Garage examples
968
858
  class Sign < AbstractField
969
859
  string :directUploadUrl
970
860
  required :directUploadUrl
971
861
 
972
- # URL of an already-attached signature image to display as a read-only
973
- # preview (typically `file_url(attachment)` when re-editing a record
974
- # that has a drawn signature). Pair with `prop:` so the field also
975
- # submits the existing signed id when left untouched.
976
- string :fileUrl
977
-
978
- # Enables the Type tab. `value` pre-populates the previously typed
979
- # name; `label`/`placeholder` decorate the text input in the dialog.
980
- # `name` is still accepted but unused: the typed name is submitted
981
- # through the field's own param (single-param contract), and the
982
- # server routes it to the name attribute.
983
- hash :typedName, optional: [:name, :value, :label, :placeholder]
984
-
985
862
  # Override
986
- # A stored signature is an attachment -- submit its signed id (mirrors
987
- # File#determine_value) so re-editing a signed submission pre-fills the
988
- # field with the existing blob's signed id instead of serializing the
989
- # whole attachment proxy.
990
- def determine_value(context, prop)
991
- if (value = context.field_value(prop)).attached?
992
- value.signed_id || ''
993
- end
863
+ # Signature field doesn't have default value
864
+ def value(value)
994
865
  end
995
866
  end
996
867
 
@@ -493,23 +493,129 @@ class Glib::JsonUi::ViewBuilder
493
493
  end
494
494
  end
495
495
 
496
- # The generic bulk-edit table: form-peer submission, per-row POSTs,
497
- # viewCells/viewHeaders, plus the full parse/gate/dedup config so any
498
- # consumer can compose specialized import behavior (it replaced the
499
- # retired bulkEdit2 and bulkEditSg -- see the component's header comment
500
- # for the full mapping).
501
- class BulkEdit3 < View
496
+ class BulkEdit2 < View
502
497
  hash :import, required: [:submitUrl, :paramName]
503
498
 
504
499
  # Override
505
500
  def created
506
- # bulkEdit3 is a form PEER, not a form child: it answers the same
501
+ # bulkEdit2 is a form PEER, not a form child: it answers the same
507
502
  # forms_submit action a form does (forms/directSubmit) and submits its
508
503
  # rows itself to `import.submitUrl`. Inside a form, its repeated
509
504
  # per-row input names would collide with the form's FormData scans
510
505
  # (duplicate-name warnings, dirty-baseline pollution) while adding
511
506
  # nothing to the form's submission -- so, like a nested form, reject
512
507
  # the arrangement at render time.
508
+ if page.current_form
509
+ raise 'panels_bulkEdit2 is not allowed inside a form; place it as a sibling and target it with forms_submit targetId'
510
+ end
511
+ end
512
+
513
+ # Maximum number of data rows an uploaded file may contain; files with
514
+ # more rows are rejected with an error dialog. No limit when unset.
515
+ int :maxRows
516
+
517
+ # string :statusViewIdPrefix
518
+ array :viewHeaders
519
+
520
+ # string :paramNameForFormData
521
+ string :paramNameForRowId
522
+ action :onCellChange
523
+ action :onRowsLoad
524
+ action :onRowsSelect
525
+ action :onRowsSucceed
526
+ action :onSubmitStart
527
+ action :onSubmitEnd
528
+
529
+ # Experimental
530
+ # panels_builder :accessory, :header, :footer
531
+
532
+ def viewCells(values)
533
+ json.set! 'viewCells' do
534
+ values.each do |value|
535
+ value.call(page.view_builder)
536
+ end
537
+ end
538
+ end
539
+
540
+ def dataRows(value)
541
+ json.dataRows value
542
+ end
543
+
544
+ def viewRows(value)
545
+ json.viewRows value
546
+ end
547
+
548
+ end
549
+
550
+ # The Surfguard members-import table: a fork of bulkEdit2's builder for
551
+ # glib-web-npm's bulkEditSg component (same skeleton — form-peer
552
+ # submission, per-row POSTs, viewCells/viewHeaders — plus the Surfguard
553
+ # parse inputs). Deliberately SG-specific; consolidate with BulkEdit2 when
554
+ # a second consumer proves which props generalize.
555
+ class BulkEditSg < View
556
+ hash :import, required: [:submitUrl, :paramName]
557
+
558
+ # Override
559
+ def created
560
+ # Same form-peer rule as bulkEdit2 (see its comment).
561
+ if page.current_form
562
+ raise 'panels_bulkEditSg is not allowed inside a form; place it as a sibling and target it with forms_submit targetId'
563
+ end
564
+ end
565
+
566
+ # Maximum number of data rows an uploaded file may contain; files with
567
+ # more rows are rejected with an error dialog. No limit when unset.
568
+ int :maxRows
569
+
570
+ array :viewHeaders
571
+
572
+ # string :paramNameForFormData
573
+ string :paramNameForRowId
574
+ action :onCellChange
575
+ action :onRowsLoad
576
+ action :onRowsSelect
577
+ action :onRowsSucceed
578
+ # Fires when non-importable rows drain out of the table on save (they
579
+ # never POST), so the page can keep its Loaded/Uploaded/Skipped
580
+ # arithmetic honest.
581
+ action :onRowsSkip
582
+ action :onSubmitStart
583
+ action :onSubmitEnd
584
+
585
+ # Surfguard file shape: CSV header => form field. The parser follows
586
+ # this map, so a header-spelling fix is a one-line server change.
587
+ hash :headerMap
588
+ array :requiredHeaders
589
+ string :statusFilter
590
+
591
+ def viewCells(values)
592
+ json.set! 'viewCells' do
593
+ values.each do |value|
594
+ value.call(page.view_builder)
595
+ end
596
+ end
597
+ end
598
+
599
+ def dataRows(value)
600
+ json.dataRows value
601
+ end
602
+
603
+ def viewRows(value)
604
+ json.viewRows value
605
+ end
606
+
607
+ end
608
+
609
+ # The generic bulk-edit table: bulkEdit2's skeleton plus everything
610
+ # bulkEditSg hard-coded for the Surfguard imports, expressed as config so
611
+ # any consumer can compose the same behavior (bulkEditSg's replacement --
612
+ # see the component's header comment for the full mapping).
613
+ class BulkEdit3 < View
614
+ hash :import, required: [:submitUrl, :paramName]
615
+
616
+ # Override
617
+ def created
618
+ # Same form-peer rule as bulkEdit2 (see its comment).
513
619
  if page.current_form
514
620
  raise 'panels_bulkEdit3 is not allowed inside a form; place it as a sibling and target it with forms_submit targetId'
515
621
  end
@@ -535,7 +641,7 @@ class Glib::JsonUi::ViewBuilder
535
641
  action :onSubmitEnd
536
642
 
537
643
  # The file shape. Without it the table takes plain CSVs (header row
538
- # first, header names = field ids):
644
+ # first, header names = field ids -- the bulkEdit2 shape):
539
645
  # detectHeader: { allOf: [...], anyOf: [...], scanRows: N } -- find
540
646
  # the header row BY CONTENT (report preambles survive);
541
647
  # headerMap: { csvHeader => field } -- header-keyed, order-independent
@@ -610,6 +716,36 @@ class Glib::JsonUi::ViewBuilder
610
716
 
611
717
  end
612
718
 
719
+ # deprecated
720
+ class BulkEdit < View
721
+ hash :nextPage
722
+ hash :export, required: [:label, :fileName]
723
+ hash :import, required: [:submitUrl, :paramName]
724
+ action :onScrollToTop
725
+ action :onScrollToBottom
726
+ string :statusViewIdPrefix
727
+ action :onLoadRows
728
+
729
+ # Experimental
730
+ panels_builder :accessory, :header, :footer
731
+
732
+ def firstSection(block)
733
+ json.sections [1] do
734
+ block.call page.table_section_builder
735
+ end
736
+ end
737
+
738
+ def sections(blocks)
739
+ json.sections do
740
+ blocks.each do |block|
741
+ json.child! do
742
+ block.call page.table_section_builder
743
+ end
744
+ end
745
+ end
746
+ end
747
+ end
748
+
613
749
  # Scrollable container panel for content that exceeds viewport height.
614
750
  #
615
751
  # Scroll panels automatically add scrollbars when content overflows.
@@ -8,7 +8,7 @@ section.rows builder: ->(template) do
8
8
  action.windows_open url: json_ui_garage_url(path: 'views/components_replace')
9
9
  end
10
10
 
11
- template.thumbnail title: 'components/set', onClick: ->(action) do
12
- action.windows_open url: json_ui_garage_url(path: 'views/components_set')
11
+ template.thumbnail title: 'logics/set', onClick: ->(action) do
12
+ action.windows_open url: json_ui_garage_url(path: 'views/logics_set')
13
13
  end
14
14
  end
@@ -5,7 +5,7 @@ json_ui_response json do |action|
5
5
  status_view_id = "row_status_#{params[:_index]}"
6
6
  # TODO: This doesn't work. It should update the status icon to indicate that the
7
7
  # import is successful.
8
- multiple.components_set \
8
+ multiple.logics_set \
9
9
  targetId: status_view_id,
10
10
  data: {
11
11
  color: 'success',
@@ -109,13 +109,13 @@ page.scroll childViews: ->(scroll) do
109
109
  childViews: ->(group) do
110
110
  group.panels_flow id: 'flow', xs: { gap: { all: 8 } }, backgroundColor: 'grey', width: 'matchParent', childViews: ->(flow) do
111
111
  flow.fields_check checkValue: '1', label: 'Choice 1', onChange: ->(action) do
112
- action.components_set targetId: 'flow', data: { backgroundColor: '#ff0000' }
112
+ action.logics_set targetId: 'flow', data: { backgroundColor: '#ff0000' }
113
113
  end
114
114
  flow.fields_check checkValue: '2', label: 'Choice 2', onChange: ->(action) do
115
- action.components_set targetId: 'flow', data: { backgroundColor: '#00ff00' }
115
+ action.logics_set targetId: 'flow', data: { backgroundColor: '#00ff00' }
116
116
  end
117
117
  flow.fields_check checkValue: '3', label: 'Choice 3', onChange: ->(action) do
118
- action.components_set targetId: 'flow', data: { backgroundColor: '#0000ff' }
118
+ action.logics_set targetId: 'flow', data: { backgroundColor: '#0000ff' }
119
119
  end
120
120
  end
121
121
  end
@@ -38,9 +38,9 @@ page.form(
38
38
  action.components_findClosest(
39
39
  view: 'panels/responsive',
40
40
  onFind: ->(find) do
41
- # This is to demonstrate that `components_set` can be a more flexible version
41
+ # This is to demonstrate that `logics_set` can be a more flexible version
42
42
  # of `components_replace`.
43
- find.components_set(
43
+ find.logics_set(
44
44
  dataBuilder: ->(set) do
45
45
  # Don't update the childViews, just update the footer
46
46
  set.panels_responsive(
@@ -159,7 +159,7 @@ page.form(
159
159
  action.components_findClosest(
160
160
  view: 'fields/select',
161
161
  onFind: ->(subaction) do
162
- subaction.components_set(
162
+ subaction.logics_set(
163
163
  data: { value: 'pt' },
164
164
  onSet: ->(subsubaction) do
165
165
  subsubaction.fields_blur
@@ -194,7 +194,7 @@ page.form(
194
194
  text: 'Reset',
195
195
  styleClass: 'text-primary',
196
196
  onClick: ->(action) do
197
- action.components_set targetId: 'lang', data: { value: [] }
197
+ action.logics_set targetId: 'lang', data: { value: [] }
198
198
  end
199
199
  )
200
200
  hori.spacer width: 16
@@ -276,7 +276,7 @@ page.form(
276
276
  form.button(
277
277
  text: 'Swap spec options',
278
278
  onClick: ->(action) do
279
- action.components_set targetId: 'spec_options_select', data: { 'spec.options' => spec_options_alt }
279
+ action.logics_set targetId: 'spec_options_select', data: { 'spec.options' => spec_options_alt }
280
280
  end
281
281
  )
282
282
  form.button text: 'Submit', onClick: ->(action) { action.forms_submit }
@@ -7,7 +7,7 @@ options = {
7
7
  }[value]
8
8
 
9
9
  json_ui_response json do |action|
10
- action.components_set targetId: 'options', data: {
10
+ action.logics_set targetId: 'options', data: {
11
11
  options: options
12
12
  }
13
13
  end
@@ -17,10 +17,10 @@ page.scroll padding: glib_json_padding_body, childViews: ->(scroll) do
17
17
  panel.button text: '3'
18
18
  end
19
19
  scroll.button text: 'show', onClick: ->(action) do
20
- action.components_set targetId: 'flow1', data: { displayed: true }
20
+ action.logics_set targetId: 'flow1', data: { displayed: true }
21
21
  end
22
22
  scroll.button text: 'hide', onClick: ->(action) do
23
- action.components_set targetId: 'flow1', data: { displayed: false }
23
+ action.logics_set targetId: 'flow1', data: { displayed: false }
24
24
  end
25
25
 
26
26
  scroll.h1 text: 'Basic'
@@ -86,8 +86,8 @@ page.scroll padding: glib_json_padding_body, childViews: ->(scroll) do
86
86
  backgroundColor: '#b3bac2',
87
87
  onMouseEnter: ->(action) {
88
88
  action.runMultiple childActions: ->(saction) do
89
- saction.components_set targetId: 'default1', data: { displayed: false }
90
- saction.components_set targetId: 'hovered1', data: { displayed: true }
89
+ saction.logics_set targetId: 'default1', data: { displayed: false }
90
+ saction.logics_set targetId: 'hovered1', data: { displayed: true }
91
91
  end
92
92
  }, childViews: ->(panel) do
93
93
  panel.label text: 'Hover me'
@@ -99,8 +99,8 @@ page.scroll padding: glib_json_padding_body, childViews: ->(scroll) do
99
99
  backgroundColor: '#b3bac2',
100
100
  onMouseLeave: ->(action) {
101
101
  action.runMultiple childActions: ->(saction) do
102
- saction.components_set targetId: 'default1', data: { displayed: true }
103
- saction.components_set targetId: 'hovered1', data: { displayed: false }
102
+ saction.logics_set targetId: 'default1', data: { displayed: true }
103
+ saction.logics_set targetId: 'hovered1', data: { displayed: false }
104
104
  end
105
105
  }, childViews: ->(panel) do
106
106
  panel.label text: 'Hover active'