Engee 文档

StippleUI.jl

StippleUI 是一个反应式用户界面元素库,用于 Stipple.jl

它是 GenieFramework的一部分。 它是 [GenieFramework] 的一部分。 StippleUI 它提供了 30 多种用户界面元素,包括表单和表单输入(按钮、滑块、复选框、单选框、切换框、范围)、列表和数据表、 更高级别的组件(徽章、横幅、卡片、对话框、芯片、图标)以及布局元素(行、列、仪表盘、标题、空格),这些元素来自 Quasar Framework

新闻:Vue 3 / Quasar 2

从 0.24 版开始,StippleUI 已将前端库升级至 Vue3 / Quasar 2,因为 Vue-2 已达到生命周期终点。

我们花了很多精力使迁移尽可能简单。不过,在某些地方,高级用户界面可能需要稍作调整。

版本 >= v0.24 的主要变更

组件

  • 常规:同步附加字段不再使用`fieldname.sync` 语法,而是使用`v-model:fieldname` 。这已由组件的应用程序接口处理,例如`table()` 中的`paginationsync` 。如果您手动添加了此类字段,则需要调整您的代码。

  • Quasars 的 Table 组件已将 content 属性的命名从`data` 改为`rows` 。 因此,从长远来看,模板中的所有引用都应更改为`rows` ,例如 table(:mytable, @showif("mytable.data.length")) 应更改为`table(:mytable, @showif("mytable.data.length"))` 。 不过,我们已经引入了一个监视器,将名为 "data "的第二个字段设置为 "rows "字段,这样在大多数情况下就能保持旧代码的运行。该解决方案的唯一缺点是,将表内容同步回服务器时会发送双倍的数据量;因此该辅助程序将来可能会被弃用。

更多迁移支持

安装

pkg> add StippleUI

使用方法

类星体组件

StippleUI 为大多数类星体 vue 组件定义了函数。大多数函数的名称与 Quasar 对应函数的名称相同,例如`btn()` 对应`q-btn` ,tab() 对应`q-tab` 等。为了清晰起见,有些函数采用了不同的名称,例如`textfield()` 代表文本类型的`q-input` ,numberfield() 代表数字类型的`q-input` 。 大多数函数都有命名的位置参数,用于定义共同属性,如`label` 或`fieldname` 。 此外,所有函数都有一个用于未命名参数和关键字参数的通用调用方案:

  • 如果组件支持内容,第一个非命名位置参数将作为内容传递给组件。

  • 以下任何非数组参数将作为参数传递给组件

  • 任何未命名的数组参数都会被连接并作为内容传递给组件

  • 如果组件不支持内容,如`btn()` ,则除数组外的所有非命名参数都将作为参数传递。(传递数组可用于为组件定义模板。更多详情请参见 Vue.js 文档)。

  • 关键字参数的传递方式与 Julia 表达式基本相同,只是

    • __ 转换为`-`

    • ! 转换为`.`

    • ! 在关键字末尾表示该参数应被视为 js 表达式

    • 符号作为关键字参数的值,表示该参数应被视为 js 表达式

    • 对于 Julia 变量名中不允许使用的非标准字符,可以使用var""符号,例如 span(var"v-html" = "My Text with <br>new lines")

    • Vue 语法中包含破折号的大多数变量名都会被自动替换,例如:"leftlabel" => "left-label" 。所有标准映射可在`StippleUI.API.ATTRIBUTES_MAPPINGS`

示例

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>

如果缺少一个组件怎么办?

如果某个类星体组件尚未被 StippleUI 涵盖,您仍然可以使用`quasar()` 函数将其添加到您的用户界面中:

julia> quasar(:btn, label = "Action!") |> println
<q-btn label="Action!"></q-btn>

与此类似,您也可以集成 vue 组件或任何其他 HTML 组件:

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>"

绑带

有两种分配组件属性的方法

  • 字符串值赋值: attribute = "lorem ipsum"

  • 通过

    • 赋值`Symbol` attribute = :input

    • 或在属性中添加`!- 字符 `attribute! = "input"

    在大多数情况下,这种语法用于直接绑定应用程序变量,但也可以绑定更复杂的表达式,例如数组元素(注意 js 采用基于 0 的索引): attribute = Symbol("data[0]") attribute = R"data[0]" attribute! = "data[0]" R"" 字符串宏是定义符号的一种便捷方式。

    + === 辅助宏

Vue.js 定义了几种快捷方式,通常以`@- 字符开头。在 Julia 中,@- 字符是为宏所保留的。因此,我们决定使用宏来实现类似的语法。但我们也提供了宏来添加其他 "禁止 "字符的属性,如.` 或`-` 。 请查看`@click`,@on,@iif,@els,@elsiif,@recur,@text,@bind,@data 和`@showif` 的文档说明,了解更多信息。要尝试渲染是否正确,请在 REPL 中输入暂定表达式,例如

julia> span(@showif(true))
"<span v-show=\"true\"></span>"

