Giter Club home page Giter Club logo

vuejs.jl's People

Contributors

andregsobral avatar antonioloureiro avatar charlieit avatar filipemlourenco avatar kuren avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

vuejs.jl's Issues

Client-side validation

#18

  • create some basic default rules library developers can use

  • Rules must be suitable for including within prop rules

Form submissions

Create default submission behavior, including parsing form elements and building output as JSON object.

VueStruct | Interpret a xmlHttpRequest

VueStruct should accept a function argument aimed at (de)constructing an incoming JSON object.

Function should be able to iterate over a JSON object or a VueJS data model, parse its content and return it's output as a Dict

Navigation | v-navigation-drawer

expand-on-hover and permanent props not being applied on nav initialization.
Most likely only being applied to the internal v-list.

<v-navigation-drawer clipped app width="300" >
<v-list :width="navigation.width"  permanent v-for="item in navigation.value"  @change="navigation.value= $event"  v-bind:key="item.id"  expand-on-hover :items="navigation.value" ><v-list-item dense link @click="open(item.href)">

Solutions:

  1. Expand whitelist of attributes that must be applied to v-navigation-drawer and not the internal list, maintaining current implementation of UPDATE_VALIDATION["v-navigation-drawer"] .

  2. Add some prefix to attributes that should be applied to the internal list, eg. list-prop= some prop value, instead of just prop=prop_value.
    Or vice-versa, a nav prefix for attributes that should be applied to navigation-drawer, e.g: @el(navigation,"v-navigation-drawer",width=300, nav-expand-on-hover=true, nav-permanent=true (...) )


Update: whitelisting "expand-on-hover" and "permanent"

x.render_func=y->begin
        
        dom_nav=dom(y,prevent_render_func=true)
        
        nav_attrs=Dict()
        
        for (k,v) in Dict("clipped"=>true,"width"=>200, "expand-on-hover"=>true, "permanent"=>true)
            haskey(y.attrs,k) ? nav_attrs[k]=y.attrs[k] : nav_attrs[k]=v
        end
        
        HtmlElement("v-navigation-drawer",nav_attrs,12,dom_nav)
    end

Fields within cards get column property (width) overriden

Example: 2 fields, field1 and field2, on the same row of a card, with cols = 3 and cols = 9, respectively, get displayed with classes col-md-6 and col-md-18, also respectively.

function test(req)
    @el(field1, "v-text-field", label = "Field 1", value = "Value 1", cols = 3)
    @el(field2, "v-text-field", label = "Field 2", value = "Value 2", cols = 9)
    
    return response(page(card([[field1, field2]], cols = 6)))
end

image

Class values differ for cards of different widths. For instance, a card with cols = 7 generates fields with classes col-md-5 and col-md-15.

Prototype v0.1

Build a prototype application based on an existing table/data based web app

Client-side validation

Allow client-side validation [potentially also sanitization] of html elements, with focus on form elements.

#8

Bug: VueElement click kwarg using ' ' inside function call

Detected with

