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 [ value(Value) ].
209input_disabled(Options) -->
210 { option(disabled(true), Options) },
211 [ disabled(disabled) ].
212input_readonly(Options) -->
213 { option(readonly(true), Options) },
214 [ readonly(readonly) ].
215
224
225bt_select(Name, Values, SelectOptions, FormOptions) -->
226 horizontal_input(\bt_select_elem(Name, Values, SelectOptions, FormOptions),
227 [], [], FormOptions).
228
229bt_select_elem(Name, Values, SelectOptions, _FormOptions) -->
230 { option(value(Value), SelectOptions, _),
231 phrase(( (select_size(SelectOptions) -> [] ; []),
232 (select_multiple(SelectOptions) -> [] ; [])
233 ), Opts)
234 },
235 html(select([name(Name),class('form-control')|Opts],
236 \select_options(Values, Value, SelectOptions))).
237
238select_size(Options) -->
239 { option(size(Size), Options) },
240 [ size(Size) ].
241select_multiple(Options) -->
242 { option(multiple(true), Options) },
243 [ multiple(multiple) ].
244
245select_options([], _, _) -->
246 [].
247select_options([H|T], Value, Options) -->
248 select_option_1(H, Value, Options),
249 select_options(T, Value, Options).
250
251select_option_1(Value, Selected, _Options) -->
252 { (atom(Value) ; string(Value)),
253 !,
254 name_label(Value, Label),
255 ( Value == Selected
256 -> Opts = [selected(selected)]
257 ; Opts = []
258 )
259 },
260 html(option([value(Value)|Opts], Label)).
261select_option_1(Value, _, _) -->
262 { domain_error(bt_select_option, Value) }.
263
267
268bt_checkboxes(Name, Values, CBOptions, FormOptions) -->
269 horizontal_input(\checkboxes(Values, CBOptions),
270 [checkboxes, array], name(Name), FormOptions).
271
272checkboxes([], _) -->
273 [].
274checkboxes([H|T], Options) -->
275 checkbox(H, Options),
276 checkboxes(T, Options).
277
278checkbox(Value, Options) -->
279 { name_label(Value, Label),
280 ( option(value(Selected), Options),
281 memberchk(Value, Selected)
282 -> Opts = [checked(checked)]
283 ; Opts = []
284 )
285 },
286 html(label(class('checkbox-inline'),
287 [ input([ type(checkbox), name(Value), autocomplete(false)
288 | Opts
289 ]),
290 Label
291 ])).
292
294
295bt_textarea(Name, TextAreaOptions, FormOptions) -->
296 horizontal_input(\bt_textarea_elem(Name, TextAreaOptions, FormOptions),
297 [], [], FormOptions),
298 !.
299bt_textarea(Name, TextAreaOptions, FormOptions) -->
300 bt_textarea_elem(Name, TextAreaOptions, FormOptions).
301
302
303bt_textarea_elem(Name, TextAreaOptions, _FormOptions) -->
304 { option(rows(Rows), TextAreaOptions, 4)
305 },
306 html(textarea([ class('form-control)'),
307 rows(Rows),
308 name(Name),
309 style('width:100%')
310 ], [])).
311
312
316
317bt_button_group(Buttons, _ButtonOptions, FormOptions) -->
318 { form_style(horizontal, FormOptions),
319 !,
320 option(label_columns(_-Count), FormOptions, sm-2),
321 Count12 is 12-Count,
322 atomic_list_concat([col,xs,offset,Count], -, Offset),
323 atomic_list_concat([col,xs,Count12], -, Width)
324 },
325 html(div(class('form-group'),
326 div(class([Offset,Width]),
327 \bt_form_content(Buttons, FormOptions)))).
328bt_button_group(Buttons, _ButtonOptions, FormOptions) -->
329 html(div(class(['col-xs-12', 'text-center']),
330 \bt_form_content(Buttons, FormOptions))).
331
332
334
335bt_button(Name, Type, IOptions, Options) -->
336 { phrase(button_classes(IOptions, Options), BtnClasses),
337 phrase(data(IOptions), DataAttrs)
338 },
339 html(button([ type(Type),
340 name(Name),
341 class([btn|BtnClasses])
342 | DataAttrs
343 ],
344 \label(Name, IOptions))).
345
349
350button_classes(IOptions, Options) -->
351 button_type_class(IOptions),
352 button_size_class(IOptions, Options).
353
354button_type_class(IOptions) -->
355 { option(type(Type), IOptions),
356 !,
357 atom_concat('btn-', Type, Class)
358 },
359 [Class].
360button_type_class(_IOptions) -->
361 ['btn-default'].
362
363button_size_class(IOptions, Options) -->
364 { ( option(button_size(Size), IOptions)
365 ; option(button_size(Size), Options)
366 ),
367 !,
368 atom_concat('btn-', Size, Class)
369 },
370 [Class].
371button_size_class(_IOptions, _Options) -->
372 [].
373
377
378data(Options) -->
379 { option(data(Data), Options, []) },
380 !,
381 data_values(Data).
382data(_) --> [].
383
384data_values([]) --> !.
385data_values([H|T]) --> !, data_values(H), data_values(T).
386data_values(Name-Value) -->
387 !,
388 data_value(Name, Value).
389data_values(Name=Value) -->
390 !,
391 data_value(Name, Value).
392data_values(NameValue) -->
393 { NameValue =.. [Name,Value] },
394 data_value(Name, Value).
395
396data_value(Name, Value) -->
397 { atom_concat('data-', Name, AttrName),
398 Attr =.. [AttrName,Value]
399 },
400 [Attr].
401
405
406classes([]) --> !.
407classes([class(Classes)|T]) --> !, class(Classes), classes(T).
408classes([_|T]) --> classes(T).
409
410class([]) --> !.
411class([H|T]) --> !, class(H), class(T).
412class(Class) --> [Class].
413
414
415 418
419label(Name, Options) -->
420 { ( option(label(Label), Options)
421 -> true
422 ; name_label(Name, Label)
423 )
424 },
425 html(Label).
426
431
432name_label(Name, Label) :-
433 atom_codes(Name, Codes),
434 phrase(name_label(up, LCodes), Codes),
435 atom_codes(Label, LCodes).
436
437name_label(up, [H|T]) --> [H0], !, {code_type(H, to_upper(H0))}, name_label(keep, T).
438name_label(keep, [0'\s|T]) --> "_", !, name_label(keep, T).
439name_label(keep, [H|T]) --> [H], !, name_label(keep, T).
440name_label(_, []) --> []