Документация Engee

"Малокодовый" API пользовательского интерфейса

StippleUI — это библиотека элементов реактивного пользовательского интерфейса для Stipple.jl, эффективного и полнофункционального решения для создания красивых, адаптивных, реактивных, высокопроизводительных интерактивных информационных панелей на чистом языке Julia.

StippleUI предоставляет более 30 элементов пользовательского интерфейса, включая формы и элементы ввода (кнопка, ползунок, флажок, переключатель, группа переключателей, диапазон), списки, таблицы данных, компоненты более высокого уровня (значки, баннеры, карточки, диалоговые окна, фишки, значки) и элементы макета (строка, столбец, информационная панель, заголовок, пробел) из фреймворка Quasar.

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

В StippleUI определены функции для большинства компонентов Quasar 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.

Привязки

Присваивать значения свойствам компонентов можно двумя способами:

  • присвоение строкового значения: attribute = "lorem ipsum";

  • присвоение выражения JavaScript:

    • путем присвоения Symbol

attribute = :input;

  • путем добавления символа ! к атрибуту

attribute! = "input".

В большинстве случаев этот синтаксис используется для прямой привязки переменных приложения, но можно привязывать и более сложные выражения, например элементы массива (имейте в виду, что в JS индексация начинается с 0): attribute = Symbol("data[0]") attribute = R"data[0]" attribute! = "data[0]" Строковый макрос R"" — это удобный способ определения символов.

Код JavaScript

Vue.js дает возможность встраивать функции JavaScript, которые вызываются вручную. Они называются методами (methods). Чтобы добавить метод в приложение, используйте макрос @methods следующим образом:

@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

Метод можно вызвать из кода Julia

@onchange trigger_variable begin
    @run raw"this.logdemo('hello world')"
end

или из другого элемента пользовательского интерфейса:

btn("Call the method", @click("this.function()"))

Можно также добавить код, который будет запускаться автоматически при возникновении определенных событий, например watch, mounted, created, computed. Такой код можно легко определить с помощью соответствующих макросов @watch, @mounted, @created, @computed, например:

@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

Отсутствующие компоненты

Если компонент Quasar еще не реализован в 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>"

Использование макетов

Определение макета полезно для задания общей структуры всех страниц, например для добавления панели навигации. Определить макет и передать его в функцию html можно следующим образом:

function layout()
    #именованный аргумент layout принимает строку, поэтому мы объединяем строки HTML
    join([head(title("Genie app")),
     body([h1("Welcome!"), "<% @yield %>"])])
end
ui() = p("Genie!")

@page("/layout", ui, layout=layout())

Здесь @yield внедряет содержимое страницы в макет.

Анализ и преобразование кода HTML в код Julia

Совсем недавно появился новый инструмент — StippleUIParser. Он преобразовывает код HTML в соответствующий код Julia и оптимизирует его. Это вспомогательный инструмент для переноса демонстрационного кода из Интернета в приложения Stipple/Genie.

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>