julia> span(@click(:mybutton))
"<span v-on:click=\"mybutton = true\"></span>"

Quasar 的柔性电网

Quasar 实现了一种名为 "Flexgrid "的网格系统,可轻松按类定义用户界面。

网格可以是垂直的(class = "column" ),也可以是水平的(class = "row" )。子元素在该容器中的大小是通过设置类`"col"` 来获得的,如果是等间距的子元素,则设置`"col-6"` ,如果是容器大小的 1/12 的固定倍数(此处`6` ,即 50%),则设置 。 此外,Quasar 还允许根据容器的大小改变子元素的大小,方法是在 col 类之后添加一个大小条件,例如`"col-md-3"` ,或直接`"col-md"` 。 在 StippleUI-API 中,我们定义了属性`col`,xs,sm,md,lg,xl ,以便更方便地定义类,例如

row(htmldiv(col = 2, md = 4)) |> println
# <div class="row"><div class="col-2 col-md-4"></div></div>

此外,子元素之间的间隔可以通过设置 class="gutter-md" 来添加。如果是柔性容器中的子元素(row 或`column` ),则需要设置为`class = "gutter-col-md"` 。同样,我们也定义了一个属性来处理这种差异,并自动选择正确的设置。

julia> row(gutter = "md", htmldiv(col = 2, md = 4), "Hello World") |> println
<div class="row q-col-gutter-md" Hello World><div class="col-2 col-md-4"></div></div>

此外,由于 Quasar 设置边距和填充的方式不同,如果子元素有背景设置,可能会导致显示效果不佳。解决的办法是用一个额外的`div` 元素来包裹子元素,使用@gutter宏就可以方便地做到这一点。

julia> row(gutter = "md", @gutter htmldiv(col = 2, md = 4, "Hello World")) |> println
<div class="row q-col-gutter-md"><div class="col-2 col-md-4"><div>Hello World</div></div></div>

更多详细信息,请参阅文档说明。

Javascript 代码

Vue.js 提供了嵌入 javascript 函数的可能性,这些函数可以手动调用 (methods) 或在特定事件发生时自动调用,例如`watch`,mounted,created,computed 。这些代码可以很容易地通过相应的宏来定义`@methods`,@watch,@mounted,@created,@computed, 例如

@methods """
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
}
"""

@created """"
    console.log('This app has just been created!')
"""

更多信息请参见 可编辑树演示。 这些宏也适用于显式模型,例如

@created MyApp """"
    console.log('This app has just been created!')
"""

用户自定义事件

宏`@event` 支持用户自定义事件。

@event :uploaded begin
    println("Files have been uploaded!")
end

这些行定义了当客户端的事件被转发到服务器时要执行的 julia 代码。 通常,客户端的事件源自某些 vue 组件,例如`q-uploader` 。它们可以通过调用`@on` 宏和两个符号参数来转发。 宏,其中包含两个符号参数。

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>"

也可以通过在客户端调用`handle_event(event, 'uploaded')` 手动触发事件。

更多信息请参阅 文件上传演示。 同样,@event 宏也适用于显式模型,例如

@event MyApp :uploaded begin
    println("Files have been uploaded to MyApp!")
end

示例应用程序

下面的代码段说明了如何基于 StippleUI 构建反应式用户界面。你可以找到三种不同的触发处理程序的方法。

  • 输入字段中的每次输入都会触发一个函数,在输出字段中设置反转的输入字符串。

  • 按下输入框中的`Enter` 键可反转输出字符串。

  • 按下操作按钮可反转输出字符串。 StippleUI

using Stipple, StippleUI

@vars Inverter begin
  process = false
  input = ""
  output::String = "", READONLY
end

function handlers(model)
  on(model.input) do input
      model.output[] = input |> reverse
  end

  onbutton(model.process) do
      model.output[] = model.output[] |> reverse
  end

  model
end

function ui()
  row(cell(class = "st-module", [
    textfield(class = "q-my-md", "Input", :input, hint = "Please enter some words", @on("keyup.enter", "process = true"))

    btn(class = "q-my-md", "Action!", @click(:process), color = "primary")

    card(class = "q-my-md", [
      card_section(h2("Output"))
      card_section("Variant 1: {{ output }}")
      card_section(["Variant 2: ", span(class = "text-red", @text(:output))])
    ])
  ]))
end

route("/") do
  model = Inverter |> init |> handlers
  page(model, ui()) |> html
end

Genie.isrunning(:webserver) || up()

StippleUIParser

工具

  • parse_vue_html

  • test_vue_parsing

  • prettify

StippleUIParser 是一款非常新颖的工具。它能将 html 代码转换为相应的 Julian 代码,并对 html 代码进行美化。这是一个辅助工具,可将演示代码从互联网移植到 Stipple/Genie 应用程序中。

将 vue 的 HTML 代码解析为 Julia 代码

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")
      ])
    )
  )
)

测试解析结果

还有一个测试工具`test_vue_parsing()` ,测试解析是否成功:

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>

