glib-web 5.0.7 → 5.0.8

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: 6885a1b150291881895f5b3cdfda3c2424961e5fd953e7faebd2d421242d930c
4
- data.tar.gz: 2c9b74ee8ac40b6b887d3b14a06915b98b0562a207d578d63b955a12b7e235f9
3
+ metadata.gz: ad9135a710cddc6a961596146b40ff9a33f91357de190b94843ed054d8bb6188
4
+ data.tar.gz: 0e01d6df7e9a7a3db6b4cbb380970e6e68a75797559e4341e6fab1212626f187
5
5
  SHA512:
6
- metadata.gz: 67e1d34169a5cea5970378da912372ff6b0c5b1e0b7793c9e05fc50347cd1e118f55d0b9b63a328a60fdff810c913ba94575198eee0cd147d161feb8272c6c27
7
- data.tar.gz: dadf602d4b72f9c2c2ef4434182c94eb6690f05e1ec934226de01d8c24cef88719f29cba394d12c5e13df93e82144f46ae8425443b4318807780bcde5381d761
6
+ metadata.gz: b33f64738a36e98fb1dccd6422a9dc0c0acdb5ad91f1d6c0276dd672314130809d2dc668129b888599ac4696a0c57b3c1bbfa82998937f705cdcec125ae96c37
7
+ data.tar.gz: 3949cb0ee96b5955f716eea9d423e2ae7564d8b41ee9f2cbd482535bd332716a539c0ef5492be88de4cc4ca6ec27e4f4597eb8b51ee4290c181aefbf5345ec59
@@ -182,6 +182,12 @@ module Glib
182
182
  hash :row
183
183
  action :onAppend
184
184
  end
185
+
186
+ class Remove < Action
187
+ string :targetId
188
+ string :rowId
189
+ action :onRemove
190
+ end
185
191
  end
186
192
 
187
193
  module Cookies
@@ -10,6 +10,11 @@ module Glib
10
10
  module SoftDeletable
11
11
  extend ActiveSupport::Concern
12
12
 
13
+ # Dedicated deprecator so warnings can be silenced/behave independently of Rails'.
14
+ def self.deprecator
15
+ @deprecator ||= ActiveSupport::Deprecation.new('6.0', 'glib-web')
16
+ end
17
+
13
18
  module ClassMethods
14
19
  def auto_hide_soft_deleted_records
15
20
  column_name = :deleted_at
@@ -28,6 +33,44 @@ module Glib
28
33
  def __mark_for_destruction_behaviour
29
34
  @__glib_mark_for_destruction_behaviour ||= :disallowed
30
35
  end
36
+
37
+ # Declarative way to register associations that should be soft-deleted together with this record.
38
+ # Preferred over overriding `soft_deletable_associations` because it lets us validate at load time
39
+ # (in dev/test) that each association exists and its model is itself soft-deletable.
40
+ #
41
+ # NOTE: Declare the associations (e.g. `has_many`) *before* calling this so the reflection exists.
42
+ def soft_deletes_with(*association_names)
43
+ @__glib_soft_deletable_associations = association_names.map(&:to_sym)
44
+
45
+ if Rails.env.development? || Rails.env.test?
46
+ @__glib_soft_deletable_associations.each { |name| __validate_soft_deletable_association!(name) }
47
+ end
48
+ end
49
+
50
+ def __soft_deletable_associations
51
+ @__glib_soft_deletable_associations ||= []
52
+ end
53
+
54
+ def __validate_soft_deletable_association!(name)
55
+ reflection = reflect_on_association(name)
56
+ unless reflection
57
+ raise(
58
+ ActiveRecord::ConfigurationError,
59
+ "#{self.name} declares `soft_deletes_with #{name.inspect}` but has no association `#{name}`. " \
60
+ 'Make sure the association is declared before `soft_deletes_with`.'
61
+ )
62
+ end
63
+
64
+ return if reflection.polymorphic? # Can't resolve a single target class for polymorphic associations.
65
+
66
+ return if reflection.klass.include?(Glib::SoftDeletable)
67
+
68
+ raise(
69
+ ActiveRecord::ConfigurationError,
70
+ "#{self.name} declares `soft_deletes_with #{name.inspect}`, " \
71
+ "but #{reflection.klass.name} does not include Glib::SoftDeletable"
72
+ )
73
+ end
31
74
  end
