35
36:- module(swish_bootstrap,
37 [ bt_form//2, 38 bt_button//4, 39
40 name_label/2 41 ]). 42:- use_module(library(option)). 43:- use_module(library(occurs)). 44:- use_module(library(error)). 45:- use_module(library(http/html_write)). 46
51
72
73bt_form(Fields, Options) -->
74 { form_attributes(Atts, Options) },
75 html(form(Atts, \bt_form_content(Fields, Options))).
76
77form_attributes([class(Class)], Options) :-
78 option(class(Class), Options).
79form_attributes([], _).
80
81bt_form_content([], _) --> [].
82bt_form_content([H|T], Options) -->
83 bt_form_element(H, Options),
84 bt_form_content(T, Options).
85
86
88
89form_style(horizontal, Options) :-
90 option(class(Class), Options),
91 sub_term('form-horizontal', Class).
92form_style(inline, Options) :-
93 option(class(Class), Options),
94 sub_term('form-inline', Class).
95form_style(vertical, Options) :-
96 ( option(class(Class), Options)
97 -> \+ sub_term('form-inline', Class),
98 \+ sub_term('form-horizontal', Class)
99 ; true
100 ).
101
102
106
107bt_form_element(input(Name, Type, IOptions), Options) -->
108 html(div(class('form-group'),
109 [ \bt_label(Name, IOptions, Options),
110 \bt_input(Name, Type, IOptions, Options)
111 ])).
112bt_form_element(select(Name, Values, IOptions), Options) -->
113 html(div(class('form-group'),
114 [ \bt_label(Name, IOptions, Options),
115 \bt_select(Name, Values, IOptions, Options)
116 ])).
117bt_form_element(checkboxes(Name, Values, IOptions), Options) -->
118 html(div(class('form-group'),
119 [ \bt_label(Name, IOptions, Options),
120 \bt_checkboxes(Name, Values, IOptions, Options)
121 ])).
122bt_form_element(textarea(Name, IOptions), Options) -->
123 html(div(class('form-group'),
124 [ \bt_label(Name, IOptions, Options),
125 \bt_textarea(Name, IOptions, Options)
126 ])).
127bt_form_element(button(Name, Type, IOptions), Options) -->
128 bt_button(Name, Type, IOptions, Options).
129bt_form_element(button_group(Buttons, IOptions), Options) -->
130 bt_button_group(Buttons, IOptions, Options).
131bt_form_element(hidden(Name, Value), _Options) -->
132 html(input([type(hidden),name(Name),value(Value)])).
133
137
138bt_label(Name, IOptions, Options) -->
139 { phrase(label_attr(Options), Attrs) },
140 html(label([for(Name)|Attrs], \label(Name, IOptions))).
141
142label_attr(Options) -->
143 { form_style(horizontal, Options),
144 option(label_columns(Size-Count), Options, sm-2),
145 atomic_list_concat([col,Size,Count], -, Class)
146 },
147 [ class(['control-label', Class]) ].
148label_attr(_) --> [].
149
150
151
162
163:- html_meta(horizontal_input(html, +, +, +, ?, ?)). 164
165bt_input(Name, Type, InputOptions, FormOptions) -->
166 horizontal_input(\bt_input_elem(Name, Type, InputOptions, FormOptions),
167 [], [], FormOptions),
168 !.
169bt_input(Name, Type, InputOptions, FormOptions) -->
170 bt_input_elem(Name, Type, InputOptions, FormOptions).
171
172horizontal_input(HTML, Classes, Attrs, FormOptions) -->
173 { form_style(horizontal, FormOptions),
174 option(label_columns(Size-Count), FormOptions, sm-2),
175 FieldCols is 12-Count,
176 atomic_list_concat([col,Size,FieldCols], -, Class)
177 },
178 html(div([class([Class|Classes])|Attrs], HTML)).
179
180bt_input_elem(Name, checkbox, InputOptions, _FormOptions) -->
181 !,
182 { phrase(checkbox_attr(InputOptions), Attrs) },
183 html(input([type(checkbox), name(Name)|Attrs])).
184bt_input_elem(Name, Type, InputOptions, _FormOptions) -->
185 { phrase(input_attr(InputOptions), Attrs),
186 phrase(classes(InputOptions), Classes),
187 list_to_set(['form-control'|Classes], InputClasses)
188 },
189 html(input([type(Type), class(InputClasses), name(Name)|Attrs])).
190
191checkbox_attr(Options) -->
192 ( checkbox_value(Options) -> [] ; [] ),
193 ( input_disabled(Options) -> [] ; [] ),
194 ( input_readonly(Options) -> [] ; [] ),
195 data(Options).
196
197checkbox_value(Options) -->
198 { option(value(true), Options) },
199 [ checked(checked) ].
200
201input_attr(Options) -->
202 ( input_value(Options) -> [] ; [] ),
203 ( input_disabled(Options) -> [] ; [] ),
204 data(Options).
205
206input_value(Options) -->
207 { ( option(value(Value), Options)
208 -> true
209 ; option(default(Value), Options)
210 )
211 },
212 [ value(Value) ].
213input_disabled(Options) -->
214 { option(disabled(true), Options) },
215 [ disabled(disabled) ].
216input_readonly(Options) -->
217 { option(readonly(true), Options) },
218 [ readonly(readonly) ].
219
228
229bt_select(Name, Values, SelectOptions, FormOptions) -->
230 horizontal_input(\bt_select_elem(Name, Values, SelectOptions, FormOptions),
231 [], [], FormOptions).
232
233bt_select_elem(Name, Values, SelectOptions, _FormOptions) -->
234 { option(value(Value), SelectOptions, _),
235 phrase(( (select_size(SelectOptions) -> [] ; []),
236 (select_multiple(SelectOptions) -> [] ; [])
237 ), Opts)
238 },
239 html(select([name(Name),class('form-control')|Opts],
240 \select_options(Values, Value, SelectOptions))).
241
242select_size(Options) -->
243 { option(size(Size), Options) },
244 [ size(Size) ].
245select_multiple(Options) -->
246 { option(multiple(true), Options) },
247 [ multiple(multiple) ].
248
249select_options([], _, _) -->
250 [].
251select_options([H|T], Value, Options) -->
252 select_option_1(H, Value, Options),
253 select_options(T, Value, Options).
254
255select_option_1(Value, Selected, _Options) -->
256 { (atom(Value) ; string(Value)),
257 !,
258 name_label(Value, Label),
259 ( Value == Selected
260 -> Opts = [selected(selected)]
261 ; Opts = []
262 )
263 },
264 html(option([value(Value)|Opts], Label)).
265select_option_1(Value, _, _) -->
266 { domain_error(bt_select_option, Value) }.
267
271
272bt_checkboxes(Name, Values, CBOptions, FormOptions) -->
273 horizontal_input(\checkboxes(Values, CBOptions),
274 [checkboxes, array], name(Name), FormOptions).
275
276checkboxes([], _) -->
277 [].
278checkboxes([H|T], Options) -->
279 checkbox(H, Options),
280 checkboxes(T, Options).
281
282checkbox(Value, Options) -->
283 { name_label(Value, Label),
284 ( option(value(Selected), Options),
285 memberchk(Value, Selected)
286 -> Opts = [checked(checked)]
287 ; Opts = []
288 )
289 },
290 html(label(class('checkbox-inline'),
291 [ input([ type(checkbox), name(Value), autocomplete(false)
292 | Opts
293 ]),
294 Label
295 ])).
296
298
299bt_textarea(Name, TextAreaOptions, FormOptions) -->
300 horizontal_input(\bt_textarea_elem(Name, TextAreaOptions, FormOptions),
301 [], [], FormOptions),
302 !.
303bt_textarea(Name, TextAreaOptions, FormOptions) -->
304 bt_textarea_elem(Name, TextAreaOptions, FormOptions).
305
306
307bt_textarea_elem(Name, TextAreaOptions, _FormOptions) -->
308 { option(rows(Rows), TextAreaOptions, 4)
309 },
310 html(textarea([ class('form-control)'),
311 rows(Rows),
312 name(Name),
313 style('width:100%')
314 ], [])).
315
316
320
321bt_button_group(Buttons, _ButtonOptions, FormOptions) -->
322 { form_style(horizontal, FormOptions),
323 !,
324 option(label_columns(_-Count), FormOptions, sm-2),
325 Count12 is 12-Count,
326 atomic_list_concat([col,xs,offset,Count], -, Offset),
327 atomic_list_concat([col,xs,Count12], -, Width)
328 },
329 html(div(class('form-group'),
330 div(class([Offset,Width]),
331 \bt_form_content(Buttons, FormOptions)))).
332bt_button_group(Buttons, _ButtonOptions, FormOptions) -->
333 html(div(class(['col-xs-12', 'text-center']),
334 \bt_form_content(Buttons, FormOptions))).
335
336
338
339bt_button(Name, Type, IOptions, Options) -->
340 { phrase(button_classes(IOptions, Options), BtnClasses),
341 phrase(data(IOptions), DataAttrs)
342 },
343 html(button([ type(Type),
344 name(Name),
345 class([btn|BtnClasses])
346 | DataAttrs
347 ],
348 \label(Name, IOptions))).
349
353
354button_classes(IOptions, Options) -->
355 button_type_class(IOptions),
356 button_size_class(IOptions, Options).
357
358button_type_class(IOptions) -->
359 { option(type(Type), IOptions),
360 !,
361 atom_concat('btn-', Type, Class)
362 },
363 [Class].
364button_type_class(_IOptions) -->
365 ['btn-default'].
366
367button_size_class(IOptions, Options) -->
368 { ( option(button_size(Size), IOptions)
369 ; option(button_size(Size), Options)
370 ),
371 !,
372 atom_concat('btn-', Size, Class)
373 },
374 [Class].
375button_size_class(_IOptions, _Options) -->
376 [].
377
381
382data(Options) -->
383 { option(data(Data), Options, []) },
384 !,
385 data_values(Data).
386data(_) --> [].
387
388data_values([]) --> !.
389data_values([H|T]) --> !, data_values(H), data_values(T).
390data_values(Name-Value) -->
391 !,
392 data_value(Name, Value).
393data_values(Name=Value) -->
394 !,
395 data_value(Name, Value).
396data_values(NameValue) -->
397 { NameValue =.. [Name,Value] },
398 data_value(Name, Value).
399
400data_value(Name, Value) -->
401 { atom_concat('data-', Name, AttrName),
402 Attr =.. [AttrName,Value]
403 },
404 [Attr].
405
409
410classes([]) --> !.
411classes([class(Classes)|T]) --> !, class(Classes), classes(T).
412classes([_|T]) --> classes(T).
413
414class([]) --> !.
415class([H|T]) --> !, class(H), class(T).
416class(Class) --> [Class].
417
418
419 422
423label(Name, Options) -->
424 { ( option(label(Label), Options)
425 -> true
426 ; name_label(Name, Label)
427 )
428 },
429 html(Label).
430
435
436name_label(Name, Label) :-
437 atom_codes(Name, Codes),
438 phrase(name_label(up, LCodes), Codes),
439 atom_codes(Label, LCodes).
440
441name_label(up, [H|T]) --> [H0], !, {code_type(H, to_upper(H0))}, name_label(keep, T).
442name_label(keep, [0'\s|T]) --> "_", !, name_label(keep, T).
443name_label(keep, [H|T]) --> [H], !, name_label(keep, T).
444name_label(_, []) --> []