├── .gitignore ├── AdvancedExamples ├── CameraWidget.jl ├── ContourPlots │ ├── Project.toml │ ├── README.md │ ├── assets │ │ └── animation.gif │ ├── heatlab.jl │ └── lib │ │ └── solver.jl ├── DraggableTree │ ├── DraggableTree - Vue3.jl │ ├── DraggableTree.jl │ ├── Project.toml │ ├── SortableTree.jl │ ├── assets │ │ └── js │ │ │ ├── QDraggableTree.js │ │ │ ├── QSortableTree.js │ │ │ ├── Sortable.min.js │ │ │ ├── qdraggabletree.umd.min.js │ │ │ ├── vue-draggable-next.global.js │ │ │ ├── vuedraggable.umd.min.js │ │ │ └── vuedraggable.umd.min.js.map │ └── sortable_components.jl ├── Project.toml ├── README.md ├── ServerSideFiltering.jl ├── Stipple2048.jl ├── Stipple2048_animated.jl ├── StippleMathjsDemo │ ├── Manifest.toml │ ├── Project.toml │ └── StippleMathjsDemo.jl ├── docs │ └── content │ │ └── img │ │ ├── CameraWidget.jpg │ │ ├── ServerSideFiltering.jpg │ │ ├── Stipple2048.png │ │ └── Stipple2048_animated.jpg └── public │ └── favicon.ico ├── BasicExamples ├── .JuliaFormatter.toml ├── BubbleCharts │ ├── BubbleCharts.jl │ └── Project.toml ├── CSVUploadProcess │ ├── Backend_Upload │ │ └── iris.csv │ ├── CSVUploadProcess.jl │ ├── Project.toml │ └── iris.csv ├── Card │ ├── Card.jl │ └── Project.toml ├── Checkboxes │ ├── Checkboxes.jl │ ├── CheckboxesVector.jl │ └── Project.toml ├── CsvUpload │ ├── CSVUpload.jl │ └── Project.toml ├── DatePickers │ ├── DatePickers.jl │ └── Project.toml ├── Editor │ ├── Editor.jl │ └── Project.toml ├── FileUpload │ ├── FileUpload.jl │ ├── Project.toml │ └── upload │ │ └── file.jpg ├── Form │ ├── Project.toml │ └── app.jl ├── HelloPie │ ├── HelloPie.jl │ └── Project.toml ├── HelloStipple │ ├── HelloStipple.jl │ ├── Project.toml │ └── sessions │ │ └── 91b688fa4c5b8b17d11c1e213027a43ec9813ccece9dc1ee28f44aaa170cffc4 ├── ImageGallery │ ├── ImageGallery.jl │ └── Project.toml ├── Project.toml ├── README.md ├── StippleButtons │ ├── Project.toml │ └── StippleButtons.jl ├── TableDownloadClipboard │ ├── Project.toml │ ├── TableDownloadClipboard.jl │ └── public │ │ ├── favicon.ico │ │ └── iconsets │ │ └── @mdi │ │ └── font │ │ ├── .github │ │ └── ISSUE_TEMPLATE.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── css │ │ ├── materialdesignicons.css │ │ ├── materialdesignicons.css.map │ │ ├── materialdesignicons.min.css │ │ └── materialdesignicons.min.css.map │ │ ├── fonts │ │ ├── materialdesignicons-webfont.eot │ │ ├── materialdesignicons-webfont.ttf │ │ ├── materialdesignicons-webfont.woff │ │ └── materialdesignicons-webfont.woff2 │ │ ├── package.json │ │ ├── preview.html │ │ └── scss │ │ ├── _animated.scss │ │ ├── _core.scss │ │ ├── _extras.scss │ │ ├── _functions.scss │ │ ├── _icons.scss │ │ ├── _path.scss │ │ ├── _variables.scss │ │ └── materialdesignicons.scss ├── Timeline │ ├── Project.toml │ └── Timeline.jl ├── Tree │ ├── EditableDict.jl │ ├── Project.toml │ └── Tree.jl ├── config │ └── secrets.jl └── docs │ └── content │ └── img │ ├── BubbleCharts.png │ ├── Card.png │ ├── Checkboxes.png │ ├── DatePickers.png │ ├── FileUpload.png │ ├── Form.png │ ├── HelloPie.png │ ├── HelloStipple.png │ └── ImageGallery.png ├── GermanCredits ├── GermanCredits.jl ├── Project.toml ├── README.md ├── data │ └── german_credit.csv └── docs │ └── content │ └── img │ └── germancredits.gif ├── IrisClustering ├── Dockerfile ├── IrisClustering.jl ├── Project.toml ├── README.md └── docs │ └── content │ └── img │ └── iris.gif ├── Plugins ├── StippleDownloads.jl └── StippleTypedArrays.jl ├── README.md ├── ReactiveTools API ├── Download Demo.jl ├── EditableTree.jl ├── FileUpload.jl ├── JuliaEditor.jl ├── LaTeX.jl ├── ScrollArea Demo.jl └── TypedArrays Demo.jl ├── StipplePlotly ├── 3DPlots │ └── Plot3d.jl ├── HeatMaps │ ├── Project.toml │ ├── heatmap1.jl │ └── heatmap2.jl ├── PlotData.jl ├── PlotData2.jl ├── PlotEventQuery │ ├── Project.toml │ └── app.jl ├── PlotlyBaseBar.jl ├── PlotlyBaseDemo.jl ├── PlotlyBaseEvents.jl ├── Project.toml ├── README.md ├── ReactivePointRowSelection.jl ├── ServerSideResampling.jl ├── ServerSideResamplingMixin.jl ├── docs │ └── content │ │ └── img │ │ ├── 152349703-0038c536-4077-45ce-9ee7-c38837e9f4a7.gif │ │ ├── MixedSubplots.png │ │ ├── PlotData.png │ │ ├── choropleth.png │ │ ├── lines.png │ │ └── scattergeo.png ├── example1.jl ├── example1_newapi.jl ├── example2.jl ├── groupedbar1.jl ├── maps │ ├── CHOROPLETH.jl │ ├── lines.jl │ ├── mapbox_access_token.jl │ ├── mapbox_token.jl │ ├── scattermap.jl │ └── scattermapbox.jl ├── multitype1.jl ├── scatter_polar.jl ├── subplots2x2.jl └── text_tooltip_newapi.jl ├── Test └── Test Mixins.jl ├── Vue3 └── Calendars │ └── Calendars.jl ├── _broken_ ├── DynamicContent │ ├── DynamicContent.jl │ └── Project.toml ├── HelloStippleMultiUser.jl ├── JSmethods │ ├── JSmethods.jl │ └── Project.toml ├── KnobDemo.jl ├── LineCharts.jl ├── MultiUserApp.jl ├── New API Demo Draft.jl ├── New API Demo.jl ├── New API3 Demo Draft.jl ├── ReverseText │ ├── Project.toml │ └── ReverseText.jl ├── WebCam.jl ├── WebCam2.jl └── WebCam3.jl └── _temp_ ├── IrisClustering1.jl ├── IrisClustering2.jl └── IrisClustering3.jl /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Manifest.toml 3 | !AdvancedExamples/StippleMathjsDemo/Manifest.toml 4 | StippleDemo.mp4 5 | .DS_Store 6 | .vscode 7 | /BasicExamples/sessions 8 | sessions 9 | log -------------------------------------------------------------------------------- /AdvancedExamples/ContourPlots/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" 3 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 4 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 5 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 6 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 7 | 8 | [compat] 9 | Genie = "4" 10 | Stipple = "0.23" 11 | StipplePlotly = "0.11" 12 | StippleUI = "0.18" 13 | DifferentialEquations = "7.1.0" 14 | julia = "1.6" 15 | -------------------------------------------------------------------------------- /AdvancedExamples/ContourPlots/README.md: -------------------------------------------------------------------------------- 1 | # UI Components usage using [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StippleUI.jl), [StippleCharts](https://github.com/GenieFramework/StippleCharts.jl) and [Genie](https://github.com/GenieFramework/Genie.jl) from Stipple Ecosystem 2 | 3 | ## Run Demo 4 | 5 | ```julia 6 | julia> julia --project 7 | julia> #enter package mode with ] 8 | (@v1.x) pkg> activate . 9 | (@v1.x) pkg> instantiate 10 | (@v1.x) pkg> #exit package mode with 11 | julia> include("heatlab.jl") 12 | # should open your default browser and fire up Genie server at port between `8000:9000` 13 | ``` 14 | 15 | ![Form](assets/animation.gif) 16 | 17 | ```julia 18 | julia> down() # stop the running async instance of Genie Server 19 | ``` 20 | -------------------------------------------------------------------------------- /AdvancedExamples/ContourPlots/assets/animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/AdvancedExamples/ContourPlots/assets/animation.gif -------------------------------------------------------------------------------- /AdvancedExamples/ContourPlots/lib/solver.jl: -------------------------------------------------------------------------------- 1 | using DifferentialEquations: ODEProblem, solve, Tsit5 2 | 3 | _sin(t) = 1 + sin(t) 4 | _tanh(t) = tanh(t) 5 | _sign(t) = sign(t) 6 | 7 | function get_data(T0::Float64, Tout::Float64, time::Float64, para::Float64, func::Symbol) 8 | a = 1.27E-5 9 | n = 10 10 | L = 0.2 11 | δ = L / n 12 | λ = 50 13 | h = 1.0E9 14 | A = a / δ^2 15 | B = a / (δ^2 / 2 + δ * λ / h) 16 | p = [A, B, n] 17 | 18 | function to_index(i, j, n) 19 | return (i - 1) * n + j 20 | end 21 | function heat!(dT, T, p, t) 22 | A, B, n = p 23 | n = Int(n) 24 | Tf = Tout * (eval(func)(para * t)) 25 | # 内部节点 26 | for i in 2:n-1 27 | for j in 2:n-1 28 | dT[to_index(i, j, n)] = A * (T[to_index(i + 1, j, n)] + T[to_index(i - 1, j, n)] + T[to_index(i, j + 1, n)] + T[to_index(i, j - 1, n)] - 4 * T[to_index(i, j, n)]) 29 | end 30 | end 31 | # 边边界 32 | for i in 2:n-1 33 | dT[to_index(i, 1, n)] = A * (T[to_index(i + 1, 1, n)] + T[to_index(i - 1, 1, n)] + T[to_index(i, 2, n)]) - (3B + A) * T[to_index(i, 1, n)] + B * Tf 34 | end 35 | for i in 2:n-1 36 | dT[to_index(i, n, n)] = A * (T[to_index(i + 1, n, n)] + T[to_index(i - 1, n, n)] + T[to_index(i, n - 1, n)]) - (3B + A) * T[to_index(i, n, n)] + B * Tf 37 | end 38 | for i in 2:n-1 39 | dT[to_index(1, i, n)] = A * (T[to_index(1, i + 1, n)] + T[to_index(1, i - 1, n)] + T[to_index(2, i, n)]) - (3B + A) * T[to_index(1, i, n)] + B * Tf 40 | end 41 | for i in 2:n-1 42 | dT[to_index(n, i, n)] = A * (T[to_index(n, i + 1, n)] + T[to_index(n, i - 1, n)] + T[to_index(n - 1, i, n)]) - (3B + A) * T[to_index(1, i, n)] + B * Tf 43 | end 44 | # 角边界 45 | dT[to_index(1, 1, n)] = A * (T[to_index(2, 1, n)] + T[to_index(1, 2, n)]) - (2B + 2A) * T[to_index(1, 1, n)] + 2B * Tf 46 | dT[to_index(n, n, n)] = A * (T[to_index(n - 1, n, n)] + T[to_index(n, n - 1, n)]) - (2B + 2A) * T[to_index(n, n, n)] + 2B * Tf 47 | dT[to_index(n, 1, n)] = A * (T[to_index(n, 2, n)] + T[to_index(n - 1, 1, n)]) - (2B + 2A) * T[to_index(n, 1, n)] + 2B * Tf 48 | dT[to_index(1, n, n)] = A * (T[to_index(2, n, n)] + T[to_index(1, n - 1, n)]) - (2B + 2A) * T[to_index(1, n, n)] + 2B * Tf 49 | end 50 | 51 | u0 = [T0 for i in 1:n for j in 1:n] 52 | prob = ODEProblem(heat!, u0, (0, time), p, saveat=1) 53 | sol = solve(prob, Tsit5()) 54 | 55 | an_len = length(sol.u) 56 | res = zeros(n, n, an_len) 57 | 58 | for t in 1:an_len 59 | for i in 1:n 60 | for j in 1:n 61 | res[i, j, t] = sol.u[t][to_index(i, j, n)] 62 | end 63 | end 64 | end 65 | return res 66 | end 67 | 68 | 69 | res = get_data(1000.0, 0.0, 100.0, 1.0, :_sign); # get_data test 70 | -------------------------------------------------------------------------------- /AdvancedExamples/DraggableTree/DraggableTree - Vue3.jl: -------------------------------------------------------------------------------- 1 | using Stipple, Stipple.ReactiveTools 2 | using StippleUI 3 | 4 | import Stipple.opts 5 | 6 | cd(@__DIR__) 7 | sortable_asset = Genie.Assets.add_fileroute(StippleUI.assets_config, "Sortable.min.js").path 8 | draggablenext_asset = Genie.Assets.add_fileroute(StippleUI.assets_config, "vue-draggable-next.global.js").path 9 | 10 | draggabletree_deps() = [ 11 | script(src = sortable_asset) 12 | script(src = draggablenext_asset) 13 | ] 14 | 15 | Stipple.deps!(Stipple, draggabletree_deps) 16 | include(joinpath(@__DIR__, "sortable_components.jl")) 17 | 18 | draggable(args...; kwargs...) = xelem(:draggable, args...; kwargs...) 19 | draggabletree(fieldname::Symbol; key = "label", kwargs...) = quasar(:draggable__tree; fieldname, data = get(kwargs, :data, fieldname), delete!(Dict(kwargs), :data)...) 20 | 21 | dict(; kwargs...) = Dict{Symbol, Any}(kwargs...) 22 | 23 | function filedict(startfile) 24 | if isdir(startfile) 25 | files = readdir(startfile, join = true) 26 | index = isdir.(files) 27 | files = vcat(files[index], files[.! index]) 28 | dict( 29 | label = basename(startfile), 30 | key = startfile, 31 | icon = "folder", 32 | children = filedict.(files) 33 | ) 34 | else 35 | dict( 36 | label = basename(startfile), 37 | key = startfile, 38 | icon = "insert_drive_file", 39 | children = [] 40 | ) 41 | end 42 | end 43 | 44 | files = filedict(dirname(@__DIR__)) 45 | @app DraggableTreeDemo begin 46 | @in files = [files] 47 | @in myList = [dict(id = i, name = n) for (i, n) in enumerate(["hello", "John", "Doe"])] 48 | @in element = "" 49 | end 50 | 51 | delete!(Stipple.COMPONENTS, DraggableTreeDemo) 52 | 53 | Stipple.register_components(DraggableTreeDemo, ["draggable" => "VueDraggableNext.VueDraggableNext"]) 54 | Stipple.register_components(DraggableTreeDemo, qsortable_components()) 55 | 56 | const UI = Ref(ParsedHTMLString[]) 57 | 58 | UI[] = [ 59 | row(cell(class = "st-module", [ 60 | h6("Demo of a draggable list") 61 | draggable(fieldname = :myList, 62 | htmldiv(@for("element in myList"), key=R"element.id", 63 | "{{element.name}}" 64 | ) 65 | ) 66 | ])) 67 | 68 | row(cell(class = "st-module", [ 69 | h6("Implementation of Mayank Patel's " * a("QDraggableTree", href = "https://github.com/mayank091193/quasar-draggable-tree/tree/next", style = "text-decoration: none")) 70 | draggabletree(:files, rowKey = "key") 71 | ])) 72 | ] 73 | 74 | ui() = UI[] 75 | 76 | 77 | @page("/", ui, model = DraggableTreeDemo, debounce = 0) 78 | 79 | up() -------------------------------------------------------------------------------- /AdvancedExamples/DraggableTree/DraggableTree.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | import Genie.Assets.add_fileroute 4 | 5 | cd(@__DIR__) 6 | # Genie.Secrets.secret_token!() 7 | 8 | # register_mixin(@__MODULE__) 9 | 10 | # to be implemented in Stipple or Genie ... 11 | # function add_fileroute(assets_config::Genie.Assets.AssetsConfig, filename::AbstractString; 12 | # basedir = @__DIR__, content_type::Union{Nothing, Symbol} = nothing, type::Union{Nothing, String} = nothing, ext::Union{Nothing, String} = nothing, kwargs...) 13 | 14 | # file, ex = splitext(filename) 15 | # ext = isnothing(ext) ? ex : ext 16 | # type = isnothing(type) ? ex[2:end] : type 17 | 18 | # content_type = isnothing(content_type) ? if type == "js" 19 | # :javascript 20 | # elseif type == "css" 21 | # :css 22 | # elseif type in ["jpg", "jpeg", "svg", "mov", "avi", "png", "gif", "tif", "tiff"] 23 | # imagetype = replace(type, Dict("jpg" => "jpeg", "mpg" => "mpeg", "tif" => "tiff")...) 24 | # Symbol("image/$imagetype") 25 | # else 26 | # Symbol("*.*") 27 | # end : content_type 28 | 29 | # Genie.Router.route(Genie.Assets.asset_path(assets_config, type; file, ext, kwargs...)) do 30 | # Genie.Renderer.WebRenderable( 31 | # Genie.Assets.embedded(Genie.Assets.asset_file(cwd=basedir; type, file)), 32 | # content_type) |> Genie.Renderer.respond 33 | # end 34 | # end 35 | 36 | css() = [style(""" 37 | [v-cloak] { display: none; } 38 | """)] 39 | 40 | function add_css(css::Function; update = true) 41 | update && deleteat!(Stipple.Layout.THEMES, nameof.(Stipple.Layout.THEMES) .== nameof(css)) 42 | push!(Stipple.Layout.THEMES, css) 43 | end 44 | 45 | add_css(css) 46 | 47 | add_fileroute(StippleUI.assets_config, "Sortable.min.js", basedir = pwd()) 48 | add_fileroute(StippleUI.assets_config, "vuedraggable.umd.min.js", basedir = pwd()) 49 | add_fileroute(StippleUI.assets_config, "vuedraggable.umd.min.js.map", type = "js", basedir = pwd()) 50 | add_fileroute(StippleUI.assets_config, "QDraggableTree.js", basedir = pwd()) 51 | 52 | draggabletree_deps() = [ 53 | script(src = "/stippleui.jl/master/assets/js/sortable.min.js") 54 | script(src = "/stippleui.jl/master/assets/js/vuedraggable.umd.min.js") 55 | script(src = "/stippleui.jl/master/assets/js/qdraggabletree.js") 56 | ] 57 | 58 | Stipple.DEPS[:qdraggabletree] = draggabletree_deps 59 | 60 | draggabletree(fieldname::Symbol; data::Union{Symbol, String} = fieldname, rowkey = "key", kwargs...) = quasar(:draggable__tree; fieldname, data, row__key = rowkey, kwargs...) 61 | 62 | dict(; kwargs...) = Dict{Symbol, Any}(kwargs...) 63 | 64 | function filedict(startfile) 65 | if isdir(startfile) 66 | files = readdir(startfile, join = true) 67 | index = isdir.(files) 68 | files = vcat(files[index], files[.! index]) 69 | dict( 70 | label = basename(startfile), 71 | key = startfile, 72 | icon = "folder", 73 | children = filedict.(files) 74 | ) 75 | else 76 | dict(label = basename(startfile), 77 | key = startfile, 78 | icon = "insert_drive_file" 79 | ) 80 | end 81 | end 82 | 83 | files = filedict(dirname(dirname(@__DIR__))) 84 | @reactive! struct DraggableTreeDemo <: ReactiveModel 85 | files::R{Vector{Dict{Symbol, Any}}} = [files] 86 | end 87 | 88 | Genie.Router.delete!(Symbol(DraggableTreeDemo)) 89 | Stipple.js_mounted(::DraggableTreeDemo) = "" 90 | 91 | function handlers(model) 92 | on(model.isready) do isready 93 | isready || return 94 | push!(model) 95 | end 96 | 97 | return model 98 | end 99 | 100 | function ui(model) 101 | page( 102 | model, 103 | title = "Draggable Tree Demo", 104 | 105 | row(cell(class = "st-module", [ 106 | h3("Adaptation from " * a("Mayank Patel's QDraggableTree Demo", href = "https://codepen.io/mayank091193/pen/pogEjVK", style = "text-decoration: none")) 107 | 108 | row([ 109 | cell(draggabletree(:files, rowkey = "key", group = "test")) 110 | cell(draggabletree(:files, rowkey = "key", group = "test")) 111 | ]) 112 | ])), 113 | @iif(isready), 114 | "v-cloak") 115 | end 116 | 117 | route("/") do 118 | # global model definition is for debugging/testing purpose only 119 | global model 120 | model = init(DraggableTreeDemo, debounce = 0) 121 | model |> handlers |> ui |> html 122 | end 123 | 124 | up() -------------------------------------------------------------------------------- /AdvancedExamples/DraggableTree/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.20 - 0.22" 7 | StippleUI = "0.17" 8 | julia = "1.6" 9 | -------------------------------------------------------------------------------- /AdvancedExamples/DraggableTree/SortableTree.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | # Genie.Secrets.secret_token!() 4 | 5 | register_mixin(@__MODULE__) 6 | 7 | # to be implemented in Stipple or Genie ... 8 | function add_fileroute(assets_config::Genie.Assets.AssetsConfig, filename::AbstractString; 9 | basedir = @__DIR__, content_type::Union{Nothing, Symbol} = nothing, type::Union{Nothing, String} = nothing, ext::Union{Nothing, String} = nothing, kwargs...) 10 | 11 | file, ex = splitext(filename) 12 | ext = isnothing(ext) ? ex : ext 13 | type = isnothing(type) ? ex[2:end] : type 14 | 15 | content_type = isnothing(content_type) ? if type == "js" 16 | :javascript 17 | elseif type == "css" 18 | :css 19 | elseif type in ["jpg", "jpeg", "svg", "mov", "avi", "png", "gif", "tif", "tiff"] 20 | imagetype = replace(type, Dict("jpg" => "jpeg", "mpg" => "mpeg", "tif" => "tiff")...) 21 | Symbol("image/$imagetype") 22 | else 23 | Symbol("*.*") 24 | end : content_type 25 | 26 | Genie.Router.route(Genie.Assets.asset_path(assets_config, type; file, ext, kwargs...)) do 27 | Genie.Renderer.WebRenderable( 28 | Genie.Assets.embedded(Genie.Assets.asset_file(cwd=basedir; type, file)), 29 | content_type) |> Genie.Renderer.respond 30 | end 31 | end 32 | 33 | 34 | add_fileroute(StippleUI.assets_config, "Sortable.min.js") 35 | add_fileroute(StippleUI.assets_config, "vuedraggable.umd.min.js") 36 | add_fileroute(StippleUI.assets_config, "vuedraggable.umd.min.js.map", type = "js") 37 | add_fileroute(StippleUI.assets_config, "QSortableTree.js") 38 | 39 | draggabletree_deps() = [ 40 | script(src = "/stippleui.jl/master/assets/js/sortable.min.js") 41 | script(src = "/stippleui.jl/master/assets/js/vuedraggable.umd.min.js") 42 | script(src = "/stippleui.jl/master/assets/js/qsortabletree.js") 43 | ] 44 | 45 | Stipple.DEPS[:qdraggabletree] = draggabletree_deps 46 | 47 | draggabletree(nodes::Symbol; nodekey = "label", kwargs...) = quasar(:sortable__tree; nodes, node__key = get(kwargs, :node__key, nodekey), delete!(Dict(kwargs), :node__key)...) 48 | 49 | dict(; kwargs...) = Dict{Symbol, Any}(kwargs...) 50 | 51 | function filedict(startfile) 52 | if isdir(startfile) 53 | files = readdir(startfile, join = true) 54 | index = isdir.(files) 55 | files = vcat(files[index], files[.! index]) 56 | dict( 57 | label = basename(startfile), 58 | key = startfile, 59 | icon = "folder", 60 | children = filedict.(files) 61 | ) 62 | else 63 | dict(label = basename(startfile), 64 | key = startfile, 65 | icon = "insert_drive_file" 66 | ) 67 | end 68 | end 69 | 70 | files = filedict(dirname(@__DIR__)) 71 | @reactive! struct DraggableTreeDemo <: ReactiveModel 72 | files::R{Vector{Dict{Symbol, Any}}} = [files] 73 | end 74 | 75 | Genie.Router.delete!(:DraggableTreeDemo) 76 | Stipple.js_mounted(::DraggableTreeDemo) = "" 77 | 78 | function handlers(model) 79 | on(model.isready) do isready 80 | isready || return 81 | push!(model) 82 | end 83 | 84 | return model 85 | end 86 | 87 | function ui(model) 88 | page( 89 | model, 90 | title = "Sortable Tree Demo", 91 | 92 | row(cell(class = "st-module", [ 93 | h3("Implementation of " * a("mechanicalgux's QSortableTree", href = "https://gitlab.com/mechanicalgux/quasar-sortable-tree", style = "text-decoration: none")) 94 | row([ 95 | cell(draggabletree(:files, nodes_key = "key")) 96 | cell(draggabletree(:files, nodes_key = "key")) 97 | ]) 98 | ])), 99 | @iif(isready) 100 | ) 101 | end 102 | 103 | route("/") do 104 | # global model definition is for debugging/testing purpose only 105 | global model 106 | model = init(DraggableTreeDemo, debounce = 0) 107 | model |> handlers |> ui |> html 108 | end 109 | 110 | up() -------------------------------------------------------------------------------- /AdvancedExamples/DraggableTree/assets/js/qdraggabletree.umd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * quasar-ui-qdraggabletree v1.0.3 3 | * (c) 2022 mayank091193@gmail.com 4 | * Released under the MIT License. 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("src/components/QDraggableTree")):"function"==typeof define&&define.amd?define(["src/components/QDraggableTree"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).qdraggabletree=t(e.QDraggableTree)}(this,function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=t(e);return Object.freeze({__proto__:null,version:"1.0.3",QDraggableTree:n.default,install:function(e){e.component("QDraggableTree",n.default)}})}); -------------------------------------------------------------------------------- /AdvancedExamples/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Electron = "a1bb12fb-d4d1-54b4-b10a-ee7951ef7ad3" 3 | FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" 4 | Game2048Core = "449ebaaf-06bc-4dda-9a3f-b1c40666d086" 5 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 6 | HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" 7 | JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" 8 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 9 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 10 | VideoIO = "d6d074c3-1acf-5d4c-9a43-ef38773959a2" 11 | 12 | [compat] 13 | Electron = "3.1" 14 | FileIO = "1.13" 15 | Game2048Core = "0.1.1" 16 | Genie = "4" 17 | HTTP = "0.9" 18 | JSON = "0.21" 19 | Stipple = "0.20 - 0.22" 20 | StippleUI = "0.17" 21 | julia = "1.6" 22 | -------------------------------------------------------------------------------- /AdvancedExamples/README.md: -------------------------------------------------------------------------------- 1 | # UI Components usage using [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StippleUI.jl), [StippleCharts](https://github.com/GenieFramework/StippleCharts.jl) and [Genie](https://github.com/GenieFramework/Genie.jl) from Stipple Ecosystem 2 | 3 | ## Run Demo 4 | ```julia 5 | julia> julia --project 6 | julia> #enter package mode with ] 7 | (@v1.x) pkg> activate . 8 | (@v1.x) pkg> instantiate 9 | (@v1.x) pkg> #exit package mode with 10 | julia> include("Stipple2048.jl") 11 | # should open your default browser and fire up Genie server at port between `8000:9000` 12 | ``` 13 | ![Form](docs/content/img/Stipple2048.png) 14 | ```julia 15 | julia> down() # stop the running async instance of Genie Server 16 | ``` 17 | Current Advanced Examples: 18 | 19 | | Name | Features | Screenshot | 20 | |---------------------------|--------------------------------------------|-----------------------------------------| 21 | | **Stipple2048.jl** | Stipple Implementation of the Game 2048
key handling, advanced ui | ![Form](docs/content/img/Stipple2048.png) | 22 | | **Stipple2048_animated.jl** | Animated Stipple Implementation of the Game 2048
key handling, animations, advanced ui | ![Form](docs/content/img/Stipple2048_animated.jpg) | 23 | | **ServerSideFiltering.jl**| Select with server-side filtering of options
select, js_methods | ![Form](docs/content/img/ServerSideFiltering.jpg) | 24 | | **CameraWidget.jl** | Circular resizable camera widget
webcam, js_methods, Electron browser | ![Form](docs/content/img/CameraWidget.jpg) | 25 | | **ContourPlot/heatlab.jl** | Two Dimensional Plate Heat Transfer Virtual Simulation Laboratory| ![Form](ContourPlots/assets/animation.gif)| -------------------------------------------------------------------------------- /AdvancedExamples/ServerSideFiltering.jl: -------------------------------------------------------------------------------- 1 | using Pkg 2 | pkg"activate" 3 | cd(@__DIR__) 4 | 5 | using Stipple 6 | using StippleUI 7 | 8 | import Stipple: js_methods 9 | 10 | using Random 11 | 12 | @reactive! mutable struct Example <: ReactiveModel 13 | select::R{Vector{String}} = String[] 14 | options::R{Vector{String}} = ["Options 1", "Options 2", "Options 3"] 15 | filter::R{String} = "" 16 | end 17 | 18 | js_methods(::Example) = """ 19 | filterFn (val, update, abort) { 20 | console.log('Filtering started!') 21 | update(() => { 22 | this.filter = val 23 | }) 24 | }, 25 | 26 | filterAbortFn () { 27 | console.log('Filtering aborted!') 28 | } 29 | """ 30 | 31 | row_module(args...; kwargs...) = row(cell(class="st-module", args...; kwargs...)) 32 | 33 | function ui(model) 34 | page(model, class="container", title="Example", [ 35 | heading(string(row([ 36 | a(icon("help", size = "xl", style = "width: auto"), href = "/"), 37 | cell(class = "text-h2", style ="padding-left: 20px", "Server-side filtering") 38 | ]))), 39 | 40 | row_module([ 41 | Stipple.select( 42 | :select, options = :options, label = "Options", "", 43 | :multiple, :use__chips, :options__dense, 44 | :use__input, 45 | @on(:filter, "filterFn"), 46 | @on(:filter, "filterAbortFn") 47 | ) 48 | ]) 49 | ]) 50 | end 51 | 52 | route("/") do 53 | init(Example) |> handlers |> ui|> html 54 | end 55 | 56 | function handlers(model) 57 | on(model.filter) do filter 58 | if isempty(filter) 59 | model.options[] = "Option " .* string.(1:10) 60 | elseif length(filter) > 2 61 | model.options[] = filter .* "_" .* [join([randstring(rand(2:5)) for _ in 1:3], "_") for _ in 1:10] 62 | end 63 | end 64 | 65 | model 66 | end 67 | 68 | up(8080, open_browser=true) 69 | -------------------------------------------------------------------------------- /AdvancedExamples/Stipple2048.jl: -------------------------------------------------------------------------------- 1 | cd(@__DIR__) 2 | using Pkg 3 | pkg"activate ." 4 | 5 | using Game2048Core 6 | using Stipple 7 | using StippleUI 8 | 9 | import Stipple: js_methods, js_created 10 | 11 | const G = Game2048Core 12 | const mydiv = Genie.Renderer.Html.div 13 | 14 | colors = ["#4355db", "#34bbe6", "#49da9a", "#a3e048", "#f7d038", "#eb7532", "#e6261f"] 15 | 16 | css() = style(media="screen",""" 17 | .board { 18 | width: 65vh !important; 19 | height: 65vh !important; 20 | position: relative; 21 | } 22 | 23 | .field{ 24 | position: absolute !important; 25 | width: 24% !important; 26 | height: 24% !important; 27 | border-radius: 2%; 28 | background-color: #ff4119ff; 29 | box-shadow: -0.3em 0.2em 1.5em 0.2em #ff4119ff; 30 | transition: all 0.5s ease-in-out; 31 | } 32 | 33 | .center{ 34 | position: absolute !important; 35 | padding: 0 !important; 36 | margin: 0 !important; 37 | top: 50%; 38 | left: 50%; 39 | transform: translate(-50%, -50%); 40 | } 41 | 42 | .size{ 43 | width: 65vh !important 44 | } 45 | """) 46 | 47 | @reactive! mutable struct G2048 <: ReactiveModel 48 | score::R{Int} = 0 49 | bitboard::R{Bitboard} = G.initbboard() 50 | lastboard::R{Bitboard} = Bitboard(0), READONLY 51 | board::R{Matrix{Int8}} = zeros(Int8, 4, 4) 52 | styles :: R{Matrix{String}} = fill("", 4, 4) 53 | key::R{UInt8} = 0 54 | end 55 | 56 | function handlers(model) 57 | on(model.isready) do _ 58 | model.board[!] = G.bitboard_to_array(model.bitboard[]) 59 | push!(model) 60 | animate_board(model) 61 | end 62 | 63 | on(model.bitboard) do bitboard 64 | model.board[] = G.bitboard_to_array(bitboard) 65 | model.score[] = score(model.bitboard[]) 66 | animate_board(model) 67 | end 68 | 69 | on(model.key) do key 70 | key == 0 && return 71 | keyhandler(model, key) 72 | model.key[] = 0 73 | end 74 | 75 | model 76 | end 77 | 78 | js_created(::G2048) = """ 79 | window.addEventListener('keydown', this.keydown); 80 | """ 81 | 82 | js_methods(::G2048) = """ 83 | keydown: function(e) { 84 | this.key = e.keyCode 85 | } 86 | """ 87 | 88 | function coords(i, j) 89 | top, left = 25 .* [i - 1, j - 1] 90 | end 91 | 92 | function fieldstyle(i, j, color) 93 | global colors 94 | top, left = coords(i, j) 95 | s = "top: $top%; left: $left%; color: $color; box-shadow: 0 0 1.5em -0.2em $(color * "aa");" 96 | end 97 | 98 | function animate_board(model) 99 | for i = 1:4, j = 1:4 100 | color = get(colors, 1 + model.board[i, j], colors[end]) 101 | model.styles[i, j] = fieldstyle.(i, j, color) 102 | end 103 | end 104 | 105 | import Game2048Core.move! 106 | 107 | function move!(model::G2048, dir::Dirs) 108 | newboard = move(model.bitboard[], dir) 109 | newboard == model.bitboard[] && return 110 | 111 | model.lastboard[] = model.bitboard[] 112 | model.bitboard[] = newboard 113 | model.bitboard[] = add_tile(model.bitboard[]) 114 | animate_board(model) 115 | end 116 | 117 | function undo(model) 118 | model.bitboard[] = model.lastboard[] 119 | end 120 | 121 | function keyhandler(model, key) 122 | if 37 <= key <= 40 123 | move!(model, Dirs(key - 37)) 124 | elseif lowercase(Char(key)) == 'r' 125 | undo(model) 126 | else 127 | println("Key down: $key") 128 | end 129 | end 130 | 131 | function ui(model) 132 | css() * 133 | page(model, class = "container", row(cell(class="st-module", [ 134 | row(cell([ 135 | cell(class = "text-h4", "Stipple 2048"), 136 | numberfield(class="size", "Score", :score, :readonly, :outlined, :filled, style="top: 25%; left:30%;") 137 | ])), 138 | 139 | row(mydiv(class="board", [ 140 | 141 | [card(class = "field", "", style = Symbol("styles[$i * 4 + $j - 5]"), 142 | [cardsection(class="text-h3 center", 143 | """{{ board[$i * 4 + $j - 5] > 0 ? 2 ** board[$i * 4 + $j - 5] : ""}}""" 144 | )] 145 | ) for i = 1:4, j = 1:4]..., 146 | 147 | ])) 148 | ])), title = "Stipple 2048") 149 | end 150 | 151 | route("/") do 152 | model |> handlers |> ui |> html 153 | end 154 | 155 | Genie.config.server_host = "127.0.0.1" 156 | 157 | model = init(G2048, debounce = 0) 158 | 159 | Genie.up(open_browser = false) -------------------------------------------------------------------------------- /AdvancedExamples/StippleMathjsDemo/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 3 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 4 | StippleMathjs = "7fb1dcd0-db8f-4201-9509-3dea5d7792e9" 5 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 6 | 7 | [compat] 8 | Genie = "5.23.2" 9 | Stipple = "0.27.22" 10 | StippleUI "0.22.10" 11 | StippleMathjs = "0.1.0" -------------------------------------------------------------------------------- /AdvancedExamples/StippleMathjsDemo/StippleMathjsDemo.jl: -------------------------------------------------------------------------------- 1 | using Pkg 2 | Pkg.activate(@__DIR__) 3 | 4 | using Stipple, Stipple.ReactiveTools 5 | using StippleUI 6 | using StippleMathjs 7 | 8 | 9 | # you need to define x0 and y0 outside the loop in order to 10 | # guarantee that z is of the correct type. 11 | # Alternatively, you could declare z explicitly `z::ComplexF64` 12 | 13 | 14 | x0 = 1.0 15 | y0 = 2.0 16 | 17 | @app begin 18 | @in x = x0 19 | @in y = y0 20 | @in z::ComplexF64 = x0 + y0 * im 21 | 22 | @onchange x, y begin 23 | z[!] = x + y*im 24 | @push z 25 | end 26 | 27 | @onchange z begin 28 | @show z 29 | x[!] = z.re 30 | y[!] = z.im 31 | @push x 32 | @push y 33 | end 34 | end 35 | 36 | @deps StippleMathjs 37 | 38 | function ui() 39 | [ 40 | card(class = "q-pa-md", [ 41 | numberfield(class = "q-ma-md", "x", :x) 42 | numberfield(class = "q-ma-md", "y", :y) 43 | ]) 44 | 45 | card(class = "q-pa-md q-my-md", [ 46 | row([cell(col = 2, "z"), cell("{{ z }}")]) 47 | row([cell(col = 2, "z.mul(z)"), cell("{{ z.mul(z) }}")]) 48 | row([cell(col = 2, "z.abs()"), cell("{{ z.abs() }}")]) 49 | 50 | btn(class = "q-my-md", "square(z)", color = "primary", @click("z = z.mul(z)")) 51 | ]) 52 | ] 53 | end 54 | 55 | @page("/", ui, debounce = 10) 56 | up() -------------------------------------------------------------------------------- /AdvancedExamples/docs/content/img/CameraWidget.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/AdvancedExamples/docs/content/img/CameraWidget.jpg -------------------------------------------------------------------------------- /AdvancedExamples/docs/content/img/ServerSideFiltering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/AdvancedExamples/docs/content/img/ServerSideFiltering.jpg -------------------------------------------------------------------------------- /AdvancedExamples/docs/content/img/Stipple2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/AdvancedExamples/docs/content/img/Stipple2048.png -------------------------------------------------------------------------------- /AdvancedExamples/docs/content/img/Stipple2048_animated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/AdvancedExamples/docs/content/img/Stipple2048_animated.jpg -------------------------------------------------------------------------------- /AdvancedExamples/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/AdvancedExamples/public/favicon.ico -------------------------------------------------------------------------------- /BasicExamples/.JuliaFormatter.toml: -------------------------------------------------------------------------------- 1 | remove_extra_newlines = true 2 | indent = 2 3 | -------------------------------------------------------------------------------- /BasicExamples/BubbleCharts/BubbleCharts.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | @reactive mutable struct Model <: ReactiveModel 4 | marker::PlotDataMarker = 5 | PlotDataMarker(opacity = [1, 0.8, 0.6, 0.4], size = [40, 60, 80, 100]) 6 | 7 | title::PlotLayoutTitle = PlotLayoutTitle(text = "Marker Size") 8 | 9 | plotdata::R{PlotData} = 10 | PlotData(x = [1, 2, 3, 4], y = [10, 11, 12, 13], mode = "markers", marker = marker) 11 | 12 | layout::R{PlotLayout} = 13 | PlotLayout(title = title, showlegend = false, height = 600, width = 600) 14 | end 15 | 16 | function ui(model) 17 | [ 18 | page( 19 | model, 20 | class = "container", 21 | title = "Bubble Chart", 22 | partial = true, 23 | [ 24 | row( 25 | cell( 26 | class = "st-module", 27 | [plot(:plotdata, layout = :layout, config = "{ displayLogo:false }")], 28 | ), 29 | ), 30 | ], 31 | ), 32 | ] 33 | end 34 | 35 | route("/") do 36 | model = Model |> init 37 | html(ui(model), context = @__MODULE__) 38 | end 39 | 40 | up() 41 | -------------------------------------------------------------------------------- /BasicExamples/BubbleCharts/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 4 | 5 | [compat] 6 | StipplePlotly = "0.10" 7 | Stipple = "0.20 - 0.22" 8 | julia = "1.6" -------------------------------------------------------------------------------- /BasicExamples/CSVUploadProcess/Backend_Upload/iris.csv: -------------------------------------------------------------------------------- 1 | sepal_length,sepal_width,petal_length,petal_width,species 2 | 5.1,3.5,1.4,0.2,Iris-setosa 3 | 4.9,3,1.4,0.2,Iris-setosa 4 | 4.7,3.2,1.3,0.2,Iris-setosa 5 | 4.6,3.1,1.5,0.2,Iris-setosa 6 | 5,3.6,1.4,0.2,Iris-setosa 7 | 5.4,3.9,1.7,0.4,Iris-setosa 8 | 4.6,3.4,1.4,0.3,Iris-setosa 9 | 5,3.4,1.5,0.2,Iris-setosa 10 | 4.4,2.9,1.4,0.2,Iris-setosa 11 | 4.9,3.1,1.5,0.1,Iris-setosa 12 | 5.4,3.7,1.5,0.2,Iris-setosa 13 | 4.8,3.4,1.6,0.2,Iris-setosa 14 | 4.8,3,1.4,0.1,Iris-setosa 15 | 4.3,3,1.1,0.1,Iris-setosa 16 | 5.8,4,1.2,0.2,Iris-setosa 17 | 5.7,4.4,1.5,0.4,Iris-setosa 18 | 5.4,3.9,1.3,0.4,Iris-setosa 19 | 5.1,3.5,1.4,0.3,Iris-setosa 20 | 5.7,3.8,1.7,0.3,Iris-setosa 21 | 5.1,3.8,1.5,0.3,Iris-setosa 22 | 5.4,3.4,1.7,0.2,Iris-setosa 23 | 5.1,3.7,1.5,0.4,Iris-setosa 24 | 4.6,3.6,1,0.2,Iris-setosa 25 | 5.1,3.3,1.7,0.5,Iris-setosa 26 | 4.8,3.4,1.9,0.2,Iris-setosa 27 | 5,3,1.6,0.2,Iris-setosa 28 | 5,3.4,1.6,0.4,Iris-setosa 29 | 5.2,3.5,1.5,0.2,Iris-setosa 30 | 5.2,3.4,1.4,0.2,Iris-setosa 31 | 4.7,3.2,1.6,0.2,Iris-setosa 32 | 4.8,3.1,1.6,0.2,Iris-setosa 33 | 5.4,3.4,1.5,0.4,Iris-setosa 34 | 5.2,4.1,1.5,0.1,Iris-setosa 35 | 5.5,4.2,1.4,0.2,Iris-setosa 36 | 4.9,3.1,1.5,0.1,Iris-setosa 37 | 5,3.2,1.2,0.2,Iris-setosa 38 | 5.5,3.5,1.3,0.2,Iris-setosa 39 | 4.9,3.1,1.5,0.1,Iris-setosa 40 | 4.4,3,1.3,0.2,Iris-setosa 41 | 5.1,3.4,1.5,0.2,Iris-setosa 42 | 5,3.5,1.3,0.3,Iris-setosa 43 | 4.5,2.3,1.3,0.3,Iris-setosa 44 | 4.4,3.2,1.3,0.2,Iris-setosa 45 | 5,3.5,1.6,0.6,Iris-setosa 46 | 5.1,3.8,1.9,0.4,Iris-setosa 47 | 4.8,3,1.4,0.3,Iris-setosa 48 | 5.1,3.8,1.6,0.2,Iris-setosa 49 | 4.6,3.2,1.4,0.2,Iris-setosa 50 | 5.3,3.7,1.5,0.2,Iris-setosa 51 | 5,3.3,1.4,0.2,Iris-setosa 52 | 7,3.2,4.7,1.4,Iris-versicolor 53 | 6.4,3.2,4.5,1.5,Iris-versicolor 54 | 6.9,3.1,4.9,1.5,Iris-versicolor 55 | 5.5,2.3,4,1.3,Iris-versicolor 56 | 6.5,2.8,4.6,1.5,Iris-versicolor 57 | 5.7,2.8,4.5,1.3,Iris-versicolor 58 | 6.3,3.3,4.7,1.6,Iris-versicolor 59 | 4.9,2.4,3.3,1,Iris-versicolor 60 | 6.6,2.9,4.6,1.3,Iris-versicolor 61 | 5.2,2.7,3.9,1.4,Iris-versicolor 62 | 5,2,3.5,1,Iris-versicolor 63 | 5.9,3,4.2,1.5,Iris-versicolor 64 | 6,2.2,4,1,Iris-versicolor 65 | 6.1,2.9,4.7,1.4,Iris-versicolor 66 | 5.6,2.9,3.6,1.3,Iris-versicolor 67 | 6.7,3.1,4.4,1.4,Iris-versicolor 68 | 5.6,3,4.5,1.5,Iris-versicolor 69 | 5.8,2.7,4.1,1,Iris-versicolor 70 | 6.2,2.2,4.5,1.5,Iris-versicolor 71 | 5.6,2.5,3.9,1.1,Iris-versicolor 72 | 5.9,3.2,4.8,1.8,Iris-versicolor 73 | 6.1,2.8,4,1.3,Iris-versicolor 74 | 6.3,2.5,4.9,1.5,Iris-versicolor 75 | 6.1,2.8,4.7,1.2,Iris-versicolor 76 | 6.4,2.9,4.3,1.3,Iris-versicolor 77 | 6.6,3,4.4,1.4,Iris-versicolor 78 | 6.8,2.8,4.8,1.4,Iris-versicolor 79 | 6.7,3,5,1.7,Iris-versicolor 80 | 6,2.9,4.5,1.5,Iris-versicolor 81 | 5.7,2.6,3.5,1,Iris-versicolor 82 | 5.5,2.4,3.8,1.1,Iris-versicolor 83 | 5.5,2.4,3.7,1,Iris-versicolor 84 | 5.8,2.7,3.9,1.2,Iris-versicolor 85 | 6,2.7,5.1,1.6,Iris-versicolor 86 | 5.4,3,4.5,1.5,Iris-versicolor 87 | 6,3.4,4.5,1.6,Iris-versicolor 88 | 6.7,3.1,4.7,1.5,Iris-versicolor 89 | 6.3,2.3,4.4,1.3,Iris-versicolor 90 | 5.6,3,4.1,1.3,Iris-versicolor 91 | 5.5,2.5,4,1.3,Iris-versicolor 92 | 5.5,2.6,4.4,1.2,Iris-versicolor 93 | 6.1,3,4.6,1.4,Iris-versicolor 94 | 5.8,2.6,4,1.2,Iris-versicolor 95 | 5,2.3,3.3,1,Iris-versicolor 96 | 5.6,2.7,4.2,1.3,Iris-versicolor 97 | 5.7,3,4.2,1.2,Iris-versicolor 98 | 5.7,2.9,4.2,1.3,Iris-versicolor 99 | 6.2,2.9,4.3,1.3,Iris-versicolor 100 | 5.1,2.5,3,1.1,Iris-versicolor 101 | 5.7,2.8,4.1,1.3,Iris-versicolor 102 | 6.3,3.3,6,2.5,Iris-virginica 103 | 5.8,2.7,5.1,1.9,Iris-virginica 104 | 7.1,3,5.9,2.1,Iris-virginica 105 | 6.3,2.9,5.6,1.8,Iris-virginica 106 | 6.5,3,5.8,2.2,Iris-virginica 107 | 7.6,3,6.6,2.1,Iris-virginica 108 | 4.9,2.5,4.5,1.7,Iris-virginica 109 | 7.3,2.9,6.3,1.8,Iris-virginica 110 | 6.7,2.5,5.8,1.8,Iris-virginica 111 | 7.2,3.6,6.1,2.5,Iris-virginica 112 | 6.5,3.2,5.1,2,Iris-virginica 113 | 6.4,2.7,5.3,1.9,Iris-virginica 114 | 6.8,3,5.5,2.1,Iris-virginica 115 | 5.7,2.5,5,2,Iris-virginica 116 | 5.8,2.8,5.1,2.4,Iris-virginica 117 | 6.4,3.2,5.3,2.3,Iris-virginica 118 | 6.5,3,5.5,1.8,Iris-virginica 119 | 7.7,3.8,6.7,2.2,Iris-virginica 120 | 7.7,2.6,6.9,2.3,Iris-virginica 121 | 6,2.2,5,1.5,Iris-virginica 122 | 6.9,3.2,5.7,2.3,Iris-virginica 123 | 5.6,2.8,4.9,2,Iris-virginica 124 | 7.7,2.8,6.7,2,Iris-virginica 125 | 6.3,2.7,4.9,1.8,Iris-virginica 126 | 6.7,3.3,5.7,2.1,Iris-virginica 127 | 7.2,3.2,6,1.8,Iris-virginica 128 | 6.2,2.8,4.8,1.8,Iris-virginica 129 | 6.1,3,4.9,1.8,Iris-virginica 130 | 6.4,2.8,5.6,2.1,Iris-virginica 131 | 7.2,3,5.8,1.6,Iris-virginica 132 | 7.4,2.8,6.1,1.9,Iris-virginica 133 | 7.9,3.8,6.4,2,Iris-virginica 134 | 6.4,2.8,5.6,2.2,Iris-virginica 135 | 6.3,2.8,5.1,1.5,Iris-virginica 136 | 6.1,2.6,5.6,1.4,Iris-virginica 137 | 7.7,3,6.1,2.3,Iris-virginica 138 | 6.3,3.4,5.6,2.4,Iris-virginica 139 | 6.4,3.1,5.5,1.8,Iris-virginica 140 | 6,3,4.8,1.8,Iris-virginica 141 | 6.9,3.1,5.4,2.1,Iris-virginica 142 | 6.7,3.1,5.6,2.4,Iris-virginica 143 | 6.9,3.1,5.1,2.3,Iris-virginica 144 | 5.8,2.7,5.1,1.9,Iris-virginica 145 | 6.8,3.2,5.9,2.3,Iris-virginica 146 | 6.7,3.3,5.7,2.5,Iris-virginica 147 | 6.7,3,5.2,2.3,Iris-virginica 148 | 6.3,2.5,5,1.9,Iris-virginica 149 | 6.5,3,5.2,2,Iris-virginica 150 | 6.2,3.4,5.4,2.3,Iris-virginica 151 | 5.9,3,5.1,1.8,Iris-virginica 152 | -------------------------------------------------------------------------------- /BasicExamples/CSVUploadProcess/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" 3 | Clustering = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5" 4 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 5 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 6 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 7 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 8 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 9 | 10 | [compat] 11 | Stipple = "=0.25.0" 12 | StipplePlotly = "=0.13.0" 13 | StippleUI = "=0.20.1" 14 | -------------------------------------------------------------------------------- /BasicExamples/CSVUploadProcess/iris.csv: -------------------------------------------------------------------------------- 1 | sepal_length,sepal_width,petal_length,petal_width,species 2 | 5.1,3.5,1.4,0.2,Iris-setosa 3 | 4.9,3,1.4,0.2,Iris-setosa 4 | 4.7,3.2,1.3,0.2,Iris-setosa 5 | 4.6,3.1,1.5,0.2,Iris-setosa 6 | 5,3.6,1.4,0.2,Iris-setosa 7 | 5.4,3.9,1.7,0.4,Iris-setosa 8 | 4.6,3.4,1.4,0.3,Iris-setosa 9 | 5,3.4,1.5,0.2,Iris-setosa 10 | 4.4,2.9,1.4,0.2,Iris-setosa 11 | 4.9,3.1,1.5,0.1,Iris-setosa 12 | 5.4,3.7,1.5,0.2,Iris-setosa 13 | 4.8,3.4,1.6,0.2,Iris-setosa 14 | 4.8,3,1.4,0.1,Iris-setosa 15 | 4.3,3,1.1,0.1,Iris-setosa 16 | 5.8,4,1.2,0.2,Iris-setosa 17 | 5.7,4.4,1.5,0.4,Iris-setosa 18 | 5.4,3.9,1.3,0.4,Iris-setosa 19 | 5.1,3.5,1.4,0.3,Iris-setosa 20 | 5.7,3.8,1.7,0.3,Iris-setosa 21 | 5.1,3.8,1.5,0.3,Iris-setosa 22 | 5.4,3.4,1.7,0.2,Iris-setosa 23 | 5.1,3.7,1.5,0.4,Iris-setosa 24 | 4.6,3.6,1,0.2,Iris-setosa 25 | 5.1,3.3,1.7,0.5,Iris-setosa 26 | 4.8,3.4,1.9,0.2,Iris-setosa 27 | 5,3,1.6,0.2,Iris-setosa 28 | 5,3.4,1.6,0.4,Iris-setosa 29 | 5.2,3.5,1.5,0.2,Iris-setosa 30 | 5.2,3.4,1.4,0.2,Iris-setosa 31 | 4.7,3.2,1.6,0.2,Iris-setosa 32 | 4.8,3.1,1.6,0.2,Iris-setosa 33 | 5.4,3.4,1.5,0.4,Iris-setosa 34 | 5.2,4.1,1.5,0.1,Iris-setosa 35 | 5.5,4.2,1.4,0.2,Iris-setosa 36 | 4.9,3.1,1.5,0.1,Iris-setosa 37 | 5,3.2,1.2,0.2,Iris-setosa 38 | 5.5,3.5,1.3,0.2,Iris-setosa 39 | 4.9,3.1,1.5,0.1,Iris-setosa 40 | 4.4,3,1.3,0.2,Iris-setosa 41 | 5.1,3.4,1.5,0.2,Iris-setosa 42 | 5,3.5,1.3,0.3,Iris-setosa 43 | 4.5,2.3,1.3,0.3,Iris-setosa 44 | 4.4,3.2,1.3,0.2,Iris-setosa 45 | 5,3.5,1.6,0.6,Iris-setosa 46 | 5.1,3.8,1.9,0.4,Iris-setosa 47 | 4.8,3,1.4,0.3,Iris-setosa 48 | 5.1,3.8,1.6,0.2,Iris-setosa 49 | 4.6,3.2,1.4,0.2,Iris-setosa 50 | 5.3,3.7,1.5,0.2,Iris-setosa 51 | 5,3.3,1.4,0.2,Iris-setosa 52 | 7,3.2,4.7,1.4,Iris-versicolor 53 | 6.4,3.2,4.5,1.5,Iris-versicolor 54 | 6.9,3.1,4.9,1.5,Iris-versicolor 55 | 5.5,2.3,4,1.3,Iris-versicolor 56 | 6.5,2.8,4.6,1.5,Iris-versicolor 57 | 5.7,2.8,4.5,1.3,Iris-versicolor 58 | 6.3,3.3,4.7,1.6,Iris-versicolor 59 | 4.9,2.4,3.3,1,Iris-versicolor 60 | 6.6,2.9,4.6,1.3,Iris-versicolor 61 | 5.2,2.7,3.9,1.4,Iris-versicolor 62 | 5,2,3.5,1,Iris-versicolor 63 | 5.9,3,4.2,1.5,Iris-versicolor 64 | 6,2.2,4,1,Iris-versicolor 65 | 6.1,2.9,4.7,1.4,Iris-versicolor 66 | 5.6,2.9,3.6,1.3,Iris-versicolor 67 | 6.7,3.1,4.4,1.4,Iris-versicolor 68 | 5.6,3,4.5,1.5,Iris-versicolor 69 | 5.8,2.7,4.1,1,Iris-versicolor 70 | 6.2,2.2,4.5,1.5,Iris-versicolor 71 | 5.6,2.5,3.9,1.1,Iris-versicolor 72 | 5.9,3.2,4.8,1.8,Iris-versicolor 73 | 6.1,2.8,4,1.3,Iris-versicolor 74 | 6.3,2.5,4.9,1.5,Iris-versicolor 75 | 6.1,2.8,4.7,1.2,Iris-versicolor 76 | 6.4,2.9,4.3,1.3,Iris-versicolor 77 | 6.6,3,4.4,1.4,Iris-versicolor 78 | 6.8,2.8,4.8,1.4,Iris-versicolor 79 | 6.7,3,5,1.7,Iris-versicolor 80 | 6,2.9,4.5,1.5,Iris-versicolor 81 | 5.7,2.6,3.5,1,Iris-versicolor 82 | 5.5,2.4,3.8,1.1,Iris-versicolor 83 | 5.5,2.4,3.7,1,Iris-versicolor 84 | 5.8,2.7,3.9,1.2,Iris-versicolor 85 | 6,2.7,5.1,1.6,Iris-versicolor 86 | 5.4,3,4.5,1.5,Iris-versicolor 87 | 6,3.4,4.5,1.6,Iris-versicolor 88 | 6.7,3.1,4.7,1.5,Iris-versicolor 89 | 6.3,2.3,4.4,1.3,Iris-versicolor 90 | 5.6,3,4.1,1.3,Iris-versicolor 91 | 5.5,2.5,4,1.3,Iris-versicolor 92 | 5.5,2.6,4.4,1.2,Iris-versicolor 93 | 6.1,3,4.6,1.4,Iris-versicolor 94 | 5.8,2.6,4,1.2,Iris-versicolor 95 | 5,2.3,3.3,1,Iris-versicolor 96 | 5.6,2.7,4.2,1.3,Iris-versicolor 97 | 5.7,3,4.2,1.2,Iris-versicolor 98 | 5.7,2.9,4.2,1.3,Iris-versicolor 99 | 6.2,2.9,4.3,1.3,Iris-versicolor 100 | 5.1,2.5,3,1.1,Iris-versicolor 101 | 5.7,2.8,4.1,1.3,Iris-versicolor 102 | 6.3,3.3,6,2.5,Iris-virginica 103 | 5.8,2.7,5.1,1.9,Iris-virginica 104 | 7.1,3,5.9,2.1,Iris-virginica 105 | 6.3,2.9,5.6,1.8,Iris-virginica 106 | 6.5,3,5.8,2.2,Iris-virginica 107 | 7.6,3,6.6,2.1,Iris-virginica 108 | 4.9,2.5,4.5,1.7,Iris-virginica 109 | 7.3,2.9,6.3,1.8,Iris-virginica 110 | 6.7,2.5,5.8,1.8,Iris-virginica 111 | 7.2,3.6,6.1,2.5,Iris-virginica 112 | 6.5,3.2,5.1,2,Iris-virginica 113 | 6.4,2.7,5.3,1.9,Iris-virginica 114 | 6.8,3,5.5,2.1,Iris-virginica 115 | 5.7,2.5,5,2,Iris-virginica 116 | 5.8,2.8,5.1,2.4,Iris-virginica 117 | 6.4,3.2,5.3,2.3,Iris-virginica 118 | 6.5,3,5.5,1.8,Iris-virginica 119 | 7.7,3.8,6.7,2.2,Iris-virginica 120 | 7.7,2.6,6.9,2.3,Iris-virginica 121 | 6,2.2,5,1.5,Iris-virginica 122 | 6.9,3.2,5.7,2.3,Iris-virginica 123 | 5.6,2.8,4.9,2,Iris-virginica 124 | 7.7,2.8,6.7,2,Iris-virginica 125 | 6.3,2.7,4.9,1.8,Iris-virginica 126 | 6.7,3.3,5.7,2.1,Iris-virginica 127 | 7.2,3.2,6,1.8,Iris-virginica 128 | 6.2,2.8,4.8,1.8,Iris-virginica 129 | 6.1,3,4.9,1.8,Iris-virginica 130 | 6.4,2.8,5.6,2.1,Iris-virginica 131 | 7.2,3,5.8,1.6,Iris-virginica 132 | 7.4,2.8,6.1,1.9,Iris-virginica 133 | 7.9,3.8,6.4,2,Iris-virginica 134 | 6.4,2.8,5.6,2.2,Iris-virginica 135 | 6.3,2.8,5.1,1.5,Iris-virginica 136 | 6.1,2.6,5.6,1.4,Iris-virginica 137 | 7.7,3,6.1,2.3,Iris-virginica 138 | 6.3,3.4,5.6,2.4,Iris-virginica 139 | 6.4,3.1,5.5,1.8,Iris-virginica 140 | 6,3,4.8,1.8,Iris-virginica 141 | 6.9,3.1,5.4,2.1,Iris-virginica 142 | 6.7,3.1,5.6,2.4,Iris-virginica 143 | 6.9,3.1,5.1,2.3,Iris-virginica 144 | 5.8,2.7,5.1,1.9,Iris-virginica 145 | 6.8,3.2,5.9,2.3,Iris-virginica 146 | 6.7,3.3,5.7,2.5,Iris-virginica 147 | 6.7,3,5.2,2.3,Iris-virginica 148 | 6.3,2.5,5,1.9,Iris-virginica 149 | 6.5,3,5.2,2,Iris-virginica 150 | 6.2,3.4,5.4,2.3,Iris-virginica 151 | 5.9,3,5.1,1.8,Iris-virginica 152 | -------------------------------------------------------------------------------- /BasicExamples/Card/Card.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | 4 | # CardDemo definition inheriting from ReactiveModel 5 | # Base.@kwdef: that defines keyword based contructor of mutable struct 6 | @reactive mutable struct CardDemo <: ReactiveModel end 7 | 8 | function ui(model) 9 | [ 10 | page( # page generates HTML code for Single Page Application 11 | model, 12 | class = "container", 13 | title = "Card Demo", 14 | partial = true, 15 | [ 16 | row( # row takes a tuple of cells. Creates a `div` HTML element with a CSS class named `row`. 17 | cell([h1("Card Component example")]), 18 | ) 19 | row( 20 | cell([ 21 | card( 22 | class = "text-white", 23 | style = "background: radial-gradient(circle, #35a2ff 0%, #014a88 100%); width: 30%", 24 | card_section("lorLorem Ipsum is simply dummy text of the printing 25 | and typesetting industry. Lorem Ipsum has been the industry's standard 26 | dummy text ever since the 1500s, when an unknown printer took a galley 27 | of type and scrambled it to make a type specimen book. It has survived 28 | not only five centuries, but also the leap into electronic typesetting, 29 | remaining essentially unchanged. It was popularised in the 1960s with 30 | the release of Letraset sheets containing Lorem Ipsum passages, and more 31 | recently with desktop publishing software like Aldus PageMaker including 32 | versions of Lorem Ipsumem"), 33 | ), 34 | ]), 35 | ) 36 | ], 37 | ), 38 | ] 39 | end 40 | 41 | route("/") do 42 | model = CardDemo |> init 43 | html(ui(model), context = @__MODULE__) 44 | end 45 | 46 | up() 47 | -------------------------------------------------------------------------------- /BasicExamples/Card/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.20 - 0.22" 7 | StippleUI = "0.17" 8 | julia = "1.6" -------------------------------------------------------------------------------- /BasicExamples/Checkboxes/Checkboxes.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | 4 | @reactive mutable struct Model <: ReactiveModel 5 | valone::R{Bool} = false 6 | valtwo::R{Bool} = false 7 | valthree::R{Bool} = false 8 | end 9 | 10 | function ui(my_model) 11 | page( 12 | my_model, 13 | class = "container", 14 | [ 15 | checkbox(label = "Apples", fieldname = :valone, dense = true), 16 | checkbox(label = "Bananas", fieldname = :valtwo, dense = true), 17 | checkbox(label = "Mangos", fieldname = :valthree, dense = true), 18 | ], 19 | ) 20 | end 21 | 22 | my_model = Stipple.init(Model) 23 | 24 | route("/") do 25 | html(ui(my_model), context = @__MODULE__) 26 | end 27 | 28 | up(async = true) 29 | -------------------------------------------------------------------------------- /BasicExamples/Checkboxes/CheckboxesVector.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | using OffsetArrays 3 | 4 | @reactive! mutable struct Model <: ReactiveModel 5 | valone::R{Bool} = false 6 | valtwo::R{Bool} = false 7 | valthree::R{Bool} = false 8 | checks::R{Vector{Bool}} = falses(3) 9 | offsetchecks::R{OffsetVector{Bool, BitVector}} = OffsetArray(falses(3), -1) 10 | end 11 | 12 | function ui(model) 13 | page(model, class = "container", [ 14 | checkbox(label = "Apples", fieldname = :valone, dense = true), 15 | checkbox(label = "Bananas", fieldname = :valtwo, dense = true), 16 | checkbox(label = "Mangos", fieldname = :valthree, dense = true), 17 | separator(), 18 | [checkbox("Checkbox $i", Symbol("checks[$(i-1)]")) for i in 1:length(model.checks[])]..., 19 | separator(), 20 | [checkbox("Offset Checkbox $i", Symbol("offsetchecks[$i]")) for i in eachindex(model.offsetchecks[])]... 21 | ]) 22 | end 23 | 24 | model = Stipple.init(Model) 25 | 26 | route("/") do 27 | html(ui(model), context = @__MODULE__) 28 | end 29 | 30 | up(async = true) 31 | 32 | model.valone[] = true 33 | model.checks[2] = true 34 | model.offsetchecks[2] = true -------------------------------------------------------------------------------- /BasicExamples/Checkboxes/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" 3 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 4 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 5 | 6 | [compat] 7 | Stipple = "0.20 - 0.22" 8 | StippleUI = "0.17" 9 | julia = "1.6" 10 | -------------------------------------------------------------------------------- /BasicExamples/CsvUpload/CSVUpload.jl: -------------------------------------------------------------------------------- 1 | using Genie, Stipple 2 | using Genie.Requests 3 | using StippleUI 4 | 5 | Genie.config.cors_headers["Access-Control-Allow-Origin"] = "*" 6 | Genie.config.cors_headers["Access-Control-Allow-Headers"] = "Content-Type" 7 | Genie.config.cors_headers["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,OPTIONS" 8 | Genie.config.cors_allowed_origins = ["*"] 9 | 10 | function create_storage_dir(name) 11 | try 12 | mkdir(joinpath(@__DIR__, name)) 13 | #= If you want to use Desktop dir then you may pass the following 14 | if Sys.iswindows() 15 | mkdir("$(homedir())\\Desktop\\$name") 16 | elseif Sys.islinux() 17 | mkdir("$(homedir())/$name") 18 | end 19 | =# 20 | catch 21 | @warn "directory already exists" 22 | end 23 | return joinpath(@__DIR__, name) 24 | end 25 | 26 | # Generate file path 27 | const FILE_PATH = create_storage_dir("Backend_Upload") 28 | # Define react model 29 | @reactive mutable struct APP <: ReactiveModel end 30 | 31 | function ui(model::APP) 32 | page(model, title="Dashboard", 33 | [ 34 | heading("Dashboard") 35 | row([ 36 | Html.div(class="col-md-12", [ 37 | uploader(label="Upload Dataset", :auto__upload, :multiple, method="POST", 38 | url="http://localhost:9000/", field__name="csv_file") 39 | ]) 40 | ]) 41 | ]) 42 | end 43 | 44 | route("/") do 45 | APP |> init |> ui |> html 46 | end 47 | 48 | #uploading csv files to the backend server 49 | route("/", method = POST) do 50 | files = Genie.Requests.filespayload() 51 | for f in files 52 | write(joinpath(FILE_PATH, f[2].name), f[2].data) 53 | @info "Uploading: " * f[2].name 54 | end 55 | if length(files) == 0 56 | @info "No file uploaded" 57 | end 58 | return "upload done" 59 | end 60 | 61 | up(9000) 62 | # To open the browser upon server starting please use this command 63 | # up (PORT, , open_browser=true) # Please change the PORT to any value, i.e. 8000 -------------------------------------------------------------------------------- /BasicExamples/CsvUpload/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 3 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 4 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 5 | 6 | [compat] 7 | Stipple = "0.20 - 0.22" 8 | StippleUI = "0.17" 9 | julia = "1.6" 10 | -------------------------------------------------------------------------------- /BasicExamples/DatePickers/DatePickers.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | import Stipple.Html: div 3 | 4 | # R{Date}, R{Vector{DateRange}} etc are type Observable and you can listen for changes 5 | # https://juliagizmos.github.io/Observables.jl/stable/ 6 | @reactive mutable struct DatePickers <: ReactiveModel 7 | date::R{Date} = today() + Day(30) 8 | dates::R{Vector{Date}} = Date[today()+Day(10), today()+Day(20), today()+Day(30)] 9 | daterange::R{DateRange} = DateRange(today(), (today() + Day(3))) 10 | dateranges::R{Vector{DateRange}} = [ 11 | DateRange(today(), (today() + Day(3))), 12 | DateRange(today() + Day(7), (today() + Day(10))), 13 | DateRange(today() + Day(14), (today() + Day(17))), 14 | ] 15 | proxydate::R{Date} = today() 16 | inputdate::R{Date} = today() 17 | end 18 | 19 | function ui(model) 20 | [ 21 | page( 22 | model, 23 | class = "container", 24 | title = "DatePickers Demo", 25 | partial = true, 26 | core_theme = true, 27 | [ 28 | row(cell([h1("Date pickers")])) 29 | row( 30 | [ 31 | cell([ 32 | datepicker(:date), # refers to line 7 date::R{Date} 33 | ]) 34 | cell([ 35 | datepicker(:dates, multiple = true), # :dates is mapped to line 8 Stipple's ReactiveModel 36 | ]) 37 | cell([ 38 | datepicker(:daterange, range = true), # :daterange -> daterange::R{DateRange} line 9 39 | ]) 40 | cell([datepicker(:dateranges, range = true, multiple = true)]) 41 | ], 42 | ) 43 | row( 44 | [ 45 | cell([ 46 | btn( 47 | icon = "event", 48 | round = true, 49 | color = "primary", 50 | [ 51 | popup_proxy([ 52 | datepicker( 53 | :proxydate, 54 | content = [ 55 | div( 56 | class = "row items-center justify-end q-gutter-sm", 57 | [ 58 | btn( 59 | label = "Cancel", 60 | color = "primary", 61 | flat = true, 62 | v__close__popup = true, 63 | ) 64 | btn( 65 | label = "OK", 66 | color = "primary", 67 | flat = true, 68 | v__close__popup = true, 69 | ) 70 | ], 71 | ), 72 | ], 73 | ), 74 | ]), 75 | ], 76 | ), 77 | ]) 78 | cell([ 79 | div( 80 | class = "q-pa-md", 81 | style = "max-width: 300px", 82 | [ 83 | textfield( 84 | "", 85 | :inputdate, 86 | filled = true, 87 | content = [ 88 | template( 89 | v__slot!!append = true, 90 | [ 91 | icon( 92 | name = "event", 93 | class = "cursor-pointer", 94 | [ 95 | popup_proxy( 96 | ref = "qDateProxy", 97 | transition__show = "scale", 98 | transition__hide = "scale", 99 | [ 100 | datepicker( 101 | :inputdate, 102 | content = [ 103 | div( 104 | class = "row items-center justify-end", 105 | [ 106 | btn( 107 | v__close__popup = true, 108 | label = "Close", 109 | color = "primary", 110 | flat = true, 111 | ), 112 | ], 113 | ), 114 | ], 115 | ), 116 | ], 117 | ), 118 | ], 119 | ), 120 | ], 121 | ), 122 | ], 123 | ), 124 | ], 125 | ), 126 | ]) 127 | ], 128 | ) 129 | ], 130 | ), 131 | ] 132 | end 133 | 134 | route("/") do 135 | model = DatePickers |> init 136 | html(ui(model), context = @__MODULE__) 137 | end 138 | 139 | up() 140 | -------------------------------------------------------------------------------- /BasicExamples/DatePickers/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.20 - 0.22" 7 | StippleUI = "0.17" 8 | julia = "1.6" 9 | -------------------------------------------------------------------------------- /BasicExamples/Editor/Editor.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | import Stipple.opts 3 | 4 | @reactive! mutable struct Test <: ReactiveModel 5 | s_editor::R{String} = "What you see is what you get." 6 | myfont::R{Dict{Symbol, Any}} = opts( 7 | arial = "Arial", 8 | arial_black = "Arial Black", 9 | courier_new = "Courier New", 10 | times_new_roman = "Times New Roman" 11 | ) 12 | mytoolbar::R{Vector{Vector{Union{Symbol,String}}}} = [ 13 | [:bold, :italic], 14 | [:customItalic], 15 | ["save", "upload"], 16 | ["spellcheck"], 17 | ["disabledButton"], 18 | ["custom_btn"] 19 | ] 20 | mydef::R{Dict{Symbol, Any}} = opts( 21 | bold = opts(cmd = "bold", label = "Bold", icon = nothing, tip = "My bold tooltip"), 22 | italic = opts(cmd = :italic, icon = "border_color", tip = "My italic tooltip"), 23 | customItalic = opts(cmd = :italic, icon = :camera_enhance, tip = "Italic"), 24 | save = opts( 25 | tip = "Save your work", icon = :save, label = "Save", 26 | handler = jsfunction"Test.$q.notify({type: 'positive', message: 'I saved your work!'})" 27 | ), 28 | upload = opts(tip = "Upload to cloud", icon = "cloud_upload", label = "Upload", 29 | handler = jsfunction"Test.upload()" 30 | ), 31 | spellcheck = opts(tip = "Run spell-check", icon = "spellcheck"), 32 | disabledButton = opts(tip = "I am disabled...", disable = true, icon = "cloud_off") 33 | ) 34 | end 35 | 36 | Stipple.js_methods(::Test) = raw""" 37 | upload: function() { this.$q.notify({ 38 | type: 'negative', 39 | message: 'Error during upload, no destination specified!' 40 | }) } 41 | """ 42 | 43 | function handlers(model) 44 | on(model.isready) do isready 45 | isready || return 46 | @async begin 47 | sleep(0.2) 48 | push!(model) 49 | end 50 | end 51 | 52 | model 53 | end 54 | 55 | model = init(Test, debounce = 0) |> handlers 56 | 57 | function ui(model) 58 | page(model, class = "container", row(cell(class = "st-module", row([ 59 | cell(editor(:s_editor, toolbar = :mytoolbar, definitions = :mydef, font = :myfont, 60 | max__height = "50vh" 61 | )) 62 | ]))), @iif(:isready)) |> html 63 | end 64 | 65 | route("/") do 66 | global model 67 | ui(model) 68 | end 69 | 70 | up(8020, open_browser=true) -------------------------------------------------------------------------------- /BasicExamples/Editor/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.20 - 0.22" 7 | StippleUI = "0.17" 8 | julia = "1.6" 9 | -------------------------------------------------------------------------------- /BasicExamples/FileUpload/FileUpload.jl: -------------------------------------------------------------------------------- 1 | using Genie, Stipple, StippleUI 2 | using Genie.Requests, Genie.Renderer 3 | 4 | Genie.config.cors_headers["Access-Control-Allow-Origin"] = "*" 5 | Genie.config.cors_headers["Access-Control-Allow-Headers"] = "Content-Type" 6 | Genie.config.cors_headers["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,OPTIONS" 7 | Genie.config.cors_allowed_origins = ["*"] 8 | 9 | const FILE_PATH = "upload/file.jpg" 10 | 11 | @reactive mutable struct Model <: ReactiveModel end 12 | 13 | #== view ==# 14 | function ui(model) 15 | page( 16 | model, 17 | partial = true, 18 | [ 19 | heading("File Upload Stipple Example") 20 | row([ 21 | Html.div( 22 | class = "col-md-12", 23 | [ 24 | uploader( 25 | label = "Upload Image", 26 | :auto__upload, 27 | :multiple, 28 | method = "POST", 29 | url = "http://localhost:8000/upload", 30 | field__name = "img", 31 | ), 32 | ], 33 | ), 34 | ]) 35 | ], 36 | ) 37 | end 38 | 39 | #== server ==# 40 | 41 | route("/") do 42 | model = Model |> init 43 | 44 | html(ui(model), context = @__MODULE__) 45 | end 46 | 47 | route("/upload", method = POST) do 48 | if infilespayload(:img) 49 | @info filename(filespayload(:img)) 50 | @info filespayload(:img).data 51 | 52 | open(FILE_PATH, "w") do io 53 | write(FILE_PATH, filespayload(:img).data) 54 | end 55 | else 56 | @info "No image uploaded" 57 | end 58 | end 59 | 60 | up() 61 | -------------------------------------------------------------------------------- /BasicExamples/FileUpload/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 3 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 4 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 5 | 6 | [compat] 7 | Stipple = "0.22.0" 8 | StippleUI = "0.17.0" 9 | Genie = "4.17" -------------------------------------------------------------------------------- /BasicExamples/FileUpload/upload/file.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/FileUpload/upload/file.jpg -------------------------------------------------------------------------------- /BasicExamples/Form/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | GenieFramework = "a59fdf5c-6bf0-4f5d-949c-a137c9e2f353" 3 | 4 | [compat] 5 | GenieFramework = "1.19.0" 6 | -------------------------------------------------------------------------------- /BasicExamples/Form/app.jl: -------------------------------------------------------------------------------- 1 | module App 2 | 3 | using GenieFramework 4 | 5 | @genietools 6 | 7 | @app begin 8 | @out name = "" 9 | @out age = 0 10 | @out objects = ["Dog", "Cat", "Beer"] 11 | @out warin = true 12 | @out accept = false 13 | end 14 | 15 | function ui() 16 | [ 17 | StippleUI.form( 18 | [ 19 | textfield( 20 | "What's your name *", 21 | :client_name, 22 | @iif(:warin), 23 | :filled, 24 | hint = "Name and surname", 25 | "lazy-rules", 26 | rules = "[val => val && val.length > 0 || 'Please type something']", 27 | ), 28 | numberfield( 29 | "Your age *", 30 | :client_age, 31 | "filled", 32 | :lazy__rules, 33 | rules = """[ 34 | val => val !== null && val !== '' || 'Please type your age', 35 | val => val > 0 && val < 100 || 'Please type a real age' 36 | ]""", 37 | ), 38 | toggle("I accept the license and terms", :accept), 39 | Stipple.Html.div( 40 | [ 41 | btn("Submit", type = "submit", color = "primary") 42 | btn("Reset", type = "reset", color = "primary", :flat, class = "q-ml-sm") 43 | ], 44 | ) 45 | ], 46 | @on(:submit, "onSubmit"), 47 | @on(:reset, "onReset"), 48 | class = "q-gutter-md", 49 | ) 50 | ] 51 | end 52 | 53 | @client_data begin 54 | client_name = nothing 55 | client_age = nothing 56 | accept = false 57 | end 58 | 59 | # when you don't want to interpolate string with $, you can use @raw 60 | # in this case, we are using @raw so that this.$q.notify is not interpolated 61 | @methods raw""" 62 | onSubmit () { 63 | console.log(this.accept) 64 | if (this.accept !== true) { 65 | this.$q.notify({ 66 | color: 'red-5', 67 | textColor: 'white', 68 | icon: 'warning', 69 | message: 'You need to accept the license and terms first' 70 | }) 71 | } 72 | else { 73 | this.$q.notify({ 74 | color: 'green-4', 75 | textColor: 'white', 76 | icon: 'cloud_done', 77 | message: 'Submitted' 78 | }); 79 | this.name = this.client_name; 80 | this.age = this.client_age; 81 | } 82 | }, 83 | onReset () { 84 | this.client_name = null 85 | this.client_age = null 86 | this.accept = false 87 | } 88 | """ 89 | 90 | @page("/", ui) 91 | 92 | end -------------------------------------------------------------------------------- /BasicExamples/HelloPie/HelloPie.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleCharts 3 | 4 | @reactive mutable struct HelloPie <: ReactiveModel 5 | plot_options::R{PlotOptions} = PlotOptions( 6 | chart_type = :pie, 7 | chart_width = 380, 8 | chart_animations_enabled = true, 9 | stroke_show = false, 10 | labels = ["Slice A", "Slice B"], 11 | ) 12 | piechart_::R{Vector} = Any[44, 55] 13 | values::R{String} = join(piechart_, ",") 14 | end 15 | 16 | Stipple.register_components(HelloPie, StippleCharts.COMPONENTS) 17 | 18 | function random_color()::String 19 | string(rand(0:255), base = 16) |> uppercase 20 | end 21 | 22 | function ui(model) 23 | on(model.values) do _ 24 | model.piechart_[] = Any[tryparse(Int, strip(x)) for x in split(model.values[], ',')] 25 | 26 | po = model.plot_options[] 27 | po.labels = ["Slice $x" for x in (collect('A':'Z')[1:length(model.piechart_[])])] 28 | 29 | while length(model.piechart_[]) > length(po.colors) 30 | push!(po.colors, string('#', random_color(), random_color(), random_color())) 31 | end 32 | 33 | model.plot_options[] = po 34 | end 35 | 36 | [ 37 | page( 38 | model, 39 | class = "container", 40 | title = "Hello Pie", 41 | partial = true, 42 | [ 43 | row( 44 | cell( 45 | [ 46 | h1([ 47 | "Your pie has the following slices: " 48 | span("", @text(:values)) 49 | ]) 50 | p( 51 | [ 52 | "Share your pie? (comma separated list of values) " 53 | input("", placeholder = "Share your pie", @bind(:values)) 54 | ], 55 | ) 56 | ], 57 | ), 58 | ) 59 | row( 60 | cell(class = "st-module", [plot(@data(:piechart_), options! = "plot_options")]), 61 | ) 62 | ], 63 | ), 64 | ] 65 | end 66 | 67 | route("/") do 68 | HelloPie |> init |> ui |> html 69 | end 70 | 71 | up() 72 | -------------------------------------------------------------------------------- /BasicExamples/HelloPie/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleCharts = "30ddb3f0-912f-4f34-9804-e4bb31290777" 4 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 5 | 6 | [compat] 7 | Stipple = "0.20.5" 8 | StippleCharts = "0.17.0" 9 | StippleUI = "0.17.0" -------------------------------------------------------------------------------- /BasicExamples/HelloStipple/HelloStipple.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | using GenieSession 4 | 5 | @vars Name begin 6 | name::R{String} = "" 7 | end 8 | 9 | function handlers(model) 10 | on(model.isready) do _ 11 | model.name[] = GenieSession.get!(:name, "") 12 | end 13 | 14 | on(model.name) do val 15 | GenieSession.set!(:name, val) |> GenieSession.persist 16 | end 17 | 18 | model 19 | end 20 | 21 | function ui(model) 22 | [ 23 | page( 24 | model, 25 | title = "Hello Stipple", 26 | [ 27 | h1([ 28 | "Hello, " 29 | span([], @text(:name)) 30 | ]) 31 | p([ 32 | "What is your name? " 33 | input("", placeholder = "Type your name", @bind(:name)) 34 | ]) 35 | ], 36 | @iif(:isready) 37 | ), 38 | ] 39 | end 40 | 41 | route("/") do 42 | init(Name) |> handlers |> ui |> html 43 | end 44 | 45 | up() 46 | -------------------------------------------------------------------------------- /BasicExamples/HelloStipple/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.22.0" 7 | StippleUI = "0.17.0" 8 | -------------------------------------------------------------------------------- /BasicExamples/HelloStipple/sessions/91b688fa4c5b8b17d11c1e213027a43ec9813ccece9dc1ee28f44aaa170cffc4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/HelloStipple/sessions/91b688fa4c5b8b17d11c1e213027a43ec9813ccece9dc1ee28f44aaa170cffc4 -------------------------------------------------------------------------------- /BasicExamples/ImageGallery/ImageGallery.jl: -------------------------------------------------------------------------------- 1 | # Note: use Stipple#master and StippleUI#master 2 | # pkg> add Stipple#master StippleUI#master to run this demo 3 | 4 | using Stipple, StippleUI 5 | import Stipple.Html: div 6 | 7 | function handlers(model) 8 | on(model.refresh) do val 9 | val || return 10 | model.url[] = "https://placeimg.com/500/300/nature?t=" * string(rand()) 11 | model.refresh[] = false 12 | end 13 | 14 | model 15 | end 16 | 17 | @reactive mutable struct Model <: ReactiveModel 18 | url::R{String} = "https://placeimg.com/500/300/nature" 19 | refresh::R{Bool} = false 20 | end 21 | 22 | function ui(model) 23 | page( 24 | model, 25 | partial = true, 26 | [ 27 | heading("Image Gallery") 28 | row([ 29 | div( 30 | class = "q-pa-md col", 31 | [ 32 | btn( 33 | color = "teal", 34 | label = "Change image", 35 | click!! = "refresh=true", 36 | class = "q-mb-md", 37 | ) 38 | div( 39 | class = "q-gutter-sm row items-start", 40 | [ 41 | imageview( 42 | src = :url, 43 | spinnercolor = "white", 44 | style = "height: 140px; max-width: 150px", 45 | ) 46 | imageview( 47 | src = :url, 48 | spinnercolor = "primary", 49 | spinnersize = "82px", 50 | style = "height: 140px; max-width: 150px", 51 | ) 52 | imageview( 53 | src = :url, 54 | style = "height: 140px; max-width: 150px", 55 | [ 56 | template( 57 | "", 58 | "v-slot:loading", 59 | [spinner(:gears, color = "white")], 60 | ), 61 | ], 62 | ) 63 | ], 64 | ) 65 | ], 66 | ), 67 | ]) 68 | ], 69 | ) 70 | end 71 | 72 | route("/") do 73 | model = Model |> init |> handlers 74 | 75 | html(ui(model), context = @__MODULE__) 76 | end 77 | 78 | up() 79 | -------------------------------------------------------------------------------- /BasicExamples/ImageGallery/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.22.0" 7 | StippleUI = "0.17.0" 8 | -------------------------------------------------------------------------------- /BasicExamples/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 3 | JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" 4 | Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" 5 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 6 | StippleCharts = "30ddb3f0-912f-4f34-9804-e4bb31290777" 7 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 8 | 9 | [compat] 10 | Genie = "4.5" 11 | Stipple = "0.19 - 0.22" 12 | StippleCharts = "0.16 - 0.17" 13 | StippleUI = "0.13 - 0.17" 14 | julia = "1.6" 15 | -------------------------------------------------------------------------------- /BasicExamples/README.md: -------------------------------------------------------------------------------- 1 | # UI Components usage using [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StippleUI.jl), [StippleCharts](https://github.com/GenieFramework/StippleCharts.jl) and [Genie](https://github.com/GenieFramework/Genie.jl) from Stipple Ecosystem 2 | 3 | ## Run Demo 4 | ```julia 5 | $ cd path-to-BasicExamples 6 | $ julia --project 7 | julia> #enter package mode with ] 8 | (@v1.x) pkg> activate . 9 | (@v1.x) pkg> instantiate 10 | (@v1.x) pkg> #exit package mode with 11 | julia> include("Form.jl") 12 | julia> up(rand((8000:9000)), open_browser=true) # should open your default browser and fire up Genie server at port between `8000:9000` 13 | julia> down() # stop the running async instance of Genie Server 14 | ``` 15 | 16 | > If you want to change code and want JULIA to automatically reflect changes in Web Page use `Revise`. `Revise` is already included in `Project.toml`. Change `your code` hit save in editor and refresh browser should reflect your changes 17 | ### How to use Revise? 18 | ```julia 19 | ---- same as above ----- 20 | (@v1.x) pkg> add Revise 21 | (@v1.x) pkg> #exit package mode with 22 | julia> using Revise 23 | julia> includet("Form.jl") # notice we are using **includet** from revise instead of include 24 | julia> up(rand((8000:9000)), open_browser=true) # should open your default browser and fire up Genie server at port between `8000:9000` 25 | julia> down() # stop the running async instance of Genie Server 26 | ``` 27 | 28 | ### Form Compontent 29 | 30 | | Components | Demo | 31 | |--------------------------|--------------------------------------------| 32 | | **Card Component** | ![Form](docs/content/img/Card.png) | 33 | | **DatePicker Component** | ![Form](docs/content/img/DatePickers.png) | 34 | | **Form Component** | ![Form](docs/content/img/Form.png) | 35 | | **Hello Pie** | ![Form](docs/content/img/HelloPie.png) | 36 | | **Hello Stipple** | ![Form](docs/content/img/HelloStipple.png) | 37 | | **Checkboxes** | ![Form](docs/content/img/Checkboxes.png) | 38 | | **FileUpload** | ![Form](docs/content/img/FileUpload.png) | 39 | | **ImageGallery** | ![Form](docs/content/img/ImageGallery.png) | 40 | | **BubbleCharts** | ![Form](docs/content/img/BubbleCharts.png) | 41 | -------------------------------------------------------------------------------- /BasicExamples/StippleButtons/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.22.0" 7 | StippleUI = "0.17.0" -------------------------------------------------------------------------------- /BasicExamples/StippleButtons/StippleButtons.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | 4 | @reactive! mutable struct SBModel <: ReactiveModel 5 | clicks::R{Int} = 0 6 | value::R{Int} = 0 7 | end 8 | 9 | function handler(model) 10 | on(model.value) do (_...) 11 | model.clicks[] += 1 12 | end 13 | model 14 | end 15 | 16 | function ui(model::SBModel) 17 | [ 18 | page(model, 19 | class = "container", 20 | title = "Buttons demo", 21 | [ 22 | heading("Buttons") 23 | row( 24 | [ 25 | cell([btn("Less! ", @click("value -= 1"))]) 26 | cell( 27 | [ 28 | p([ 29 | "Clicks: " 30 | span(model.clicks, @text(:clicks)) 31 | ]) 32 | p([ 33 | "Value: " 34 | span(model.value, @text(:value)) 35 | ]) 36 | ], 37 | ) 38 | cell([btn("More! ", @click("value += 1"))]) 39 | ], 40 | ) 41 | ], 42 | ), 43 | ] 44 | end 45 | 46 | route("/") do 47 | model = SBModel |> init |> handler 48 | ui(model) |> html 49 | end 50 | 51 | up(rand((8000:9000)), open_browser = true) 52 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" 3 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 4 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 5 | 6 | [compat] 7 | Stipple = "0.20.5" 8 | StippleUI = "0.17.0" 9 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/TableDownloadClipboard/public/favicon.ico -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Disclaimer: 2 | Hi there, thanks for contributing! Before anything else, please ensure you didn't mean to create an issue on the main MaterialDesign repo instead. 3 | If this is intentional, just erase this message. Thanks! 4 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/LICENSE: -------------------------------------------------------------------------------- 1 | Pictogrammers Free License 2 | -------------------------- 3 | 4 | This icon collection is released as free, open source, and GPL friendly by 5 | the [Pictogrammers](http://pictogrammers.com/) icon group. You may use it 6 | for commercial projects, open source projects, or anything really. 7 | 8 | # Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 9 | Some of the icons are redistributed under the Apache 2.0 license. All other 10 | icons are either redistributed under their respective licenses or are 11 | distributed under the Apache 2.0 license. 12 | 13 | # Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 14 | All web and desktop fonts are distributed under the Apache 2.0 license. Web 15 | and desktop fonts contain some icons that are redistributed under the Apache 16 | 2.0 license. All other icons are either redistributed under their respective 17 | licenses or are distributed under the Apache 2.0 license. 18 | 19 | # Code: MIT (https://opensource.org/licenses/MIT) 20 | The MIT license applies to all non-font and non-icon files. 21 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/README.md: -------------------------------------------------------------------------------- 1 | > *Note:* Please use the main [MaterialDesign](https://github.com/Templarian/MaterialDesign/issues) repo to report issues. This repo is for distribution of the Webfont files only. 2 | 3 | # Webfont - Material Design Icons 4 | 5 | Webfont distribution for the [Material Design Icons](https://materialdesignicons.com). 6 | 7 | ``` 8 | npm install @mdi/font 9 | ``` 10 | 11 | > Package built with [@mdi/font-build](https://github.com/Templarian/MaterialDesign-Font-Build). 12 | 13 | ## Related Packages 14 | 15 | [NPM @MDI Organization](https://npmjs.com/org/mdi) 16 | 17 | - JavaScript/Typescript: [MaterialDesign-JS](https://github.com/Templarian/MaterialDesign-JS) 18 | - SVG: [MaterialDesign-SVG](https://github.com/Templarian/MaterialDesign-SVG) 19 | - Font-Build [MaterialDesign-Font-Build](https://github.com/Templarian/MaterialDesign-Font-Build) 20 | - Desktop Font: [MaterialDesign-Font](https://github.com/Templarian/MaterialDesign-Font) 21 | 22 | ## Learn More 23 | 24 | - [MaterialDesignIcons.com](https://materialdesignicons.com) 25 | - https://github.com/Templarian/MaterialDesign 26 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.eot -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.woff -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/fonts/materialdesignicons-webfont.woff2 -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdi/font", 3 | "version": "5.9.55", 4 | "description": "Dist for Material Design Webfont. This includes the Stock and Community icons in a single webfont collection.", 5 | "style": "css/materialdesignicons.css", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/Templarian/MaterialDesign-Webfont.git" 12 | }, 13 | "keywords": [ 14 | "material", 15 | "design", 16 | "icons", 17 | "webfont" 18 | ], 19 | "author": { 20 | "name": "Austin Andrews", 21 | "web": "http://twitter.com/templarian" 22 | }, 23 | "license": "Apache-2.0", 24 | "bugs": { 25 | "url": "https://github.com/Templarian/MaterialDesign/issues" 26 | }, 27 | "homepage": "https://materialdesignicons.com" 28 | } 29 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // From Font Awesome 2 | .#{$mdi-css-prefix}-spin:before { 3 | -webkit-animation: #{$mdi-css-prefix}-spin 2s infinite linear; 4 | animation: #{$mdi-css-prefix}-spin 2s infinite linear; 5 | } 6 | 7 | @-webkit-keyframes #{$mdi-css-prefix}-spin { 8 | 0% { 9 | -webkit-transform: rotate(0deg); 10 | transform: rotate(0deg); 11 | } 12 | 100% { 13 | -webkit-transform: rotate(359deg); 14 | transform: rotate(359deg); 15 | } 16 | } 17 | 18 | @keyframes #{$mdi-css-prefix}-spin { 19 | 0% { 20 | -webkit-transform: rotate(0deg); 21 | transform: rotate(0deg); 22 | } 23 | 100% { 24 | -webkit-transform: rotate(359deg); 25 | transform: rotate(359deg); 26 | } 27 | } -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/_core.scss: -------------------------------------------------------------------------------- 1 | .#{$mdi-css-prefix}:before, 2 | .#{$mdi-css-prefix}-set { 3 | display: inline-block; 4 | font: normal normal normal #{$mdi-font-size-base}/1 '#{$mdi-font-name}'; // shortening font declaration 5 | font-size: inherit; // can't have font-size inherit on line above, so need to override 6 | text-rendering: auto; // optimizelegibility throws things off #1094 7 | line-height: inherit; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/_extras.scss: -------------------------------------------------------------------------------- 1 | $mdi-sizes: 18 24 36 48 !default; 2 | @each $mdi-size in $mdi-sizes { 3 | .#{$mdi-css-prefix}-#{$mdi-size}px { 4 | &.#{$mdi-css-prefix}-set, 5 | &.#{$mdi-css-prefix}:before { 6 | font-size: $mdi-size * 1px; 7 | } 8 | } 9 | } 10 | 11 | .#{$mdi-css-prefix}-dark { 12 | &:before { 13 | color: rgba(0, 0, 0, 0.54); 14 | } 15 | &.#{$mdi-css-prefix}-inactive:before { 16 | color: rgba(0, 0, 0, 0.26); 17 | } 18 | } 19 | .#{$mdi-css-prefix}-light { 20 | &:before { 21 | color: rgba(255, 255, 255, 1); 22 | } 23 | &.#{$mdi-css-prefix}-inactive:before { 24 | color: rgba(255, 255, 255, 0.3); 25 | } 26 | } 27 | 28 | $mdi-degrees: 45 90 135 180 225 270 315 !default; 29 | @each $mdi-degree in $mdi-degrees { 30 | .#{$mdi-css-prefix}-rotate-#{$mdi-degree}{ 31 | &:before { 32 | -webkit-transform: rotate(#{$mdi-degree}deg); 33 | -ms-transform: rotate(#{$mdi-degree}deg); 34 | transform: rotate(#{$mdi-degree}deg); 35 | } 36 | /* 37 | // Not included in production 38 | &.#{$mdi-css-prefix}-flip-h:before { 39 | -webkit-transform: scaleX(-1) rotate(#{$mdi-degree}deg); 40 | transform: scaleX(-1) rotate(#{$mdi-degree}deg); 41 | filter: FlipH; 42 | -ms-filter: "FlipH"; 43 | } 44 | &.#{$mdi-css-prefix}-flip-v:before { 45 | -webkit-transform: scaleY(-1) rotate(#{$mdi-degree}deg); 46 | -ms-transform: rotate(#{$mdi-degree}deg); 47 | transform: scaleY(-1) rotate(#{$mdi-degree}deg); 48 | filter: FlipV; 49 | -ms-filter: "FlipV"; 50 | } 51 | */ 52 | } 53 | } 54 | .#{$mdi-css-prefix}-flip-h:before { 55 | -webkit-transform: scaleX(-1); 56 | transform: scaleX(-1); 57 | filter: FlipH; 58 | -ms-filter: "FlipH"; 59 | } 60 | .#{$mdi-css-prefix}-flip-v:before { 61 | -webkit-transform: scaleY(-1); 62 | transform: scaleY(-1); 63 | filter: FlipV; 64 | -ms-filter: "FlipV"; 65 | } -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/_functions.scss: -------------------------------------------------------------------------------- 1 | @function char($character-code) { 2 | @if function-exists("selector-append") { 3 | @return unquote("\"\\#{$character-code}\""); 4 | } 5 | 6 | @if "\\#{'x'}" == "\\x" { 7 | @return str-slice("\x", 1, 1) + $character-code; 8 | } 9 | @else { 10 | @return #{"\"\\"}#{$character-code + "\""}; 11 | } 12 | } 13 | 14 | @function mdi($name) { 15 | @if map-has-key($mdi-icons, $name) == false { 16 | @warn "Icon #{$name} not found."; 17 | @return ""; 18 | } 19 | @return char(map-get($mdi-icons, $name)); 20 | } -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/_icons.scss: -------------------------------------------------------------------------------- 1 | @each $key, $value in $mdi-icons { 2 | .#{$mdi-css-prefix}-#{$key}::before { 3 | content: char($value); 4 | } 5 | } 6 | 7 | .#{$mdi-css-prefix}-blank::before { 8 | content: "\F68C"; 9 | visibility: hidden; 10 | } -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/_path.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: '#{$mdi-font-name}'; 3 | src: url('#{$mdi-font-path}/#{$mdi-filename}-webfont.eot?v=#{$mdi-version}'); 4 | src: url('#{$mdi-font-path}/#{$mdi-filename}-webfont.eot?#iefix&v=#{$mdi-version}') format('embedded-opentype'), 5 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.woff2?v=#{$mdi-version}') format('woff2'), 6 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.woff?v=#{$mdi-version}') format('woff'), 7 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.ttf?v=#{$mdi-version}') format('truetype'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | -------------------------------------------------------------------------------- /BasicExamples/TableDownloadClipboard/public/iconsets/@mdi/font/scss/materialdesignicons.scss: -------------------------------------------------------------------------------- 1 | /* MaterialDesignIcons.com */ 2 | @import "variables"; 3 | @import "functions"; 4 | @import "path"; 5 | @import "core"; 6 | @import "icons"; 7 | @import "extras"; 8 | @import "animated"; -------------------------------------------------------------------------------- /BasicExamples/Timeline/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 3 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 4 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 5 | -------------------------------------------------------------------------------- /BasicExamples/Timeline/Timeline.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | using DataFrames 4 | 5 | Stipple.render(df::DataFrames.DataFrame, fieldname::Union{Nothing, Symbol} = nothing) = Dict(zip(names(df), eachcol(df))) 6 | 7 | @vars TimeSeries begin 8 | button_pressed = false 9 | df = DataFrame(:Title => ["Title A", "Title B"], :Message => ["message 1", "message 2"]), READONLY 10 | private = "private", PRIVATE 11 | nonreactive = "nr", NON_REACTIVE 12 | end 13 | 14 | function ui(model) 15 | page( 16 | model, 17 | class = "container", 18 | row(cell(class = "st-module", [ 19 | btn("Update Timeline", color = "primary", @click("button_pressed = true")) 20 | timeline("", color = "primary", [ 21 | timelineentry("Timeline", heading = true), 22 | timelineentry("{{ df.Message[index] }}", @recur("(t, index) in df.Title"), title = :t) 23 | ]) 24 | ])), 25 | ) 26 | end 27 | 28 | function handlers(model) 29 | on(model.isready) do ready 30 | ready || return 31 | push!(model) 32 | end 33 | 34 | onbutton(model.button_pressed) do 35 | @show "Button Pressed" 36 | model.df[] = DataFrame(:Title => ["Title C", "Title D", "Title E"], :Message => ["message 3", "message 4", "message 5"]) 37 | end 38 | 39 | model 40 | end 41 | 42 | 43 | route("/") do 44 | model = TimeSeries |> init |> handlers 45 | model |> ui |> html 46 | end 47 | 48 | up() -------------------------------------------------------------------------------- /BasicExamples/Tree/EditableDict.jl: -------------------------------------------------------------------------------- 1 | # Demo for editing a Dict by help of a tree with templates 2 | # work in progress 3 | # to be done: 4 | # - move edit field into header 5 | # - support arrays 6 | 7 | using Stipple, StippleUI, JSON 8 | 9 | Genie.Secrets.secret_token!() 10 | 11 | testdict = JSON.parse(""" 12 | { 13 | "test": 14 | { 15 | "foo": false, 16 | "baz": "qux", 17 | "corge": 18 | { 19 | "grault": 1 20 | } 21 | } 22 | } 23 | """) 24 | 25 | 26 | dict(;kwargs...) = Dict{Symbol, Any}(kwargs...) 27 | const mydiv = Genie.Renderer.Html.div 28 | 29 | function dict_tree(startfile; parent = "d", name = "d") 30 | if startfile isa Dict 31 | k = keys(startfile) 32 | dict( 33 | label = name, 34 | key = parent, 35 | children = [dict_tree(startfile[i], parent = parent * "." * i, name = i) for i in k] 36 | ) 37 | elseif startfile isa Array && !isempty(startfile) 38 | if startfile[1] isa Dict 39 | for j in startfile 40 | k = keys(j) 41 | dict( 42 | label = name, 43 | key = parent, 44 | children = [dict_tree(j[i], parent = parent * "." * i, name=i) for i in k] 45 | ) 46 | end 47 | else 48 | dict( 49 | label = name, 50 | key = parent, 51 | children = [dict(label = i, key = parent * "." * i) for i in startfile]) 52 | end 53 | else 54 | dict(label = name, 55 | key = parent, 56 | value = startfile, 57 | body = startfile isa Bool ? "bool" : startfile isa Number ? "number" : "text", 58 | children = [] 59 | ) 60 | end 61 | end 62 | 63 | 64 | @reactive! mutable struct TreeDemo <: ReactiveModel 65 | d::R{Dict{String, Any}} = deepcopy(testdict) 66 | tree::R{Vector{Dict{Symbol, Any}}} = [dict_tree(testdict)] 67 | 68 | tree_selected::R{String} = "" 69 | tree_ticked::R{Vector{String}} = String[] 70 | tree_expanded::R{Vector{String}} = String[] 71 | end 72 | 73 | Genie.Router.delete!(:TreeDemo) 74 | Stipple.js_methods(::TreeDemo) = """ 75 | getindex: function(key) { 76 | let o = this 77 | kk = key.split('.') 78 | for(let i = 0; i < kk.length; i++){ 79 | o = o[kk[i]]; 80 | } 81 | return o 82 | }, 83 | 84 | setindex: function(key, val) { 85 | let o = this 86 | kk = key.split('.') 87 | for(let i = 0; i < kk.length - 1; i++){ 88 | o = o[kk[i]]; 89 | } 90 | o[kk[kk.length-1]] = val 91 | return val 92 | } 93 | """ 94 | 95 | function ui(model) 96 | page( 97 | model, 98 | title = "Dict Tree", 99 | row(cell( class = "st-module", [ 100 | row([tree(var"node-key" = "key", nodes = :tree, 101 | var"selected.sync" = :tree_selected, 102 | var"expanded.sync" = :tree_expanded, 103 | [ 104 | template("", var"v-slot:body-text" = "prop", [ 105 | textfield("", dense = true, label = R"prop.node.label", value = R"getindex(prop.node.key)", var"@input" = "newval => setindex(prop.node.key, newval)") 106 | ]), 107 | template("", var"v-slot:body-number" = "prop", [ 108 | textfield("", dense = true, label = R"prop.node.label", value = R"getindex(prop.node.key)", var"@input" = "newval => setindex(prop.node.key, 1 * newval)") 109 | ]), 110 | template("", var"v-slot:body-bool" = "prop", [ 111 | checkbox("", dense = true, label = R"prop.node.label", value = R"getindex(prop.node.key)", var"@input" = "newval => setindex(prop.node.key, newval)") 112 | ]) 113 | ] 114 | ) 115 | ]) 116 | 117 | # mydiv(h4("Expanded: ") * "{{ tree_expanded }}") 118 | # mydiv(h4("Selected: ") * "{{ tree_selected }}") 119 | # mydiv(h4("Ticked: ") * "{{ tree_ticked }}") 120 | ])), 121 | ) 122 | end 123 | 124 | function handlers(model) 125 | on(model.isready) do isready 126 | isready && push!(model) 127 | end 128 | 129 | model 130 | end 131 | 132 | route("/") do 133 | # model defined gloablly for debugging and testing only 134 | global model 135 | model = init(TreeDemo) |> handlers 136 | model |> ui |> html 137 | end 138 | 139 | up() -------------------------------------------------------------------------------- /BasicExamples/Tree/Project.toml: -------------------------------------------------------------------------------- 1 | name = "Tree" 2 | uuid = "a350f2a0-777b-49b1-adc4-07448ac00815" 3 | authors = ["hhaensel "] 4 | version = "0.1.0" 5 | 6 | [deps] 7 | OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" 8 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 9 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 10 | -------------------------------------------------------------------------------- /BasicExamples/Tree/Tree.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | dict(;kwargs...) = Dict{Symbol, Any}(kwargs...) 4 | const mydiv = Genie.Renderer.Html.div 5 | 6 | function filedict(startfile) 7 | if isdir(startfile) 8 | files = readdir(startfile, join = true) 9 | index = isdir.(files) 10 | files = vcat(files[index], files[.! index]) 11 | dict( 12 | label = basename(startfile), 13 | key = startfile, 14 | icon = "folder", 15 | children = filedict.(files) 16 | ) 17 | else 18 | dict(label = basename(startfile), 19 | key = startfile, 20 | icon = "insert_drive_file" 21 | ) 22 | end 23 | end 24 | 25 | cd(dirname(@__DIR__)) 26 | 27 | @reactive! mutable struct TreeDemo <: ReactiveModel 28 | name::R{String} = "" 29 | files::R{Vector{Dict{Symbol, Any}}} = [filedict(pwd())] 30 | files_selected::R{String} = "" 31 | files_ticked::R{Vector{String}} = String[] 32 | files_expanded::R{Vector{String}} = String[] 33 | end 34 | 35 | 36 | # alternative definition with the new @mixin macro 37 | # this will work with StippleUI v0.19.3 or latest master 38 | 39 | register_mixin(@__MODULE__) 40 | @reactive! mutable struct TreeDemo <: ReactiveModel 41 | name::R{String} = "" 42 | @mixin files::TreeSelectable([filedict(pwd())]) 43 | end 44 | 45 | Genie.Router.delete!(:TreeDemo) 46 | 47 | function ui(model) 48 | page( 49 | model, 50 | title = "Hello Stipple", 51 | row(cell( class = "st-module", [ 52 | row([tree(var"node-key" = "key", nodes = :files, 53 | var"tick-strategy"="leaf", 54 | var"selected.sync" = :files_selected, 55 | var"ticked.sync" = :files_ticked, 56 | var"expanded.sync" = :files_expanded 57 | ) 58 | 59 | tree(var"node-key" = "key", nodes = :files, 60 | var"tick-strategy"="leaf", 61 | var"selected.sync" = :files_selected, 62 | var"ticked.sync" = :files_ticked, 63 | var"expanded.sync" = :files_expanded 64 | )]) 65 | 66 | mydiv(h4("Expanded: ") * "{{ files_expanded }}") 67 | mydiv(h4("Selected: ") * "{{ files_selected }}") 68 | mydiv(h4("Ticked: ") * "{{ files_ticked }}") 69 | ])), 70 | ) 71 | end 72 | 73 | function handlers(model) 74 | on(model.isready) do isready 75 | isready && push!(model) 76 | end 77 | 78 | model 79 | end 80 | 81 | route("/") do 82 | # model defined gloablly for debugging and testing only 83 | global model 84 | model = init(TreeDemo) |> handlers 85 | model |> ui |> html 86 | end 87 | 88 | up() 89 | -------------------------------------------------------------------------------- /BasicExamples/config/secrets.jl: -------------------------------------------------------------------------------- 1 | Genie.secret_token!("7e1242b52998533b6b7ede539039a559c0d91ae59488636f42ade93bafb41603") 2 | -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/BubbleCharts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/BubbleCharts.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/Card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/Card.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/Checkboxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/Checkboxes.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/DatePickers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/DatePickers.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/FileUpload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/FileUpload.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/Form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/Form.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/HelloPie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/HelloPie.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/HelloStipple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/HelloStipple.png -------------------------------------------------------------------------------- /BasicExamples/docs/content/img/ImageGallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/BasicExamples/docs/content/img/ImageGallery.png -------------------------------------------------------------------------------- /GermanCredits/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" 3 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 4 | Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" 5 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 6 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 7 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 8 | 9 | [compat] 10 | DataFrames = "0.2, 1" 11 | Stipple = "= 0.20.4" 12 | StipplePlotly = "=0.9.2" 13 | StippleUI = "= 0.14.5" 14 | julia = "1.3" 15 | -------------------------------------------------------------------------------- /GermanCredits/README.md: -------------------------------------------------------------------------------- 1 | # German Credits Demo 2 | 3 | Demo uses [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StippleUI.jl), [StippleCharts](https://github.com/GenieFramework/StippleCharts.jl) and [Genie](https://github.com/GenieFramework/Genie.jl) from Stipple Ecosystem 4 | 5 | 6 | ## Run German Credits Demo 7 | ```julia 8 | julia> julia --project 9 | julia> #enter package mode with ] 10 | (@v1.x) pkg> activate . 11 | (@v1.x) pkg> instantiate 12 | (@v1.x) pkg> #exit package mode with 13 | julia> include("GermanCredits.jl") 14 | julia> up(rand((8000:9000)), open_browser=true) # should open your default browser and fire up Genie server at port between `8000:9000` 15 | julia> down() # stop the running async instance of Genie Server 16 | ``` 17 | ### German Credit Demo 18 | 19 | ![GCDemoGIF](docs/content/img/germancredits.gif) 20 | 21 | > If you want to change code and want JULIA to automatically reflect changes in Web Page use `Revise`. `Revise` is already included in `Project.toml`. Change `your code` hit save in editor and refresh browser should reflect your changes 22 | 23 | 24 | ### How to use Revise? 25 | ```julia 26 | ---- same as above ----- 27 | (@v1.x) pkg> add Revise 28 | (@v1.x) pkg> #exit package mode with 29 | julia> using Revise 30 | julia> includet("Form.jl") # notice we are using **includet** from revise instead of include 31 | julia> up(rand((8000:9000)), open_browser=true) # should open your default browser and fire up Genie server at port between `8000:9000` 32 | julia> down() # stop the running async instance of Genie Server 33 | ``` 34 | 35 | ## Using Docker and deploying on Heroku -------------------------------------------------------------------------------- /GermanCredits/docs/content/img/germancredits.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/GermanCredits/docs/content/img/germancredits.gif -------------------------------------------------------------------------------- /IrisClustering/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM julia:latest 2 | 3 | # user 4 | RUN useradd --create-home --shell /bin/bash genie 5 | 6 | # app 7 | RUN mkdir /home/genie/IrisClustering 8 | COPY . /home/genie/IrisClustering 9 | WORKDIR /home/genie/IrisClustering 10 | 11 | RUN chown genie:genie -R * 12 | 13 | USER genie 14 | 15 | RUN julia -e 'using Pkg; Pkg.activate("."); Pkg.instantiate(); Pkg.precompile(); ' 16 | 17 | ENV JULIA_DEPOT_PATH "/home/genie/.julia" 18 | ENV GENIE_ENV "dev" 19 | ENV HOST "0.0.0.0" 20 | ENV EARLYBIND "true" 21 | 22 | CMD julia -e 'using Pkg; Pkg.activate("."); include("IrisClustering_StipplePlotly.jl"); ' 23 | -------------------------------------------------------------------------------- /IrisClustering/IrisClustering.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | using StipplePlotly 4 | 5 | using Clustering 6 | import RDatasets: dataset 7 | import DataFrames 8 | 9 | data = DataFrames.insertcols!(dataset("datasets", "iris"), :Cluster => zeros(Int, 150)) 10 | 11 | @reactive mutable struct IrisModel <: ReactiveModel 12 | iris_data::R{DataTable} = DataTable(data) 13 | credit_data_pagination::DataTablePagination = 14 | DataTablePagination(rows_per_page=50) 15 | 16 | features::R{Vector{String}} = 17 | ["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"] 18 | xfeature::R{String} = "" 19 | yfeature::R{String} = "" 20 | 21 | iris_plot_data::R{Vector{PlotData}} = [] 22 | cluster_plot_data::R{Vector{PlotData}} = [] 23 | layout::R{PlotLayout} = PlotLayout(plot_bgcolor = "#fff") 24 | 25 | no_of_clusters::R{Int} = 3 26 | no_of_iterations::R{Int} = 10 27 | end 28 | 29 | function plot_data(cluster_column::Symbol, ic_model::IrisModel) 30 | plot_collection = Vector{PlotData}() 31 | isempty(ic_model.xfeature[]) || isempty(ic_model.yfeature[]) && return plot_collection 32 | 33 | for species in Array(data[:, cluster_column]) |> unique! 34 | x_feature_collection, y_feature_collection = Vector{Float64}(), Vector{Float64}() 35 | for r in eachrow(data[data[!, cluster_column] .== species, :]) 36 | push!(x_feature_collection, (r[Symbol(ic_model.xfeature[])])) 37 | push!(y_feature_collection, (r[Symbol(ic_model.yfeature[])])) 38 | end 39 | plot = PlotData( 40 | x = x_feature_collection, 41 | y = y_feature_collection, 42 | mode = "markers", 43 | name = string(species), 44 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER) 45 | push!(plot_collection, plot) 46 | end 47 | plot_collection 48 | end 49 | 50 | function compute_clusters!(ic_model::IrisModel) 51 | features = collect(Matrix(data[:, [Symbol(c) for c in ic_model.features[]]])') 52 | result = kmeans(features, ic_model.no_of_clusters[]; maxiter=ic_model.no_of_iterations[]) 53 | data[!, :Cluster] = assignments(result) 54 | ic_model.iris_data[] = DataTable(data) 55 | ic_model.cluster_plot_data[] = plot_data(:Cluster, ic_model) 56 | 57 | nothing 58 | end 59 | 60 | function ui(model::IrisModel) 61 | onany(model.xfeature, model.yfeature, model.no_of_clusters, model.no_of_iterations) do (_...) 62 | model.iris_plot_data[] = plot_data(:Species, model) 63 | compute_clusters!(model) 64 | end 65 | 66 | page( 67 | model, class="container", title="Iris Flowers Clustering", head_content=Genie.Assets.favicon_support(), 68 | 69 | prepend = style( 70 | """ 71 | tr:nth-child(even) { 72 | background: #F8F8F8 !important; 73 | } 74 | 75 | .modebar { 76 | display: none!important; 77 | } 78 | 79 | .st-module { 80 | background-color: #FFF; 81 | border-radius: 2px; 82 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.04); 83 | } 84 | 85 | .stipple-core .st-module > h5, 86 | .stipple-core .st-module > h6 { 87 | border-bottom: 0px !important; 88 | } 89 | """ 90 | ), 91 | 92 | [ 93 | heading("Iris data k-means clustering") 94 | 95 | row([ 96 | cell(class="st-module", [ 97 | h6("Number of clusters") 98 | slider( 1:1:20, 99 | @data(:no_of_clusters); 100 | label=true) 101 | ]) 102 | cell(class="st-module", [ 103 | h6("Number of iterations") 104 | slider( 10:10:200, 105 | @data(:no_of_iterations); 106 | label=true) 107 | ]) 108 | 109 | cell(class="st-module", [ 110 | h6("X feature") 111 | Stipple.select(:xfeature; options=:features) 112 | ]) 113 | 114 | cell(class="st-module", [ 115 | h6("Y feature") 116 | Stipple.select(:yfeature; options=:features) 117 | ]) 118 | ]) 119 | 120 | row([ 121 | cell(class="st-module", [ 122 | h5("Species clusters") 123 | plot(:iris_plot_data, layout = :layout, config = "{ displayLogo:false }") 124 | ]) 125 | 126 | cell(class="st-module", [ 127 | h5("k-means clusters") 128 | plot(:cluster_plot_data, layout = :layout, config = "{ displayLogo:false }") 129 | ]) 130 | ]) 131 | 132 | row([ 133 | cell(class="st-module", [ 134 | h5("Iris data") 135 | table(:iris_data; pagination=:credit_data_pagination, dense=true, flat=true, style="height: 350px;") 136 | ]) 137 | ]) 138 | ] 139 | ) 140 | end 141 | 142 | route("/") do 143 | IrisModel |> init |> ui |> html 144 | end 145 | 146 | up(9000; async = true, server = Stipple.bootstrap()) -------------------------------------------------------------------------------- /IrisClustering/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" 3 | Clustering = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5" 4 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 5 | RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b" 6 | Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" 7 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 8 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 9 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 10 | VersionCheck = "a637dc6b-bca1-447e-a4fa-35264c9d0580" 11 | 12 | [compat] 13 | Clustering = "0.14" 14 | DataFrames = "1" 15 | RDatasets = "0.7" 16 | Stipple = "= 0.20.4" 17 | StipplePlotly = "=0.9.2" 18 | StippleUI = "= 0.14.5" 19 | julia = "1.6" 20 | -------------------------------------------------------------------------------- /IrisClustering/README.md: -------------------------------------------------------------------------------- 1 | # UI Components usage using [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StippleUI.jl), [StippleCharts](https://github.com/GenieFramework/StippleCharts.jl) and [Genie](https://github.com/GenieFramework/Genie.jl) from Stipple Ecosystem 2 | 3 | ## Run Iris Cluster Demo 4 | ```julia 5 | julia> julia --project 6 | julia> #enter package mode with ] 7 | (@v1.x) pkg> activate . 8 | (@v1.x) pkg> instantiate 9 | (@v1.x) pkg> up 10 | (@v1.x) pkg> #exit package mode with 11 | julia> include("IrisClustering.jl") 12 | ``` 13 | ### Iris Cluter App Demo 14 | ![Iris](docs/content/img/iris.gif) 15 | 16 | > NOTE: Last line IrisClutering `up(async = false, server = Stipple.bootstrap())` file can be modified # you can set async = true to interact with application in repl 17 | 18 | > If you want to change code and want JULIA to automatically reflect changes in Web Page use `Revise`. `Revise` is already included in `Project.toml`. Change `your code` hit save in editor and refresh browser should reflect your changes ### How to use Revise? 19 | ```julia 20 | ---- same as above ----- 21 | (@v1.x) pkg> add Revise 22 | (@v1.x) pkg> #exit package mode with 23 | julia> using Revise 24 | julia> includet("Form.jl") # notice we are using **includet** from revise instead of include 25 | julia> up(rand((8000:9000)), open_browser=true) # should open your default browser and fire up Genie server at port between `8000:9000` 26 | julia> down() # stop the running async instance of Genie Server 27 | ``` 28 | 29 | ## Using Docker and deploying on Heroku 30 | -------------------------------------------------------------------------------- /IrisClustering/docs/content/img/iris.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/IrisClustering/docs/content/img/iris.gif -------------------------------------------------------------------------------- /Plugins/StippleDownloads.jl: -------------------------------------------------------------------------------- 1 | module StippleDownloads 2 | 3 | using Stipple 4 | 5 | import Stipple.Genie.WebChannels: CLIENTS, SUBSCRIPTIONS 6 | export download_binary, download_text 7 | 8 | type_dict = LittleDict( 9 | UInt8 => "Uint8Array", 10 | UInt16 => "Uint16Array", 11 | UInt32 => "Uint32Array", 12 | Int8 => "Int8Array", 13 | Int16 => "Int16Array", 14 | Int32 => "Int32Array", 15 | Float32 => "Float32Array", 16 | Float64 => "Float64Array", 17 | Int64 => "BigInt64Array", 18 | UInt64 => "BigUint64Array", 19 | ) 20 | 21 | js_download(data, filename, mime::MIME) = """ 22 | function () { 23 | const blob = new Blob([$data], {type: "$mime"}); 24 | const url = window.URL.createObjectURL(blob); 25 | 26 | const link = document.createElement('a'); 27 | link.href = url; 28 | link.download = '$filename'; 29 | link.click() 30 | 31 | setTimeout(() => { 32 | window.URL.revokeObjectURL(url); 33 | link.remove(); 34 | }, 100); 35 | } 36 | """ 37 | 38 | function download_binary(model::ReactiveModel, js_data::JSONText, filename; client::Union{Nothing,UInt,Vector{UInt}} = nothing) 39 | # if client is specified, send only to that client (i.e. exempt all subscribed clients but the specified client) 40 | run(model::ReactiveModel, js_download(js_data.s, filename, MIME("application/octet-stream")); restrict = client) 41 | end 42 | 43 | function download_binary(model::ReactiveModel, field::Symbol, filename, array_type::Type{<:Real} = UInt8; client::Union{Nothing,UInt,Vector{UInt}} = nothing) 44 | big = array_type <: Union{UInt64, Int64} ? ".map(String)" : "" 45 | download_binary( 46 | model, 47 | JSONText("this.$field instanceof Array ? $(get(type_dict, array_type, UInt8)).from(this.$field$big) : this.$field"), 48 | filename; 49 | client 50 | ) 51 | end 52 | 53 | function download_binary(model, data, filename = "file.bin", array_type::Type{<:Real} = UInt8; client::Union{Nothing,UInt,Vector{UInt}} = nothing) 54 | # we use a model field to send the data, in order to make use of popssibly defined revivers 55 | push!(model, :__download__ => data, channel = getchannel(model); restrict = client) 56 | download_binary(model, :__download__, filename, array_type; client) 57 | push!(model, :__download__ => nothing, channel = getchannel(model); restrict = client) 58 | end 59 | 60 | 61 | function download_text(model::ReactiveModel, js_data::JSONText, filename; client::Union{Nothing,UInt,Vector{UInt}} = nothing) 62 | # if client is specified, send only to that client (i.e. exempt all subscribed clients but the specified client) 63 | run(model::ReactiveModel, js_download(js_data.s, filename, MIME("text/plain;charset=utf-8")); restrict = client) 64 | end 65 | 66 | function download_text(model::ReactiveModel, field::Symbol, filename; client::Union{Nothing,UInt,Vector{UInt}} = nothing) 67 | download_text(model, JSONText("this.$field"), filename; client) 68 | end 69 | 70 | function download_text(model, data, filename = "file.txt"; client::Union{Nothing,UInt} = nothing) 71 | # we use a model field to send the text data, in order to avoid any possible confusion with quotes 72 | push!(model, :__download__ => data, channel = getchannel(model); restrict = client) 73 | download_text(model, :__download__, filename; client) 74 | push!(model, :__download__ => nothing, channel = getchannel(model); restrict = client) 75 | end 76 | 77 | 78 | # -------------------------- DEMO ---------------------------- 79 | # download_binary(model, codeunits("Hello World ϕ π 1"), "file.txt") 80 | # download_text(model, "Hello World ϕ 3π", "file.txt") 81 | # download_text(model, :mytext, "file.txt") 82 | # 83 | # function download_df_xlsx(model, df::DataFrame, filename = "file.xlsx") 84 | # download_binary(model, df_to_xlsx(df), filename) 85 | # end 86 | 87 | end -------------------------------------------------------------------------------- /Plugins/StippleTypedArrays.jl: -------------------------------------------------------------------------------- 1 | module StippleTypedArrays 2 | 3 | using Stipple 4 | 5 | export TypedArray 6 | 7 | type_dict = LittleDict( 8 | UInt8 => "Uint8Array", 9 | UInt16 => "Uint16Array", 10 | UInt32 => "Uint32Array", 11 | Int8 => "Int8Array", 12 | Int16 => "Int16Array", 13 | Int32 => "Int32Array", 14 | Float32 => "Float32Array", 15 | Float64 => "Float64Array", 16 | Int64 => "BigInt64Array", 17 | UInt64 => "BigUint64Array", 18 | ) 19 | 20 | struct TypedArray{T} 21 | array::Vector{T} 22 | end 23 | 24 | Base.setindex!(x::TypedArray, args...) = Base.setindex!(x.array, args...) 25 | Base.getindex(x::TypedArray, args...) = Base.getindex(x.array, args...) 26 | 27 | Base.convert(::Type{TypedArray}, v::AbstractVector{T}) where T = TypedArray{T}(convert(Vector{T}, v)) 28 | Base.convert(::Type{T}, ta::TypedArray) where T <: Union{AbstractVector, TypedArray} = convert(T, ta.array) 29 | Base.convert(::Type{TypedArray{T1}}, v::AbstractVector{T2}) where {T1, T2} = TypedArray{T1}(convert(Vector{T1}, v)) 30 | 31 | Stipple.render(ta::TypedArray{T}) where T = LittleDict(:typedArray => T, :array => ta.array) 32 | Stipple.render(ta::TypedArray{T}) where T <: Int64 = LittleDict(:typedArray => T, :array => string.(ta.array)) 33 | Stipple.render(ta::TypedArray{T}) where T <: UInt64 = LittleDict(:typedArray => T, :array => string.(reinterpret(Int64, ta.array))) 34 | 35 | Stipple.jsrender(ta::TypedArray{T}, args...) where T <: Real = JSONText("$(type_dict[T]).from($(json(ta.array)))") 36 | Stipple.jsrender(ta::TypedArray{T}, args...) where T <: UInt64 = JSONText("$(type_dict[T]).from($(json(string.(ta.array))))") 37 | Stipple.jsrender(ta::TypedArray{T}, args...) where T <: Int64 = JSONText("$(type_dict[T]).from($(json(string.(ta.array))))") 38 | 39 | Stipple.stipple_parse(::Type{TypedArray{T}}, v::Vector) where T = TypedArray(Vector{T}(v)) 40 | 41 | Stipple.stipple_parse(T::Type{TypedArray{UInt64}}, v::Vector) = Stipple.stipple_parse(T, [v...]) 42 | Stipple.stipple_parse(::Type{TypedArray{UInt64}}, v::Vector{String}) = TypedArray(parse.(UInt64, v)) 43 | Stipple.stipple_parse(::Type{TypedArray{UInt64}}, v::Vector{T}) where T <: Number = TypedArray(Vector{UInt64}(v)) 44 | 45 | Stipple.stipple_parse(T::Type{TypedArray{Int64}}, v::Vector) = Stipple.stipple_parse(T, [v...]) 46 | Stipple.stipple_parse(::Type{TypedArray{Int64}}, v::Vector{String}) = TypedArray(parse.(Int64, v)) 47 | Stipple.stipple_parse(::Type{TypedArray{Int64}}, v::Vector{T}) where T <: Number = TypedArray(Vector{UInt64}(v)) 48 | 49 | 50 | js_revive_typedArray = """ 51 | function (k, v) { 52 | if ( (typeof v==='object') && (v!=null) && (v.typedArray) ) { 53 | switch (v.typedArray) { 54 | case 'UInt8': a = Uint8Array.from(v.array); break 55 | case 'UInt16': a = Uint16Array.from(v.array); break 56 | case 'UInt32': a = Uint32Array.from(v.array); break 57 | case 'UInt64': a = BigUint64Array.from(v.array.map(BigInt)); break 58 | case 'Int8': a = Int8Array.from(v.array); break 59 | case 'Int16': a = Int16Array.from(v.array); break 60 | case 'Int32': a = Int32Array.from(v.array); break 61 | case 'Int64': a = BigInt64Array.from(v.array.map(BigInt)); break 62 | case 'Float32': a = Float32Array.from(v.array); break 63 | case 'Float64': a = Float64Array.from(v.array); break 64 | default: a = v.array 65 | } 66 | return a 67 | } else { 68 | return v 69 | } 70 | } 71 | """ 72 | 73 | 74 | function deps() 75 | [ 76 | script("\n", [ 77 | " $atype.prototype['toJSON'] = function () { return $(startswith(atype, "Big") ? "this.toString().split(',')" : "Array.from(this)") };\n" 78 | for atype in values(type_dict) 79 | ]) 80 | script(Stipple.js_add_reviver(js_revive_typedArray)) 81 | ] 82 | end 83 | 84 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Deprecation Warning !!! 2 | ### Stipple Ecosystem, now GenieFramework, is evolving very fast. You'll find some of these demos outdated and broken(if not used with relevant .toml). You can find the new demos [here](https://github.com/builtwithgenie) 3 | ----- 4 | 5 | # Demos for Stipple Ecosystem 6 | This repository includes a collection of Stipple demo application. Uses [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StippleUI.jl), [StippleCharts](https://github.com/GenieFramework/StippleCharts.jl) and [Genie](https://github.com/GenieFramework/Genie.jl) from Stipple Ecosystem 7 | 8 | 9 | 10 | 11 | ## Set up 12 | 13 | 1. Download or clone *STIPPLEDEMOS* repo. 14 | 15 | 2. `cd ` and follow the `README.md` instructions in respective **directories** 16 | 17 | > NOTE: path_to_demos_folder_ could be IrisClustering | GermanCredits | BasicExamples | AdvancedExamples 18 | 19 | 20 | ## BasicExamples 21 | 22 | - Card Component 23 | - Date Picker Compontent 24 | - Form Component 25 | - Pie Chart 26 | - Text Example 27 | - Using Javascript methods with Stipple Julia 28 | - Reverse Input Text 29 | - Button Compontent and methods 30 | 31 | 32 | ## AdvancedExamples 33 | - Using Custom CSS for syling 34 | - Using WebCAM to capture frame 35 | - Using Contour Plot and animation for heat transfer simulation 36 | 37 | ## Iris Cluture Project 38 | 39 | ## German Credit Project 40 | -------------------------------------------------------------------------------- /ReactiveTools API/Download Demo.jl: -------------------------------------------------------------------------------- 1 | # Download Demo 2 | 3 | using Stipple, Stipple.ReactiveTools 4 | using StippleUI 5 | using DataFrames 6 | using XLSX 7 | 8 | import Stipple.opts 9 | import StippleUI.Tables.table 10 | 11 | using StippleTypedArrays 12 | using StippleDownloads 13 | 14 | function df_to_xlsx(df) 15 | io = IOBuffer() 16 | mktempdir() do d 17 | f = joinpath(d, "blend.xlsx") 18 | XLSX.writetable(f, "Blend" => df) 19 | write(io, read(f)) 20 | end 21 | take!(io) 22 | end 23 | 24 | 25 | @app begin 26 | @out table = DataTable(DataFrame(:a => [1, 1, 2, 3, 2], :b => 1:5)) 27 | @in data = TypedArray(UInt8[]) 28 | @in data64 = TypedArray(UInt64[]) 29 | @in text = "The quick brown fox jumped over the ..." 30 | 31 | @event download_text begin 32 | # download content of an app variable 33 | download_text(__model__, :text) 34 | end 35 | 36 | @event download_df begin 37 | # download an xlsx file that is generated on the fly from an app variable 38 | download_binary(__model__, df_to_xlsx(table.data), "file.xlsx"; client = event["_client"]) 39 | end 40 | end 41 | 42 | function ui() 43 | row(cell(class = "st-module", [ 44 | 45 | row([ 46 | cell(textfield(class = "q-pr-md", "Download text", :text, placeholder = "no output yet ...", :outlined, :filled, type = "textarea")) 47 | cell(table(class = "q-pl-md", :table)) 48 | ]) 49 | 50 | row([ 51 | cell(col = 1, "Without client info") 52 | cell(btn("Text File", icon = "download", @on(:click, :download_text), color = "primary", nocaps = true)) 53 | cell(col = 1, "With client info") 54 | cell(btn(class = "q-ml-lg", "Excel File", icon = "download", @on(:click, :download_df, :addclient), color = "primary", nocaps = true)) 55 | ]) 56 | ])) 57 | end 58 | 59 | @page("/", ui) 60 | 61 | up(open_browser = true) -------------------------------------------------------------------------------- /ReactiveTools API/EditableTree.jl: -------------------------------------------------------------------------------- 1 | # Demo for editing a Dict by help of a tree with templates 2 | # work in progress 3 | # to be done: 4 | # - move edit field into header 5 | # - support arrays 6 | 7 | using Stipple, StippleUI 8 | using Stipple.ReactiveTools 9 | 10 | dict(;kwargs...) = Dict(kwargs...) 11 | stringdict(; kwargs...) = Dict(zip(String.(getindex.(collect(kwargs), 1)), getindex.(collect(kwargs), 2))) 12 | 13 | const mydiv = Genie.Renderer.Html.div 14 | 15 | function dict_tree(startdict; parent = "d", name = "d") 16 | if startdict isa Dict 17 | k = keys(startdict) 18 | dict( 19 | label = name, 20 | key = parent, 21 | children = [dict_tree(startdict[i], parent = parent * "." * i, name = i) for i in k] 22 | ) 23 | elseif startdict isa Array && !isempty(startdict) 24 | if startdict[1] isa Dict 25 | for j in startdict 26 | k = keys(j) 27 | dict( 28 | label = name, 29 | key = parent, 30 | children = [dict_tree(j[i], parent = parent * "." * i, name=i) for i in k] 31 | ) 32 | end 33 | else 34 | dict( 35 | label = name, 36 | key = parent, 37 | children = [dict(label = i, key = parent * "." * i) for i in startdict]) 38 | end 39 | else 40 | dict(label = name, 41 | key = parent, 42 | value = startdict, 43 | body = startdict isa Bool ? "bool" : startdict isa Number ? "number" : "text", 44 | children = [] 45 | ) 46 | end 47 | end 48 | 49 | testdict = stringdict(test = stringdict( 50 | corge = stringdict(grault = 1), 51 | baz = "qux", 52 | foo = false) 53 | ) 54 | 55 | @appname TreeDemo 56 | 57 | @app begin 58 | @in d = deepcopy(testdict) 59 | @in tree = [dict_tree(testdict)] 60 | 61 | @in tree_selected = "" 62 | @in tree_ticked = String[] 63 | @in tree_expanded = String[] 64 | 65 | @onchange isready begin 66 | isready && @push 67 | end 68 | end 69 | 70 | @methods """ 71 | getindex: function(key) { 72 | let o = this 73 | kk = key.split('.') 74 | for(let i = 0; i < kk.length; i++){ 75 | o = o[kk[i]]; 76 | } 77 | return o 78 | }, 79 | 80 | setindex: function(key, val) { 81 | let o = this 82 | kk = key.split('.') 83 | for(let i = 0; i < kk.length - 1; i++){ 84 | o = o[kk[i]]; 85 | } 86 | o[kk[kk.length-1]] = val 87 | return val 88 | } 89 | """ 90 | 91 | function ui() 92 | row(cell( class = "st-module", [ 93 | row([tree(var"node-key" = "key", nodes = :tree, 94 | var"selected.sync" = :tree_selected, 95 | var"expanded.sync" = :tree_expanded, 96 | [ 97 | template("", var"v-slot:body-text" = "prop", [ 98 | textfield("", dense = true, label = R"prop.node.label", 99 | value = R"getindex(prop.node.key)", 100 | @on(:input, "newval => setindex(prop.node.key, newval)") 101 | ) 102 | ]), 103 | template("", var"v-slot:body-number" = "prop", [ 104 | textfield("", dense = true, label = R"prop.node.label", 105 | value = R"getindex(prop.node.key)", 106 | @on(:input, "newval => setindex(prop.node.key, 1 * newval)") 107 | ) 108 | ]), 109 | template("", var"v-slot:body-bool" = "prop", [ 110 | checkbox("", dense = true, label = R"prop.node.label", 111 | value = R"getindex(prop.node.key)", 112 | @on(:input, "newval => setindex(prop.node.key, newval)") 113 | ) 114 | ]) 115 | ] 116 | )]) 117 | 118 | mydiv(h4("Expanded: ") * "{{ tree_expanded }}") 119 | mydiv(h4("Selected: ") * "{{ tree_selected }}") 120 | mydiv(h4("Ticked: ") * "{{ tree_ticked }}") 121 | ])) 122 | end 123 | 124 | route("/") do 125 | # model defined gloablly for debugging and testing only 126 | global model 127 | model = @init 128 | page(model, ui()) |> html 129 | end 130 | 131 | up() -------------------------------------------------------------------------------- /ReactiveTools API/FileUpload.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI, Stipple.ReactiveTools 2 | using CSV, DataFrames 3 | 4 | const UPLOAD = "upload" 5 | 6 | write(joinpath(pwd(), "test.csv"), "a,b\n1,2\n10,11") 7 | 8 | # do something with files, parse them, and put in model.df... 9 | @handlers begin 10 | @out message = "" 11 | @private df = DataFrame() 12 | @out! table = DataTable() 13 | @onchange isready begin 14 | @show "App is loaded" 15 | end 16 | @onchange df table = DataTable(df) 17 | end 18 | 19 | @event :uploaded begin 20 | for f in event["files"] 21 | f["__status"] == "uploaded" || continue 22 | filepath = joinpath(pwd(), UPLOAD, f["fname"]) 23 | message *= f["fname"] * ":
first line: " * readline(filepath) * "


