Skip to content

Commit 105fa99

Browse files
authored
clean up togglebuttons code and layout (#134)
1 parent af5a05c commit 105fa99

File tree

4 files changed

+92
-60
lines changed

4 files changed

+92
-60
lines changed

src/classes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function getclass(T::WidgetTheme, arg, typ...)
4040
elseif arg==:div
4141
return "field"
4242
elseif arg==:togglebuttons
43-
return "field is-grouped has-addons is-oneline is-centered"
43+
return "buttons has-addons is-centered"
4444
elseif arg==:tabs
4545
return "tabs"
4646
elseif arg==:radiobuttons

src/optioninput.jl

Lines changed: 77 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
struct Automatic; end
2+
const automatic = Automatic()
3+
14
function _js_array(x::AbstractDict; process=string, placeholder=nothing)
25
v = OrderedDict[OrderedDict("key" => key, "val" => i, "id" => "id"*randstring()) for (i, (key, val)) in enumerate(x)]
36
placeholder !== nothing && pushfirst!(v, OrderedDict("key" => placeholder, "val" => 0, "id" => "id"*randstring()))
@@ -52,7 +55,7 @@ end
5255
function initvalueindex(value, index, vals2idxs;
5356
multiple = false, default = multiple ? eltype(vals2idxs[])[] : first(vals2idxs[]), rev = false)
5457

55-
if value === Some(nothing)
58+
if value === automatic
5659
value = (index === nothing) ? default : vals2idxs[][Observables._val(index)]
5760
end
5861
(value isa AbstractObservable) || (value = Observable{Any}(value))
@@ -117,7 +120,7 @@ function dropdown(::WidgetTheme, options::AbstractObservable;
117120
placeholder = nothing,
118121
label = nothing,
119122
multiple = false,
120-
value = Some(nothing),
123+
value = automatic,
121124
index = nothing,
122125
className = "",
123126
style = PropDict(),
@@ -159,8 +162,7 @@ multiselect(T::WidgetTheme, options; kwargs...) =
159162

160163
function multiselect(T::WidgetTheme, options::AbstractObservable; container=node(:div, className=:field), wrap=identity,
161164
label = nothing, typ="radio", wdgtyp=typ, stack=true, skip=1em, hskip=skip, vskip=skip,
162-
value = Some(nothing), index = nothing, kwargs...)
163-
attributes = merge(get(props(container), :attributes, Dict()), Dict("data-bind" => "foreach : options_js"))
165+
value = automatic, index = nothing, kwargs...)
164166
vals2idxs = map(Vals2Idxs, options)
165167
p = initvalueindex(value, index, vals2idxs, multiple = (typ != "radio"))
166168
value, index = p.first, p.second
@@ -169,7 +171,7 @@ function multiselect(T::WidgetTheme, options::AbstractObservable; container=node
169171
option_array = _js_array(options)
170172
entry = wrap(InteractBase.entry(s; typ=typ, wdgtyp=wdgtyp, stack=stack, kwargs...))
171173
(entry isa Tuple )|| (entry = (entry,))
172-
template = container(attributes = attributes)(
174+
template = container(attributes = Dict("data-bind" => "foreach : options_js"))(
173175
entry...
174176
)
175177
ui = knockout(template, ["index" => index, "options_js" => option_array])
@@ -306,45 +308,8 @@ wdg[:options][] = ["c", "d", "e"]
306308
toggles(T::WidgetTheme, options; kwargs...) =
307309
Widget{:toggles}(multiselect(T, options; typ="checkbox", wdgtyp="toggle", kwargs...))
308310

309-
for (wdg, tag, singlewdg, div, process) in zip([:togglebuttons, :tabs], [:button, :li], [:button, :tab], [:div, :ul], [:string, :identity])
310-
@eval begin
311-
$wdg(T::WidgetTheme, options; kwargs...) = $wdg(T::WidgetTheme, Observable(options); kwargs...)
312-
313-
function $wdg(T::WidgetTheme, options::AbstractObservable; tag = $(Expr(:quote, tag)),
314-
className = getclass($(Expr(:quote, singlewdg)), "fullwidth"),
315-
activeclass = getclass($(Expr(:quote, singlewdg)), "active"),
316-
index = nothing, value = Some(nothing),
317-
label = nothing, readout = false, vskip = 1em, kwargs...)
318-
319-
vals2idxs = map(Vals2Idxs, options)
320-
p = initvalueindex(value, index, vals2idxs; default = first(vals2idxs[]))
321-
value, index = p.first, p.second
322-
323-
className = mergeclasses(getclass($(Expr(:quote, singlewdg))), className)
324-
updateSelected = js_lambda("\$root.index(val)")
325-
btn = node(tag,
326-
node(:label, attributes = Dict("data-bind" => "text : key")),
327-
attributes=Dict("data-bind"=>
328-
"click: $updateSelected, css: {'$activeclass' : \$root.index() == val, '$className' : true}"),
329-
)
330-
option_array = _js_array(options; process = $process)
331-
template = node($(Expr(:quote, div)), className = getclass($(Expr(:quote, wdg))), attributes = "data-bind" => "foreach : options_js")(
332-
btn
333-
)
334-
335-
label != nothing && (template = flex_row(wdglabel(label), template))
336-
ui = knockout(template, ["index" => index, "options_js" => option_array])
337-
slap_design!(ui)
338-
339-
w = Widget{$(Expr(:quote, wdg))}(["options"=>options, "index" => ui["index"], "vals2idxs" => vals2idxs];
340-
scope = ui, output = value, layout = node(:div, className = "field interact-widget")Widgets.scope)
341-
if readout
342-
w[:display] = mask(map(parent, vals2idxs); index = index)
343-
w.layout = t -> div(dom"div.field"(Widgets.scope(t)), CSSUtil.vskip(vskip), t[:display], className = "interact-widget")
344-
end
345-
w
346-
end
347-
end
311+
for wdg in [:togglebuttons, :tabs]
312+
@eval $wdg(T::WidgetTheme, options; kwargs...) = $wdg(T::WidgetTheme, Observable(options); kwargs...)
348313
end
349314

350315
"""
@@ -378,7 +343,39 @@ Note that the `options` can be modified from the widget directly:
378343
wdg[:options][] = ["c", "d", "e"]
379344
```
380345
"""
381-
function togglebuttons end
346+
function togglebuttons(T::WidgetTheme, options::AbstractObservable;
347+
className = "",
348+
activeclass = getclass(:button, "active"),
349+
index = nothing, value = automatic,
350+
container = node(:div, className = getclass(:togglebuttons)), wrap=identity,
351+
label = nothing, readout = false, vskip = 1em, kwargs...)
352+
353+
vals2idxs = map(Vals2Idxs, options)
354+
p = initvalueindex(value, index, vals2idxs; default = first(vals2idxs[]))
355+
value, index = p.first, p.second
356+
357+
className = mergeclasses("interact-widget", getclass(:button), className)
358+
updateSelected = js_lambda("\$root.index(val)")
359+
btn = node(:span,
360+
node(:label, attributes = Dict("data-bind" => "text : key")),
361+
attributes=Dict("data-bind"=>
362+
"click: $updateSelected, css: {'$activeclass' : \$root.index() == val, '$className' : true}"),
363+
)
364+
option_array = _js_array(options)
365+
template = container(attributes = "data-bind" => "foreach : options_js")(wrap(btn))
366+
367+
label != nothing && (template = flex_row(wdglabel(label), template))
368+
ui = knockout(template, ["index" => index, "options_js" => option_array])
369+
slap_design!(ui)
370+
371+
w = Widget{:togglebuttons}(["options"=>options, "index" => ui["index"], "vals2idxs" => vals2idxs];
372+
scope = ui, output = value, layout = Widgets.scope)
373+
if readout
374+
w[:display] = mask(map(parent, vals2idxs); index = index)
375+
w.layout = t -> div(Widgets.scope(t), CSSUtil.vskip(vskip), t[:display], className = "interact-widget")
376+
end
377+
w
378+
end
382379

383380
"""
384381
`tabs(options::AbstractDict; value::Union{T, Observable})`
@@ -411,4 +408,38 @@ Note that the `options` can be modified from the widget directly:
411408
wdg[:options][] = ["c", "d", "e"]
412409
```
413410
"""
414-
function tabs end
411+
function tabs(T::WidgetTheme, options::AbstractObservable;
412+
className = "",
413+
activeclass = getclass(:tab, "active"),
414+
index = nothing, value = automatic,
415+
container=node(:div, className = getclass(:tabs)), wrap=identity,
416+
label = nothing, readout = false, vskip = 1em, kwargs...)
417+
418+
vals2idxs = map(Vals2Idxs, options)
419+
p = initvalueindex(value, index, vals2idxs; default = first(vals2idxs[]))
420+
value, index = p.first, p.second
421+
422+
className = mergeclasses("interact-widget", getclass(:tab), className)
423+
updateSelected = js_lambda("\$root.index(val)")
424+
tab = node(:li,
425+
wrap(node(:a, attributes = Dict("data-bind" => "text: key"))),
426+
attributes=Dict("data-bind"=>
427+
"click: $updateSelected, css: {'$activeclass' : \$root.index() == val, '$className' : true}"),
428+
)
429+
option_array = _js_array(options)
430+
template = container(
431+
node(:ul, attributes = "data-bind" => "foreach : options_js")(tab)
432+
)
433+
434+
label != nothing && (template = flex_row(wdglabel(label), template))
435+
ui = knockout(template, ["index" => index, "options_js" => option_array])
436+
slap_design!(ui)
437+
438+
w = Widget{:tabs}(["options"=>options, "index" => ui["index"], "vals2idxs" => vals2idxs];
439+
scope = ui, output = value, layout = Widgets.scope)
440+
if readout
441+
w[:display] = mask(map(parent, vals2idxs); index = index)
442+
w.layout = t -> div(Widgets.scope(t), CSSUtil.vskip(vskip), t[:display], className = "interact-widget")
443+
end
444+
w
445+
end

src/output.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ keys represent the labels and whose values represent what is shown in each entry
208208
`options` changes.
209209
"""
210210
function accordion(::WidgetTheme, options::Observable;
211-
multiple = true, value = nothing, index = value, key = Some(nothing))
211+
multiple = true, value = nothing, index = value, key = automatic)
212212

213213
vals2idxs = map(Vals2Idxscollect_keys, options)
214214
p = initvalueindex(key, index, vals2idxs, rev = true, multiple = multiple)
@@ -274,7 +274,7 @@ Note that the `options` can be modified from the widget directly:
274274
wdg[:options][] = ["c", "d", "e"]
275275
```
276276
"""
277-
function mask(::WidgetTheme, options; value = nothing, index = value, key = Some(nothing), multiple = false)
277+
function mask(::WidgetTheme, options; value = nothing, index = value, key = automatic, multiple = false)
278278

279279
options isa AbstractObservable || (options = Observable{Any}(options))
280280
vals2idxs = map(Vals2Idxscollect_keys, options)
@@ -311,11 +311,12 @@ tabulator(OrderedDict("plot" => plot(rand(10)), "scatter" => scatter(rand(10))),
311311
see `tabulator(options::AbstractDict; ...)` for more details
312312
313313
```
314-
tabulator(options::Observable; kwargs...)
314+
tabulator(options::Observable; navbar=tabs, kwargs...)
315315
```
316316
317317
Tabulator whose `options` are a given `Observable`. Set the `Observable` to some other
318-
value to update the options in real time.
318+
value to update the options in real time. Defaults to `navbar=tabs`: use `navbar=togglebuttons`
319+
to have buttons instead of tabs.
319320
320321
## Examples
321322
@@ -331,7 +332,7 @@ Note that the `options` can be modified from the widget directly:
331332
wdg[:options][] = ["c", "d", "e"]
332333
```
333334
"""
334-
function tabulator(T::WidgetTheme, options; navbar = togglebuttons, skip = 1em, vskip = skip, value = nothing, index = value, key = Some(nothing), kwargs...)
335+
function tabulator(T::WidgetTheme, options; navbar = tabs, skip = 1em, vskip = skip, value = nothing, index = value, key = automatic, kwargs...)
335336
options isa AbstractObservable || (options = Observable{Any}(options))
336337
vals2idxs = map(Vals2Idxscollect_keys, options)
337338
p = initvalueindex(key, index, vals2idxs, rev = true)
@@ -341,7 +342,7 @@ function tabulator(T::WidgetTheme, options; navbar = togglebuttons, skip = 1em,
341342
buttons = navbar(T, d; index = index, readout = false, kwargs...)
342343
content = mask(options; index = index)
343344

344-
layout = t -> div(t[:buttons], CSSUtil.vskip(vskip), t[:content], className = "interact-widget")
345-
Widget{:tabulator}(["index" => index, "key" => key, "buttons" => buttons, "content" => content, "options" => options];
345+
layout = t -> div(t[:navbar], CSSUtil.vskip(vskip), t[:content], className = "interact-widget")
346+
Widget{:tabulator}(["index" => index, "key" => key, "navbar" => buttons, "content" => content, "options" => options];
346347
output = index, layout = layout)
347348
end

test/test_observables.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,17 @@ end
296296
@test observe(wdg)[] == 2
297297

298298
a = tabulator(OrderedDict("a" => 1.1, "b" => 1.2, "c" => 1.3))
299-
@test a[:buttons] isa InteractBase.Widget{:togglebuttons}
300-
@test a[:buttons][:index][] == 1
301-
@test observe(a, :buttons)[] == 1
299+
@test a[:navbar] isa InteractBase.Widget{:tabs}
300+
@test a[:navbar][:index][] == 1
301+
@test observe(a, :navbar)[] == 1
302302
observe(a)[] = 2
303303
sleep(0.1)
304-
@test a[:buttons][:index][] == 2
305-
@test observe(a, :buttons)[] == 2
304+
@test a[:navbar][:index][] == 2
305+
@test observe(a, :navbar)[] == 2
306306
@test observe(a, "key")[] == "b"
307307

308308
a = tabulator(OrderedDict("a" => 1.1, "b" => 1.2, "c" => 1.3), value = 0)
309-
@test a[:buttons][:index][] == 0
309+
@test a[:navbar][:index][] == 0
310310
@test observe(a, :key)[] == nothing
311311

312312
v = OrderedDict("a" => checkbox(), "b" => 12)

0 commit comments

Comments
 (0)