32
75
 
33
76
  included do
@@ -85,6 +128,8 @@ module Glib
85
128
  # records are updated/validated one at a time.
86
129
  # Besides, it's probably not a good idea to prevent soft-deletion of objects that are already not valid.
87
130
  def _soft_destroy
131
+ return if deleted? # Already soft-deleted. Stop here to avoid redundant work and infinite recursion on circular associations.
132
+
88
133
  before_soft_destroy
89
134
  update_columns(deleted_at: DateTime.current) # rubocop:disable Rails/SkipsModelValidations
90
135
  after_soft_destroy
@@ -92,6 +137,8 @@ module Glib
92
137
  end
93
138
 
94
139
  def _undo_soft_destroy
140
+ return unless deleted? # Already restored. Stop here to avoid redundant work and infinite recursion on circular associations.
141
+
95
142
  undo_soft_destroy_associated_records
96
143
  update_columns(deleted_at: nil) # rubocop:disable Rails/SkipsModelValidations
97
144
  end
@@ -130,25 +177,48 @@ module Glib
130
177
  end
131
178
 
132
179
  def soft_destroy_associated_records
133
- soft_deletable_associations.each do |relationship|
134
- process_soft_deletable_relationship(relationship) { |record| record._soft_destroy }
180
+ __resolved_soft_deletable_associations.each do |relationship|
181
+ process_soft_deletable_relationship(relationship) do |record|
182
+ assert_soft_deletable!(relationship, record)
183
+ record._soft_destroy
184
+ end
135
185
  end
136
186
  end
137
187
 
138
188
  def undo_soft_destroy_associated_records
139
- soft_deletable_associations.each do |relationship|
140
- process_soft_deletable_relationship(relationship) { |record| record._undo_soft_destroy }
189
+ __resolved_soft_deletable_associations.each do |relationship|
190
+ process_soft_deletable_relationship(relationship) do |record|
191
+ assert_soft_deletable!(relationship, record)
192
+ record._undo_soft_destroy
193
+ end
141
194
  end
142
195
  end
143
196
 
144
- # This should return an array of all associated records of an object that
145
- # should be soft deleted with it. This provides a non-intrusive way that can
146
- # co-exist with hard deletion (e.g. using `dependent: :destroy`), which means
147
- # that the model can use both for different purposes.
148
- # For example, a model may use soft-deletion for archiving and hard-deletion
149
- # for editing (e.g. using `accepts_nested_attributes_for``)
150
- def soft_deletable_associations
151
- []
197
+ # Resolves the associations to soft-delete. Honours a (deprecated) override of
198
+ # `soft_deletable_associations` if present, warning the caller; otherwise returns
199
+ # the associations declared via `soft_deletes_with`.
200
+ def __resolved_soft_deletable_associations
201
+ if method(:soft_deletable_associations).owner != Glib::SoftDeletable
202
+ Glib::SoftDeletable.deprecator.warn(
203
+ "Overriding `#{self.class.name}#soft_deletable_associations` is deprecated and will be " \
204
+ 'removed; declare associations with the `soft_deletes_with` class macro instead.'
205
+ )
206
+ end
207
+
208
+ soft_deletable_associations
209
+ end
210
+
211
+ # Fail fast (and clearly) when an association is registered in `soft_deletable_associations`
212
+ # but its model doesn't include `Glib::SoftDeletable`. Otherwise this would surface as an
213
+ # obscure `NoMethodError` for `_soft_destroy`/`_undo_soft_destroy`.
214
+ def assert_soft_deletable!(relationship, record)
215
+ return if record.respond_to?(:_soft_destroy)
216
+
217
+ raise(
218
+ ActiveRecord::ConfigurationError,
219
+ "#{self.class.name} registers `#{relationship}` in `soft_deletable_associations`, " \
220
+ "but #{record.class.name} does not include Glib::SoftDeletable"
221
+ )
152
222
  end
