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 +4 -4
- data/app/helpers/glib/json_ui/action_builder.rb +6 -0
- data/app/models/concerns/glib/soft_deletable.rb +97 -12
- data/app/views/json_ui/garage/test_page/_header.json.jbuilder +0 -1
- data/app/views/json_ui/garage/test_page/fields.json.jbuilder +23 -0
- data/app/views/json_ui/garage/test_page/fields_date_time.json.jbuilder +4 -0
- data/app/views/json_ui/garage/test_page/fields_select.json.jbuilder +25 -0
- data/app/views/json_ui/garage/test_page/list.json.jbuilder +187 -0
- metadata +2 -8
- data/app/views/json_ui/garage/test_page/lists_append.json.jbuilder +0 -152
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ad9135a710cddc6a961596146b40ff9a33f91357de190b94843ed054d8bb6188
|
|
4
|
+
data.tar.gz: 0e01d6df7e9a7a3db6b4cbb380970e6e68a75797559e4341e6fab1212626f187
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b33f64738a36e98fb1dccd6422a9dc0c0acdb5ad91f1d6c0276dd672314130809d2dc668129b888599ac4696a0c57b3c1bbfa82998937f705cdcec125ae96c37
|
|
7
|
+
data.tar.gz: 3949cb0ee96b5955f716eea9d423e2ae7564d8b41ee9f2cbd482535bd332716a539c0ef5492be88de4cc4ca6ec27e4f4597eb8b51ee4290c181aefbf5345ec59
|
|
@@ -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
|
-
|
|
134
|
-
process_soft_deletable_relationship(relationship)
|
|
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
|
-
|
|
140
|
-
process_soft_deletable_relationship(relationship)
|
|
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
|
-
#
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
@@ -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(
|
|
@@ -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.
|
|
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:
|
|
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
|
-
)
|