该工具了解绑定语法的细节,并尊重 html 保留标记 <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>

美化 HTML 代码

test_vue_parsing() 默认已使用新的预修饰器

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>

演示

StippleDemos

我们在 Github 上专门开辟了一个 Stipple 演示页面,其中包含许多可下载的示例: https://github.com/GenieFramework/StippleDemos (并非所有示例都已更新至 API 的最新版本。但它们中的大部分都能正常运行)。

德国学分可视化仪表板

鸢尾花数据集 k-Means 聚类仪表板

完整的应用程序可在以下网址获取: https://github.com/GenieFramework/Stipple-Demo-IrisClustering

更多信息

虽然 Stipple/StippleUI 文档仍在不断完善中,但您可以通过函数的文档说明找到帮助和许多小例子。

help?> btn
search: btn Btn btngroup btndropdown q__btn q__btn__group q__btn__dropdown button Buttons onbutton SubString @onbutton bitstring

  Stipple has a component called btn which is a button with a few extra useful features. For instance, it comes in two shapes:
  rectangle (default) and round. It also has the material ripple effect baked in (which can be disabled).

  The button component also comes with a spinner or loading effect. You would use this for times when app execution may cause a
  delay and you want to give the user some feedback about that delay. When used, the button will display a spinning animation as
  soon as the user clicks the button.

  When not disabled or spinning, btn emits a @click event, as soon as it is clicked or tapped.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> btn("Move Left", color = "primary", icon = "mail", @click("press_btn = true"))

  julia> btn("Go to Hello World", color = "red", type = "a", href = "hello", icon = "map", iconright = "send")

  julia> btn("Connect to server!", color="green", textcolor="black", @click("btnConnect=!btnConnect"), [
            tooltip(contentclass="bg-indigo", contentstyle="font-size: 16px",
            style="offset: 10px 10px",  "Ports bounded to sockets!")]
         )

  ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  Arguments
  ≡≡≡≡≡≡≡≡≡≡≡

  ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

    1. Behavior
       • loading::Bool - Put button into loading state (displays a spinner – can be overridden by using a 'loading'
       slot)
       • percentage::Union{Int, Float64} - Percentage (0.0 < x < 100.0); To be used along 'loading' prop; Display a
       progress bar on the background ex. 23
       • darkpercentage::Bool - Progress bar on the background should have dark color; To be used along with
       'percentage' and 'loading' props

    2. Content
       • label::Union{String, Int} - The text that will be shown on the button ex. Button Label
       • icon::String - Icon name following Quasar convention; Make sure you have the icon library installed unless
       you are using 'img:' prefix; If 'none' (String) is used as value then no icon is rendered (but screen real
       estate will still be used for it) ex. map ion-add img:https://cdn.quasar.dev/logo/svg/quasar-logo.svg
       img:path/to/some_image.png
       • iconright::String - Icon name following Quasar convention; Make sure you have the icon library installed
       unless you are using 'img:' prefix; If 'none' (String) is used as value then no icon is rendered (but screen
       real estate will still be used for it) ex. map ion-add img:https://cdn.quasar.dev/logo/svg/quasar-logo.svg
       img:path/to/some_image.png
       • nocaps::Bool - Avoid turning label text into caps (which happens by default)
       • nowrap::Bool - Avoid label text wrapping
       • align::String - Label or content alignment default. center accepted values. left right center around between
       evenly
       • stack::Bool - Stack icon and label vertically instead of on same line (like it is by default)
       • stretch::Bool - When used on flexbox parent, button will stretch to parent's height

    3. General
       • type::String - 1) Define the button native type attribute (submit, reset, button) or 2) render component with
       <a> tag so you can access events even if disable or 3) Use 'href' prop and specify 'type' as a media tag
       default. button ex. a submit reset button image/png href="https://some-site.net" target="_blank"
       • tabindex::Union{Int, String} - Tabindex HTML attribute value

    4. Navigation
       • href::String - Native <a> link href attribute; Has priority over the 'to' and 'replace' props ex.
       https://quasar.dev href="https://quasar.dev" target="_blank"
       • target::String - Native <a> link target attribute; Use it only with 'to' or 'href' props ex. _blank _self
       _parent _top

    5. State
       • loading::Bool - Put button into loading state (displays a spinner – can be overridden by using a 'loading'
       slot)
       • padding::String - Apply custom padding (vertical [horizontal]); Size in CSS units, including unit name or
       standard size name (none|xs|sm|md|lg|xl); Also removes the min width and height when set
       • color::String - Color name for component from the Color Palette (https://quasar.dev/style/color-palette) eg.
       primary teal-10
       • textcolor::String - Overrides text color (if needed); Color name from the Color Palette
       (https://quasar.dev/style/color-palette) eg. primary teal-10
       • dense::Bool - Dense mode; occupies less space
       • round::Bool - Makes a circle shaped button

致谢

StippleUI 基于优秀的 Quasar Vue 组件和底层 Vue.js Javascript 框架。