" 24 | if endswith(filepath, r"csv"i) 25 | df = CSV.read(filepath, DataFrame) 26 | end 27 | rm(filepath) 28 | end 29 | end 30 | 31 | @event :clear begin 32 | message = "" 33 | println(event) 34 | end 35 | 36 | function ui() 37 | [ 38 | uploader("Upload files", url = "/upload" , autoupload = true, :multiple, 39 | # the event does not transport the `files` object automatically, therefore 40 | # we preprocess the event to contain the filenames in the field `fname` 41 | # the @on macro supports a third parameter that contains a preprocessing expression. 42 | # Inspect the following line at the REPL for deeper understanding. 43 | @on(:uploaded, :uploaded, "for (let f in event.files) { event.files[f].fname = event.files[f].name }") 44 | ) 45 | 46 | card(class = "q-mt-lg q-pa-md text-white", 47 | style = "background: radial-gradient(circle, #35a2ff 0%, #014a88 100%); width: 30%", 48 | [ 49 | cardsection(Stipple.Html.div(class = "text-h5 text-white", "Upload logs")) 50 | separator() 51 | cardsection(span(v__html = :message)) 52 | ] 53 | ) 54 | 55 | btn(class = "q-mt-lg", "Clear Log", color = "primary", @on(:click, :clear)) 56 | 57 | table(class = "q-mt-lg", :table) 58 | ] |> join 59 | end 60 | 61 | 62 | @page("/", ui) 63 | 64 | 65 | route("/upload", method = POST) do 66 | files = Genie.Requests.filespayload() 67 | upload_dir = joinpath(pwd(), UPLOAD) 68 | mkpath(upload_dir) 69 | 70 | for f in files 71 | filepath = joinpath(upload_dir, f[2].name) 72 | write(filepath, f[2].data) 73 | @info "Uploading: " * f[2].name 74 | # if no model context is necessary, the data can be loaded here 75 | if endswith(filepath, r"csv"i) 76 | df = CSV.read(filepath, DataFrame) 77 | @info df 78 | end 79 | end 80 | 81 | if length(files) == 0 82 | @info "No file uploaded" 83 | end 84 | 85 | end 86 | 87 | 88 | up() -------------------------------------------------------------------------------- /ReactiveTools API/LaTeX.jl: -------------------------------------------------------------------------------- 1 | using Stipple, Stipple.ReactiveTools 2 | using StippleUI 3 | using StippleLatex 4 | 5 | # define a small formula generator 6 | function nestlist(f, a; init = nothing) 7 | T = eltype(a) 8 | list = T[] 9 | el = init 10 | for (i, x) in enumerate(a) 11 | el = i == 1 && init === nothing ? x : f(el, x) 12 | push!(list, el) 13 | end 14 | list 15 | end 16 | 17 | formula = nestlist(*, ["", raw"\sin", "^2", " x", " +", raw" \sqrt{", "a", "^2", " +", " b", "^2"]) 18 | formula[contains.(formula, "sqrt")] .*= "}" 19 | 20 | # setting up the app 21 | @app begin 22 | @in x = 0 23 | @in formula_1 = raw"\int_{a}^{b} f(x) \, dx = F(x)\Biggr|^b_a" 24 | @in formula_2 = raw"" 25 | @private p = @task 1 + 1 26 | 27 | @onchange isready begin 28 | if !istaskstarted(p) || istaskdone(p) 29 | p = @task begin 30 | println("Task started") 31 | while x <= 100 32 | sleep(1) 33 | x += 1 34 | pos = x < 6 ? 1 : (x - 5) % (length(formula) + 5) + 1 35 | formula_2 = formula[min(pos, length(formula))] 36 | end 37 | end 38 | schedule(p) 39 | end 40 | end 41 | end 42 | 43 | function ui() 44 | [ 45 | row(cell(class = "st-module", [ 46 | cell(h1(latex("\\LaTeX") * "-Demo")) 47 | cell(h2(latex"a^2 + b^2 = c^2")) 48 | ])) 49 | 50 | row(cell(class = "st-module", [ 51 | textfield("Enter your LaTeX-Forumla", :formula_1,) 52 | cell(class = "q-pa-md", latex":formula_1"display) 53 | row([ 54 | cell(class = "q-pa-md bg-red-1", raw"""cell(latex"\cos^2x"display)""") 55 | cell(class = "q-pa-md bg-green-1", latex"\cos^2x"display) 56 | ]) 57 | row([ 58 | cell(class = "q-pa-md bg-red-1", raw"""cell(latex"This is auto mode with a formula \(\cos^2x\)"auto)""") 59 | cell(class = "q-pa-md bg-green-1", latex"This is auto mode with a formula \(\cos^2x\)"auto) 60 | ]) 61 | row([ 62 | cell(class = "q-pa-md bg-red-1", raw"""latex(class = "q-pa-md", raw"\tan^2x", display = true)""") 63 | cell(class = "bg-green-1 q-pa-md", latex(class = "q-pa-md", raw"\tan^2x", display = true)) 64 | ]) 65 | h3("Wait for 5 - ", ["{{ x }}"], color = R"x >= 5 ? 'negative' : 'positive'", icon = "calculate") 66 | 67 | row([ 68 | cell(class = "q-pa-md bg-red-1", raw"""cell(class = "q-pa-md", @latex(raw"\tanh^2 y", display = R"x >= 5"))""") 69 | cell(class = "q-pa-md bg-green-1", @latex(raw"\tanh^2 y", display = R"x >= 5")) 70 | ]) 71 | 72 | 73 | row([ 74 | cell(class = "q-pa-md bg-red-1", raw"""cell(class = "q-pa-md", @latex(raw"\tanh^2 y", display = R"x >= 5"))""") 75 | cell(class = "q-pa-md bg-green-1", @latex(raw"This is auto mode with an inline formula \(\cos^2x\) and a display formula $$\sin^2x$$", auto = true)) 76 | ]) 77 | ])) 78 | 79 | row(cell(class = "st-module", [ 80 | textfield(class = "q-pa-lg", "LaTeX", :formula_2) 81 | cell(class = "q-pa-md", "Result:") 82 | cell(class = "q-pa-md", latex":formula_2"display) 83 | ])) 84 | ] 85 | end 86 | 87 | route("/") do 88 | page(@init, ui) |> html 89 | end 90 | 91 | up() -------------------------------------------------------------------------------- /ReactiveTools API/ScrollArea Demo.jl: -------------------------------------------------------------------------------- 1 | # ScrollArea Demo 2 | 3 | using Stipple, Stipple.ReactiveTools 4 | using StippleUI 5 | 6 | @appname ScrollDemo 7 | 8 | @app begin 9 | @in position = 300 10 | @in scroll = false 11 | 12 | @onbutton scroll begin 13 | run(__model__, "this.\$refs.scrollArea.setScrollPosition($position, 500)") 14 | position = rand(0:1000) 15 | end 16 | end 17 | 18 | ui() = [ 19 | heading("Scroll Area Demo" * h4("with server-side and client-side update")) 20 | 21 | row(cell(class = "st-module", [ 22 | 23 | row(class = "row q-gutter-md q-mb-md", [ 24 | btn(R"`Scroll to ${position}px`", color = "primary", @click(raw"() => this.$refs.scrollArea.setScrollPosition(position)")) 25 | btn(R"`Animate to ${position}px`", color = "primary", @click(:scroll)) 26 | ]) 27 | 28 | scrollarea(ref = "scrollArea", style = "height: 150px; max-width: 300px;", 29 | ol( 30 | li(@recur("n in 1000"), key! = "n", "Lorem ipsum dolor sit amet, consectetur adipisicing elit.") 31 | ) 32 | ) 33 | ])) 34 | ] 35 | 36 | route("/") do 37 | model = @init 38 | page(model, ui()) |> html 39 | end 40 | 41 | up(open_browser = true) 42 | 43 | -------------------------------------------------------------------------------- /ReactiveTools API/TypedArrays Demo.jl: -------------------------------------------------------------------------------- 1 | # Download Demo 2 | 3 | using Stipple, Stipple.ReactiveTools 4 | using StippleUI 5 | 6 | import Stipple.opts 7 | import StippleUI.Tables.table 8 | 9 | using StippleTypedArrays 10 | using StippleDownloads 11 | 12 | @deps StippleTypedArrays 13 | 14 | @app begin 15 | @in data = TypedArray(UInt8[]) 16 | @in data64 = TypedArray(UInt64[]) 17 | 18 | @in add_data = false 19 | @in clear_data = false 20 | @in hello_world = false 21 | 22 | @onbutton add_data begin 23 | x = rand(0:255) 24 | push!(data, x) 25 | notify(data) 26 | push!(data64, x + 1000) 27 | notify(data64) 28 | end 29 | 30 | @onbutton clear_data begin 31 | data = data64 = [] 32 | end 33 | end 34 | 35 | function ui() 36 | row(cell(class = "st-module q-ma-md", [ 37 | 38 | row(class = "q-pa-md bg-green-2", "Data: [{{ data }}]") 39 | row(class = "q-pa-md q-my-lg bg-green-4", "Data64: [{{ data64 }}]") 40 | 41 | row([ 42 | btn("Add data", icon = "add", @click(:add_data), color = "primary", nocaps = true) 43 | btn(class = "q-ml-lg", "Clear data", icon = "delete_forever", @click(:clear_data), color = "primary", nocaps = true) 44 | ]) 45 | ])) 46 | end 47 | 48 | route("/") do 49 | global model 50 | model = @init 51 | page(model, ui()) |> html 52 | end 53 | 54 | up(open_browser = true) 55 | 56 | 57 | # other possible uses: 58 | 59 | # model.data[] = [1, 2, 3] 60 | # model.data[2] += 10 61 | 62 | # model.data64[] = [30, 20, 10] 63 | # model.data64[2] += 10 64 | -------------------------------------------------------------------------------- /StipplePlotly/3DPlots/Plot3d.jl: -------------------------------------------------------------------------------- 1 | using Genie, Genie.Renderer.Html, Stipple, StipplePlotly 2 | 3 | t = collect(1:1:10) 4 | 5 | pd1 = PlotData( 6 | x = cos.(t), 7 | y = sin.(t), 8 | z = t, 9 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER3D, # for 3D plots, 10 | name = "test", 11 | ) 12 | 13 | plot_data = [pd1] 14 | 15 | @reactive! mutable struct Model <: ReactiveModel 16 | data::R{Vector{PlotData}} = plot_data, READONLY 17 | layout::R{PlotLayout} = PlotLayout( 18 | plot_bgcolor = "#999", 19 | showlegend = false, 20 | title = PlotLayoutTitle(text = "Random numbers", font = Font(24)), 21 | ) 22 | config::R{PlotConfig} = PlotConfig() 23 | end 24 | 25 | function ui(model) 26 | page(model, class = "container", [plot(:data, layout = :layout, config = :config)]) 27 | end 28 | 29 | route("/") do 30 | model = Model |> init |> ui |> html 31 | end 32 | 33 | up() -------------------------------------------------------------------------------- /StipplePlotly/HeatMaps/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" 3 | Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" 4 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 5 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 6 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 7 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 8 | 9 | [compat] 10 | Genie = "4" 11 | Stipple = "0.16, 0.17" 12 | StipplePlotly = "0.6, 0.7" 13 | -------------------------------------------------------------------------------- /StipplePlotly/HeatMaps/heatmap1.jl: -------------------------------------------------------------------------------- 1 | using Genie, Genie.Renderer.Html, Stipple, StipplePlotly, Colors, ColorSchemes 2 | import ColorSchemes: colorschemes 3 | 4 | Genie.config.log_requests = false 5 | 6 | plotly_palette = ["Greys", "YlGnBu", "Greens", "YlOrRd", "Bluered", "RdBu", "Reds", "Blues", "Picnic", "Rainbow", "Portland", "Jet", "Hot", "Blackbody", "Earth", "Electric", "Viridis", "Cividis"] 7 | 8 | function rgb(pix::RGB{Float64}) 9 | R = round(Int, 255 * clamp(pix.r, 0.0, 1.0)) 10 | G = round(Int, 255 * clamp(pix.g, 0.0, 1.0)) 11 | B = round(Int, 255 * clamp(pix.b, 0.0, 1.0)) 12 | return "rgb($R,$G,$B)" 13 | end 14 | 15 | # See: https://juliagraphics.github.io/ColorSchemes.jl/stable/basics/ 16 | function ColorScale(scheme::Symbol, N = 101) 17 | x = permutedims(0.0:(1.0/(N - 1)):1.0) 18 | cs = get(colorschemes[scheme], x, :clamp) 19 | cs_rgb = rgb.(cs) 20 | return vcat(x, cs_rgb) 21 | end 22 | 23 | function to_plotly_standard(x) 24 | if isnothing(x) 25 | m = "null" 26 | elseif ismissing(x) 27 | m = "null" 28 | elseif isa(x, AbstractString) 29 | m = x 30 | elseif isinf(x) 31 | m = "null" 32 | elseif isnan(x) 33 | m = "null" 34 | else 35 | m = x 36 | end 37 | return m 38 | end 39 | 40 | pd(name) = PlotData( 41 | z = to_plotly_standard.(round.([100.0 0.0 missing; 42 | 30.30235455584154 94.9495352852204 0.7150792978758869; 43 | 63.172349721957175 37 -72.02122607433563; 44 | 51.19876097916769 18.676411380287483 314.15; 45 | 87.03992589163836 88.74217734803698 62.122844059857215]; sigdigits=3)), 46 | zmin = 0.0, 47 | zmax = 100.0, 48 | x = ["L0", "L63", "L128", "L191", "L255"], 49 | y = ["L255", "L128", "L0"], 50 | plot = StipplePlotly.Charts.PLOT_TYPE_HEATMAP, 51 | name = name, 52 | colorscale = ColorScale(:algae), 53 | colorbar = ColorBar("Z-data", 18, "right"), 54 | hoverongaps = false, 55 | hoverinfo = "x+y+z" 56 | ) 57 | 58 | pl(title) = PlotLayout( 59 | plot_bgcolor = "#FFFFFF", 60 | title = PlotLayoutTitle(text=title, font=Font(24)), 61 | margin_b = 25, 62 | margin_t = 80, 63 | margin_l = 80, 64 | margin_r = 40, 65 | xaxis = [PlotLayoutAxis(xy = "x", index = 1, 66 | title = "From", 67 | font = Font(18), 68 | ticks = "outside top", 69 | side = "top", 70 | position = 1.0, 71 | showline = true, 72 | showgrid = false, 73 | zeroline = false, 74 | mirror = "all", 75 | ticklabelposition = "outside top")], 76 | yaxis = [PlotLayoutAxis(xy = "y", index = 1, 77 | showline = true, 78 | zeroline = false, 79 | mirror = "all", 80 | showgrid = false, 81 | title = "To", 82 | font = Font(18), 83 | ticks = "outside", 84 | scaleanchor = "x", 85 | scaleratio = 1, 86 | constrain = "domain", 87 | constraintoward = "top")], 88 | ) 89 | 90 | Base.@kwdef mutable struct Model <: ReactiveModel 91 | data::R{Vector{PlotData}} = [pd("Random 1")], READONLY 92 | layout::R{PlotLayout} = pl(""), READONLY 93 | config::R{PlotConfig} = PlotConfig(), READONLY 94 | end 95 | 96 | model = Stipple.init(Model()) 97 | 98 | function ui() 99 | page( 100 | vm(model), class="container", [ 101 | plot(:data, layout = :layout, config = :config) 102 | ] 103 | ) |> html 104 | end 105 | 106 | route("/", ui) 107 | 108 | up() -------------------------------------------------------------------------------- /StipplePlotly/HeatMaps/heatmap2.jl: -------------------------------------------------------------------------------- 1 | using Genie, Genie.Renderer.Html, Stipple, StipplePlotly, Colors, ColorSchemes 2 | import ColorSchemes: colorschemes 3 | 4 | Genie.config.log_requests = false 5 | 6 | function rgb(pix::RGB{Float64}) 7 | R = round(Int, 255 * clamp(pix.r, 0.0, 1.0)) 8 | G = round(Int, 255 * clamp(pix.g, 0.0, 1.0)) 9 | B = round(Int, 255 * clamp(pix.b, 0.0, 1.0)) 10 | return "rgb($R,$G,$B)" 11 | end 12 | 13 | # See: https://juliagraphics.github.io/ColorSchemes.jl/stable/basics/ 14 | function ColorScale(scheme::Symbol, N = 101) 15 | x = permutedims(0.0:(1.0/(N - 1)):1.0) 16 | cs = get(colorschemes[scheme], x, :clamp) 17 | cs_rgb = rgb.(cs) 18 | return vcat(x, cs_rgb) 19 | end 20 | 21 | function to_plotly_standard(x) 22 | if isnothing(x) 23 | m = "null" 24 | elseif ismissing(x) 25 | m = "null" 26 | elseif isa(x, AbstractString) 27 | m = x 28 | elseif isinf(x) 29 | m = "null" 30 | elseif isnan(x) 31 | m = "null" 32 | else 33 | m = x 34 | end 35 | return m 36 | end 37 | 38 | xx = ["L0", "L63", "L128", "L191", "L255"] 39 | yy = ["L255", "L128", "L0"] 40 | zz = [ 100.0 0.0 missing; 41 | 31.30235455584154 94.9495352852204 0.7150792978758869; 42 | 63.172349721957175 37 -72.02122607433563; 43 | 51.19876097916769 18.676411380287483 314.15; 44 | 87.03992589163836 88.74217734803698 62.122844059857215]; 45 | 46 | cs = :algae # color scheme 47 | 48 | function textcolor_mask(z,zmin,zmax,cs; halfluminance=(0.5^2.2)) 49 | m = zeros(Float64, size(z)) 50 | for i=1:length(m) 51 | if !ismissing(z[i]) 52 | c = get(colorschemes[cs], Float64(z[i]), (zmin,zmax)) 53 | if xyY(c).Y < halfluminance 54 | m[i] = 1.0 55 | end 56 | end 57 | end 58 | m 59 | end 60 | 61 | function annotate_map(x, y, z, zmin, zmax, cs) 62 | m = textcolor_mask(z,zmin,zmax,cs; halfluminance=(0.5^2.2)) 63 | anv = Vector{PlotAnnotation}(undef, 0) 64 | anv = PlotAnnotation[] 65 | for i=1:size(z,1) 66 | for j=1:size(z,2) 67 | c = rgb(RGB(m[i,j], m[i,j], m[i,j])) 68 | an = PlotAnnotation(visible=!ismissing(z[i,j]), x=x[i], y=y[j], xref="x", yref="y", text= "$(round(z[i,j]; sigdigits=3))", ax=0, ay=0, showarrow=false, font=Font(color=c, size=16) ) 69 | push!(anv, an) 70 | end 71 | end 72 | anv 73 | end 74 | 75 | anv = annotate_map(xx, yy, zz, 0.0, 100.0, cs); 76 | 77 | pd(name, x, y, z; zmin = 0.0, zmax = 100.0, cs = cs) = PlotData( 78 | z = to_plotly_standard.(round.(z; sigdigits=3)), 79 | zmin = zmin, 80 | zmax = zmax, 81 | x = x, 82 | y = y, 83 | plot = StipplePlotly.Charts.PLOT_TYPE_HEATMAP, 84 | name = name, 85 | colorscale = ColorScale(cs), 86 | colorbar = ColorBar("Z-data", 18, "right"), 87 | hoverongaps = false, 88 | hoverinfo = "x+y+z" 89 | ) 90 | 91 | pl(title; annotations::Union{Nothing, Vector{PlotAnnotation}} = nothing) = PlotLayout( 92 | plot_bgcolor = "#FFFFFF", 93 | title = PlotLayoutTitle(text=title, font=Font(24)), 94 | margin_b = 25, 95 | margin_t = 80, 96 | margin_l = 60, 97 | margin_r = 40, 98 | xaxis = [PlotLayoutAxis(xy = "x", index = 1, 99 | title = "From", 100 | font = Font(18), 101 | ticks = "outside top", 102 | side = "top", 103 | position = 1.0, 104 | showline = true, 105 | showgrid = false, 106 | zeroline = false, 107 | mirror = "all", 108 | ticklabelposition = "outside top")], 109 | yaxis = [PlotLayoutAxis(xy = "y", index = 1, 110 | showline = true, 111 | zeroline = false, 112 | mirror = "all", 113 | showgrid = false, 114 | title = "To", 115 | font = Font(18), 116 | ticks = "outside", 117 | scaleanchor = "x", 118 | scaleratio = 1, 119 | constrain = "domain", 120 | constraintoward = "top")], 121 | annotations = annotations 122 | ) 123 | 124 | Base.@kwdef mutable struct Model <: ReactiveModel 125 | data::R{Vector{PlotData}} = [pd("Random 1", xx, yy, zz)], READONLY 126 | layout::R{PlotLayout} = pl(""; annotations=anv), READONLY 127 | config::R{PlotConfig} = PlotConfig(), READONLY 128 | end 129 | 130 | model = Stipple.init(Model()) 131 | 132 | function ui() 133 | page( 134 | vm(model), class="container", [ 135 | plot(:data, layout = :layout, config = :config) 136 | ] 137 | ) |> html 138 | end 139 | 140 | route("/", ui) 141 | 142 | up() -------------------------------------------------------------------------------- /StipplePlotly/PlotData.jl: -------------------------------------------------------------------------------- 1 | using Genie, Genie.Renderer.Html, Stipple, StipplePlotly 2 | 3 | pd(name) = PlotData( 4 | x = [ 5 | "Jan2019", 6 | "Feb2019", 7 | "Mar2019", 8 | "Apr2019", 9 | "May2019", 10 | "Jun2019", 11 | "Jul2019", 12 | "Aug2019", 13 | "Sep2019", 14 | "Oct2019", 15 | "Nov2019", 16 | "Dec2019", 17 | ], 18 | y = Int[rand(1:100_000) for x = 1:12], 19 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 20 | name = name, 21 | ) 22 | 23 | @reactive mutable struct Model <: ReactiveModel 24 | data::R{Vector{PlotData}} = [pd("Random 1"), pd("Random 2")] 25 | layout::R{PlotLayout} = PlotLayout( 26 | plot_bgcolor = "#333", 27 | title = PlotLayoutTitle(text = "Random numbers", font = Font(24)), 28 | ) 29 | config::R{PlotConfig} = PlotConfig() 30 | end 31 | 32 | function ui(model) 33 | page(model, class = "container", [plot(:data, layout = :layout, config = :config)]) 34 | end 35 | 36 | route("/") do 37 | Stipple.init(Model) |> ui |> html 38 | end 39 | -------------------------------------------------------------------------------- /StipplePlotly/PlotData2.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | pd(name) = PlotData( 4 | x = ["Jan2019", "Feb2019", "Mar2019", "Apr2019", "May2019", 5 | "Jun2019", "Jul2019", "Aug2019", "Sep2019", "Oct2019", 6 | "Nov2019", "Dec2019"], 7 | y = Int[rand(1:100_000) for x in 1:12], 8 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 9 | name = name 10 | ) 11 | 12 | @reactive! mutable struct Model <: ReactiveModel 13 | data::R{Vector{PlotData}} = [pd("Random 1"),pd("Random 2")] 14 | layout::R{PlotLayout} = PlotLayout( 15 | plot_bgcolor = "#333", 16 | title = PlotLayoutTitle(text="Random numbers", font=Font(24)) 17 | ) 18 | config::R{PlotConfig} = PlotConfig() 19 | end 20 | 21 | function ui(model) 22 | page(model, class="container", [ 23 | plot(:data, layout = :layout, config = :config) 24 | ] 25 | ) |> html 26 | end 27 | 28 | route("/") do 29 | Stipple.init(Model) |> ui 30 | end 31 | 32 | up() 33 | -------------------------------------------------------------------------------- /StipplePlotly/PlotEventQuery/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 4 | -------------------------------------------------------------------------------- /StipplePlotly/PlotEventQuery/app.jl: -------------------------------------------------------------------------------- 1 | using Stipple, Stipple.ReactiveTools 2 | using StippleUI 3 | using StipplePlotly 4 | using PlotlyBase 5 | 6 | # format => [lat, lon] 7 | cities = [ 8 | (51.5074, -0.1278), # London 9 | (40.7128, -74.0060), # New York 10 | (35.6895, 139.6917), # Tokyo 11 | (-33.8688, 151.2093), # Sydney 12 | (37.7749, -122.4194), # San Francisco 13 | (19.4326, -99.1332) # Mexico City 14 | ] 15 | 16 | # Define the two cities to connect 17 | city1 = 1 # London 18 | city2 = 3 # Tokyo 19 | 20 | # Create a trace for the cities with markers 21 | trace_cities = scattergeo( 22 | locationmode="ISO-3", 23 | lon=[city[2] for city in cities], 24 | lat=[city[1] for city in cities], 25 | mode="markers", 26 | marker=attr(size=10, color="blue") 27 | ) 28 | 29 | # Create a trace for the line between the two selected cities 30 | trace_line = scattergeo( 31 | locationmode="ISO-3", 32 | lon=[cities[city1][2], cities[city2][2]], 33 | lat=[cities[city1][1], cities[city2][1]], 34 | mode="lines", 35 | line=attr(width=2, color="red") 36 | ) 37 | 38 | 39 | # Create the layout 40 | mylayout = PlotlyBase.Layout( 41 | title="Connecting two cities", 42 | geo=attr( 43 | projection=attr(type="natural earth"), 44 | showland=true, showcountries=true, 45 | landcolor="#EAEAAE", countrycolor="#444444" 46 | ) 47 | ) 48 | 49 | # mylayout = PlotlyBase.Layout(title="Travelling Salesman Problem - Random Cities") 50 | 51 | myconfig = PlotlyBase.PlotConfig() 52 | 53 | # @app begin 54 | # @out appData = [trace_cities, trace_line] 55 | # @out appLayout = mylayout 56 | # @out appConfig = myconfig 57 | # end 58 | 59 | @app ARModel begin 60 | @out data = [trace_cities, trace_line] 61 | @out appLayout = mylayout 62 | @out appConfig = myconfig 63 | @mixin data::PlotlyEvents 64 | 65 | @onchange data_click begin 66 | println("plot clicked") 67 | @notify "hi" 68 | # remove point from cities 69 | # latitude = Float64(data_click["points"]["lat"]) 70 | # longitude = Float64(data_click["points"]["lon"]) 71 | 72 | @show data_click 73 | @info typeof(data_click) 74 | @info keys(data_click) 75 | 76 | @info data_click["points"] 77 | @info keys(data_click["points"]) 78 | end 79 | 80 | @onchange data_hover begin 81 | println("plot hovered") 82 | @info data_hover 83 | end 84 | 85 | @onchange data_selected begin 86 | println("plot selected") 87 | @info data_selected 88 | end 89 | 90 | model 91 | end 92 | 93 | @mounted ARModel watchplots() 94 | 95 | UI = Ref{Any}() 96 | 97 | UI[] = [ 98 | h1("GenieFramework 🧞 TSP example 🚗") 99 | plot(:data, layout=:appLayout, config=:appConfig, syncevents=true) 100 | ] 101 | 102 | ui() = UI[] 103 | 104 | route("/") do 105 | global model 106 | model = ARModel |> init |> handlers 107 | page(model, ui()) |> html 108 | end 109 | 110 | up() 111 | 112 | -------------------------------------------------------------------------------- /StipplePlotly/PlotlyBaseBar.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using PlotlyBase 3 | using StipplePlotly 4 | 5 | trace1 = PlotlyBase.bar(;x=["giraffes", "orangutans", "monkeys"], 6 | y=[20, 14, 23], 7 | name="SF Zoo", 8 | marker=attr(color="gray")) 9 | 10 | trace2 = PlotlyBase.bar(x=["giraffes", "orangutans", "monkeys"], 11 | y=[12, 18, 29], 12 | name="LA Zoo") 13 | 14 | @reactive! mutable struct BarPlot <: ReactiveModel 15 | data::R{Vector{GenericTrace}} = [trace1, trace2] 16 | 17 | my_layout::R{PlotlyBase.Layout} = PlotlyBase.Layout(;barmode="stack") 18 | 19 | my_config::R{PlotlyBase.PlotConfig} = PlotlyBase.PlotConfig() 20 | end 21 | 22 | function ui(model::Example) 23 | page(model, class="container", [ 24 | plot(:data, layout=:my_layout, config=:my_config) 25 | ]) 26 | end 27 | 28 | route("/") do 29 | BarPlot |> init |> ui |> html 30 | end 31 | 32 | up(8800) 33 | -------------------------------------------------------------------------------- /StipplePlotly/PlotlyBaseDemo.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | using PlotlyBase 3 | 4 | @reactive! mutable struct Example <: ReactiveModel 5 | plot::R{Plot} = Plot() 6 | end 7 | 8 | function ui(model::Example) 9 | page(model, class = "container", [ 10 | plot("plot.data", layout = "plot.layout", config = "plot.config") 11 | ]) 12 | end 13 | 14 | model = init(Example, debounce=0) 15 | route("/") do 16 | model |> ui |> html 17 | end 18 | 19 | up(8800) 20 | 21 | for i in 1:30 22 | model.plot[] = Plot(rand(100,2)) 23 | sleep(0.1) 24 | end -------------------------------------------------------------------------------- /StipplePlotly/PlotlyBaseEvents.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | using PlotlyBase 3 | 4 | @reactive! mutable struct Example <: ReactiveModel 5 | plot1::R{Plot} = Plot() 6 | plot1_selected::R{Dict{String, Any}} = Dict{String, Any}() 7 | plot1_hover::R{Dict{String, Any}} = Dict{String, Any}() 8 | 9 | plot2::R{Plot} = Plot() 10 | plot2_selected::R{Dict{String, Any}} = Dict{String, Any}() 11 | plot2_hover::R{Dict{String, Any}} = Dict{String, Any}() 12 | end 13 | 14 | Genie.Router.delete!(:Example) 15 | 16 | function ui(model::Example) 17 | page(model, class = "container", 18 | # append = script([ 19 | # watchplot("plot1", model), 20 | # watchplot("plot2", model) 21 | # ]), 22 | row(class = "st-module", [ 23 | plotly(:plot1, id = "plot1"), 24 | plotly(:plot2, id = "plot2") 25 | ])) 26 | end 27 | 28 | Stipple.js_mounted(::Example) = join([ 29 | watchplot(:plot1), 30 | watchplot(:plot2) 31 | ]) 32 | 33 | model = init(Example, debounce=0) 34 | 35 | route("/") do 36 | model |> handlers |> ui |> html 37 | end 38 | 39 | function handlers(model) 40 | on(model.isready) do isready 41 | isready || return 42 | push!(model) 43 | end 44 | 45 | on(model.plot1_selected) do data 46 | model.plot2.data[1][:selectedpoints] = getindex.(data["points"], "pointIndex") 47 | notify(model.plot2) 48 | end 49 | 50 | on(model.plot2_selected) do data 51 | model.plot1.data[1][:selectedpoints] = getindex.(data["points"], "pointIndex") 52 | notify(model.plot1) 53 | end 54 | 55 | on(model.plot1_selected) do data 56 | haskey(data, "points") && @info "Selection: $(getindex.(data["points"], "pointIndex"))" 57 | end 58 | 59 | return model 60 | end 61 | 62 | up(8000) 63 | 64 | for i in 1:3 65 | model.plot1[] = Plot(scatter(y = rand(5))) 66 | model.plot2[] = Plot(scatter(y = rand(5))) 67 | sleep(0.1) 68 | end -------------------------------------------------------------------------------- /StipplePlotly/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" 3 | Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" 4 | Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e" 5 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 6 | StipplePlotly = "ec984513-233d-481d-95b0-a3b58b97af2b" 7 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 8 | 9 | [compat] 10 | Genie = "4.18.1" 11 | Stipple = "0.23" 12 | StipplePlotly = "0.11" 13 | -------------------------------------------------------------------------------- /StipplePlotly/README.md: -------------------------------------------------------------------------------- 1 | # Plotly components using [Stipple](https://github.com/GenieFramework/Stipple.jl), [StippleUI](https://github.com/GenieFramework/StipplePlotly.jl) from Stipple Ecosystem 2 | 3 | ## Run Demo 4 | ```julia 5 | julia> julia --project 6 | julia> #enter package mode with ] 7 | (@v1.x) pkg> activate . 8 | (@v1.x) pkg> instantiate 9 | (@v1.x) pkg> #exit package mode with 10 | julia> include("subplots2x2.jl") 11 | julia> # app running at http://127.0.0.1:8000 12 | julia> down() # stop the running async instance of Genie Server 13 | ``` 14 | 15 | > If you want to change code and want JULIA to automatically reflect changes in Web Page use `Revise`. `Revise` is already included in `Project.toml`. Change `your code` hit save in editor and refresh browser should reflect your changes 16 | ### How to use Revise? 17 | ```julia 18 | ---- same as above ----- 19 | (@v1.x) pkg> add Revise 20 | (@v1.x) pkg> #exit package mode with 21 | julia> using Revise 22 | julia> includet("subplots2x2.jl") # notice we are using **includet** from revise instead of include 23 | julia> # app running at http://127.0.0.1:8000 24 | julia> down() # stop the running async instance of Genie Server 25 | ``` 26 | 27 | ### Form Compontent 28 | 29 | | Components | Demo | 30 | |-------------------------|------------------------------------| 31 | | **Plot3d** | ![Plot](docs/content/img/152349703-0038c536-4077-45ce-9ee7-c38837e9f4a7.gif) | 32 | | **Lines Map** | ![Plot](docs/content/img/lines.png) | 33 | | **Choropleth Map** | ![Plot](docs/content/img/choropleth.png) | 34 | | **Scatter Geo** | ![Plot](docs/content/img/scattergeo.png) | 35 | | **PlotData** | ![Plot](docs/content/img/PlotData.png) | 36 | | **SubPlots 2x2**| ![Plot](docs/content/img/MixedSubplots.png) | 37 | 38 | -------------------------------------------------------------------------------- /StipplePlotly/ReactivePointRowSelection.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI, StipplePlotly, PlotlyBase, DataFrames 2 | import Stipple.table 3 | 4 | register_mixin(@__MODULE__) 5 | 6 | df = DataFrame(a = [1, 2, 4, 6, 8, 10], b = ["Hello", "world", ",", "hello", "sun", "!"]) 7 | pl = PlotlyBase.Plot(scatter(x = df.a, text = df.b)) 8 | datatable = DataTable(df) 9 | 10 | @reactive! struct TableDemo <: ReactiveModel 11 | @mixin table::DataTableWithSelection(var"" = DataTable(copy(df))) 12 | @mixin plot::PBPlotWithEvents(var"" = copy(pl)) 13 | showplot::R{Bool} = true 14 | end 15 | 16 | Genie.Router.delete!(:TableDemo) 17 | Stipple.js_mounted(::TableDemo) = watchplots() 18 | 19 | function handlers(model) 20 | on(model.isready) do isready 21 | isready || return 22 | push!(model) 23 | end 24 | 25 | on(model.plot_selected) do data 26 | selectrows!(model, :table, getindex.(data["points"], "pointIndex") .+ 1) 27 | end 28 | 29 | # the commented lines show a version that changes data on the backend first and then 30 | # updates the full plot. That redraws the plot completely and resets the mode to default. 31 | # So you will be in zoom-mode again, even if you were in select-mode before. 32 | # The active version needs at least Stipple v0.24.2 33 | on(model.table_selection) do selection 34 | ii = getindex.(selection, "__id") .- 1 35 | model["plot.data[0].selectedpoints"] = isempty(ii) ? nothing : ii 36 | # model.plot.data[1][:selectedpoints] = isempty(ii) ? nothing : ii 37 | 38 | notify(model, js"plot.data") 39 | # notify(model.plot) 40 | end 41 | 42 | return model 43 | end 44 | 45 | function ui(model) 46 | page( 47 | model, 48 | title = "Hello Stipple", 49 | row(cell(class = "st-module",[ 50 | table(:table, selection = "multiple", var":selected.sync" = "table_selection", pagination = :table_pagination) 51 | toggle("Show plot", :showplot) 52 | plotly(:plot, @iif("showplot"), syncevents = true) 53 | ])), 54 | @iif(isready) 55 | ) 56 | end 57 | 58 | route("/") do 59 | # global model definition is for debugging/testing purpose only 60 | global model 61 | model = init(TableDemo, debounce = 0) 62 | model |> handlers |> ui |> html 63 | end 64 | 65 | up() -------------------------------------------------------------------------------- /StipplePlotly/docs/content/img/152349703-0038c536-4077-45ce-9ee7-c38837e9f4a7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/StipplePlotly/docs/content/img/152349703-0038c536-4077-45ce-9ee7-c38837e9f4a7.gif -------------------------------------------------------------------------------- /StipplePlotly/docs/content/img/MixedSubplots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/StipplePlotly/docs/content/img/MixedSubplots.png -------------------------------------------------------------------------------- /StipplePlotly/docs/content/img/PlotData.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/StipplePlotly/docs/content/img/PlotData.png -------------------------------------------------------------------------------- /StipplePlotly/docs/content/img/choropleth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/StipplePlotly/docs/content/img/choropleth.png -------------------------------------------------------------------------------- /StipplePlotly/docs/content/img/lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/StipplePlotly/docs/content/img/lines.png -------------------------------------------------------------------------------- /StipplePlotly/docs/content/img/scattergeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenieFramework/StippleDemos/9795ba5be0e343a7061aae48ecf12093bb6ca7bf/StipplePlotly/docs/content/img/scattergeo.png -------------------------------------------------------------------------------- /StipplePlotly/example1.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI, StipplePlotly 2 | 3 | #=== config ==# 4 | 5 | for m in [Genie, Stipple, StippleUI, StipplePlotly] 6 | m.assets_config.host = "https://cdn.statically.io/gh/GenieFramework" 7 | end 8 | 9 | # WEB_TRANSPORT = Genie.WebChannels #Genie.WebThreads # 10 | 11 | #== data ==# 12 | 13 | pd(name) = PlotData( 14 | x = ["Jan2019", "Feb2019", "Mar2019", "Apr2019", "May2019", 15 | "Jun2019", "Jul2019", "Aug2019", "Sep2019", "Oct2019", 16 | "Nov2019", "Dec2019"], 17 | y = Int[rand(1:100_000) for x in 1:12], 18 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 19 | name = name 20 | ) 21 | 22 | #== reactive model ==# 23 | 24 | @reactive! mutable struct Model <: ReactiveModel 25 | data::R{Vector{PlotData}} = [pd("Random 1"),pd("Random 2")] 26 | layout::R{PlotLayout} = PlotLayout( 27 | plot_bgcolor = "#333", 28 | title = PlotLayoutTitle(text="Random numbers", font=Font(24)) 29 | ) 30 | config::R{PlotConfig} = PlotConfig() 31 | end 32 | 33 | #== ui ==# 34 | 35 | function ui(model) 36 | page(model, 37 | class="container", [ 38 | heading("Plotly example") 39 | 40 | row([ 41 | cell(class="st-module", [ 42 | h6("Plot of random values") 43 | plot(:data, layout = :layout, config = :config) 44 | ]) 45 | ]) 46 | ] 47 | ) 48 | end 49 | 50 | #== server ==# 51 | 52 | route("/") do 53 | Stipple.init(Model) |> ui |> html 54 | end 55 | 56 | up() 57 | -------------------------------------------------------------------------------- /StipplePlotly/example1_newapi.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | 4 | pd(name) = PlotData( 5 | x = ["Jan2019", "Feb2019", "Mar2019", "Apr2019", "May2019", 6 | "Jun2019", "Jul2019", "Aug2019", "Sep2019", "Oct2019", 7 | "Nov2019", "Dec2019"], 8 | y = Int[rand(1:100_000) for x in 1:12], 9 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 10 | name = name 11 | ) 12 | 13 | @handlers begin 14 | @out data = [pd("Random 1"),pd("Random 2")] 15 | @out layout = PlotLayout(plot_bgcolor = "#333", title = PlotLayoutTitle(text="Random numbers", font=Font(24))) 16 | @out config = PlotConfig() 17 | end 18 | 19 | function ui() 20 | [ 21 | plot(:data, layout = :layout, config = :config) 22 | ] |> join 23 | end 24 | 25 | @page("/", ui) 26 | 27 | up() 28 | -------------------------------------------------------------------------------- /StipplePlotly/example2.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | pd(name) = PlotData( 4 | x = ["Jan2019", "Feb2019", "Mar2019", "Apr2019", "May2019", 5 | "Jun2019", "Jul2019", "Aug2019", "Sep2019", "Oct2019", 6 | "Nov2019", "Dec2019"], 7 | y = Int[rand(1:100_000) for x in 1:12], 8 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 9 | name = name 10 | ) 11 | 12 | @reactive! mutable struct Model <: ReactiveModel 13 | data::R{Vector{PlotData}} = [pd("Random 1"),pd("Random 2")] 14 | layout::R{PlotLayout} = PlotLayout( 15 | plot_bgcolor = "#333", 16 | title = PlotLayoutTitle(text="Random numbers", font=Font(24)) 17 | ) 18 | config::R{PlotConfig} = PlotConfig() 19 | end 20 | 21 | function ui(model) 22 | page(model, class="container", [ 23 | plot(:data, layout = :layout, config = :config) 24 | ] 25 | ) |> html 26 | end 27 | 28 | route("/") do 29 | Stipple.init(Model) |> ui 30 | end 31 | 32 | up() 33 | -------------------------------------------------------------------------------- /StipplePlotly/groupedbar1.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | # for m in [Genie, Stipple, StipplePlotly] 4 | # m.assets_config.host = "https://cdn.statically.io/gh/GenieFramework" 5 | # end 6 | 7 | xx = ["start", "1d", "1w", "2w", "6w"] 8 | spaces = " " 9 | 10 | # Data: 11 | 12 | y1 = [98.5, 96.0, 95.1, 94.4, 94.0] 13 | pb1 = PlotData( 14 | x = xx, y = y1, xaxis="x", yaxis="y", 15 | error_y = ErrorBar(3 .* ones(Float64,size(y1)); color="rgba(0,0,0,0.6)"), 16 | plot = StipplePlotly.Charts.PLOT_TYPE_BAR, name = "Product 1", 17 | text = spaces .* string.(y1), textposition="auto" 18 | ) 19 | 20 | y2 = [99.5, 94.0, 93.1, 92.4, 92.0] 21 | pb2 = PlotData( 22 | x = xx, y = y2, xaxis="x", yaxis="y", 23 | error_y = ErrorBar(3 .* ones(Float64,size(y1)); color="rgba(0,0,0,0.6)"), 24 | plot = StipplePlotly.Charts.PLOT_TYPE_BAR, name = "Product 2", 25 | text = spaces .* string.(y2), textposition="auto" 26 | ) 27 | 28 | y3 = [97.5, 92.0, 92.1, 91.4, 91.0] 29 | pb3 = PlotData( 30 | x = xx, y = y3, xaxis="x", yaxis="y", 31 | error_y = ErrorBar(3 .* ones(Float64,size(y1)); color="rgba(0,0,0,0.6)"), 32 | plot = StipplePlotly.Charts.PLOT_TYPE_BAR, name = "Product 3", 33 | text = spaces .* string.(y3), textposition="auto" 34 | ) 35 | 36 | y4 = [92.5, 88.1, 87.1, 85.9, 84.0] 37 | pb4 = PlotData( 38 | x = xx, y = y4, xaxis="x", yaxis="y", 39 | error_y = ErrorBar(3 .* ones(Float64,size(y1)); color="rgba(0,0,0,0.6)"), 40 | plot = StipplePlotly.Charts.PLOT_TYPE_BAR, name = "Product 4", 41 | text = spaces .* string.(y4), textposition="auto" 42 | ) 43 | 44 | plotdata = [pb1, pb2, pb3, pb4] 45 | 46 | layout = PlotLayout(barmode="group", font=Font(16), 47 | title = PlotLayoutTitle(text="Product comparison", font=Font(24)), 48 | xaxis = [PlotLayoutAxis(xy = "x", index = 1, title="duration", font=Font(size=24), showline = true, zeroline = false)], 49 | yaxis = [PlotLayoutAxis(xy = "y", index = 1, ticks = "outside", title="ratio (%)", font=Font(size=24), autorange=false, range=[70.0,100.0], showline = true, zeroline = false)] 50 | ) 51 | 52 | @reactive! mutable struct Model <: ReactiveModel 53 | data::R{Vector{PlotData}} = plotdata, READONLY 54 | layout::R{PlotLayout} = layout, READONLY 55 | config::R{PlotConfig} = PlotConfig(), READONLY 56 | end 57 | 58 | function ui(model) 59 | page(model, class="container", [ 60 | plot(:data, layout = :layout, config = :config) 61 | ] 62 | ) |> html 63 | end 64 | 65 | route("/") do 66 | Stipple.init(Model) |> ui 67 | end 68 | 69 | up() 70 | -------------------------------------------------------------------------------- /StipplePlotly/maps/CHOROPLETH.jl: -------------------------------------------------------------------------------- 1 | using CSV, DataFrames 2 | using Stipple, StipplePlotly 3 | 4 | data = CSV.read(download("https://raw.githubusercontent.com/plotly/datasets/master/2010_alcohol_consumption_by_country.csv"), DataFrame) 5 | 6 | 7 | @reactive! mutable struct Model <: ReactiveModel 8 | data::R{PlotData} = PlotData( 9 | plot = StipplePlotly.Charts.PLOT_TYPE_CHOROPLETH, 10 | locationmode = "country names", 11 | locations = data.location, 12 | z = data.alcohol, 13 | text = data.location, 14 | autocolorscale = true 15 | ) 16 | 17 | layout::R{PlotLayout} = PlotLayout( 18 | title = PlotLayoutTitle(text="Pure alcohol consumption
among adults (age 15+) in 2010", font=Font(24)) 19 | ) 20 | 21 | config::R{PlotConfig} = PlotConfig() 22 | end 23 | 24 | model = Model |> init 25 | 26 | function ui(model) 27 | page( 28 | model, 29 | class = "container", 30 | 31 | plot(:data, layout = :layout, config = :config) 32 | ) 33 | end 34 | 35 | route("/") do 36 | Stipple.init(Model) |> ui |> html 37 | end -------------------------------------------------------------------------------- /StipplePlotly/maps/lines.jl: -------------------------------------------------------------------------------- 1 | using CSV, DataFrames 2 | using Stipple, StipplePlotly 3 | 4 | csv_data = CSV.read(download("https://raw.githubusercontent.com/plotly/datasets/master/globe_contours.csv"), DataFrame) 5 | 6 | scl = ["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"]; 7 | all_lats = [] 8 | all_lons = [] 9 | 10 | for i in 1:length(scl) 11 | lat_head = "lat-" * string(i) 12 | lon_head = "lon-" * string(i) 13 | lat = csv_data[:, lat_head] 14 | lon = csv_data[:, lon_head] 15 | push!(all_lats, lat) 16 | push!(all_lons, lon) 17 | end 18 | 19 | data = PlotData[] 20 | 21 | for i in 1:length(scl) 22 | current = PlotData( 23 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTERGEO, 24 | lon = all_lons[i], 25 | lat = all_lats[i], 26 | mode = "lines", 27 | line = Dict("width" => 2, "color" => scl[i]) 28 | ) 29 | 30 | push!(data, current) 31 | end 32 | 33 | @reactive! mutable struct Model <: ReactiveModel 34 | data::R{Vector{PlotData}} = data 35 | layout::R{PlotLayout} = PlotLayout( 36 | geo = PlotLayoutGeo( 37 | geoprojection = GeoProjection(type="orthographic", rotation=PRotation(-100,40, 0)), 38 | showocean = true, 39 | oceancolor = "rgb(0, 255, 255)", 40 | showland = true, 41 | landcolor = "rgb(230, 145, 56)", 42 | showlakes = true, 43 | lakecolor = "rgb(0, 255, 255)", 44 | showcountries = true, 45 | lonaxis = PlotLayoutAxis(xy="x", showgrid=true, gridcolor="rgb(102, 102, 102)"), 46 | lataxis = PlotLayoutAxis(xy="y", showgrid=true, gridcolor="rgb(102, 102, 102)") 47 | ) 48 | ) 49 | 50 | config::R{PlotConfig} = PlotConfig() 51 | end 52 | 53 | model = Model |> init 54 | 55 | function ui(model) 56 | page( 57 | model, 58 | class = "container", 59 | 60 | plot(:data, layout = :layout, config = :config) 61 | ) 62 | end 63 | 64 | route("/") do 65 | Stipple.init(Model) |> ui |> html 66 | end -------------------------------------------------------------------------------- /StipplePlotly/maps/mapbox_access_token.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | 4 | @reactive! mutable struct Model <: ReactiveModel 5 | data::R{PlotData} = PlotData( 6 | plot = StipplePlotly.Charts.PLOT_TYPE_CHOROPLETHMAPBOX, 7 | name = "US States", 8 | geojson = "https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json", 9 | locations= [ "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY" ], 10 | z = [ 141, 140, 155, 147, 132, 146, 151, 137, 146, 136, 145, 141, 149, 151, 138, 158, 164, 141, 146, 145, 142, 150, 155, 160, 156, 161, 147, 164, 150, 152, 155, 167, 145, 146, 151, 154, 161, 145, 155, 150, 151, 162, 172, 169, 170, 151, 152, 173, 160, 176 ], 11 | zmin = 25, 12 | zmax = 280, 13 | colorbar =Dict("y" => 0, "yanchor" => "bottom", "title" => Dict("text" => "US states", "side" => "right")) 14 | 15 | ) 16 | 17 | layout::R{PlotLayout} = PlotLayout( 18 | mapbox = PlotLayoutMapbox(style="dark", zoom =0, center = MCenter(-110, 50)), 19 | height= 400, 20 | width= 600, 21 | margin_t = 0, 22 | margin_b = 0 23 | ) 24 | 25 | config::R{PlotConfig} = PlotConfig( 26 | mapbox_access_token = "your mapbox access token") 27 | end 28 | 29 | model = Model |> init 30 | 31 | function ui(model) 32 | page( 33 | model, 34 | class = "container", 35 | 36 | plot(:data, layout = :layout, config = :config) 37 | ) 38 | end 39 | 40 | route("/") do 41 | Stipple.init(Model) |> ui |> html 42 | end -------------------------------------------------------------------------------- /StipplePlotly/maps/mapbox_token.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using PlotlyBase 3 | using StipplePlotly 4 | 5 | mapbox_token = "pk.xxxxxxxxxxx.yyyyyyyyyy" 6 | 7 | lat = [37.7749, 40.7128, 51.5074] 8 | lon = [-122.4194, -74.0060, -0.1278] 9 | z = [0.5, 1, 1.5] 10 | 11 | mydata = [ 12 | PlotlyBase.scattermapbox( 13 | lat = lat, 14 | lon = lon, 15 | mode = "markers", 16 | marker = attr( 17 | size = 10, 18 | color = z, 19 | colorscale = "Viridis" 20 | ), 21 | text = ["San Francisco", "New York City", "London"], 22 | hoverinfo = "text" 23 | ) 24 | ] 25 | 26 | mylayout = PlotlyBase.Layout( 27 | 28 | mapbox = attr( 29 | accesstoken = mapbox_token, 30 | style = "mapbox://styles/mapbox/light-v9", 31 | center = attr( 32 | lat = 45, 33 | lon = 0 34 | ), 35 | pitch = 45, 36 | zoom = 2 37 | ) 38 | ) 39 | 40 | @vars ARModel begin 41 | mydata::R{Vector{GenericTrace}} = mydata 42 | mylayout::R{PlotlyBase.Layout} = mylayout 43 | myconfig::R{PlotlyBase.PlotConfig} = PlotlyBase.PlotConfig() 44 | end 45 | 46 | function ui(model::ARModel) 47 | page(model, class="container", [ 48 | h1("GenieFramework 🧞 Mapbox example 📊") 49 | plot(:mydata, layout=:mylayout, config=:myconfig) 50 | ]) 51 | end 52 | 53 | route("/") do 54 | ARModel |> init |> ui |> html 55 | end 56 | 57 | up() -------------------------------------------------------------------------------- /StipplePlotly/maps/scattermap.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | 4 | @reactive! mutable struct Model <: ReactiveModel 5 | data::R{PlotData} = PlotData( 6 | locations= ["FRA", "DEU", "RUS", "ESP"], 7 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTERGEO, 8 | mode = "markers", 9 | marker = PlotDataMarker( 10 | size = [20, 30, 15, 10], 11 | color = [10.0, 20.0, 40.0, 50.0], 12 | cmin = 0.0, 13 | cmax = 50.0, 14 | colorscale = "Greens", 15 | colorbar = ColorBar(title_text = "Some rate", ticksuffix = "%", showticksuffix = "last"), 16 | line = PlotlyLine(color = "black") 17 | ), 18 | name = "Europe Data") 19 | 20 | 21 | layout::R{PlotLayout} = PlotLayout( 22 | plot_bgcolor = "#333", 23 | title = PlotLayoutTitle(text="Europe Plot", font=Font(24)), 24 | geo = PlotLayoutGeo(scope = "europe", resolution="50") 25 | ) 26 | config::R{PlotConfig} = PlotConfig() 27 | end 28 | 29 | function ui(model) 30 | page(model, class = "container", [ 31 | plot(:data, layout= :layout, config = :config) 32 | ]) 33 | end 34 | 35 | route("/") do 36 | model = Model |> init |> ui |> html 37 | end 38 | 39 | -------------------------------------------------------------------------------- /StipplePlotly/maps/scattermapbox.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | 4 | @reactive! mutable struct Model <: ReactiveModel 5 | data::R{PlotData} = PlotData( 6 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTERMAPBOX, 7 | fill = "toself", 8 | lon = [-74, -70, -70, -74], 9 | lat = [47, 47, 45, 45], 10 | # marker = PlotDataMarker(size=10, color="orange") 11 | ) 12 | 13 | layout::R{PlotLayout} = PlotLayout( 14 | mapbox = PlotLayoutMapbox(style="stamen-terrain", zoom =5, center = MCenter(-73, 46)), 15 | showlegend= false, 16 | height= 450, 17 | width= 600 18 | ) 19 | 20 | config::R{PlotConfig} = PlotConfig() 21 | end 22 | 23 | model = Model |> init 24 | 25 | function ui(model) 26 | page( 27 | model, 28 | class = "container", 29 | 30 | plot(:data, layout = :layout, config = :config) 31 | ) 32 | end 33 | 34 | route("/") do 35 | Stipple.init(Model) |> ui |> html 36 | end -------------------------------------------------------------------------------- /StipplePlotly/multitype1.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | xrange = 0.0:(2π/1000):2π 4 | 5 | dxmeasured = 0.05 6 | dymeasured = 0.1 7 | 8 | xexperiment = (0.0:(2π/10):2π) .+ 3 .* dxmeasured .* (rand(Float64,11) .- 0.5) 9 | dx = dxmeasured .* ones(Float64, size(xexperiment)) 10 | yexperiment = sin.(xexperiment) .+ 3 .* dymeasured .* (rand(Float64,size(xexperiment)) .- 0.5) 11 | dy = dymeasured .* ones(Float64, size(yexperiment)) 12 | # create outlier: 13 | yexperiment[6] = 0.5 14 | 15 | pd_line(name, xar) = PlotData( 16 | x = xar, 17 | y = sin.(xar), 18 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 19 | mode = "lines", 20 | name = name 21 | ) 22 | 23 | pd_scatter(name, xar, dx, yar, dy) = PlotData( 24 | x = xar, 25 | error_x = ErrorBar(dx), 26 | y = yar, 27 | error_y = ErrorBar(dy), 28 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 29 | mode = "markers", 30 | name = name 31 | ) 32 | 33 | pl() = PlotLayout( 34 | plot_bgcolor = "#FFFFFF", 35 | title = PlotLayoutTitle(text="Wave", font=Font(24)), 36 | legend = PlotLayoutLegend(bgcolor = "rgb(212,212,212)", font=Font(6)), 37 | hovermode = "closest", 38 | showlegend = true, 39 | xaxis = [PlotLayoutAxis(xy="x", index=1, 40 | title = "time (s)", 41 | ticks = "outside", 42 | tickfont = Font(size=24, color="#FF00FF"), 43 | showline = true, 44 | zeroline = false, 45 | mirror = true 46 | )], 47 | yaxis = [PlotLayoutAxis(xy="y", index=1, 48 | showline = true, 49 | zeroline = false, 50 | title = "displacement (mm)", 51 | ticks = "outside", 52 | mirror = true 53 | )], 54 | annotations = [PlotAnnotation(visible=true, x=xexperiment[6], y=yexperiment[6], text="possible outlier")] 55 | ) 56 | 57 | @reactive! mutable struct Model <: ReactiveModel 58 | data::R{Vector{PlotData}} = [pd_line("Sinus", xrange), pd_scatter("Experiment", xexperiment, dx, yexperiment, dy)] 59 | layout::R{PlotLayout} = pl() 60 | config::R{PlotConfig} = PlotConfig() 61 | end 62 | 63 | 64 | function ui(model) 65 | page(model, class="container", [ 66 | plot(:data, layout = :layout, config = :config) 67 | ] 68 | ) |> html 69 | end 70 | 71 | route("/") do 72 | Stipple.init(Model) |> ui 73 | end 74 | 75 | up() 76 | -------------------------------------------------------------------------------- /StipplePlotly/scatter_polar.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | pd(name) = PlotData( 4 | r = [39, 28, 8, 7, 28, 39], 5 | theta = ['A','B','C', 'D', 'E', 'A'], 6 | plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER_POLAR, 7 | fill = "toself", 8 | name = name, 9 | ) 10 | 11 | @reactive mutable struct Model <: ReactiveModel 12 | data::R{Vector{PlotData}} = [pd("Random 1"), pd("Random 2")] 13 | layout::R{PlotLayout} = PlotLayout( 14 | polar = PlotLayoutPolar(radialaxis= RadialAxis(true, [0, 50])), 15 | showlegend = false 16 | ) 17 | config::R{PlotConfig} = PlotConfig() 18 | end 19 | 20 | function ui(model) 21 | page(model, class = "container", [plot(:data, layout = :layout, config = :config)]) 22 | end 23 | 24 | route("/") do 25 | Stipple.init(Model) |> ui |> html 26 | end -------------------------------------------------------------------------------- /StipplePlotly/subplots2x2.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StipplePlotly 2 | 3 | xx = -π:(2π/250):π 4 | 5 | xxs = -3.0:0.2:3.0 6 | 7 | pl1 = PlotData( 8 | x = xx, y = sin.(xx), plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 9 | name = "sine", mode = "lines", xaxis = "x", yaxis = "y", line = PlotlyLine(color = "rgb(0,0,192)", dash="solid") 10 | ) 11 | 12 | pl2 = PlotData( 13 | x = xx, y = sinh.(xx), plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 14 | name = "sinh", mode = "lines", xaxis = "x2", yaxis = "y2", line = PlotlyLine(color = "rgb(0,192,0)", dash="dot") 15 | ) 16 | 17 | pl3 = PlotData( 18 | x = xx, y = cos.(xx), plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 19 | name = "cosine", mode = "lines", xaxis = "x3", yaxis = "y3", line = PlotlyLine(color = "rgb(192,0,0)", dash="dash") 20 | ) 21 | 22 | pl4 = PlotData( 23 | x = xx, y = cosh.(xx), plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 24 | name = "cosh", mode = "lines", xaxis = "x4", yaxis = "y4", line = PlotlyLine(color = "rgb(192,0,192)", dash="dashdot") 25 | ) 26 | 27 | ps1 = PlotData( 28 | x = xxs, y = sin.(xxs) .+ rand(Float64, size(xxs)) .- 0.5, plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 29 | name = "sine", mode = "markers", xaxis = "x", yaxis = "y", marker = PlotDataMarker(color="rgb(0,0,192)", symbol="circle", size=10, opacity=0.5) 30 | ) 31 | 32 | ps2 = PlotData( 33 | x = xxs, y = sinh.(xxs) .+ 3.0 .* rand(Float64, size(xxs)) .- 1.5, plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 34 | name = "sinh", mode = "markers", xaxis = "x2", yaxis = "y2", marker = PlotDataMarker(color = "rgb(0,192,0)", symbol="circle-open", size=14) 35 | ) 36 | 37 | ps3 = PlotData( 38 | x = xxs, y = cos.(xxs) .+ rand(Float64, size(xxs)) .- 0.5, plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 39 | name = "cosine", mode = "markers", xaxis = "x3", yaxis = "y3", marker = PlotDataMarker(color = "rgb(192,0,0)", symbol="diamond", size=10, opacity=0.5) 40 | ) 41 | 42 | ps4 = PlotData( 43 | x = xxs, y = cosh.(xxs) .+ 3.0 .* rand(Float64, size(xxs)) .- 1.5, plot = StipplePlotly.Charts.PLOT_TYPE_SCATTER, 44 | name = "cosh", mode = "markers", xaxis = "x4", yaxis = "y4", marker = PlotDataMarker(color = "rgb(192,0,192)", symbol="diamond-open", size=3) 45 | ) 46 | 47 | plotdata = [ps1, ps2, ps3, ps4, pl1, pl2, pl3, pl4]; 48 | 49 | # Layout 50 | 51 | layout = PlotLayout( 52 | title = PlotLayoutTitle(text="Multiple Mixed Subplots", font=Font(24)), 53 | showlegend = false, 54 | grid = PlotLayoutGrid(rows = 2, columns = 2, pattern = "independent"), 55 | xaxis = [ 56 | PlotLayoutAxis(xy = "x", index = 1, ticks = "outside", showline = true, zeroline = false), 57 | PlotLayoutAxis(xy = "x", index = 2, ticks = "outside", showline = true, zeroline = false), 58 | PlotLayoutAxis(xy = "x", index = 3, ticks = "outside", showline = true, zeroline = false, title="range (arb. units)"), 59 | PlotLayoutAxis(xy = "x", index = 4, ticks = "outside", showline = true, zeroline = false, title="range (arb. units)") 60 | ], 61 | yaxis = [ 62 | PlotLayoutAxis(xy = "y", index = 1, ticks = "outside", showline = true, zeroline = false, title="response A"), 63 | PlotLayoutAxis(xy = "y", index = 2, ticks = "outside", showline = true, zeroline = false, title="response B"), 64 | PlotLayoutAxis(xy = "y", index = 3, ticks = "outside", showline = true, zeroline = false, title="response C"), 65 | PlotLayoutAxis(xy = "y", index = 4, ticks = "outside", showline = true, zeroline = false, title="response D") 66 | ], 67 | ) 68 | 69 | @reactive! mutable struct Model <: ReactiveModel 70 | data::R{Vector{PlotData}} = plotdata, READONLY 71 | layout::R{PlotLayout} = layout, READONLY 72 | config::R{PlotConfig} = PlotConfig(), READONLY 73 | end 74 | 75 | function ui(model) 76 | page(model, class="container", [ 77 | plot(:data, layout = :layout, config = :config) 78 | ] 79 | ) |> html 80 | end 81 | 82 | route("/") do 83 | Stipple.init(Model) |> ui 84 | end 85 | 86 | up() 87 | -------------------------------------------------------------------------------- /StipplePlotly/text_tooltip_newapi.jl: -------------------------------------------------------------------------------- 1 | # contributed by zygmuntszpak: https://github.com/GenieFramework/StipplePlotly.jl/pull/53 2 | using Stipple, Stipple.ReactiveTools, StipplePlotly, DataFrames 3 | 4 | function create_example_dataframe() 5 | xs = [1.0, 2.0, 3.0, 4.0, 5.0, 1.5, 2.5, 3.5, 4.5, 5.5] 6 | ys = [1.0, 6.0, 3.0, 6.0, 1.0, 4.0, 1.0, 7.0, 1.0, 4.0] 7 | groups = ["Team A", "Team A", "Team A", "Team A", "Team A", "Team B", "Team B", "Team B", "Team B", "Team B"] 8 | text = ["A-1", "A-2", "A-3", "A-4", "A-5","B-a", "B-b", "B-c", "B-d", "B-e"] 9 | return DataFrame(X = xs, Y = ys, Group = groups, Text = text) 10 | end 11 | 12 | df = create_example_dataframe() 13 | pd = plotdata(df, :X, :Y; groupfeature = :Group, text = df.Text) 14 | 15 | @handlers begin 16 | @out data = pd 17 | @out layout = PlotLayout() 18 | @out config = PlotConfig() 19 | end 20 | 21 | function ui() 22 | [ 23 | plot(:data, layout = :layout, config = :config) 24 | ] |> join 25 | end 26 | 27 | @page("/", ui) 28 | 29 | up() 30 | -------------------------------------------------------------------------------- /Test/Test Mixins.jl: -------------------------------------------------------------------------------- 1 | # Test of `@mixins` from branch hh-rm-mixins (https://github.com/GenieFramework/Stipple.jl/pull/245) 2 | 3 | using Stipple, Stipple.ReactiveTools 4 | using StippleUI 5 | 6 | @app GreetMixin begin 7 | @in name = "John Doe" 8 | 9 | @onchange name begin 10 | println("'name' changed to $(name)!") 11 | end 12 | 13 | end greet_handlers 14 | 15 | @mounted GreetMixin = "console.log('Just mounted the App including the GreetMixin')" 16 | 17 | @methods GreetMixin :greet => "function() { console.log('Hi ' + this.name + '!') }" 18 | 19 | @mixins [GreetMixin] 20 | 21 | @app begin 22 | @mixin GreetMixin 23 | @in s = "Hi" 24 | @in i = 10 25 | 26 | @onchange i begin 27 | println("'i' changed to $(i)!") 28 | end 29 | end 30 | 31 | ui() = row(cell(class = "st-module", [ 32 | cell(class = "q-my-md q-pa-md bg-green-3", "Name: {{name}}") 33 | cell(class = "q-my-md q-pa-md bg-green-4", "i: {{i}}") 34 | 35 | btn("client mixin 'greet'", @click("greet"), color = "red-3", nocaps = true) 36 | btn(class = "q-ml-md", "backend mixin '@onchange name'", @click("name = 'John ' + (name.endsWith('Doe') ? 'Dough' : 'Doe')"), color = "red-3", nocaps = true) 37 | ])) 38 | 39 | @page("/", ui) 40 | 41 | up(open_browser = true) -------------------------------------------------------------------------------- /Vue3/Calendars/Calendars.jl: -------------------------------------------------------------------------------- 1 | module Calendars 2 | 3 | using Stipple, Stipple.ReactiveTools 4 | using StippleUI 5 | 6 | using Dates 7 | 8 | 9 | UI = Ref(ParsedHTMLString[]) 10 | ui() = UI[] 11 | 12 | 13 | calendar_css() = [ 14 | stylesheet("https://cdn.jsdelivr.net/npm/@quasar/quasar-ui-qcalendar@next/dist/QCalendarMonth.min.css", type = "text/css") 15 | ] 16 | 17 | calendar_deps() = [ 18 | script(src="https://cdn.jsdelivr.net/npm/@quasar/quasar-ui-qcalendar@next/dist/QCalendarMonth.umd.min.js") 19 | script(src="https://cdn.jsdelivr.net/npm/@quasar/quasar-ui-qcalendar@next/dist/Timestamp.umd.min.js") 20 | ] 21 | 22 | 23 | UI[] = [ 24 | row(class = "text-center text-h5", [cell("{{ monthName }}"), cell("{{ selectedDate.substring(0,4)}}")]), 25 | row(class = "q-pa-md q-gutter-sm", [ 26 | cell() 27 | btn(col = :auto, icon = "arrow_left", var"v-on:click" = "onPrev", dense = true) 28 | btn(col = :auto, "Today", var"v-on:click" = "onToday", ) 29 | btn(col = :auto, icon = "arrow_right", var"v-on:click" = "onNext", dense = true) 30 | cell() 31 | ]), 32 | cell(class = "q-pa-md full-width", style = "height: 400px;", 33 | quasar(:calendar__month, ref = "calendar", fieldname = "selectedDate", "", 34 | var"day-min-height" = R"70", 35 | :focusable, :hoverable, :bordered, 36 | @on("change", "onChange"), @on("moved", "onMoved"), @on("click-date", "onClickDate"), 37 | @on("click-day", "onClickDay"), @on("click-head-day", "onClickHeadDay") 38 | )) 39 | ] 40 | 41 | @app Calendar begin 42 | @in selectedDate::Any = today() 43 | @in startDate = today() 44 | @in endDate = today() 45 | end 46 | 47 | @computed Calendar [ 48 | :monthName => """function () { 49 | return Timestamp.getMonthFormatter()(Timestamp.parsed(this.selectedDate).month - 1) 50 | }""" 51 | ] 52 | 53 | @methods Calendar [ 54 | :onMoved => """function(data) { 55 | console.log("onMoved", data); 56 | }""" 57 | 58 | :onChange => """function(data) { 59 | this.startDate = data.start; 60 | this.endDate = data.end; 61 | }""" 62 | 63 | :onClickDate => """function(data) { 64 | console.log("onClickDate", data); 65 | }""" 66 | 67 | :onClickDay => """function(data) { 68 | console.log("onClickDay", data); 69 | }""" 70 | 71 | :onClickHeadDay => """function(data) { 72 | console.log("onClickHeadDay", data); 73 | }""" 74 | 75 | :onToday => """function() { 76 | this.selectedDate = QCalendarMonth.today(); 77 | }""" 78 | 79 | :onPrev => """function() { 80 | this.\$refs.calendar.prev(); 81 | }""" 82 | 83 | :onNext => """function() { 84 | this.\$refs.calendar.next(); 85 | }""" 86 | ] 87 | 88 | function __init__() 89 | Stipple.ReactiveTools.HANDLERS_FUNCTIONS[Calendar] = handlers 90 | 91 | add_css(calendar_css) 92 | @deps Calendar calendar_deps 93 | Stipple.register_components(Calendar, "QCalendarMonth" => "QCalendarMonth.QCalendarMonth") 94 | 95 | route("/") do 96 | global model 97 | model = @init Calendar 98 | page(model, ui) |> html 99 | end 100 | end 101 | 102 | using PrecompileTools 103 | @compile_workload begin 104 | Stipple.PRECOMPILE[] = true 105 | ui() 106 | __init__() 107 | model = @init Calendar 108 | page(model, ui) |> html 109 | Stipple.PRECOMPILE[] = false 110 | end 111 | 112 | end -------------------------------------------------------------------------------- /_broken_/DynamicContent/DynamicContent.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | const MAX_BUTTONS = 10 # allow for a maximum of 10 buttons 4 | 5 | function generate_buttons() # returns the array of button-labels (and functions) 6 | n = rand(1:MAX_BUTTONS) # choose a random number of buttons 7 | map(1:n) do i 8 | label = string(i) # the label of the button 9 | label # skipping the function part for now 10 | # click = _ -> println("You pressed on ", label) # run this function owhen the button is clicked 11 | # (; label, click) 12 | end 13 | end 14 | 15 | Base.@kwdef mutable struct DynamicContent <: ReactiveModel 16 | generate::R{Int} = 0 # pressing this will update the array of buttons 17 | buttons::R{Vector} = Any[] 18 | end 19 | 20 | function restart() 21 | global model 22 | println("Test") 23 | model = Stipple.init(DynamicContent(), debounce = 1) 24 | on(model.generate) do _ 25 | model.buttons[] = generate_buttons() 26 | end 27 | 28 | on(model.buttons) do labels 29 | println(labels) # replace this to something that generates the buttons 30 | end 31 | end 32 | 33 | function ui() 34 | dashboard( 35 | vm(model), 36 | [ 37 | heading("DynamicContent"), 38 | row( 39 | cell( 40 | class = "st-module", 41 | [p(button("Generate new buttons", @click("generate += 1")))], 42 | ), 43 | ), 44 | ], 45 | title = "DynamicContent", 46 | ) |> html 47 | end 48 | 49 | route("/", ui) 50 | Genie.config.server_host = "127.0.0.1" 51 | restart() 52 | -------------------------------------------------------------------------------- /_broken_/DynamicContent/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | julia = "1.6" 7 | -------------------------------------------------------------------------------- /_broken_/HelloStippleMultiUser.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | 3 | Base.@kwdef mutable struct Name <: ReactiveModel 4 | name::R{String} = "Stipple!" 5 | end 6 | 7 | hs_models = Dict{String,Name}() 8 | 9 | function ui() 10 | channel = String(@params(:session_id)) 11 | hs_model = if haskey(hs_models, channel) 12 | hs_models[channel] 13 | else 14 | hs_models[channel] = Stipple.init(Name(), channel = channel) 15 | end 16 | 17 | on(hs_models[channel].name) do _ 18 | @show "changed" 19 | end 20 | 21 | [ 22 | page( 23 | vm(hs_model), class="container", title="Hello Stipple", partial=true, channel=channel, 24 | [ 25 | h1([ 26 | "Hello, " 27 | span("", @text(:name)) 28 | ]) 29 | 30 | p([ 31 | "What is your name? " 32 | input("", placeholder="Type your name", @bind(:name)) 33 | ]) 34 | ] 35 | ) 36 | ] |> html 37 | end 38 | 39 | route("/") do 40 | Stipple.Genie.Renderer.redirect("/dashboards/$(rand(10_000:99_999))") 41 | end 42 | 43 | route("/dashboards/:session_id", ui) 44 | 45 | up(rand((8000:9000)), open_browser=true) -------------------------------------------------------------------------------- /_broken_/JSmethods/JSmethods.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | Base.@kwdef mutable struct JSmethods <: ReactiveModel 4 | x::R{Int} = 0 # pressing this will update the array of buttons 5 | end 6 | 7 | function restart() 8 | global hs_model 9 | hs_model = JSmethods |> init 10 | on(println, hs_model.x) 11 | end 12 | 13 | Stipple.js_methods(::JSmethods) = raw""" 14 | showNotif () { 15 | alert("Welcome to JSMethods!") // some blocking javascript. Hit "OK" on alert to proceed with notification 16 | this.$q.notify({ 17 | message: 'I am notifying you!', 18 | color: 'purple' 19 | }) 20 | } 21 | """ 22 | 23 | function ui() 24 | app = dashboard( 25 | vm(hs_model), 26 | [ 27 | heading("jsmethods"), 28 | row(cell(class = "st-module", [p(button("Notify me", @click("showNotif()")))])), 29 | ], 30 | title = "jsmethods", 31 | ) 32 | 33 | html(app) 34 | end 35 | 36 | route("/", ui) 37 | restart() 38 | -------------------------------------------------------------------------------- /_broken_/JSmethods/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | -------------------------------------------------------------------------------- /_broken_/KnobDemo.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | Base.@kwdef mutable struct KnobDemo <: ReactiveModel 4 | 5 | end 6 | 7 | hs_model = Stipple.init(KnobDemo()) 8 | 9 | function ui() 10 | [ 11 | page( 12 | vm(hs_model), class="container", title="Knob Demo", partial=true, 13 | [ 14 | row( 15 | cell([ 16 | knob(1:1:100) 17 | ]) 18 | ) 19 | ] 20 | ) 21 | ] |> html 22 | end 23 | 24 | route("/", ui) 25 | 26 | up(rand((8000:9000)), open_browser=true) -------------------------------------------------------------------------------- /_broken_/LineCharts.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleCharts 2 | 3 | @kwredef struct ChartData <: ReactiveModel 4 | plot_options = PlotOptions(chart_type=:line, chart_sparkline_enabled=true, chart_width=100, chart_height=75, 5 | grid_show=false, grid_row_opacity = 0) 6 | d = PlotData(44, 55, 122, 10, 34) 7 | end 8 | 9 | Stipple.register_components(ChartData, StippleCharts.COMPONENTS) 10 | 11 | function ui() 12 | [ 13 | page( 14 | vm(ChartData() |> Stipple.init), class="container", title="Line Charts", 15 | [ 16 | row( 17 | cell(class="st-module", [ 18 | plot(@data(:d), options=:plot_options, width=100, height=70) 19 | ]) 20 | ) 21 | ] 22 | ) 23 | ] |> html 24 | end 25 | 26 | route("/", ui) 27 | 28 | up(rand((8000:9000)), open_browser=true) -------------------------------------------------------------------------------- /_broken_/MultiUserApp.jl: -------------------------------------------------------------------------------- 1 | # Demo case for setting up a page for multiple users 2 | # In this example the Browser identification ('User-Agent' from the header) 3 | # is used to distinguish users. 4 | # If you open two instances of the app in the same browser, you will see that they synchronise, 5 | # whereas an instance in a different browser will keep its own values. 6 | 7 | cd(@__DIR__) 8 | using Pkg 9 | pkg"activate ." 10 | 11 | 12 | using Stipple 13 | using StippleCharts 14 | using StippleUI 15 | 16 | # Genie.Assets.assets_config!([Genie, Stipple, StippleUI, StippleCharts], 17 | # host = "https://cdn.statically.io/gh/GenieFramework") 18 | 19 | # extra css for correct padding of st-br blocks ('st-pv' is not used here) 20 | const CSS = style(""" 21 | .st-ph { 22 | padding-left: 20px; 23 | padding-right: 20px; 24 | } 25 | .st-ph:first-child { 26 | padding-left: 0px; 27 | } 28 | .st-ph:last-child { 29 | padding-right: 0px; 30 | } 31 | 32 | .st-pv { 33 | padding-top: 20px; 34 | padding-bottom: 20px; 35 | } 36 | .st-pv:first-child { 37 | padding-top: 0px; 38 | } 39 | .st-pv:last-child { 40 | padding-bottom: 0px; 41 | } 42 | 43 | .st-bb:last-child { 44 | border-bottom: 0 45 | } 46 | """) 47 | 48 | # helper function for defining dictionaries 49 | const OptDict = Dict{Symbol, Any} 50 | const plot_options = OptDict( 51 | :chart => OptDict(:type => :line), 52 | :xaxis => OptDict(:type => :numeric), 53 | :yaxis => OptDict(:min => -5, :max => 5, :tickAmount => 10, 54 | :labels => OptDict(:formatter => JSONText("function(val, index) { return val.toFixed(1); }")) 55 | ) 56 | ) 57 | 58 | # alternatively if you prefer python-style 59 | # opts(;kwargs...) = OptDict(kwargs...) 60 | # const plot_options = opts( 61 | # chart = opts(type = :line), 62 | # xaxis = opts(type = :numeric), 63 | # yaxis = opts(min = -5, max = 5, tickAmount = 10, 64 | # labels = opts(formatter = JSONText("function(val, index) { return val.toFixed(1); }")) 65 | # ) 66 | # ) 67 | 68 | const xx = Base.range(0, 4π, length=200) |> collect 69 | 70 | @reactive mutable struct MyDashboard <: ReactiveModel 71 | a::R{Float64} = 1.0 72 | b::R{Float64} = 0.0 73 | c::R{Float64} = 0.0 74 | plot_data::R{Vector{PlotSeries}} = [PlotSeries("Sine", PlotData(zip(xx, a .* sin.(xx .- b) .+ c) |> collect))] 75 | plot_options::R{OptDict} = plot_options, JSFUNCTION 76 | end 77 | 78 | # Stipple.register_components(MyDashboard, StippleCharts.COMPONENTS) 79 | 80 | models = Dict{String, ReactiveModel}() 81 | 82 | function getmodel(channel) 83 | if haskey(models, channel) 84 | models[channel] 85 | else 86 | model = models[channel] = init(MyDashboard, channel = channel) 87 | 88 | # update plot_data when a, b or c are changed 89 | onany(model.a, model.b, model.c) do a, b, c 90 | @info "amplitude: $a, phase: $b, offset: $c" 91 | model.plot_data[] = [PlotSeries("Sine", PlotData(zip(xx, a .* sin.(xx .- b) .+ c) |> collect))] 92 | end 93 | 94 | on(model.isready) do _ 95 | push!(model) 96 | end 97 | 98 | model 99 | end 100 | end 101 | 102 | function ui(user_id) 103 | CSS * 104 | page(getmodel(user_id), class="container", [ 105 | 106 | heading("Demo Stipple App with multi-user and multi-client support (user $user_id)") 107 | 108 | row([ 109 | cell(class="st-br st-ph", [ 110 | h5("Sine oder Cosine?") 111 | 112 | row([ 113 | cell(size=4, h6("Amplitude")) 114 | cell(slider( 0:0.01:5, @data(:a); markers=true, label=true, labelalways = true)) 115 | ]) 116 | 117 | row([ 118 | cell(size=4, h6("Phase")) 119 | cell(slider( 0:0.01:5, @data(:b); markers=true, label=true, labelalways = true)) 120 | ]) 121 | 122 | row([ 123 | cell(size=4, h6("Offset")) 124 | cell(slider( -3:0.01:3, @data(:c); markers=true, label=true, labelalways = true)) 125 | ]) 126 | ]) 127 | ]) 128 | 129 | row(cell(class="st-module", plot(:plot_data; options=:plot_options))) 130 | 131 | row([ 132 | footer([ 133 | h6("Powered by Stipple") 134 | ]) 135 | ]) 136 | ], title = "Stipple x-y ApexChart", @iif(:isready)) 137 | end 138 | 139 | route("/") do 140 | # deliver a user-spcific ui 141 | redirect("/session/$(rand(1:1_000_000))") 142 | end 143 | 144 | route("/session/:sid") do 145 | params(:sid) |> ui |> html 146 | end 147 | 148 | Stipple.up() 149 | -------------------------------------------------------------------------------- /_broken_/New API Demo Draft.jl: -------------------------------------------------------------------------------- 1 | using Revise 2 | using Stipple, StippleUI 3 | import Stipple: opts, OptDict 4 | 5 | Stipple.@kwdef mutable struct Example <: ReactiveModel 6 | name::R{String} = "Stipple!" 7 | firstname::String = "You" 8 | name_::String = "readonly" 9 | name__::String = "private" 10 | noauto::R{String} = "Don't autoupdate", NO_WATCHER 11 | noauto_backend::R{String} = "Don't autoupdate", NO_BACKEND_WATCHER 12 | noauto_frontend::R{String} = "Don't autoupdate", NO_FRONTEND_WATCHER 13 | private::R{String} = "private", :private 14 | readonly::R{String} = "readonly", :readonly 15 | options::R{OptDict} = opts(f = js"()=>{}"), :jsfunction 16 | js::R{JSONText} = js"()=>{}", :jsfunction 17 | header::R{Bool} = true 18 | darkmode::R{Bool} = true 19 | end 20 | 21 | css() = style(""" 22 | :root.stipple-blue body.body--dark{ 23 | --st-dashboard-module: #4e4e4e; 24 | --st-dashboard-line: #a2a2a2; 25 | --st-dashboard-bg: #616367; 26 | --st-slider--track: #aab2b2; 27 | } 28 | .stipple-core body.body--dark{ 29 | --st-dashboard-bg: #616367; 30 | } 31 | """) 32 | 33 | model = Stipple.init(Example()) 34 | Stipple.js_watch(model) = raw""" 35 | js: function(val) { if (typeof(val)=="function") { val() } }, 36 | darkmode: function(val) { this.$q.dark.set(val) } 37 | """ 38 | 39 | Stipple.js_created(model) = raw""" 40 | this.$q.dark.set(this.darkmode) 41 | """ 42 | 43 | function ui() 44 | [ 45 | dashboard( 46 | vm(model), class="container", title="Hello Stipple", 47 | [ 48 | template([ 49 | h1(["Hello, ", span("", @text(:name))]) 50 | ], v__if=:header) 51 | row(cell(class="st-module", [ 52 | p(toggle("Camera on", fieldname = :header)), 53 | p(toggle("Darkmode", :darkmode)), 54 | ])) 55 | p([ 56 | h1("What is your name? ") 57 | textfield("", :name, placeholder="type your name", label="Name", outlined="", filled="") 58 | ]) 59 | ] 60 | ) 61 | css() 62 | ] |> html 63 | end 64 | 65 | route("/", ui) 66 | 67 | up(open_browser=true) 68 | 69 | model.js[] = js"""() => console.log("Hello")""" 70 | model.js[] = js"""() => console.log("World")""" 71 | 72 | model.options[] = opts(f = js"""() => console.log("Let's Stipple!")""") 73 | model.js[] = model.options[][:f] 74 | -------------------------------------------------------------------------------- /_broken_/New API3 Demo Draft.jl: -------------------------------------------------------------------------------- 1 | using Revise 2 | using Stipple, StippleUI, OffsetArrays 3 | import Stipple: opts, OptDict 4 | 5 | Stipple.@kwdef mutable struct Example <: ReactiveModel 6 | name::R{String} = "Stipple!" 7 | name_::R{String} = "readonly" 8 | name__::R{String} = "private", PUBLIC 9 | firstname::String = "You" 10 | readonly_::String = "readonly" 11 | privatestring__::String = "private" 12 | noauto::R{String} = "Don't autoupdate", NO_WATCHER 13 | no_backend_watcher::R{String} = "No backend watcher", READONLY, NO_BACKEND_WATCHER 14 | no_frontend_watcher::R{String} = "No frontend watcher", NO_FRONTEND_WATCHER 15 | private::R{String} = "private", PRIVATE 16 | readonly::R{String} = "readonly", READONLY 17 | options::R{OptDict} = opts(f = js"() => {}"), JSFUNCTION 18 | js::R{JSONText} = js"()=>{}", JSFUNCTION 19 | header::R{Bool} = true 20 | darkmode::R{Bool} = true 21 | array::OffsetArray = OffsetArray([true, false, false], -1) 22 | rarray::R{OffsetArray} = OffsetArray([true, false, false], -1) 23 | end 24 | 25 | css() = Stipple.style(""" 26 | :root.stipple-blue body.body--dark{ 27 | --st-dashboard-module: #4e4e4e; 28 | --st-dashboard-line: #a2a2a2; 29 | --st-dashboard-bg: #616367; 30 | --st-slider--track: #aab2b2; 31 | } 32 | .stipple-core body.body--dark{ 33 | --st-dashboard-bg: #616367; 34 | } 35 | """) 36 | 37 | model = Stipple.init(Example()) 38 | Stipple.js_watch(m::Example) = raw""" 39 | js: function(val) { if (typeof(val)=="function") { val() } }, 40 | darkmode: function(val) { this.$q.dark.set(val) } 41 | """ 42 | 43 | Stipple.js_created(m::Example) = raw""" 44 | this.$q.dark.set(this.darkmode) 45 | """ 46 | 47 | function ui() 48 | [ 49 | dashboard( 50 | vm(model), class="container", title="Hello Stipple", 51 | [ 52 | template([ 53 | row(h1(["Hello, ", span("", @text(:name))])) 54 | ], v__if=:header) 55 | 56 | row(cell(class="st-module", [ 57 | p(toggle("Camera on", fieldname = :header)), 58 | p(toggle("Darkmode", :darkmode, disable=:header)) 59 | ])) 60 | 61 | row(cell(class="st-module", row([ 62 | cell(class="st-br", [ 63 | p(toggle("array[0]", R"array[0]")), 64 | p(toggle("array[1]", R"array[1]")), 65 | p(toggle("array[2]", R"array[2]", disable=:header)) 66 | ]), 67 | cell(class="st-br", [ 68 | p(toggle("rarray[0]", R"rarray[0]")), 69 | p(toggle("rarray[1]", R"rarray[1]")), 70 | p(toggle("rarray[2]", R"rarray[2]")) 71 | ]) 72 | ]))) 73 | 74 | row(cell(class="st-module", row([ 75 | cell(class="st-br", [ 76 | h1("array") 77 | p("{{ array }}") 78 | ]) 79 | 80 | cell(class="st-br", [ 81 | h1("rarray") 82 | p("{{ rarray }}") 83 | ]) 84 | ]))) 85 | 86 | p([ 87 | h1("What is your name? ") 88 | textfield("", :name, placeholder="type your name", label="Name", outlined="", filled="") 89 | ]) 90 | ] 91 | ) 92 | css() 93 | ] |> html 94 | end 95 | 96 | route("/", ui) 97 | 98 | up(open_browser=true) 99 | 100 | model.js[] = js"""() => console.log("Hello")""" 101 | model.js[] = js"""() => console.log("World")""" 102 | 103 | model.options[] = opts(f = js"""() => console.log("Let's Stipple!")""") 104 | model.js[] = model.options[][:f] 105 | 106 | # standard (non-notifying) indexing into Reactive elements 107 | @show model.rarray[][1] 108 | @show model.options[][:f] 109 | 110 | model.rarray[][1] = true 111 | model.options[][:hh] = "test" 112 | 113 | 114 | # new notifying version of setindex! 115 | @show model.rarray[1] 116 | @show model.options[:f] 117 | model.js[] = model.options[:f] 118 | 119 | model.rarray[1] = true 120 | model.options[:hh] = "test" 121 | 122 | model.array[1] = true 123 | 124 | model.array 125 | 126 | function togglefun(tf) 127 | if tf 128 | @info "true" 129 | else 130 | @info "false" 131 | end 132 | end -------------------------------------------------------------------------------- /_broken_/ReverseText/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" 3 | StippleUI = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" 4 | 5 | [compat] 6 | Stipple = "0.22.0" 7 | StippleUI = "0.17.0" -------------------------------------------------------------------------------- /_broken_/ReverseText/ReverseText.jl: -------------------------------------------------------------------------------- 1 | using Stipple, StippleUI 2 | 3 | Genie.Assets.assets_config!( 4 | [Genie, Stipple, StippleUI], 5 | host = "https://cdn.statically.io/gh/GenieFramework", 6 | ) 7 | 8 | WEB_TRANSPORT = Genie.WebChannels #Genie.WebThreads # 9 | 10 | Base.@kwdef mutable struct RTModel <: ReactiveModel 11 | process::R{Bool} = false 12 | output::R{String} = "" 13 | input::R{String} = "Stipple" 14 | end 15 | 16 | rt_model = Stipple.init(RTModel(), transport = WEB_TRANSPORT) 17 | 18 | on(rt_model.process) do _ 19 | if (rt_model.process[]) 20 | rt_model.output[] = rt_model.input[] |> reverse 21 | rt_model.process[] = false 22 | end 23 | end 24 | 25 | function ui() 26 | [ 27 | page( 28 | vm(rt_model), 29 | class = "container", 30 | title = "Reverse text", 31 | [ 32 | p([ 33 | "Input " 34 | input("", @bind(:input), @on("keyup.enter", "process = true")) 35 | ]) 36 | p(button("Reverse", @click("process = true"))) 37 | p([ 38 | "Output: " 39 | span("", @text(:output)) 40 | ]) 41 | ], 42 | ), 43 | ] |> html 44 | end 45 | 46 | route("/", ui) 47 | -------------------------------------------------------------------------------- /_broken_/WebCam2.jl: -------------------------------------------------------------------------------- 1 | # Camera Widget based on VideoIO. This version is currently less performant than the FFMPEG version 2 | # because the png compression is done by Julia whereas in the FFMPEG version 3 | # this is done by an external process which is not competing for resources. 4 | 5 | # Requires a webcam to be connected to the computer running this 6 | 7 | using Stipple, StippleUI 8 | using HTTP 9 | using VideoIO, ImageIO, FileIO 10 | 11 | import Base.cconvert 12 | Base.cconvert(::Type{Ptr{Ptr{VideoIO.AVDictionary}}}, d::VideoIO.AVDict) = d.ref_ptr_dict 13 | 14 | const img = Ref{Any}() 15 | const IMG = Ref{Vector{UInt8}}() 16 | const CAM_PROCESS = Ref{Task}() # a container for the camera process 17 | const CAM = Ref{VideoIO.VideoReader}() 18 | 19 | const FPS = 30 # some cameras only support fix values, e.g. 30 20 | const FPS_CLIENT = 8 21 | const Δt = 1/FPS 22 | 23 | const PORT = 8001 24 | 25 | function img2png(image) 26 | io = IOBuffer() 27 | save(Stream(format"PNG", io), image) 28 | take!(io) 29 | end 30 | 31 | 32 | cameraon = true 33 | 34 | function start_camera() 35 | CAM[] = VideoIO.opencamera() 36 | CAM_PROCESS[] = @async begin 37 | global cameraon, FPS, i_raw, i_png 38 | cameraon = true 39 | i_raw = 0 40 | i_png = -1 41 | img[] = read(CAM[]) 42 | while cameraon 43 | t0 = now() 44 | i_raw += 1 45 | read!(CAM[], img[]) 46 | (i_raw % 300 == 0) && println("Camera image: $i_raw") 47 | dt = (now() - t0).value 48 | sleep(Δt - dt > 0 ? Δt - dt : 1/100) # minimum of 1 ms to have Julia responsive 49 | end 50 | end 51 | end 52 | 53 | function stop_camera() 54 | global cameraon 55 | cameraon = false 56 | close(CAM[]) 57 | end 58 | 59 | # emergency break: 60 | # Base.throwto(cameraproc, InterruptException()) 61 | 62 | @reactive! mutable struct WebCam <: ReactiveModel 63 | cameraon::R{Bool} = true 64 | imageurl::String = "" 65 | cameratimer::Int = 0 66 | end 67 | 68 | Stipple.js_methods(model::WebCam) = """ 69 | updateimage: function () { 70 | this.imageurl = "frame/" + new Date().getTime(); 71 | }, 72 | start_camera: function () { 73 | console.log("start camera") 74 | this.cameratimer = setInterval(this.updateimage, 1000/$FPS_CLIENT); 75 | }, 76 | stopcamera: function () { 77 | console.log("stop camera") 78 | clearInterval(this.cameratimer); 79 | } 80 | """ 81 | 82 | Stipple.js_created(model::WebCam) = """ 83 | if (this.cameraon) { this.start_camera() } 84 | """ 85 | 86 | Stipple.js_watch(model::WebCam) = """ 87 | cameraon: function (newval, oldval) { 88 | this.stopcamera() 89 | if (newval) { this.start_camera() } 90 | } 91 | """ 92 | 93 | function ui(model) 94 | start_camera() 95 | 96 | on(model.cameraon) do ison 97 | ison ? start_camera() : stop_camera() 98 | end 99 | 100 | dashboard(model, [ 101 | p(quasar(:img, "", src=:imageurl, :basic, style=" 102 | -webkit-app-region: drag; 103 | border-radius: 50%; 104 | width: 95vw; 105 | height: 95vw"), 106 | style = "margin: 1px"), 107 | 108 | p(toggle("", fieldname = :cameraon)), 109 | ], title = "WebCam") * 110 | script("""document.documentElement.style.setProperty("--st-dashboard-bg", "#0000")""") * 111 | style("::-webkit-scrollbar { width: 0px; }") 112 | end 113 | 114 | 115 | route("/") do 116 | init(WebCam) |> ui |> html 117 | end 118 | 119 | route("frame/:timestamp") do 120 | # conert only the frames that are needed 121 | global i_raw, i_png, IMG, img 122 | if i_png != i_raw 123 | IMG[] = img2png(img[]) 124 | i_png = i_raw 125 | end 126 | HTTP.Messages.Response(200, IMG[]) 127 | end 128 | 129 | Genie.config.server_host = "127.0.0.1" 130 | 131 | up(PORT) 132 | 133 | using Electron 134 | 135 | function camerawidget() 136 | win = Window(URI("http://localhost:$PORT"), options = Dict( 137 | "transparent" => true, 138 | "frame" => false, 139 | "width" => 145, 140 | "height" => 200, 141 | )) 142 | 143 | ElectronAPI.setAlwaysOnTop(win, true) 144 | win 145 | end 146 | 147 | win = camerawidget() -------------------------------------------------------------------------------- /_temp_/IrisClustering1.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | using StippleCharts 4 | 5 | #= Data =# 6 | 7 | Base.@kwdef mutable struct IrisModel <: ReactiveModel 8 | end 9 | 10 | #= Stipple setup =# 11 | 12 | const ic_model = Stipple.init(IrisModel()) 13 | 14 | #= UI =# 15 | 16 | function ui(model::IrisModel) 17 | [ 18 | dashboard( 19 | vm(model), class="container", title="Iris Flowers Clustering", head_content=Genie.Assets.favicon_support(), 20 | [ 21 | heading("Iris data k-means clustering") 22 | 23 | row([ 24 | cell(class="st-module", [ 25 | h6("Number of clusters") 26 | ]) 27 | cell(class="st-module", [ 28 | h6("Number of iterations") 29 | ]) 30 | 31 | cell(class="st-module", [ 32 | h6("X feature") 33 | ]) 34 | 35 | cell(class="st-module", [ 36 | h6("Y feature") 37 | ]) 38 | ]) 39 | 40 | row([ 41 | cell(class="st-module", [ 42 | h5("Species clusters") 43 | ]) 44 | 45 | cell(class="st-module", [ 46 | h5("k-means clusters") 47 | ]) 48 | ]) 49 | 50 | row([ 51 | cell(class="st-module", [ 52 | h5("Iris data") 53 | ]) 54 | ]) 55 | ]) 56 | ] 57 | end 58 | 59 | #= routing =# 60 | 61 | route("/") do 62 | ui(ic_model) |> html 63 | end 64 | 65 | #= start server =# 66 | 67 | up(rand((8000:9000)), open_browser=true) -------------------------------------------------------------------------------- /_temp_/IrisClustering2.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | using StippleCharts 4 | 5 | using Clustering 6 | import RDatasets: dataset 7 | import DataFrames 8 | 9 | #= Data =# 10 | 11 | data = DataFrames.insertcols!(dataset("datasets", "iris"), :Cluster => zeros(Int, 150)) 12 | 13 | Base.@kwdef mutable struct IrisModel <: ReactiveModel 14 | iris_data::R{DataTable} = DataTable(data) 15 | credit_data_pagination::DataTablePagination = 16 | DataTablePagination(rows_per_page=50) 17 | 18 | plot_options::PlotOptions = 19 | PlotOptions(chart_type=:scatter, xaxis_type=:numeric) 20 | iris_plot_data::R{Vector{PlotSeries}} = PlotSeries[] 21 | cluster_plot_data::R{Vector{PlotSeries}} = PlotSeries[] 22 | 23 | features::R{Vector{String}} = 24 | ["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"] 25 | xfeature::R{String} = "" 26 | yfeature::R{String} = "" 27 | 28 | no_of_clusters::R{Int} = 3 29 | no_of_iterations::R{Int} = 10 30 | end 31 | 32 | #= Stipple setup =# 33 | 34 | Stipple.register_components(IrisModel, StippleCharts.COMPONENTS) 35 | const ic_model = Stipple.init(IrisModel()) 36 | 37 | #= UI =# 38 | 39 | function ui(model::IrisModel) 40 | [ 41 | dashboard( 42 | vm(model), class="container", title="Iris Flowers Clustering", head_content=Genie.Assets.favicon_support(), 43 | [ 44 | heading("Iris data k-means clustering") 45 | 46 | row([ 47 | cell(class="st-module", [ 48 | h6("Number of clusters") 49 | slider( 1:1:20, 50 | @data(:no_of_clusters); 51 | label=true) 52 | ]) 53 | cell(class="st-module", [ 54 | h6("Number of iterations") 55 | slider( 10:10:200, 56 | @data(:no_of_iterations); 57 | label=true, color="red", label__color="green") 58 | ]) 59 | 60 | cell(class="st-module", [ 61 | h6("X feature") 62 | select(:xfeature; options=:features) 63 | ]) 64 | 65 | cell(class="st-module", [ 66 | h6("Y feature") 67 | select(:yfeature; options=:features) 68 | ]) 69 | ]) 70 | 71 | row([ 72 | cell(class="st-module", [ 73 | h5("Species clusters") 74 | plot(:iris_plot_data; options=:plot_options) 75 | ]) 76 | 77 | cell(class="st-module", [ 78 | h5("k-means clusters") 79 | plot(:cluster_plot_data; options=:plot_options) 80 | ]) 81 | ]) 82 | 83 | row([ 84 | cell(class="st-module", [ 85 | h5("Iris data") 86 | table(:iris_data; pagination=:credit_data_pagination, dense=true, flat=true, style="height: 350px;") 87 | ]) 88 | ]) 89 | ]) 90 | ] 91 | end 92 | 93 | #= routing =# 94 | 95 | route("/") do 96 | ui(ic_model) |> html 97 | end 98 | 99 | #= start server =# 100 | 101 | up(rand((8000:9000)), open_browser=true) -------------------------------------------------------------------------------- /_temp_/IrisClustering3.jl: -------------------------------------------------------------------------------- 1 | using Stipple 2 | using StippleUI 3 | using StippleCharts 4 | 5 | using Clustering 6 | import RDatasets: dataset 7 | import DataFrames 8 | 9 | #= Data =# 10 | 11 | data = DataFrames.insertcols!(dataset("datasets", "iris"), :Cluster => zeros(Int, 150)) 12 | 13 | Base.@kwdef mutable struct IrisModel <: ReactiveModel 14 | iris_data::R{DataTable} = DataTable(data) 15 | credit_data_pagination::DataTablePagination = 16 | DataTablePagination(rows_per_page=50) 17 | 18 | plot_options::PlotOptions = 19 | PlotOptions(chart_type=:scatter, xaxis_type=:numeric) 20 | iris_plot_data::R{Vector{PlotSeries}} = PlotSeries[] 21 | cluster_plot_data::R{Vector{PlotSeries}} = PlotSeries[] 22 | 23 | features::R{Vector{String}} = 24 | ["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"] 25 | xfeature::R{String} = "" 26 | yfeature::R{String} = "" 27 | 28 | no_of_clusters::R{Int} = 3 29 | no_of_iterations::R{Int} = 10 30 | end 31 | 32 | #= Stipple setup =# 33 | 34 | Stipple.register_components(IrisModel, StippleCharts.COMPONENTS) 35 | const ic_model = Stipple.init(IrisModel()) 36 | 37 | #= Event handlers =# 38 | 39 | onany(ic_model.xfeature, ic_model.yfeature, ic_model.no_of_clusters, ic_model.no_of_iterations) do (_...) 40 | ic_model.iris_plot_data[] = plot_data(:Species) 41 | compute_clusters!() 42 | end 43 | 44 | #= Computation =# 45 | 46 | function plot_data(cluster_column::Symbol) 47 | result = Vector{PlotSeries}() 48 | isempty(ic_model.xfeature[]) || isempty(ic_model.yfeature[]) && return result 49 | 50 | dimensions = Dict() 51 | for s in Array(data[:, cluster_column]) |> unique! 52 | dimensions[s] = [] 53 | 54 | for r in eachrow(data[data[cluster_column] .== s, :]) 55 | push!(dimensions[s], [r[Symbol(ic_model.xfeature[])], r[Symbol(ic_model.yfeature[])]]) 56 | end 57 | 58 | push!(result, PlotSeries("$s", PlotData(dimensions[s]))) 59 | end 60 | 61 | result 62 | end 63 | 64 | function compute_clusters!() 65 | features = collect(Matrix(data[:, [Symbol(c) for c in ic_model.features[]]])') 66 | result = kmeans(features, ic_model.no_of_clusters[]; maxiter=ic_model.no_of_iterations[]) 67 | data[:Cluster] = assignments(result) 68 | ic_model.iris_data[] = DataTable(data) 69 | ic_model.cluster_plot_data[] = plot_data(:Cluster) 70 | 71 | nothing 72 | end 73 | 74 | #= UI =# 75 | 76 | function ui(model::IrisModel) 77 | [ 78 | dashboard( 79 | vm(model), class="container", title="Iris Flowers Clustering", head_content=Genie.Assets.favicon_support(), 80 | [ 81 | heading("Iris data k-means clustering") 82 | 83 | row([ 84 | cell(class="st-module", [ 85 | h6("Number of clusters") 86 | slider( 1:1:20, 87 | @data(:no_of_clusters); 88 | label=true) 89 | ]) 90 | cell(class="st-module", [ 91 | h6("Number of iterations") 92 | slider( 10:10:200, 93 | @data(:no_of_iterations); 94 | label=true) 95 | ]) 96 | 97 | cell(class="st-module", [ 98 | h6("X feature") 99 | select(:xfeature; options=:features) 100 | ]) 101 | 102 | cell(class="st-module", [ 103 | h6("Y feature") 104 | select(:yfeature; options=:features) 105 | ]) 106 | ]) 107 | 108 | row([ 109 | cell(class="st-module", [ 110 | h5("Species clusters") 111 | plot(:iris_plot_data; options=:plot_options) 112 | ]) 113 | 114 | cell(class="st-module", [ 115 | h5("k-means clusters") 116 | plot(:cluster_plot_data; options=:plot_options) 117 | ]) 118 | ]) 119 | 120 | row([ 121 | cell(class="st-module", [ 122 | h5("Iris data") 123 | table(:iris_data; pagination=:credit_data_pagination, dense=true, flat=true, style="height: 350px;") 124 | ]) 125 | ]) 126 | ]) 127 | ] 128 | end 129 | 130 | #= routing =# 131 | 132 | route("/") do 133 | ui(ic_model) |> html 134 | end 135 | 136 | #= start server =# 137 | 138 | up(rand((8000:9000)), open_browser=true) --------------------------------------------------------------------------------