diff --git a/src/Layout.jl b/src/Layout.jl
index 96088c09..89cc8b2f 100644
--- a/src/Layout.jl
+++ b/src/Layout.jl
@@ -8,11 +8,13 @@ module Layout
using Genie, Stipple
export layout, add_css, remove_css
-export page, app, row, column, cell, container, flexgrid_kwargs, htmldiv
+export page, app, row, column, cell, container, flexgrid_kwargs, htmldiv, @gutter
export theme
const THEMES = Ref(Function[])
+const FLEXGRID_KWARGS = [:col, :xs, :sm, :md, :lg, :xl, :gutter, :xgutter, :ygutter]
+
"""
function layout(output::Union{String,Vector}; partial::Bool = false, title::String = "", class::String = "", style::String = "",
head_content::String = "", channel::String = Genie.config.webchannels_default_route) :: String
@@ -108,7 +110,46 @@ function container(args...; fluid = false, kwargs...)
Genie.Renderer.Html.div(args...; kwargs...)
end
+function iscontainer(class::String)
+ !isempty(intersect(split(class), ("row", "column")))
+end
+
+function iscontainer(class::Vector)
+ length(class) > 0 && class[end] in ("row", "column")
+end
+
+function iscontainer(class)
+ false
+end
+
+function flexgrid_class(tag::Symbol, value::Union{String,Int,Nothing,Symbol} = -1, container = false)
+ gutter = container ? "q-col-gutter" : "q-gutter"
+ (value == -1 || value === nothing) && return ""
+ out = String[]
+ if tag in (:col, :xs, :sm, :md, :lg, :xl)
+ tag == :col || push!(out, "col")
+ push!(out, "$tag")
+ elseif tag == :gutter
+ push!(out, gutter)
+ elseif tag == :xgutter
+ push!(out, "$gutter-x")
+ elseif tag == :ygutter
+ push!(out, "$gutter-y")
+ else
+ push!(out, "$tag")
+ end
+
+ if value isa Int
+ value > 0 && push!(out, "$value")
+ else
+ value isa Symbol && (value = String(value))
+ length(value) > 0 && value != "col" && push!(out, value)
+ end
+ return join(out, '-')
+end
+
function flexgrid_kwargs(; class = "", class! = nothing, symbol_class::Bool = true, flexgrid_mappings::Dict{Symbol,Symbol} = Dict{Symbol,Symbol}(), kwargs...)
+ container = iscontainer(class)
kwargs = Dict{Symbol,Any}(kwargs...)
# support all different types of classes that vue supports: String, Expressions (Symbols), Arrays, Dicts
@@ -129,10 +170,10 @@ function flexgrid_kwargs(; class = "", class! = nothing, symbol_class::Bool = tr
end
classes = String[]
- for key in (:col, :xs, :sm, :md, :lg, :xl)
+ for key in FLEXGRID_KWARGS
newkey = get(flexgrid_mappings, key, key)
if haskey(kwargs, newkey)
- colclass = sizetocol(kwargs[newkey], key)
+ colclass = flexgrid_class(key, kwargs[newkey], container)
length(colclass) > 0 && push!(classes, colclass)
delete!(kwargs, newkey)
end
@@ -147,7 +188,8 @@ function flexgrid_kwargs(; class = "", class! = nothing, symbol_class::Bool = tr
elseif class isa Vector
vcat(class, classes)
else
- join(pushfirst!(classes, class), ' ')
+ isempty(class) || pushfirst!(classes, class)
+ join(classes, ' ')
end
end
@@ -178,6 +220,153 @@ function append_class(class, subclass)
end
end
+"""
+ extract_kwargs!(args::Vector, kwarg_names)
+
+Low-level function that finds all kwargs in kwargs names from an expression if the expression is a function call and returns them as an expression that
+can be plugged in a function expression.
+"""
+function extract_kwargs!(args::Vector, kwarg_names)
+ kwargs = Expr(:parameters)
+ params = []
+ inds = Int[]
+ for n in length(args):-1:1
+ if args[n] isa Expr && args[n].head == :kw && args[n].args[1] in kwarg_names
+ pushfirst!(kwargs.args, popat!(args, n))
+ end
+ end
+ n = length(args)
+ pos = n > 0 && args[1] isa Expr && args[1].head == :parameters ? 1 : n > 1 && args[2] isa Expr && args[2].head == :parameters ? 2 : 0
+
+ pos == 0 && return kwargs
+
+ parameters = args[pos].args
+ for n in length(parameters):-1:1
+ println("parameters[$n]", parameters[n])
+ if parameters[n] isa Expr && parameters[n].head == :kw && parameters[n].args[1] in kwarg_names ||
+ parameters[n] isa Symbol && parameters[n] in kwarg_names
+ push!(params, popat!(parameters, n))
+ end
+ end
+ append!(kwargs.args, reverse(params))
+ kwargs
+end
+
+function _wrap_expression(expr)
+ new_expr = if expr isa Expr && expr.head == :call
+ kwargs = extract_kwargs!(expr.args, FLEXGRID_KWARGS[1:6])
+ # extra treatment for cell(), because col = 0 is default:
+ # So if not set explicitly then add col = 0 to the wrapper kwargs
+ if expr.args[1] == :cell && :col ∉ [kwarg isa Expr ? kwarg.args[1] : kwarg for kwarg in kwargs.args]
+ push!(kwargs.args, Expr(:kw, :col, 0))
+ end
+ new_expr = :(Stipple.htmldiv())
+ push!(new_expr.args, kwargs, expr)
+ new_expr
+ else
+ :(Stipple.htmldiv($expr))
+ end
+
+ new_expr
+end
+
+
+"""
+ @gutter(child_or_children)
+
+Wraps an element in a div-element to be part of a gutter container.
+(For the two-argument version of the macro that sets the gutter size see below.)
+
+### Example 1
+```julia
+julia> @gutter [
+ card("Hello", sm = 12, lg = 4)
+ card("World", sm = 12, md = 8)
+ ]
+
+2-element Vector{ParsedHTMLString}:
+ "
Hello
"
+ "World
"
+```
+Note that the child elements need to be explicitly written in the code, for more info see below.
+### Example 2
+```
+julia> row(gutter = :md, @gutter [
+ card("Hello", sm = 12, lg = 4)
+ card("World", sm = 12, md = 8)
+ ]) |> prettify |> println
+
+
+
+ Hello
+
+
+
+
+ World
+
+
+
+```
+"""
+macro gutter(expr)
+ if expr isa Expr && expr.head ∈ (:vcat, :vect)
+ expr.args = _wrap_expression.(expr.args)
+ else
+ expr = _wrap_expression(expr)
+ end
+ expr |> esc
+end
+
+"""
+ @gutter(size, children)
+
+Sets the spacing of child elements.
+(We use `card()` and `prettify()` from `StippleUI` for the examples.)
+
+```julia
+julia> row(@gutter :md [
+ card("Hello", sm = 12, lg = 4)
+ card("World", sm = 12, md = 8)
+ ]) |> prettify |> println
+
+
+
+ Hello
+
+
+
+
+ World
+
+
+
+```
+The internal reason for this macro is that elements in a gutter need to be wrapped in div-elements as you can see above.
+
+Note, that the macro can only handle children if they are explicitily written in the command. The macro cannot handle content of a variable,
+so `row(@gutter [c1, c2])` will fail.
+
+Instead, you'd move the gutter macro to the definition of c1 and pass the gutter size to the parent element
+```julia
+c1 = @gutter card("Hello", sm = 2, md = 8)
+c2 = @gutter card("World", sm = 10, md = 4)
+
+row(gutter = :md, [c1, c2])
+```
+If you need c1 unwrapped in a different context you'd go for manual wrapping. You can also go for a mixed approach.
+```
+c1 = card("Hello", sm = 2, md = 8)
+row(gutter = :md, [
+ cell(c1, sm = 12, md = 8, lg = 4, xl = 12)
+ @gutter card("World", sm = 12, md = 8, lg = 4, xl = 12)
+])
+```
+"""
+macro gutter(size, expr)
+ :((; gutter = $size, inner = @gutter($expr))...) |> esc
+end
+
"""
function row(args...; size=-1, xs=-1, sm=-1, md=-1, lg=-1, xl=-1, kwargs...)
@@ -195,15 +384,16 @@ julia> row(span("Hello"))
function row(args...;
col::Union{Int,AbstractString,Symbol,Nothing} = -1,
xs::Union{Int,AbstractString,Symbol,Nothing} = -1, sm::Union{Int,AbstractString,Symbol,Nothing} = -1, md::Union{Int,AbstractString,Symbol,Nothing} = -1,
- lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1, size::Union{Int,AbstractString,Symbol,Nothing} = -1,
- class = "", kwargs...)
+ lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1,
+ gutter::Union{AbstractString,Symbol,Nothing} = nothing, xgutter::Union{AbstractString,Symbol,Nothing} = nothing, ygutter::Union{AbstractString,Symbol,Nothing} = nothing,
+ class::Union{AbstractString,Symbol,AbstractDict,Vector} = "", size::Union{Int,AbstractString,Symbol,Nothing} = 0, kwargs...)
# for backward compatibility with `size` kwarg
col == -1 && size != -1 && (col = size)
# class = class isa Symbol ? Symbol("$class + ' row'") : class isa Vector ? push!(class, "row") : join(push!(split(class), "row"), " ")
class = append_class(class, "row")
- kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...))
+ kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, gutter, xgutter, ygutter, symbol_class = false, kwargs...))
Genie.Renderer.Html.div(args...; kwargs...)
end
@@ -226,13 +416,13 @@ julia> column(span("Hello"))
function column(args...;
col::Union{Int,AbstractString,Symbol,Nothing} = -1,
xs::Union{Int,AbstractString,Symbol,Nothing} = -1, sm::Union{Int,AbstractString,Symbol,Nothing} = -1, md::Union{Int,AbstractString,Symbol,Nothing} = -1,
- lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1, size::Union{Int,AbstractString,Symbol,Nothing} = -1,
- class = "", kwargs...)
-
+ lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1,
+ gutter::Union{AbstractString,Symbol,Nothing} = nothing, xgutter::Union{AbstractString,Symbol,Nothing} = nothing, ygutter::Union{AbstractString,Symbol,Nothing} = nothing,
+ class::Union{AbstractString,Symbol,AbstractDict,Vector} = "", size::Union{Int,AbstractString,Symbol,Nothing} = 0, kwargs...)
# for backward compatibility with `size` kwarg
col == -1 && size != -1 && (col = size)
class = append_class(class, "column")
- kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...))
+ kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, gutter, xgutter, ygutter, symbol_class = false, kwargs...))
Genie.Renderer.Html.div(args...; kwargs...)
end
@@ -268,15 +458,16 @@ julia> row(cell(size = 2, md = 6, sm = 12, span("Hello")))
function cell(args...;
col::Union{Int,AbstractString,Symbol,Nothing} = 0,
xs::Union{Int,AbstractString,Symbol,Nothing} = -1, sm::Union{Int,AbstractString,Symbol,Nothing} = -1, md::Union{Int,AbstractString,Symbol,Nothing} = -1,
- lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1, size::Union{Int,AbstractString,Symbol,Nothing} = 0,
- class = "", kwargs...
+ lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1,
+ gutter::Union{AbstractString,Symbol,Nothing} = nothing, xgutter::Union{AbstractString,Symbol,Nothing} = nothing, ygutter::Union{AbstractString,Symbol,Nothing} = nothing,
+ class::Union{AbstractString,Symbol,AbstractDict,Vector} = "", size::Union{Int,AbstractString,Symbol,Nothing} = 0, kwargs...
)
# for backward compatibility with `size` kwarg
col == 0 && size != 0 && (col = size)
# class = class isa Symbol ? Symbol("$class + ' st-col'") : join(push!(split(class), "st-col"), " ")
class = append_class(class, "st-col")
- kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...))
+ kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, gutter, xgutter, ygutter, symbol_class = false, kwargs...))
Genie.Renderer.Html.div(args...; kwargs...)
end
@@ -284,30 +475,18 @@ end
function htmldiv(args...;
col::Union{Int,AbstractString,Symbol,Nothing} = -1,
xs::Union{Int,AbstractString,Symbol,Nothing} = -1, sm::Union{Int,AbstractString,Symbol,Nothing} = -1, md::Union{Int,AbstractString,Symbol,Nothing} = -1,
- lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1, size::Union{Int,AbstractString,Symbol,Nothing} = -1,
- class = "", kwargs...)
+ lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1,
+ gutter::Union{AbstractString,Symbol,Nothing} = nothing, xgutter::Union{AbstractString,Symbol,Nothing} = nothing, ygutter::Union{AbstractString,Symbol,Nothing} = nothing,
+ class::Union{AbstractString,Symbol,AbstractDict,Vector} = "", size::Union{Int,AbstractString,Symbol,Nothing} = 0, kwargs...)
# for backward compatibility with `size` kwarg
col == -1 && size != -1 && (col = size)
- kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...))
+ kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, gutter, xgutter, ygutter, symbol_class = false, kwargs...))
Genie.Renderer.Html.div(args...; kwargs...)
end
-function sizetocol(size::Union{String,Int,Nothing,Symbol} = -1, tag::Symbol = :col)
- (size == -1 || size === nothing) && return ""
- out = ["col"]
- tag != :col && push!(out, String(tag))
- if size isa Int
- size > 0 && push!(out, "$size")
- else
- size isa Symbol && (size = String(size))
- length(size) > 0 && size != "col" && push!(out, size)
- end
- return join(out, '-')
-end
-
"""
function theme() :: String