gloo-web 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/objs/field.rb ADDED
@@ -0,0 +1,428 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # An HTML Form Field.
5
+ #
6
+ # A Form Field is the definition of a form field, including label, type, etc.
7
+ #
8
+
9
+
10
+ module Objs
11
+ class Field < Gloo::Core::Obj
12
+
13
+ KEYWORD = 'field'.freeze
14
+ KEYWORD_SHORT = 'field'.freeze
15
+
16
+ # Form
17
+ NAME = 'name'.freeze
18
+ ID = 'id'.freeze
19
+ TYPE = 'type'.freeze
20
+ VALUE = 'value'.freeze
21
+ LABEL = 'label'.freeze
22
+ PLACEHOLDER = 'placeholder'.freeze
23
+ AUTOFOCUS = 'autofocus'.freeze
24
+ COLS = 'cols'.freeze
25
+ ROWS = 'rows'.freeze
26
+ DESCRIPTION = 'description'.freeze
27
+ CHECKED = 'checked'.freeze
28
+ OPTIONS = 'options'.freeze
29
+
30
+ # Style attributes
31
+ FIELD_GROUP = 'field_group'.freeze
32
+ FIELD_LABEL = 'field_label'.freeze
33
+ FIELD_CONTROL = 'field_control'.freeze
34
+
35
+ #
36
+ # The name of the object type.
37
+ #
38
+ def self.typename
39
+ return KEYWORD
40
+ end
41
+
42
+ #
43
+ # The short name of the object type.
44
+ #
45
+ def self.short_typename
46
+ return KEYWORD_SHORT
47
+ end
48
+
49
+ #
50
+ # Get the name for the form field.
51
+ #
52
+ def name_value
53
+ o = find_child NAME
54
+
55
+ # If there is no child, use the obj's name
56
+ return self.name unless o
57
+
58
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
59
+ return o ? o.value : nil
60
+ end
61
+
62
+ #
63
+ # Get the type for the form field.
64
+ # For example, 'text', 'password', 'checkbox', etc.
65
+ #
66
+ def type_value
67
+ o = find_child TYPE
68
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
69
+ return o ? o.value : nil
70
+ end
71
+
72
+ #
73
+ # Get the value for the form field.
74
+ #
75
+ def field_value
76
+ o = find_child VALUE
77
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
78
+ return o ? o.value : nil
79
+ end
80
+
81
+ #
82
+ # Get the value tag for the form field.
83
+ #
84
+ def value_tag
85
+ value = field_value
86
+ return "value='#{value}'" if value
87
+ return ''
88
+ end
89
+
90
+ #
91
+ # Get the label for the form field.
92
+ #
93
+ def label_value
94
+ o = find_child LABEL
95
+
96
+ # If there is no child, use the obj's name
97
+ return self.name_value.capitalize unless o
98
+
99
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
100
+ return o ? o.value : nil
101
+ end
102
+
103
+ #
104
+ # Get the label tag for the form field.
105
+ #
106
+ def label_tag
107
+ label = label_value
108
+ label_data = ''
109
+ if label
110
+ label_data = <<~HTML
111
+ <label class="#{field_label_styles}" for="#{name_value}">
112
+ #{label}
113
+ </label>
114
+ HTML
115
+ end
116
+ return label_data
117
+ end
118
+
119
+ #
120
+ # Get the placeholder for the form field.
121
+ #
122
+ def placeholder_value
123
+ o = find_child PLACEHOLDER
124
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
125
+ return o ? o.value : nil
126
+ end
127
+
128
+ #
129
+ # Get the placeholder tag for the form field.
130
+ #
131
+ def placeholder_tag
132
+ placeholder = placeholder_value
133
+ return "placeholder='#{placeholder}'" if placeholder
134
+ return ''
135
+ end
136
+
137
+ #
138
+ # Should this field autofocus?
139
+ #
140
+ def autofocus?
141
+ o = find_child AUTOFOCUS
142
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
143
+ return nil unless o
144
+ return o.value
145
+ end
146
+
147
+ #
148
+ # Get the autofocus tag for the form field.
149
+ #
150
+ def autofocus_tag
151
+ return "autofocus='autofocus'" if autofocus?
152
+ return ''
153
+ end
154
+
155
+ #
156
+ # Get the cols for the form field.
157
+ #
158
+ def cols_value
159
+ o = find_child COLS
160
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
161
+ return o ? o.value : nil
162
+ end
163
+
164
+ #
165
+ # Get the cols tag for the form field.
166
+ #
167
+ def cols_tag
168
+ cols = cols_value
169
+ return "col-#{cols}" if cols
170
+ return ''
171
+ end
172
+
173
+ #
174
+ # Get the rows for the form field.
175
+ # Only applies to textarea fields.
176
+ #
177
+ def rows_value
178
+ o = find_child ROWS
179
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
180
+ return o ? o.value : nil
181
+ end
182
+
183
+ #
184
+ # Get the rows tag for the form field.
185
+ # Only applies to textarea fields.
186
+ #
187
+ def rows_tag
188
+ rows = rows_value
189
+ return "rows='#{rows}'" if rows
190
+ return ''
191
+ end
192
+
193
+ #
194
+ # Get the description for the form field.
195
+ #
196
+ def description_value
197
+ o = find_child DESCRIPTION
198
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
199
+ return o ? o.value : nil
200
+ end
201
+
202
+ #
203
+ # Should this field be checked?
204
+ #
205
+ def checked?
206
+ o = find_child CHECKED
207
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
208
+ return nil unless o
209
+ return o.value
210
+ end
211
+
212
+ #
213
+ # Get the checked tag for the form field.
214
+ #
215
+ def checked_tag
216
+ return "checked='checked'" if checked?
217
+ return ''
218
+ end
219
+
220
+ #
221
+ # Get options for the select list.
222
+ #
223
+ def select_options
224
+ o = find_child OPTIONS
225
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
226
+ return nil unless o
227
+
228
+ selected_value = field_value
229
+
230
+ options = ''
231
+ o.children.each do |child|
232
+ if selected_value == child.name || selected_value == child.value
233
+ selected = 'selected="selected"'
234
+ else
235
+ selected = ''
236
+ end
237
+ options += <<~HTML
238
+ <option value="#{child.name}" #{selected}>#{child.value}</option>
239
+ HTML
240
+ end
241
+ return options
242
+ end
243
+
244
+ #
245
+ # Get the field group styles.
246
+ #
247
+ def field_group_styles
248
+ o = find_child FIELD_GROUP
249
+ if o
250
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
251
+ return o ? o.value : ''
252
+ end
253
+
254
+ return @styles[FIELD_GROUP] || ''
255
+ end
256
+
257
+ #
258
+ # Get the field label styles.
259
+ #
260
+ def field_label_styles
261
+ o = find_child FIELD_LABEL
262
+ if o
263
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
264
+ return o ? o.value : ''
265
+ end
266
+
267
+ return @styles[FIELD_LABEL] || ''
268
+ end
269
+
270
+ #
271
+ # Get the field control styles.
272
+ #
273
+ def field_control_styles
274
+ o = find_child FIELD_CONTROL
275
+ if o
276
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
277
+ return o ? o.value : ''
278
+ end
279
+
280
+ return @styles[FIELD_CONTROL] || ''
281
+ end
282
+
283
+
284
+ # ---------------------------------------------------------------------
285
+ # Children
286
+ # ---------------------------------------------------------------------
287
+
288
+ #
289
+ # Does this object have children to add when an object
290
+ # is created in interactive mode?
291
+ # This does not apply during obj load, etc.
292
+ #
293
+ def add_children_on_create?
294
+ return true
295
+ end
296
+
297
+ #
298
+ # Add children to this object.
299
+ # This is used by containers to add children needed
300
+ # for default configurations.
301
+ #
302
+ def add_default_children
303
+ fac = @engine.factory
304
+
305
+ # Create attributes with ID and Classes
306
+ fac.create_string NAME, '', self
307
+ fac.create_string TYPE, 'text', self
308
+ end
309
+
310
+
311
+ # ---------------------------------------------------------------------
312
+ # Messages
313
+ # ---------------------------------------------------------------------
314
+
315
+ #
316
+ # Get a list of message names that this object receives.
317
+ #
318
+ def self.messages
319
+ return super + [ 'render' ]
320
+ end
321
+
322
+ #
323
+ # Render the form field.
324
+ #
325
+ def msg_render
326
+ content = self.render
327
+ @engine.heap.it.set_to content
328
+ return content
329
+ end
330
+
331
+
332
+ # ---------------------------------------------------------------------
333
+ # Render
334
+ # ---------------------------------------------------------------------
335
+
336
+ #
337
+ # Render the field, switch on type.
338
+ def render styles = {}
339
+ @styles = styles
340
+
341
+ case type_value
342
+ when 'text'
343
+ return render_text
344
+ when 'hidden'
345
+ return render_hidden
346
+ when 'textarea'
347
+ return render_textarea
348
+ when 'checkbox'
349
+ return render_checkbox
350
+ when 'search'
351
+ return render_text
352
+ when 'select'
353
+ return render_select
354
+ end
355
+ end
356
+
357
+ #
358
+ # Render the hidden field as HTML.
359
+ #
360
+ def render_hidden
361
+ return <<~HTML
362
+ <input type="hidden" #{value_tag} name="#{name_value}" id="#{name_value}" />
363
+ HTML
364
+ end
365
+
366
+ #
367
+ # Render the text field as HTML.
368
+ #
369
+ def render_text
370
+ return <<~HTML
371
+ <div class="#{field_group_styles} #{cols_tag}">
372
+ #{label_tag}
373
+ <input #{placeholder_tag} #{autofocus_tag} #{rows_tag}
374
+ class="#{field_control_styles}"
375
+ type="#{type_value}" #{value_tag}
376
+ name="#{name_value}" id="#{name_value}" />
377
+ </div>
378
+ HTML
379
+ end
380
+
381
+ #
382
+ # Render the textarea field as HTML.
383
+ #
384
+ def render_textarea
385
+ return <<~HTML
386
+ <div class="#{field_group_styles} #{cols_tag}">
387
+ #{label_tag}
388
+ <textarea #{placeholder_tag} #{autofocus_tag} #{rows_tag}
389
+ class="#{field_control_styles}"
390
+ name="#{name_value}" id="#{name_value}">#{field_value}</textarea>
391
+ </div>
392
+ HTML
393
+ end
394
+
395
+ #
396
+ # Render the checkbox field as HTML.
397
+ #
398
+ def render_checkbox
399
+ return <<~HTML
400
+ <div class="#{field_group_styles} #{cols_tag}">
401
+ #{label_tag}
402
+ <label class="checkbox #{field_control_styles}">
403
+ <input type="checkbox"
404
+ #{checked_tag} value="true"
405
+ name="#{name_value}" id="#{name_value}" />
406
+ #{description_value}
407
+ </label>
408
+ </div>
409
+ HTML
410
+ end
411
+
412
+ #
413
+ # Render the select field as HTML.
414
+ #
415
+ def render_select
416
+ return <<~HTML
417
+ <div class="#{field_group_styles} #{cols_tag}">
418
+ #{label_tag}
419
+ <select class="#{field_control_styles}"
420
+ name="#{name_value}" id="#{name_value}">
421
+ #{select_options}
422
+ </select>
423
+ </div>
424
+ HTML
425
+ end
426
+
427
+ end
428
+ end
data/lib/objs/form.rb ADDED
@@ -0,0 +1,269 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # An HTML Form.
5
+ #
6
+ # A Form is the definition of a form, with a collection of form fields
7
+ #
8
+
9
+ module Objs
10
+ class Form < Gloo::Core::Obj
11
+
12
+ KEYWORD = 'form'.freeze
13
+ KEYWORD_SHORT = 'form'.freeze
14
+
15
+ # Form
16
+ NAME = 'name'.freeze
17
+ ID = 'id'.freeze
18
+ METHOD = 'method'.freeze
19
+ METHOD_DEFAULT = 'post'.freeze
20
+ ACTION = 'action'.freeze
21
+ CANCEL_PATH = 'cancel_path'.freeze
22
+ CONTENT = 'content'.freeze
23
+ STYLES = 'styles'.freeze
24
+
25
+
26
+ #
27
+ # The name of the object type.
28
+ #
29
+ def self.typename
30
+ return KEYWORD
31
+ end
32
+
33
+ #
34
+ # The short name of the object type.
35
+ #
36
+ def self.short_typename
37
+ return KEYWORD_SHORT
38
+ end
39
+
40
+ #
41
+ # Get the name for the form.
42
+ #
43
+ def name_value
44
+ o = find_child NAME
45
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
46
+ return o ? o.value : nil
47
+ end
48
+
49
+ #
50
+ # Get the method for the form.
51
+ # 'post' is the default.
52
+ #
53
+ def method_value
54
+ o = find_child METHOD
55
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
56
+ return o.value || METHOD_DEFAULT
57
+ end
58
+
59
+ #
60
+ # Get the action for the form.
61
+ # This is the path to POST to, for example.
62
+ #
63
+ def action_value
64
+ o = find_child ACTION
65
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
66
+ return o ? o.value : nil
67
+ end
68
+
69
+ #
70
+ # Get the cancel path for the form.
71
+ #
72
+ def cancel_path_value
73
+ o = find_child CANCEL_PATH
74
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
75
+ return o ? o.value : nil
76
+ end
77
+
78
+ #
79
+ # Get all the form content, the collection of form fields.
80
+ #
81
+ def form_content
82
+ o = find_child CONTENT
83
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
84
+ return o
85
+ end
86
+
87
+ #
88
+ # Get the styles for the form.
89
+ # Retuns styles in the form of a hash:
90
+ # { 'field_group' => 'form-group mt-3', … }
91
+ #
92
+ def styles
93
+ style_h = {}
94
+ o = find_child STYLES
95
+ return style_h unless o
96
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
97
+
98
+ o.children.each do |c|
99
+ style_h[ c.name ] = c.value
100
+ end
101
+
102
+ # puts "styles: #{style_h}"
103
+ return style_h
104
+ end
105
+
106
+ #
107
+ # Get the form styles.
108
+ # Use the name if none is provided.
109
+ #
110
+ def form_styles
111
+ return @styles['form'] || name_value
112
+ end
113
+
114
+ #
115
+ # Get the submit button styles.
116
+ #
117
+ def submit_button_styles
118
+ return @styles['submit'] || ''
119
+ end
120
+
121
+ #
122
+ # Get the cancel button styles.
123
+ #
124
+ def cancel_button_styles
125
+ return @styles['cancel'] || ''
126
+ end
127
+
128
+
129
+ # ---------------------------------------------------------------------
130
+ # Children
131
+ # ---------------------------------------------------------------------
132
+
133
+ #
134
+ # Does this object have children to add when an object
135
+ # is created in interactive mode?
136
+ # This does not apply during obj load, etc.
137
+ #
138
+ def add_children_on_create?
139
+ return true
140
+ end
141
+
142
+ #
143
+ # Add children to this object.
144
+ # This is used by containers to add children needed
145
+ # for default configurations.
146
+ #
147
+ def add_default_children
148
+ fac = @engine.factory
149
+
150
+ # Create attributes with ID and Classes
151
+ fac.create_string NAME, '', self
152
+ fac.create_string METHOD, 'post', self
153
+ fac.create_string ACTION, '', self
154
+ fac.create_string CANCEL_PATH, '', self
155
+
156
+ fac.create_can CONTENT, self
157
+ end
158
+
159
+
160
+ # ---------------------------------------------------------------------
161
+ # Messages
162
+ # ---------------------------------------------------------------------
163
+
164
+ #
165
+ # Get a list of message names that this object receives.
166
+ #
167
+ def self.messages
168
+ return super + [ 'render' ]
169
+ end
170
+
171
+ #
172
+ # Render the form and all contained fields.
173
+ #
174
+ def msg_render
175
+ content = self.render
176
+ @engine.heap.it.set_to content
177
+ return content
178
+ end
179
+
180
+
181
+ # ---------------------------------------------------------------------
182
+ # Render
183
+ # ---------------------------------------------------------------------
184
+
185
+ #
186
+ # Open the form.
187
+ #
188
+ def open_form
189
+ name = name_value
190
+
191
+ cancel_button = ""
192
+ if cancel_path_value
193
+ cancel_button = <<~HTML
194
+ <a class="#{cancel_button_styles}"
195
+ href="#{cancel_path_value}">
196
+ Cancel</a>
197
+ HTML
198
+ end
199
+ return <<~HTML
200
+ <form class='#{form_styles}'
201
+ id='#{name}'
202
+ method='#{method_value}'
203
+ action='#{action_value}'
204
+ accept-charset='UTF-8'>
205
+ <div class="actions">
206
+ <input type="submit"
207
+ name="commit"
208
+ value="Save"
209
+ class="#{submit_button_styles}"
210
+ data-disable-with="Saving..." />
211
+
212
+ #{cancel_button}
213
+ </div>
214
+ HTML
215
+ end
216
+
217
+ #
218
+ # Close the form.
219
+ #
220
+ def close_form
221
+ return "</form>"
222
+ end
223
+
224
+ #
225
+ # Render the Form as HTML.
226
+ #
227
+ def render
228
+ @styles = styles
229
+ return open_form + render_content + close_form
230
+ end
231
+
232
+ #
233
+ # Render the element content using the specified render function.
234
+ # This is a recursive function (through one of the other render functions).
235
+ #
236
+ def render_content
237
+ fields = ""
238
+ field_can = form_content
239
+ return "" if field_can.nil?
240
+
241
+ field_can.children.each do |o|
242
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
243
+ if o.class == Field
244
+ fields << o.render( @styles )
245
+ elsif o.class == Element
246
+ fields << o.render_html
247
+ elsif o
248
+ data = render_thing o
249
+ ( fields << data ) if data
250
+ end
251
+ end
252
+
253
+ return fields
254
+ end
255
+
256
+ #
257
+ # Render a string or other object.
258
+ #
259
+ def render_thing e
260
+ begin
261
+ return e.render( 'render_html' )
262
+ rescue => e
263
+ @engine.log_exception e
264
+ return ''
265
+ end
266
+ end
267
+
268
+ end
269
+ end