mr2 = VueElement("r2", "v-text-field", label="clickable", value="clickable", click="say('hi')"
mpage = page([mr2], methods=Dict("say"=>"alert(teste)"), data=Dict("r2"=>"teste"))

HTML output is erroneous

')'="" :value="r2.value" @input="r2.value= $event">

Does not occur when
[Correctly associates events with the DOM and alerts hi on text-field click]

mr2 = VueElement("r2", "v-text-field", label="clickable", value="clickable", click=""" say("hi") """)
mpage = page([mr2], methods=Dict("say"=>"alert(teste)"), data=Dict("r2"=>"teste"))

[Will provide further detail]

Form submissions | FormData

When using FormData, ensure element hierarchy is kept.

If submission is a FormData object, add a special HTTP Header.

(Implement server side routine to parse incoming HTTP Requests that can handle multipart content or any regular String/Json content seamlessly, while also maintaining object hierarchy)

v-data-table | Filtering nth

  • Wait for user to finish typing before filtering and rendering search results.

  • Adjusts filtering operators based on columns data types [different possible filtering options based on the data to which filter applies]

v-treeview | Component Implementation

Added the component "v-treeview" to the other already available components in the library.

Implemented as a VueElement since it does not contain any nested elements.

To be defined correctly, it only needs the items attribute to have data in the tree format.
ex: Parent nodes -> id, name, children

ex: Leaf nodes -> id, name

Ex: Rendering - with more options

Ex: Rendering - expecting icons

Each node may have a new field specified by the "prepend-icon-field" attribute, where the icon information is stored. If it doesn't have it, it is considered a folder (eases file explorer rendering)

Code added to Update_validation.jl
image

Docs | Complement example documentation

Up to 6 pages. Extend general use examples in simple_page.

Create pages for special components.

Create pages to show page (app) layout.

Create complex page, with multiple components, multiple bindings and submissions

Page Instance | globals feature

Enables the creation of a datastructure "globals" in the page instance.
Here we can store variable information and then refer to it dynamically in our VueElements that compose our web page.

It works in a similar fashion as the "data" of a vue instance. It gives us the ability to define data in our page instance, in our server side, instead of relying on javascript to define auxiliary datastructures inside elements.

Examples of usage:

  • Loading state
    All Elements "disabled" prop are binded to "globals.loading_state", mimicking a loading state.

  • Keeping context
    In a mapping view, I need to keep the context of the selected taxonomy and this info is not stored in any visual element, i can do this by setting a "globals.selected_taxonomy".

  • Easy rendering
    Mimics the data of a Vue Application and thus can be used to bind to "v-model" cases. The rendering of a stepper relies on defining the v-model the keeps the step state. This can be achieved by defining in server side a "globals.stepState".

v-data-table | Default filtering logic

Implement a default filtering logic that:

  • Allows table filtering across all values in all columns [searchAll]

  • Allows column based filtering [searchColumn]

  • Adjusts filtering results based on operators [either as string Greather than or via operators >]

Implement a default v-data-table generation behavior that:

  • Automatically creates methods for column based filtering

  • Is capable of mapping a filtering method to a given column

  • Is capable of binding dom events and elements with a given filtering behavior [e.g. associating an onchange event to a v-text-field that will only filter table values based on values in column prices]

VueElement slots

Current slot definition does not handle more complex slots where the slot operates upon arguments.

For instance, a DataTable with expandable rows

<template>
  <v-data-table
    :headers="headers"
    :items="desserts"
    :single-expand="singleExpand"
    :expanded.sync="expanded"
    item-key="name"
    show-expand
    class="elevation-1"
  >
    <template v-slot:top>
      <v-toolbar flat>
        <v-toolbar-title>Expandable Table</v-toolbar-title>
        <v-spacer></v-spacer>
        <v-switch v-model="singleExpand" label="Single expand" class="mt-2"></v-switch>
      </v-toolbar>
    </template>
    <template v-slot:expanded-item="{ headers, item }">
      <td :colspan="headers.length">More info about {{ item.name }}</td>
    </template>
  </v-data-table>
</template>

v-slot:expanded-item="{ headers, item }" takes 2 arguments, whereas current implementation does not allow arguments in v-slot definition.

push!(child,HtmlElement("template",Dict("v-slot:$k"=>true),dom(v)))

Proposed solution
When a slot pair value's also a Dict, allow usage of specific keys to signal whether, or not, to create complex slots.

Example for defining a expandable row slot in a DataTable

slots=Dict("expanded-item"=>
            Dict("args"=>"{ headers, item }", 
                  "content"=>HtmlElement("td", Dict("style"=>"height:50px;", ":colspan"=>"headers.length/2"), "Column 1: {{item.ccolumn1}} )
                    )
                )

Changes to VueElement.jl, that need better handling as not force every slot to use these specific keys (args and content)

    ## Slots
    if length(vuel.slots)!=0
        child=[]
        for (k,v) in vuel.slots
            if v isa Dict
                push!(child,HtmlElement("template",Dict("v-slot:$k"=>v["args"]),dom(v["content"])))
            else
                push!(child,HtmlElement("template",Dict("v-slot:$k"=>true),dom(v)))
            end
        end
        vuel.child=child
    end

Introduce Scope in Methods

Script is already performed in JS Closure that allows to use object context.
Use Path to modify app_state.

Getindex of VueElement and HTMLElement

Original Code:

using Base

Base.getproperty(el::HtmlElement, prop::String) = Base.getproperty(el, Symbol(prop))
function Base.getproperty(el::HtmlElement, prop::Symbol)
    return getfield(el, prop)
end

function Base.getindex(v::VueElement, i::String)
    if i in string.(fieldnames(typeof(v.dom)))
        return getproperty(v.dom, Symbol(i))
    end
end

My suggestion:

import Base.getindex

function Base.getindex(el::HtmlElement, i::String)
    return Base.getindex(el.attrs, i)
end

function Base.getindex(v::VueElement, i::String)
    return Base.getindex(v.dom.attrs, i)
end

The idea is to have attrs from VueElement/HtmlElement we just need to get it from dom

SetIndex should invoke update_validate!!

Introduce Computed

Use similar function to the one used in methods, but assign it to var (key of calling Dict).

Vuetify Alerts

  • Implement default timeout feature, 5s default. 0s = Always visible (will require a created hook in the data model)

  • Implement dismiss feature. Add toggle() to KNOWN_JS_EVENTS (unnecessary)

  • Leverage dismissible prop (server-side) to indicate whether message should be dynamically toggled.

  • (Potentially) Implement timeout with similar functionality to col_format, function defined in the element itself (implement configurable delay after clicking dismissible slot/icon)

Datatable issues

Datatable

  • Datatable not allowing header attribute, conflict with implemented column filtering logic

  • Implement click:row event to handle row events

v-progress-linear | Component Implementation | loading bar

The v-progress-linear component is used to convey data visually to users. It's a practical way to show users that a loading or importing process is being executed at the moment. (see loading bar examples here)

Default v-progress-linear;
By default it has width corresponding to the size of its container, is indeterminate and is hidden (inactive).
@el(loading, "v-progress-linear")

See live example here (user-password: root-root) using the add taxonomy by url screen. (Taxonomy label - "any value", Taxonomy URL - "http://www.xbrl.org/2005/xbrldt-2005.xsd")

v-snackbar | Component Implementation

The v-snackbar component is used to display a quick message to a user. . It's a practical way to show users a success, warning or error message without taking "space" in the page as opposed to the v-alert .

@el(snackbar, "v-snackbar", timeout = 3000)

Timeout, color and content properties are all binded to the elements data id.timeout, id.color, id.content, so they can be dynamically changed during page interaction.

source code in pull request #83 and commit 4bede2b

WIP | Documentation

  • V-list component doc

  • V-alert component doc

  • V-toolbar component doc

  • Lifecycle hooks

  • Handsontable

  • Data-table

    • Component docs
    • Different forms of applying custom headers
    • Example for filters over multiple select
    • Extending filter capabilities
  • VueStruct

    • Document iterable capabilities
    • Document how to leverage structs to build reusable components of an application
    • Create custom implementation example(s) that exactly matches the use cases of VueStruct(s)
    • Usability of getindex and setindex! to access and manipulate a struct's elements
  • Methods & Events

    • Document/list STANDARD_APP_EVENTS in a documentation page
    • Browser storage capabilities of VueElements
    • Submission method
      • How to use argument sub_content to customize submission payload
    • Examples of Computed and Async Computed
    • Examples for other lifecycle hooks and events
  • Live examples

    • Simple and complex dialogs
    • Card(s)
    • Alert(s)
    • Add v-text-field of type=date to basic examples
    • v-toolbar
  • Architectural logic #17

    • Define and specify core architectural decisions.
    • Build support documentation with code examples and respective outputs, both for dev and maintenance purposes

Handle validation `rules`

Correctly handle rules properties, as functions defined within rules cannot be converted or handled as a quoted string inside a JSON object.

VueElements used as templates

When a VueElement is used as col_template in datatable, the attrs are binded to non existent var.

Check behaviour in v-lists.

v-html directive and element slots

When using v-html directive, it appears that any slot will be ignored. Most likely a Vue and/or Vuetify issue, not a library issue.

Detected at v-alert, where its contents are bound to a v-html directive.

Datatable filters

Filters do not correctly handle comprehensive search of String fields/values.

Example:
Searching AB for equality in a column where ABC, AB and ABD exist, should return all 3 values, as AB exists in all of them.
Current behaviour is to return only values exactly matching AB.
While typing, table is automatically rendering results and, in cases like the aforementioned, table will yield empty results until an exact match is found, making it difficult for users to search efficiently.

Proposals:
Either have an occurs operator that will search String columns for occurrences of the supplied search value
or,
Alter == operator's default behaviour such that when used over a column of Strings returns occurrence of the supplied search, and not only the exact matches.

VueStruct and Vuejs App

Currently, when building a vuejs app/page, app name (VueStructยดs id field) is immutable and always "app".
Same behavior occurs with el, state and data objects, example bellow:

comp=VueStruct("app",garr,data=data,binds=binds,methods=methods)
    scripts=[]
    push!(scripts,"const app_state = $(JSON.json(comp.def_data))")
    ## component script
    comp_script=[]
    push!(comp_script,"el: '#app'")
    push!(comp_script,"vuetify: new Vuetify()")
    push!(comp_script,"data: app_state")

Proposal is to make el mutable and user defined, keeping "app" as a fallback.
For a given name of type String , let's say "mail_application", code above would look as follows:

$> julia page(..., id="mail_application")

function page(...., id::String="app")
(...)

comp=VueStruct(id, garr,data=data,binds=binds,methods=methods)
    scripts=[]
    stateObject = "$(id)_state"
    push!(scripts,"const $(stateObject) = $(JSON.json(comp.def_data))")
    ## component script
    comp_script=[]
    push!(comp_script,"el: '#$id'")
    push!(comp_script,"vuetify: new Vuetify()")
    push!(comp_script,"data: $(stateObject)")

Example | Hooks and Events

function home(req)
    @el(alert, "v-alert", content="Erro 1", type="error", value=false,
         icon="mdi-shield-lock-outline", timeout=1200)
     @el(alert2, "v-alert", content="Erro 2", type="error", value=true,
          icon="mdi-shield-lock-outline", timeout=5000)
    @el(okay, "v-alert", content="OKay", type="success", value=true,
        dismissible=true, delay=2500, close-icon="mdi-close-box")
    @el(nokay, "v-alert", content="Nested oKay", type="success", value=true,
        dismissible=true, delay=1500, close-icon="mdi-close-box")
    @el(texto, "v-text-field", value="Teste",
        outlined=missing,
        label="Texto",
        append-icon="mdi-close", prepend-icon="mdi-pencil",
        created=["this.vv.texto.value='Texto modificado: Hook Created'"],
        mounted=["this.vv.texto.value='kooh detnuom morf desrever'"],
        storage=false)
    @el(innertext, "v-text-field", label="Inner stuff",
        slots=Dict("append"=>"{{reversed}}"),
        events=
            Dict(
            "methods"=>Dict(),
            "mounted"=>["this.vv.inner.innertext.value='Alterado por hook'"],
            "watch"=>
                    Dict("vv.inner.innertext.value"=>
                    (args=["newval"],
                    script="window.alert('watched: ' + newval);"))
            )
        )
    @el(tostore, "v-text-field", label="From storage", storage=true)
    @el(cookie, "v-text-field", label="From cookie",
        value="sem cookie",
        cookie-read-only=false,
        cookie="ras",
        expiration=90,
        interval=1500)

    vt = VueStruct("inner", [innertext, cookie])
    vv = VueStruct("vv", [alert, nokay, [tostore, vt], texto])
    @el(btnteste, "v-btn", value="ativar nested success alert", click="vv.nokay.value=true")
    @el(btn, "v-btn", value="ativar erro", click="vv.alert.value=true")

    @el(btns, "v-btn", value="ativar alert2", click="alert2.value=true")
    @el(google, "v-btn", value="Go to google method", click="togoogle('teste')")
    vs = VueStruct("vs", [alert2, [btns, google]])

    vt = VueStruct("fileholder", [vs])

    p = page([okay, vv, vs, [btn, btnteste]],
            binds=Dict("vs.fileholder.r1.value"=>"r2.value"),
            methods=Dict(
                "togoogle"=>(args=["file"], script="open('http://google.com')")),
            computed=Dict("reversed"=>
                """return this.vv.texto.value.split('').reverse().join('');""")
            )
    return response(p)
end
router = HTTP.Router()
@HTTP.register(router, "/", home)
try close(server) catch; end
server = Sockets.listen(ip"127.0.0.1", 8887)
@async HTTP.serve(router, server=server)
@info ("Ready")

Introduce Hooks

Implemented support for Hooks that receive no input arguments.

Struct HookHandler @ VueElement.jl

mutable struct HookHandler <: EventHandler
    kind::String
    path::String
    function_script::String
end

Changes to VueStruct.jl
Added hooks property

(...) hooks::Vector{EventHandler}

Hooks are created from a VueStruct's grid array garr

hooks=create_hooks(garr)

And then handled similarly to other Events, but does not call function_script! since there's no parsing or special internal construction over the provided script to obtain the final script. Inclusion of our closure script was not working properly and is potentially not required as the keyword this can be used to refer to the intended app variable.
Further testing required for deeper VueStructs

new_hs=Vector{HookHandler}()
update_events!(comp,new_hs)
sort!(new_hs,by=x->length(x.path),rev=false)
comp.hooks=new_hs

Altered update_events! method signatures to allow dispatch on different instances of EventHandler

update_events!(vs,new_es::Vector{T} where T<:EventHandler,scope="")
function update_events!(vs::Array,new_es::Vector{T} where T<:EventHandler, scope="")
function update_events!(vs::VueStruct,new_es::Vector{EventHandler},scope="")
function update_events!(vs::VueStruct,new_hs::Vector{HookHandler}, scope="")

Altered Events.jl method events_script since hook instantiation slightly differs from previous implemented logic (methods, computed and watched)

for hook in KNOWN_HOOKS
        hf=filter(x->x.kind==hook, vs.hooks)
        if length(hf) > 0
            push!(els,"$hook() {"*join(map(y->y.function_script, hf),";")*"}")
        end
    end
    return join(els,",")

Examples

timeout attribute will be internally handled to produce a created hook for both v-alerts. alert will automatically be dismissed after x milliseconds.
close-icon attribute allows dismissible prop icon customization. Could not find any native way to implement this without forcing the user to create a slot. This way, user does not need to provide a complete slot definition just to alter an icon.
delay attribute refers to the time between a user's click on dismiss and the alert disappearing.
The v-text-field has explicit created and beforeCreate hooks. These will be internally handled similarly to how other events are handled.

@el(alert, "v-alert", content="Erro", type="error", value=true,
        timeout=1500, icon="mdi-shield-lock-outline")
@el(okay, "v-alert", content="OKay", type="success", value=true,
        dismissible=true, timeout=2000, delay=1500, close-icon="mdi-close-box")
@el(texto, "v-text-field", value="Teste", created=["this.texto.value='Texto modificado'"], beforeCreate="window.alert('teste')")
return response(page([alert, okay, texto]))

List of known hooks

const KNOWN_HOOKS = [
    "beforeCreate",
    "created",
    "beforeMount",
    "mounted",
    "beforeUpdate",
    "updated",
    "beforeDestroy",
    "destroyed",
    "activated",
    "deactivated"]

Handsontable | Boolean attribute values

Some components will not correctly handle props with stringified boolean values ("true" or "false").

E.g: Handsontable will incorrectly display "true" for every row header when given rowHeaders="true", and some properties do not accept stringified booleans (like search and filter).

Handsontable also does not seem to accept empty attributes/props.

Client-side validation | Rules library

  • Extend default rules library

Implemented LIBRARY_RULES in Core/Base.jl

LIBRARY_RULES =
    Dict("maxchars"=> (x->return """ value => value.length <= $x || 'Max $x characters' """),
         "minchars"=> (x->return """ value => value.length > $x  || 'Min $x characters' """),
         "required"=> (x->return " value => !!value || 'Required' "),
         "min"=>(x->return " value => value >= $x || 'Minimum  value is $x' "),
         "max"=>(x->return " value => value <= $x || 'Maximum  value is $x' "),
         "type"=>(x->return "value => typeof(value) === '$x' || 'Please provide a $x'"),
         "in"=>(x->return """ value => $x.includes(value) || 'Value not in $x' """)
    )

Further testing and validation required for pattern and/or regex expressions

# do further testing for 
"pattern"=>(x->return "value => !value || RegExp($x).test(value) || 'E-mail must be valid'")

Lists based on list-item-groups

Implement wrapper around vuetify's v-list family components, adding improved display capabilities.

Implement default logic for adding list-elements, client-side..

Architectural logic

Define and specify core architectural decisions.
Build support documentation with code examples and respective outputs

VueElement | Parsers & Validators

VueElement should receive both a validation function and a parsing function.

Validator shall return true or false

Parser shall operate on the VueElement and return a significant value

VS | Datatables

Any v-data-table with non-empty items will not work when placed inside a VueStruct.

In the images, t2 is placed at page level and works as expected.

t is placed inside a @vs teste and will not display due to an error when accessing the table's col_format properties.

image (2)
image

The reactive prop :col_format is correctly declared as teste.t.col_format

but the v-html directive is trying to access t.col_format.col_X instead of teste.t.col_format.col_X , which leads to a TypeError: Cannot read property 'col_format' of undefined

image

v-html definition for col_format occurs @ Update_validation_datatable.jl, lines 123 to 125

The datatable validator is oblivious to the fact the table may be contained in one, or more, nested structures, therefore cannot correctly define the path for the col_format properties.

v-toolbar | Review implementation

Proposed implementation for toolbar using VueHolder

Commits 6667890, bde5d92

Requesting review and feedback for improvement and potential bug fixing

Extensive examples for v-toolbar usage

function exemplo_toolbar(req)
    
    title_template(dialog) = "{{$dialog.mockup_select.value !== null && $dialog.mockup_select.value.length > 0 ? String($dialog.mockup_select.value.length) + ' selected ' : 'Dynamic title'}}"
    generic_title(x) = toolbartitle(x, align="left", cols=4)
    
    @el(mockup_close,  "v-btn",        value="<v-icon>mdi-close</v-icon>", icon=true, dark=true, large=true, cols=1)
    @el(dialog_close,  "v-btn",        value="<v-icon>mdi-close</v-icon>", icon=true, dark=true, large=true, cols=1, click="toggledialog(this.app['active'])")
    @el(mockup_submit, "v-btn",        value="Save", click="this.app['active'].mockup_alert.value = true", style=Dict("padding-top"=>"1%"), text=true, color="white", large=true)
    
    @el(mockup_input,  "v-text-field", label="Mockup", cols=4)
    @el(mockup_select, "v-select",     label="Select stuff", items=["A","B","C","D"], multiple=true, cols=6)
    @el(mockup_switch, "v-switch",     hint="Show sub form", persistent-hint=true, cols=4)
    @el(mockup_area,   "v-textarea",   label="Mockup", cols=2)
    
    @el(mockup_alert,  "v-alert",      type="info", content="Saved!", value=false)
    @el(mockup_search, "v-btn",        value="<v-icon>mdi-magnify<v-icon>", icon=true, color="white", cols=1)
    
    # menu
    mockup_actions = [Dict("title"=>"Clear", "val"=>Dict("what"=>"clear")), Dict("title"=>"Toggle", "val"=>Dict("what"=>"toggle"))]
    @el(mockup_menu, "v-menu", items=mockup_actions)
    @el(mockup_menu_btn, "v-btn", menu=mockup_menu,
        content="<v-icon>mdi-dots-vertical</v-icon>",
        click="side_actions(item.val.what)",
        icon=true, color="white", cols=1)
    @el(mockup_hamburger, "v-btn", menu=mockup_menu, click="side_actions(item.val.what)", value="<v-icon>mdi-menu</v-icon>", icon=true, color="white", cols=1)
    ## 
    
    #= Page components =#
    
    # a simple toolbar in a card with a title and an action button
    c1 = card([
            toolbar([toolbartitle("Toolbar", align="left"), spacer(cols=7), mockup_hamburger]),
            spacer(rows=1), # spacer here because toolbar will have an absolute position, thus the card content would get slightly behind the toolbar
            [card(["Content"], cols=6)] ], cols=6)
    
    # card where content is placed over the toolbar 
    c2 = card([
            toolbar([toolbartitle("Toolbar extended", align="left"), spacer(cols=7), mockup_menu_btn], extended=true),
            card([html("h3", "Content", Dict(), cols=4), html("v-divider", "", Dict(), cols=2), mockup_area], cols=4)
        ], cols=6)
    
    # card with nested toolbars
    @el(smaller_close, "v-btn", icon=true, small=true, color="white", value="<v-icon>mdi-close</v-icon>", cols=1) #using dense requires some resizing of elements so that they do not extend past the bar
    c3 = card([
          toolbar([toolbartitle("Toolbar #3", align="left"), spacer(cols=7), mockup_menu_btn]),
          spacer(rows=1),
          card([
                toolbar([toolbartitle("Inner toolbar (nav=true & dense=true)", style=Dict("padding"=>0), align="left"), spacer(cols=7), smaller_close], dense=true),
                spacer(rows=1),
                [card([html("h3", "Content", Dict("align"=>"left"), cols=6), html("v-divider", "", Dict(), cols=6)]),
                 card([html("h3", "Content", Dict("align"=>"left"), cols=6), html("h3", "More content", Dict("align"=>"left"), cols=6)])]
                ], cols=12)
            ], cols=12)
    
    # simple dialog
    @el(toggle_d1, "v-btn", value="Open simple dialog", click="toggledialog(d1)", cols=2, text=true, outlined=true)
    d1 = dialog("d1", 
        [
         toolbar([generic_title(title_template("d1")), spacer(cols=7), dialog_close]),
         spacer(rows=1),
         card([mockup_select], cols=12)
        ], width="50vw", active=false)
    
    # Using toolbar for form actions
    @el(toggle_d2, "v-btn", value="Open form dialog", click="toggledialog(d2)", cols=2, text=true, outlined=true)
    d2 = dialog("d2", 
        [toolbar(
            [generic_title("Save placed in the toolbar"),
             spacer(cols=6), 
             mockup_submit,
             mockup_menu_btn
            ]),
         spacer(rows=1),
         card([[mockup_input, mockup_input], mockup_select], cols=12),
         mockup_alert
        ], width="50vw", active=false)
    
    # Toolbar where nav = false
    @el(toggle_d3, "v-btn", value="Open form dialog #2", click="toggledialog(d3)", cols=2, text=true, outlined=true)
    @dialog(d3, 
        [toolbar(
            [generic_title("Toolbar nav = false #1"),
             spacer(cols=7), 
             mockup_menu_btn
            ], nav=false),
         html("h4", "When nav=false, the toolbar is not placed in the container edges", Dict(), cols=12),
         card([
                [mockup_input, mockup_input], 
                mockup_select, 
                toolbar([generic_title("Toolbar nav = false #2"), spacer(cols=7),  dialog_close], nav=false, dense=true, color="black")], 
            cols=12),
        ], width="50vw", active=false)
    
    # Card with more interactions 
    @el(toggle_d4, "v-btn", value="Open form dialog #3", click="toggledialog(d4)", text=true, outlined=true, cols=2)
    @el(mockup_date_input, "v-text-field", type="date", label="Birthday", cols=6)
    @el(mockup_input_2,    "v-text-field", label="Name", cols=6)
    
    subform = card([
                toolbar([toolbartitle("Sub form", cols=4, align="left"), spacer(cols=6), mockup_search, mockup_menu_btn], nav=true),
                spacer(rows=1),
                [mockup_input_2, mockup_date_input]
            ], hover=true, tile=true, cols=8)
    subform.attrs["v-show"] = "d4.mockup_switch.value == true"
    
    @dialog(d4, 
        [toolbar(
            [generic_title(title_template("d4")),
             spacer(cols=6), 
             mockup_search,
             mockup_menu_btn
            ], nav=true),
         mockup_alert,
         card([toolbar([mockup_search], collapse=true), spacer(rows=1),
              [mockup_input, mockup_input], mockup_select, mockup_switch], cols=12),
         subform #sub form
        ], width="50vw", active=false)
    
    bottom_toolbar = toolbar([toolbartitle("Bottom Toolbar", style=Dict("color"=>"black"), cols=2), toggle_d1, toggle_d2, toggle_d3, toggle_d4], color="white", outlined=true, bottom=true, cols=12)
    #= =#
    return response(page(
            [ 
                [c1, c2], c3, 
                d1,d2,d3,d4,
                bottom_toolbar
            ], 
            methods=Dict(
                "rand_dialog"=>"function(){dialogs = ['d1','d2','d3','d4']; return app[dialogs[Math.floor(Math.random() * dialogs.length)]]}",
                "toggledialog"=>"function(dialog){
                    if (!dialog) {dialog = this.rand_dialog()}
                    app['active'] = dialog;
                    dialog.active.value = !dialog.active.value;
                }",
                "side_actions"=>"
                    function(what){
                        if (what == 'toggle') { this.toggledialog(app['active']) } 
                        else if (what == 'clear') {
                            for (let key in app['active']) { if (key !== 'active' && app['active'][key].hasOwnProperty('value')) { app['active'][key].value = null;} }
                        }
                }"
                )))
end

Result should be similar to figure

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.