Low-code UI API
StippleUI is a library of reactive UI elements for Stipple.jl a powerful and complete solution for building beautiful, responsive, reactive, high performance interactive data dashboards in pure Julia.
StippleUI
provides over 30 UI elements, including forms and form inputs (button, slider, checkbox, radio, toggle, range), lists, data tables,
higher level components (badges, banners, cards, dialogs, chips, icons), and layout elements (row, col, dashboard, heading, space) from the Quasar Framework.
julia> btn("Just do it!", @click(:mybutton), loading = :mybutton) |> println
<q-btn :loading="mybutton" label="Just do it!" v-on:click="mybutton = true"></q-btn>
julia> textfield("Label", :mytext) |> println
<q-input label="Label" v-model="mytext"></q-input>
Quasar Components
StippleUI defines functions for most of the Quasar Vue components. Most functions are named like their Quasar counterparts, e.g. btn()
for q-btn
, tab()
for q-tab
, etc. Some functions have received different names for clarity, e.g. textfield()
for q-input
with text type and numberfield()
for q-input
with number type.
Most functions have named positional arguments that are used for definition of common attributes, e.g. label
or fieldname
.
Besides that all functions have a general calling scheme for unnamed arguments and keyword arguments:
- if a component supports content, the first non-named positional argument is passed as content to the components.
- any following non-array argument is passed as parameter to the components
- any non-named array argument is joined and passed as content
- if a component does not support content, e.g.
btn()
, all non-named arguments except arrays are passed as parameter. (Passing arrays can be used to define templates for a component. More details can be found in Vue.js docs.) - keyword arguments are passed pretty much the same as the Julia expression, except that
__
is converted to-
!
in the middle of a keyword is converted to.
!
at the end of a keyword indicates that the argument should be treated as js expression- a Symbol as value of a keyword argument indicates that the argument should be treated as js expression
- non-standard characters which are not allowed in Julia variable names can be used by employing the
var""
notation, e.g.span(var"v-html" = "My Text with <br>new lines")
- most variable names that contain a dash in Vue-syntax are automatically replaced, e.g.
"leftlabel" => "left-label"
. All standard mappings can be found inStippleUI.API.ATTRIBUTES_MAPPINGS
Bindings
There are two ways of assigning component properties
- assignment of a string value:
attribute = "lorem ipsum"
- assignment of a Javascript expression, by
- assigning a
Symbol
attribute = :input
- or by appending a
!
-character to the attributeattribute! = "input"
attribute = Symbol("data[0]")
attribute = R"data[0]"
attribute! = "data[0]"
TheR""
string macro is a convenient way of defining Symbols. - assigning a
Javascript code
Vue.js offers the possibility of embedding javascript functions that are called either manually, known as methods
. To add one to your app, use the @methods
macro as
@methods begin
"""
logdemo: function(text) {
console.log("Text from Stipple: " + text)
return text
},
squaredemo: function(number) {
console.log("The square of " + number + " is " + number**2)
return number**2
}
"""
end
A method can be called from the Julia code as
@onchange trigger_variable begin
@run raw"this.logdemo('hello world')"
end
or from another element in the UI as
btn("Call the method", @click("this.function()"))
You can also add code that is triggered automatically when certain events occur, e.g. watch
, mounted
, created
, computed
. Such code can easily be defined by the respective macros @watch
, @mounted
, @created
, @computed
, e.g.
@created """"
console.log('This app has just been created!')
"""
See the editable tree demo for more information. These macros also work for explicit models, e.g.
@created MyApp """"
console.log('This app has just been created!')
"""
User-defined events
There is support for user-defined events by the macro @event
.
@event :uploaded begin
println("Files have been uploaded!")
end
These lines define julia code that is executed when an event from the client is forwarded to the server.
Typically, events at the client originate from certain vue components, e.g. q-uploader
. They can be forwarded by calling the @on
macro
with two Symbol arguments.
julia> uploader("Upload files", url = "/upload" , @on(:uploaded, :uploaded))
"<q-uploader url=\"/upload\" v-on:uploaded=\"function(event) { handle_event(event, 'uploaded') }\">Upload files</q-uploader>"
Events can also be triggered manually by calling handle_event(event, 'uploaded')
on the client side.
See the file upload demo for more information.
Again the @event
macro also works for explicit models, e.g.
@event MyApp :uploaded begin
println("Files have been uploaded to MyApp!")
end
Missing components
If a quasar component is not yet covered by StippleUI, you can still use the quasar()
function to make it available in your UI:
julia> quasar(:btn, label = "Action!") |> println
<q-btn label="Action!"></q-btn>
In a very similar way, you can also integrate vue components or any other html component:
julia> vue(:calender, date = "today", "This is still a dream!")
"<vue-calender date=\"today\">This is still a dream!</vue-calender>"
julia> xelem(:br)
"<br></br>"
Using layouts
A layout definition is useful to set a common structure across pages, such as when adding a navigation bar. You can define layouts and pass them to the html
function as
function layout()
#the layout kwarg takes a string, so we join the HTML strings
join([head(title("Genie app")),
body([h1("Welcome!"), "<% @yield %>"])])
end
ui() = p("Genie!")
@page("/layout", ui, layout=layout())
Here, @yield
injects the page's content into the layout.
Parsing html code to Julia
A very new tool is StippleUIParser. It converts html code to the respective Julian code and prettifies html code. This is meant as a helper tool to port demo code from the internet into Stipple/Genie apps.
julia> using StippleUI.StippleUIParser
julia> doc_string = """
<template>
<div class="q-pa-md">
<q-scroll-area style="height: 230px; max-width: 300px;">
<div class="row no-wrap">
<div v-for="n in 10" :key="n" style="width: 150px" class="q-pa-sm">
Lorem @ipsum \$dolor sit amet consectetur adipisicing elit.
</div>
<q-btn color=\"primary\" label=\"`Animate to \${position}px`\" @click=\"scroll = true\"></q-btn>
<q-input hint=\"Please enter some words\" v-on:keyup.enter=\"process = true\" label=\"Input\" v-model=\"input\"></q-input>
<q-input hint=\"Please enter a number\" label=\"Input\" v-model.number=\"numberinput\" class=\"q-my-md\"></q-input>
</div>
</q-scroll-area>
</div>
</template>
""";
julia> parse_vue_html(html_string, indent = 2) |> println
template(
Stipple.Html.div(class = "q-pa-md",
scrollarea(style = "height: 230px; max-width: 300px;",
Stipple.Html.div(class = "row no-wrap", [
Stipple.Html.div(var"v-for" = "n in 10", key! = "n", style = "width: 150px", class = "q-pa-sm",
"Lorem @ipsum dolor sit amet consectetur adipisicing elit."
)
btn(raw"`Animate to ${position}px`", color = "primary", var"v-on:click" = "scroll = true")
textfield("Input", :input, hint = "Please enter some words", var"v-on:keyup.enter" = "process = true")
numberfield("Input", :numberinput, hint = "Please enter a number", class = "q-my-md")
])
)
)
)
Testing parsing result
There is also a testing tool test_vue_parsing()
whether the parsing was successful:
julia> test_vue_parsing(raw"""<a :hello="I need $$$">asap</a>""")
Original HTML string:
<a :hello="I need $$$">asap</a>
Julia code:
a(hello! = raw"I need $$$",
"asap"
)
Produced HTML:
<a :hello="I need $$$">
asap
</a>
which knows the details of binding syntax and which does respect the html preserve tag <pre>
julia> test_vue_parsing(raw"""<q-test :hello-world="I need $$$"> asap\n or\ntoday <pre>asap\n or\ntoday </pre></q-test>"""; indent = 2)
Original HTML string:
<q-test :hello-world="I need $$$"> asap\n or\ntoday <pre>asap\n or\ntoday </pre></q-test>
Julia code:
quasar(:test, var"hello-world" = R"I need $$$", [
"asap\n or\ntoday",
pre(
"asap\n or\ntoday "
)
])
Produced HTML:
<q-test :hello-world="I need $$$">
asap
or
today
<pre>
asap
or
today </pre>
</q-test>
Prettify html code
The new prettifier is already used in test_vue_parsing()
by default
julia> prettify("""<div class="first">single line<div> more\nlines</div></div>"""; indent = 5) |> println
<div class="first">
single line
<div>
more
lines
</div>
</div>