Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
essenciary committed Nov 22, 2023
2 parents eaa82ef + ad79b14 commit 9314f8b
Show file tree
Hide file tree
Showing 11 changed files with 341 additions and 14 deletions.
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Stipple"
uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998"
authors = ["Adrian <[email protected]>"]
version = "0.27.14"
version = "0.27.19"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand All @@ -27,7 +27,7 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
DataFrames = "1"
Dates = "1.6"
FilePathsBase = "0.9"
Genie = "5.20"
Genie = "5.23.0"
GenieSession = "1"
GenieSessionFileSession = "1"
JSON = "0.20, 0.21"
Expand Down
8 changes: 8 additions & 0 deletions assets/js/watchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ const watcherMixin = {

const reviveMixin = {
methods: {
revive_jsfunction: function (k, v) {
if ( (typeof v==='object') && (v!=null) && (v.jsfunction) ) {
return Function(v.jsfunction.arguments, v.jsfunction.body)
} else {
return v
}
},
// deprecated, kept for compatibility
revive_payload: function(obj) {
if (typeof obj === 'object') {
for (var key in obj) {
Expand Down
5 changes: 2 additions & 3 deletions src/Elements.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,13 @@ function vue_integration(::Type{M};
window.parse_payload = function(payload){
if (payload.key) {
window.$(vue_app_name).revive_payload(payload)
window.$(vue_app_name).updateField(payload.key, payload.value);
window.$(vue_app_name).updateField(payload.key, payload.value);
}
}
function app_ready() {
$vue_app_name.isready = true;
Genie.Revivers.addReviver(window.$(vue_app_name).revive_jsfunction);
$(transport == Genie.WebChannels &&
"
try {
Expand Down
44 changes: 43 additions & 1 deletion src/Layout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Layout

using Genie, Stipple

export layout
export layout, add_css, remove_css
export page, app, row, column, cell, container, flexgrid_kwargs

export theme
Expand Down Expand Up @@ -300,5 +300,47 @@ function theme(; core_theme::Bool = true) :: Vector{String}
output
end

"""
add_css(css::Function; update = true)
Add a css function to the `THEMES`.
### Params
* `css::Function` - a function that results in a vector of style elements
* `update` - determines, whether existing style sheets with the same name shall be removed
### Example
```julia
# css to remove the stipple-core color format of q-table rows
# (this will enable font color setting by the attribute `table-class`)
function mycss()
[
style(\"\"\"
.stipple-core .q-table tbody tr { color: inherit; }
\"\"\")
]
end
add_css(mycss)
```
`
"""
function add_css(css::Function; update = true)
# removal can only be done by name, as the old function has already been overwritten
update && remove_css(css::Function, byname = true)
push!(Stipple.Layout.THEMES, css)
end

"""
remove_css(css::Function, byname::Bool = false)
Remove a stylesheet function from the stack (`Stipple.Layout.THEMES`)
If called with `byname = true`, the function will be identified by name rather than by the function itself.
"""
function remove_css(css::Function; byname::Bool = false)
inds = byname ? nameof.(Stipple.Layout.THEMES) .== nameof(css) : Stipple.Layout.THEMES .== css
deleteat!(Stipple.Layout.THEMES, inds)
end

end
9 changes: 6 additions & 3 deletions src/Pages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ function Page( route::Union{Route,String};
else
model
end

view = if isa(view, ParsedHTMLString) || isa(view, Vector{<:AbstractString})
string(view)
view = if isa(view, Function) || isa(view, ParsedHTMLString)
view
elseif isa(view, Vector{ParsedHTMLString})
ParsedHTMLString(view)
elseif isa(view, Vector{<:AbstractString})
join(view)
elseif isa(view, AbstractString)
isfile(view) ? filepath(view) : view
else
Expand Down
68 changes: 68 additions & 0 deletions src/ReactiveTools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export @readonly, @private, @in, @out, @jsfn, @readonly!, @private!, @in!, @out!
#definition of handlers/events
export @onchange, @onbutton, @event, @notify

# definition of dependencies
export @deps, @clear_deps

# deletion
export @clear, @clear_vars, @clear_handlers

Expand Down Expand Up @@ -1302,4 +1305,69 @@ macro modelstorage()
end |> esc
end

"""
@deps f
Add a function f to the dependencies of the current app.
------------------------
@deps M::Module
Add the dependencies of the module M to the dependencies of the current app.
"""
macro deps(expr)
quote
Stipple.deps!(Stipple.@type(), $expr)
end |> esc
end

"""
@deps(MyApp::ReactiveModel, f::Function)
Add a function f to the dependencies of the app MyApp.
The module needs to define a function `deps()`.
------------------------
@deps(MyApp::ReactiveModel, M::Module)
Add the dependencies of the module M to the dependencies of the app MyApp.
The module needs to define a function `deps()`.
"""
macro deps(M, expr)
quote
Stipple.deps!($M, $expr)
end |> esc
end

"""
@clear_deps
Delete all dependencies of the current app.
------------------------
@clear_deps MyApp
Delete all dependencies of the app MyApp.
"""
macro clear_deps()
quote
Stipple.clear_deps!(Stipple.@type())
end |> esc
end

macro clear_deps(M)
quote
Stipple.clear_deps!($M)
end |> esc
end

end
39 changes: 36 additions & 3 deletions src/Stipple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ using Logging, Mixers, Random, Reexport, Dates, Tables
@reexport using Observables
@reexport @using_except Genie: download
import Genie.Router.download
@reexport @using_except Genie.Renderer.Html: mark, div, time, view, render, Headers
@reexport @using_except Genie.Renderers.Html: mark, div, time, view, render, Headers, menu
const htmldiv = Html.div
export render, htmldiv, js_attr
@reexport using JSON3
Expand Down Expand Up @@ -731,11 +731,21 @@ end
function injectdeps(output::Vector{AbstractString}, M::Type{<:ReactiveModel}) :: Vector{AbstractString}
for (key, f) in DEPS
key isa DataType && key <: ReactiveModel && continue
# exclude keys starting with '_'
key isa Symbol && startswith("$key", '_') && continue
push!(output, f()...)
end
AM = get_abstract_type(M)
haskey(DEPS, AM) && push!(output, DEPS[AM]()...)

if haskey(DEPS, AM)
# DEPS[AM] contains the stipple-generated deps
push!(output, DEPS[AM]()...)
# furthermore, include deps who's keys start with "_<name of the ReactiveModel>_"
model_prefix = "_$(vm(AM))_"
for (key, f) in DEPS
key isa Symbol || continue
startswith("$key", model_prefix) && push!(output, f()...)
end
end
output
end

Expand Down Expand Up @@ -774,6 +784,29 @@ function deps!(m::Any, f::Function)
DEPS[m] = f
end

function deps!(m::Any, M::Module)
DEPS[m] = M.deps
end

function deps!(M::Type{<:ReactiveModel}, f::Function; extra_deps = true)
key = extra_deps ? Symbol("_$(vm(M))_$(nameof(f))") : M
DEPS[key] = f isa Function ? f : f.deps
end

deps!(M::Type{<:ReactiveModel}, modul::Module; extra_deps = true) = deps!(M, modul.deps; extra_deps)

deps!(m::Any, v::Vector{Union{Function, Module}}) = deps!.(Ref(m), v)
deps!(m::Any, t::Tuple) = [deps!(m, f) for f in t]
deps!(m, args...) = [deps!(m, f) for f in args]

function clear_deps!(M::Type{<:ReactiveModel})
delete!(DEPS, M)
model_prefix = "_$(vm(M))_"
for k in keys(Stipple.DEPS)
k isa Symbol && startswith("$k", model_prefix) && delete!(DEPS, k)
end
end

@specialize

"""
Expand Down
52 changes: 51 additions & 1 deletion src/stipple/jsmethods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,54 @@ for (f, field) in (
end

const jscreated = js_created
const jsmounted = js_mounted
const jsmounted = js_mounted

"""
js_add_reviver(revivername::String)
Add a reviver function to the list of Genie's revivers.
### Example
Adding the reviver function of 'mathjs'
```julia
js_add_reviver("math.reviver")
```
This function is meant for package developer who want to make additional js content available for the user.
This resulting script needs to be added to the dependencies of an app in order to be executed.
For detailed information have a look at the package StippleMathjs.
If you want to add a custom reviver to your model you should rather consider using `@mounted`, e.g.
```julia
@methods \"\"\"
myreviver: function(key, value) { return (key.endsWith('_onebased') ? value - 1 : value) }
\"\"\"
@mounted "Genie.Revivers.addReviver(this.myreviver)"
```
"""
function js_add_reviver(revivername::String)
"""
Genie.WebChannels.subscriptionHandlers.push(function(event) {
Genie.Revivers.addReviver($revivername);
});
"""
end

"""
js_initscript(initscript::String)
Add a js script that is executed as soon as the connection to the server is established.
It needs to be added to the dependencies of an app in order to be executed, e.g.
```julia
@deps () -> [script(js_initscript("console.log('Hello from my App')"))]
```
"""
function js_initscript(initscript::String)
"""
Genie.WebChannels.subscriptionHandlers.push(function(event) {
$(initscript)
});
"""
end
3 changes: 3 additions & 0 deletions src/stipple/reactivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ end
import Base.map!
@inline Base.map!(f::F, r::Reactive, os...; update::Bool=true) where F = Base.map!(f::F, getfield(r, :o), os...; update=update)

Base.axes(r::Reactive, args...) = Base.axes(getfield(getfield(r, :o), :val), args...)
Base.lastindex(r::Reactive, args...) = Base.lastindex(getfield(getfield(r, :o), :val), args...)

const R = Reactive
const PUBLIC = 1
const PRIVATE = 2
Expand Down
23 changes: 22 additions & 1 deletion src/stipple/rendering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ function julia_to_vue(field, mapping_keys = mapping_keys()) :: String
end
end

"""
jsrender(x, args...)
Defines separate rendering for the Vue instance. This method is only necessary for non-standard types that
are not transmittable by regular json. Such types need a reviver to be transmitted via json and (optionally)
a render method that is only applied for the rendering of the model.
The model is not transmitted via json but via a js-file. So there it is possible to define non-serializable values.
### Example
```
Stipple.render(z::Complex) = Dict(:mathjs => "Complex", :re => z.re, :im => z.im)
function Stipple.jsrender(z::Union{Complex, R{<:Complex}}, args...)
JSONText("math.complex('\$(replace(strip(repr(Observables.to_value(z)), '"'), 'm' => ""))')")
end
Stipple.stipple_parse(::Complex, z::Dict{String, Any}) = float(z["re"]) + z["im"]
```
"""
jsrender(x, args...) = render(x, args...)
jsrender(r::Reactive, args...) = jsrender(getfield(getfield(r,:o), :val), args...)

"""
function Stipple.render(app::M, fieldname::Union{Symbol,Nothing} = nothing)::Dict{Symbol,Any} where {M<:ReactiveModel}
Expand All @@ -107,7 +128,7 @@ function Stipple.render(app::M)::Dict{Symbol,Any} where {M<:ReactiveModel}
occursin(SETTINGS.private_pattern, String(field)) && continue
f isa Reactive && f.r_mode == PRIVATE && continue

result[julia_to_vue(field)] = Stipple.render(f, field)
result[julia_to_vue(field)] = Stipple.jsrender(f, field)
end

vue = Dict( :el => JSONText("rootSelector"),
Expand Down
Loading

0 comments on commit 9314f8b

Please sign in to comment.