153
223
 
154
224
  def before_soft_destroy
@@ -159,5 +229,20 @@ module Glib
159
229
  # To be overridden
160
230
  end
161
231
  end
232
+
233
+ # Returns the names of associations that should be soft deleted together with this record.
234
+ #
235
+ # Declare them with the `soft_deletes_with` class macro (validated at load time). This provides
236
+ # a non-intrusive way that can co-exist with hard deletion (e.g. using `dependent: :destroy`),
237
+ # which means that the model can use both for different purposes. For example, a model may use
238
+ # soft-deletion for archiving and hard-deletion for editing (e.g. `accepts_nested_attributes_for`).
239
+ #
240
+ # @deprecated Overriding this instance method is deprecated; use `soft_deletes_with` instead.
241
+ # Kept as a module method (rather than defined in `included do`) so that overrides in the
242
+ # host class are detectable, letting us emit a deprecation warning.
243
+ private
244
+ def soft_deletable_associations
245
+ self.class.__soft_deletable_associations
246
+ end
162
247
  end
163
248
  end
@@ -11,7 +11,6 @@ nav_groups = {
11
11
  'http',
12
12
  'lifecycle',
13
13
  'logics_set',
14
- 'lists_append',
15
14
  'panels',
16
15
  'popovers',
17
16
  'sheets',
@@ -142,6 +142,29 @@ page.body(
142
142
  res.hr width: 'matchParent'
143
143
  res.spacer height: 16
144
144
 
145
+ res.h2 text: 'Tooltip affordance'
146
+ res.spacer height: 8
147
+ res.label text: 'A checkbox whose label carries a tooltip gets a dotted-underline "hover for info" affordance.'
148
+ res.spacer height: 8
149
+ res.fields_check(
150
+ id: 'field_terms',
151
+ name: 'user[terms]',
152
+ label: 'Accept terms and conditions',
153
+ checkValue: true,
154
+ tooltip: { text: 'You must accept the terms before continuing.' }
155
+ )
156
+ res.spacer height: 8
157
+ res.fields_check(
158
+ id: 'field_news',
159
+ name: 'user[news]',
160
+ label: 'Subscribe to newsletter (no tooltip)',
161
+ checkValue: true
162
+ )
163
+
164
+ res.spacer height: 16
165
+ res.hr width: 'matchParent'
166
+ res.spacer height: 16
167
+
145
168
  res.h2 text: 'Edge/advanced'
146
169
  res.spacer height: 8
147
170
  res.button(
@@ -204,6 +204,10 @@ page.body(
204
204
  action.components_set targetId: 'datetime_plain', data: { template: { type: 'plain' } }
205
205
  end
206
206
  )
207
+ flow.spacer width: 8
208
+ flow.fields_submit(
209
+ text: 'Submit'
210
+ )
207
211
  end
208
212
  )
209
213
  end
@@ -34,6 +34,13 @@ page.body(
34
34
  option.merge(imageUrl: "https://picsum.photos/seed/select-#{index + 1}/32/24")
35
35
  end
36
36
 
37
+ subtitle_options = [
38
+ { text: 'Apple', value: 'apple', subtitle: 'A sweet red fruit' },
39
+ { text: 'Banana', value: 'banana', subtitle: 'A long yellow fruit' },
40
+ { text: 'Carrot', value: 'carrot', subtitle: 'An orange root vegetable' },
41
+ { text: 'Daikon', value: 'daikon', subtitle: 'A white root vegetable' }
42
+ ]
43
+
37
44
  form.h2 text: 'Basic example'
38
45
  form.spacer height: 8
39
46
  form.fields_select(
@@ -53,6 +60,24 @@ page.body(
53
60
  form.hr width: 'matchParent'
54
61
  form.spacer height: 12
55
62
 
63
+ form.h2 text: 'Searchable with subtitle'
64
+ form.spacer height: 8
65
+ form.label text: 'Type to filter options by text or subtitle (filterBySubtitle).'
66
+ form.spacer height: 8
67
+ form.fields_select(
68
+ id: 'select_searchable_subtitle',
69
+ name: 'user[select_searchable_subtitle]',
70
+ label: 'Searchable select with subtitle',
71
+ width: 'matchParent',
72
+ searchable: true,
73
+ clearable: true,
74
+ options: subtitle_options
75
+ )
76
+
77
+ form.spacer height: 12
78
+ form.hr width: 'matchParent'
79
+ form.spacer height: 12
80
+
56
81
  form.h2 text: 'Variants/props'
57
82
  form.spacer height: 8
58
83
  form.label text: 'Swap options between icon and image styles.'
@@ -105,6 +105,193 @@ page.body(
105
105
  render 'json_ui/garage/lists/autoload_section', page: page, page_index: 0
106
106
  end
107
107
  )
108
+
109
+ res.spacer height: 12
110
+ res.hr width: 'matchParent'
111
+ res.h2 text: 'Append rows (lists/append)'
112
+ res.spacer height: 8
113
+ res.label text: 'Use lists/append when a list should grow without a full refresh.'
114
+ res.spacer height: 8
115
+ res.label id: 'lists_append_status', text: 'Status: idle'
116
+ res.spacer height: 8
117
+ res.panels_list id: 'list_append_demo', firstSection: ->(section) do
118
+ section.rows builder: ->(template) do
119
+ template.thumbnail title: 'Starter row', subtitle: 'Existing item'
120
+ end
121
+ end
122
+ res.spacer height: 8
123
+ res.panels_flow(
124
+ innerPadding: { bottom: 0 },
125
+ childViews: ->(flow) do
126
+ flow.button(
127
+ text: 'lists/append (thumbnail)',
128
+ onClick: ->(action) do
129
+ action.lists_append(
130
+ targetId: 'list_append_demo',
131
+ row: {
132
+ template: 'thumbnail',
133
+ title: "New row #{DateTime.current.to_i}",
134
+ subtitle: 'Appended via action'
135
+ },
136
+ onAppend: ->(subaction) do
137
+ subaction.logics_set targetId: 'lists_append_status', data: { text: 'Status: appended' }
138
+ end
139
+ )
140
+ end
141
+ )
142
+ flow.spacer width: 4
143
+ flow.button(
144
+ text: 'Append + snackbar',
145
+ onClick: ->(action) do
146
+ action.lists_append(
147
+ targetId: 'list_append_demo',
148
+ row: {
149
+ template: 'thumbnail',
150
+ title: 'Snackbar row',
151
+ subtitle: 'onAppend triggers feedback'
152
+ },
153
+ onAppend: ->(subaction) do
154
+ subaction.snackbars_alert message: 'Row appended'
155
+ end
156
+ )
157
+ end
158
+ )
159
+ flow.spacer width: 4
160
+ flow.button(
161
+ text: 'Append two rows',
162
+ onClick: ->(action) do
163
+ action.runMultiple(
164
+ childActions: ->(multi) do
165
+ multi.lists_append(
166
+ targetId: 'list_append_demo',
167
+ row: { template: 'thumbnail', title: 'Batch row A', subtitle: 'RunMultiple append' }
168
+ )
169
+ multi.lists_append(
170
+ targetId: 'list_append_demo',
171
+ row: { template: 'thumbnail', title: 'Batch row B', subtitle: 'RunMultiple append' }
172
+ )
173
+ end
174
+ )
175
+ end
176
+ )
177
+ end
178
+ )
179
+
180
+ res.spacer height: 12
181
+ res.hr width: 'matchParent'
182
+ res.h2 text: 'Remove a row by id (lists/remove)'
183
+ res.spacer height: 8
184
+ res.label text: 'lists/remove deletes a row by id in-place, preserving scroll position and loaded pages.'
185
+ res.spacer height: 8
186
+ res.label id: 'lists_remove_status', text: 'Status: idle'
187
+ res.spacer height: 8
188
+ res.panels_list id: 'list_remove_demo', firstSection: ->(section) do
189
+ section.rows builder: ->(template) do
190
+ template.thumbnail id: 'remove_row_alpha', title: 'Removable Alpha', subtitle: 'id: remove_row_alpha'
191
+ template.thumbnail id: 'remove_row_bravo', title: 'Removable Bravo', subtitle: 'id: remove_row_bravo'
192
+ template.thumbnail id: 'remove_row_charlie', title: 'Removable Charlie', subtitle: 'id: remove_row_charlie'
193
+ end
194
+ end
195
+ res.spacer height: 8
196
+ res.panels_flow(
197
+ innerPadding: { bottom: 0 },
198
+ childViews: ->(flow) do
199
+ flow.button(
200
+ text: 'Remove Alpha',
201
+ onClick: ->(action) do
202
+ action.lists_remove(
203
+ targetId: 'list_remove_demo',
204
+ rowId: 'remove_row_alpha',
205
+ onRemove: ->(subaction) do
206
+ subaction.logics_set targetId: 'lists_remove_status', data: { text: 'Status: removed Alpha' }
207
+ end
208
+ )
209
+ end
210
+ )
211
+ flow.spacer width: 4
212
+ flow.button(
213
+ text: 'Remove Bravo',
214
+ onClick: ->(action) do
215
+ action.lists_remove(
216
+ targetId: 'list_remove_demo',
217
+ rowId: 'remove_row_bravo',
218
+ onRemove: ->(subaction) do
219
+ subaction.logics_set targetId: 'lists_remove_status', data: { text: 'Status: removed Bravo' }
220
+ end
221
+ )
222
+ end
223
+ )
224
+ flow.spacer width: 4
225
+ flow.button(
226
+ text: 'Remove missing (no-op)',
227
+ onClick: ->(action) do
228
+ action.lists_remove(
229
+ targetId: 'list_remove_demo',
230
+ rowId: 'does_not_exist',
231
+ onRemove: ->(subaction) do
232
+ subaction.logics_set targetId: 'lists_remove_status', data: { text: 'Status: onRemove ran (no row matched)' }
233
+ end
234
+ )
235
+ end
236
+ )
237
+ end
238
+ )
239
+
240
+ res.spacer height: 12
241
+ res.hr width: 'matchParent'
242
+ res.h2 text: 'Target a row by id (logics/set)'
243
+ res.spacer height: 8
244
+ res.label text: "A row template's id now registers in GLib.component, so logics/set (and components/replace) can target the whole row instead of juggling ids on inner elements."
245
+ res.spacer height: 8
246
+ res.panels_list id: 'list_target_demo', firstSection: ->(section) do
247
+ section.rows builder: ->(template) do
248
+ template.thumbnail id: 'target_row_alpha', title: 'Target Alpha', subtitle: 'id: target_row_alpha', imageUrl: glib_json_image_standard_url
249
+ template.thumbnail id: 'target_row_bravo', title: 'Target Bravo', subtitle: 'id: target_row_bravo', imageUrl: glib_json_image_standard_url
250
+ end
251
+ end
252
+ res.spacer height: 8
253
+ res.panels_flow(
254
+ innerPadding: { bottom: 0 },
255
+ childViews: ->(flow) do
256
+ flow.button(
257
+ text: 'Update Alpha (logics/set)',
258
+ onClick: ->(action) do
259
+ action.logics_set targetId: 'target_row_alpha', data: { title: 'Target Alpha (updated)', subtitle: 'Updated via logics/set on the row id' }
260
+ end
261
+ )
262
+ flow.spacer width: 4
263
+ flow.button(
264
+ text: 'Update Bravo (logics/set)',
265
+ onClick: ->(action) do
266
+ action.logics_set targetId: 'target_row_bravo', data: { title: 'Target Bravo (updated)', subtitle: 'Updated via logics/set on the row id' }
267
+ end
268
+ )
269
+ end
270
+ )
271
+
272
+ res.spacer height: 12
273
+ res.hr width: 'matchParent'
274
+ res.h2 text: 'Custom row template (row.custom)'
275
+ res.spacer height: 8
276
+ res.label text: 'row.custom renders a chosen template from a data payload, mirroring panels/custom.'
277
+ res.spacer height: 8
278
+ res.panels_list id: 'list_custom_demo', firstSection: ->(section) do
279
+ section.rows builder: ->(template) do
280
+ template.custom(
281
+ template: 'thumbnail',
282
+ data: { title: 'Custom (thumbnail)', subtitle: 'Rendered via row.custom', imageUrl: glib_json_image_standard_url }
283
+ )
284
+ template.custom(
285
+ template: 'featured',
286
+ data: { title: 'Custom (featured)', subtitle: 'Rendered via row.custom', imageUrl: glib_json_image_standard_url }
287
+ )
288
+ template.custom(
289
+ template: 'nonExistentTemplate',
290
+ data: { title: 'Missing template', subtitle: 'Falls back to template-unsupported' }
291
+ )
292
+ end
293
+ end
294
+
108
295
  res.spacer height: 12
109
296
  res.hr width: 'matchParent'
110
297
  res.h2 text: 'Edge and Advanced'
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glib-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.7
4
+ version: 5.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
10
  date: 2019-10-04 00:00:00.000000000 Z
@@ -150,7 +149,6 @@ dependencies:
150
149
  - - ">="
151
150
  - !ruby/object:Gem::Version
152
151
  version: '0'
153
- description:
154
152
  email: ''
155
153
  executables: []
156
154
  extensions: []
@@ -428,7 +426,6 @@ files:
428
426
  - app/views/json_ui/garage/test_page/lifecycle.json.jbuilder
429
427
  - app/views/json_ui/garage/test_page/list.json.jbuilder
430
428
  - app/views/json_ui/garage/test_page/list_editable.json.jbuilder
431
- - app/views/json_ui/garage/test_page/lists_append.json.jbuilder
432
429
  - app/views/json_ui/garage/test_page/logics_set.json.jbuilder
433
430
  - app/views/json_ui/garage/test_page/multimedia_video.json.jbuilder
434
431
  - app/views/json_ui/garage/test_page/pagination.json.jbuilder
@@ -530,10 +527,8 @@ files:
530
527
  - lib/glib/value.rb
531
528
  - lib/glib/version.rb
532
529
  - lib/tasks/db.rake
533
- homepage:
534
530
  licenses: []
535
531
  metadata: {}
536
- post_install_message:
537
532
  rdoc_options: []
538
533
  require_paths:
539
534
  - lib
@@ -548,8 +543,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
548
543
  - !ruby/object:Gem::Version
549
544
  version: '0'
550
545
  requirements: []
551
- rubygems_version: 3.4.6
552
- signing_key:
546
+ rubygems_version: 4.0.6
553
547
  specification_version: 4
554
548
  summary: ''
555
549
  test_files: []
@@ -1,152 +0,0 @@
1
- json.title 'Test Page (Lists Append)'
2
-
3
- page = json_ui_page json
4
-
5
- render 'json_ui/garage/test_page/header', json: json, page: page
6
-
7
- page.body(
8
- childViews: ->(body) do
9
- body.panels_responsive(
10
- padding: glib_json_padding_body,
11
- childViews: ->(res) do
12
- res.h2 text: 'Lists Append'
13
- res.label text: 'Append rows to list panels from actions for live updates.'
14
- res.spacer height: 12
15
- res.hr width: 'matchParent'
16
- res.spacer height: 12
17
-
18
- res.h2 text: 'Overview'
19
- res.label text: 'Use lists/append when a list should grow without a full refresh.'
20
- res.spacer height: 12
21
- res.hr width: 'matchParent'
22
- res.spacer height: 12
23
-
24
- res.h2 text: 'Basic example'
25
- res.spacer height: 8
26
- res.label id: 'lists_append_status', text: 'Status: idle'
27
- res.spacer height: 8
28
- res.panels_list id: 'list_append_demo', firstSection: ->(section) do
29
- section.rows builder: ->(template) do
30
- template.thumbnail title: 'Starter row', subtitle: 'Existing item'
31
- end
32
- end
33
- res.spacer height: 8
34
- res.button(
35
- text: 'lists/append (thumbnail)',
36
- onClick: ->(action) do
37
- action.lists_append(
38
- targetId: 'list_append_demo',
39
- row: {
40
- template: 'thumbnail',
41
- title: "New row #{DateTime.current.to_i}",
42
- subtitle: 'Appended via action'
43
- },
44
- onAppend: ->(subaction) do
45
- subaction.logics_set targetId: 'lists_append_status', data: { text: 'Status: appended' }
46
- end
47
- )
48
- end
49
- )
50
-
51
- res.spacer height: 16
52
- res.hr width: 'matchParent'
53
- res.spacer height: 16
54
-
55
- res.h2 text: 'Variants/props'
56
- res.spacer height: 8
57
- res.panels_flow(
58
- innerPadding: { bottom: 0 },
59
- width: 'matchParent',
60
- childViews: ->(flow) do
61
- flow.button(
62
- text: 'Append standard row',
63
- onClick: ->(action) do
64
- action.lists_append(
65
- targetId: 'list_append_demo',
66
- row: {
67
- template: 'standard',
68
- title: 'Standard row',
69
- subtitle: 'Uses the standard template'
70
- }
71
- )
72
- end
73
- )
74
- flow.spacer width: 8
75
- flow.button(
76
- text: 'Append + snackbar',
77
- onClick: ->(action) do
78
- action.lists_append(
79
- targetId: 'list_append_demo',
80
- row: {
81
- template: 'thumbnail',
82
- title: 'Snackbar row',
83
- subtitle: 'onAppend triggers feedback'
84
- },
85
- onAppend: ->(subaction) do
86
- subaction.snackbars_alert message: 'Row appended'
87
- end
88
- )
89
- end
90
- )
91
- end
92
- )
93
-
94
- res.spacer height: 16
95
- res.hr width: 'matchParent'
96
- res.spacer height: 16
97
-
98
- res.h2 text: 'Actions/events'
99
- res.spacer height: 8
100
- res.button(
101
- text: 'Append two rows',
102
- onClick: ->(action) do
103
- action.runMultiple(
104
- childActions: ->(multi) do
105
- multi.lists_append(
106
- targetId: 'list_append_demo',
107
- row: {
108
- template: 'thumbnail',
109
- title: 'Batch row A',
110
- subtitle: 'RunMultiple append'
111
- }
112
- )
113
- multi.lists_append(
114
- targetId: 'list_append_demo',
115
- row: {
116
- template: 'thumbnail',
117
- title: 'Batch row B',
118
- subtitle: 'RunMultiple append'
119
- }
120
- )
121
- end
122
- )
123
- end
124
- )
125
-
126
- res.spacer height: 16
127
- res.hr width: 'matchParent'
128
- res.spacer height: 16
129
-
130
- res.h2 text: 'Edge/advanced'
131
- res.spacer height: 8
132
- res.label text: 'Append into an empty list'
133
- res.spacer height: 8
134
- res.panels_list id: 'list_append_empty', sections: []
135
- res.spacer height: 8
136
- res.button(
137
- text: 'lists/append (empty list)',
138
- onClick: ->(action) do
139
- action.lists_append(
140
- targetId: 'list_append_empty',
141
- row: {
142
- template: 'standard',
143
- title: 'First row',
144
- subtitle: 'Created from empty list'
145
- }
146
- )
147
- end
148
- )
149
- end
150
- )
151
- end
152
- )