From 0212b26a7d6bbcdd041df8fe37c4eeca98a7de3d Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Wed, 28 Aug 2024 16:04:39 +0200 Subject: [PATCH 1/4] wip --- Project.toml | 6 ++-- demos/tables/app.jl | 2 +- demos/tables/app2.jl | 43 +++++++++++++++++++++++++ src/Tables.jl | 75 +++++++++++++++++++++++++++++++++----------- 4 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 demos/tables/app2.jl diff --git a/Project.toml b/Project.toml index 86509677..22f77e93 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "StippleUI" uuid = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" authors = ["Adrian Salceanu "] -version = "0.23.3" +version = "0.23.4" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -22,10 +22,10 @@ StippleUIDataFramesExt = "DataFrames" Colors = "0.12" DataFrames = "1" Dates = "1.6" -Genie = "5.23.8" +Genie = "5.30.4" OrderedCollections = "1" PrecompileTools = "1" -Stipple = "0.28.11" +Stipple = "0.28.13" Tables = "1" julia = "1.6" diff --git a/demos/tables/app.jl b/demos/tables/app.jl index e76d7047..38b57060 100644 --- a/demos/tables/app.jl +++ b/demos/tables/app.jl @@ -6,7 +6,7 @@ using DataFrames @app begin NO_OF_ROWS = 1000 - DATA = sort!(DataFrame(rand(NO_OF_ROWS, 2), ["x1", "x2"])) + DATA = sort!(DataFrame(rand(NO_OF_ROWS, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating ROWS_PER_PAGE = 10 @out df = DATA diff --git a/demos/tables/app2.jl b/demos/tables/app2.jl new file mode 100644 index 00000000..bf4410bc --- /dev/null +++ b/demos/tables/app2.jl @@ -0,0 +1,43 @@ +module App + +using GenieFramework +using DataFrames +@genietools + +@app begin + NO_OF_ROWS = 1000 + DATA = sort!(DataFrame(rand(NO_OF_ROWS, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating + ROWS_PER_PAGE = 10 + + @out df = DATA + @out dt = DataTable(DataFrame([]), DataTableOptions(DATA)) + @out pagination = DataTablePagination(rows_per_page = ROWS_PER_PAGE, rows_number = NO_OF_ROWS) + @out loading = false + @in filter = "" + + @onchange isready begin + dt = DataTable(DATA[1:ROWS_PER_PAGE, :]) + filter = "" + end + + @event request begin + loading = true + state = process_request(DATA, dt, pagination, filter) + dt = state.datatable + pagination = state.pagination + loading = false + end + + @onchange filter begin + loading = true + pagination.page = 1 + state = process_request(DATA, dt, pagination[], filter) + dt = state.datatable + pagination = state.pagination + loading = false + end +end + +@page("/", "ui.jl") + +end \ No newline at end of file diff --git a/src/Tables.jl b/src/Tables.jl index ff75ef70..ed8939a2 100644 --- a/src/Tables.jl +++ b/src/Tables.jl @@ -1,5 +1,6 @@ module Tables +using Base: @kwdef import Tables as TablesInterface using Genie, Stipple, StippleUI, StippleUI.API import Genie.Renderer.Html: HTMLString, normal_element, table, template, register_normal_element @@ -10,14 +11,14 @@ export cell_template, qtd, qtr register_normal_element("q__table", context = @__MODULE__) const ID = "__id" -const DATAKEY = "data" # has to be changed to `rows` for Quasar 2 +const DATAKEY = "data" # has to be changed to `rows` for Quasar 2 const DataTableSelection = Vector{Dict{String, Any}} struct2dict(s::T) where T = Dict{Symbol, Any}(zip(fieldnames(T), getfield.(Ref(s), fieldnames(T)))) #===# -Base.@kwdef mutable struct Column +@kwdef mutable struct Column name::String required::Bool = false label::String = name @@ -71,7 +72,7 @@ end julia> DataTablePagination(rows_per_page=50) ``` """ -Base.@kwdef mutable struct DataTablePagination +@kwdef mutable struct DataTablePagination sort_by::Symbol = :desc descending::Bool = false page::Int = 1 @@ -103,7 +104,7 @@ julia> dt.opts.columnspecs[r"^(a|b)\$"] = opts(format = jsfunction(raw"(val, row julia> model.table[] = dt ``` """ -Base.@kwdef mutable struct DataTableOptions +@kwdef mutable struct DataTableOptions addid::Bool = false idcolumn::String = "ID" columns::Union{Vector{Column},Nothing} = nothing @@ -111,11 +112,6 @@ Base.@kwdef mutable struct DataTableOptions end -mutable struct DataTable{T} - data::T - opts::DataTableOptions -end - """ DataTable(data::T, opts::DataTableOptions) @@ -140,12 +136,53 @@ julia> Tables.table([1 2 3; 3 4 5], header = ["a", "b", "c"]) julia> dt = DataTable(t1) ``` """ +mutable struct DataTable{T} + data::T + opts::DataTableOptions + filter::AbstractString + pagination::DataTablePagination +end + +# function DataTable{T}() where {T} +# DataTable{T}(T(), DataTableOptions(), "", DataTablePagination()) +# end + +function DataTable{T}(data::T, opts::DataTableOptions) where {T} + DataTable{T}(data, opts, "", DataTablePagination()) +end +function DataTable(data::T, opts::DataTableOptions) where {T} + DataTable{T}(data, opts) +end + function DataTable(data::T) where {T} DataTable{T}(data, DataTableOptions()) end +function DataTable{T}( + data::T + + ; + filter::AbstractString = "", + + # DataTableOptions + addid::Bool = false, + idcolumn::String = "ID", + columns::Union{Vector{Column},Nothing} = nothing, + columnspecs::Dict{Union{String, Regex}, Dict{Symbol, Any}} = Dict(), + + # DataTablePagination + sort_by::Symbol = :desc, + descending::Bool = false, + page::Int = 1, + rows_per_page::Int = 10, + rows_number::Union{Int,Nothing} = nothing, -function DataTable{T}() where {T} - DataTable{T}(T(), DataTableOptions()) + _filter::AbstractString = filter +) where {T} + DataTable{T}( data, + DataTableOptions(addid, idcolumn, columns, columnspecs), + filter, + DataTablePagination(sort_by, descending, page, rows_per_page, rows_number, _filter) + ) end #===# @@ -326,7 +363,7 @@ function cell_template(; columns isa Vector || columns === nothing || (columns = [columns]) if edit isa Bool # `columns` decides on which columns should be editable - # + # columns === nothing && (columns = [""]) if edit edit_columns = columns @@ -353,7 +390,7 @@ function cell_template(; ) ]) push!(cell_templates, t) - end + end isempty(edit_columns) && return cell_templates @@ -361,7 +398,7 @@ function cell_template(; if change_class == "text-red " change_class = change_style === nothing ? "text-red" : nothing end - + # in contrast to `props.value` `props.row[props.col.name]` can be written to value = "props.row[props.col.name]" # ref_rows are calculated from ref_table, if not defined explicitly @@ -386,11 +423,11 @@ function cell_template(; if inner_class !== nothing && isempty(inner_class) inner_class = nothing end - + # add standard settings from stipplecore.css table_style = Dict("font-weight" => 400, "font-size" => "0.9rem", "padding-top" => 0, "padding-bottom" => 0) inner_style = inner_style === nothing ? table_style : [table_style, inner_style] - + # add custom style for changed entries if ref_rows !== nothing && change_style !== nothing change_style_js = JSON3.write(render(change_style)) @@ -479,7 +516,7 @@ function table( fieldname::Symbol, columnskey::String = "$fieldname.columns", filter::Union{Symbol,String,Nothing} = nothing, paginationsync::Union{Symbol,String,Nothing} = nothing, - + columns::Union{Nothing,Bool,Integer,AbstractString,Vector{<:AbstractString},Vector{<:Integer}} = nothing, cell_class::Union{Nothing,AbstractString,AbstractDict,Vector} = nothing, cell_style::Union{Nothing,AbstractString,AbstractDict,Vector} = nothing, @@ -492,7 +529,7 @@ function table( fieldname::Symbol, change_style::Union{Nothing,AbstractString,AbstractDict,Vector} = nothing, change_inner_class::Union{Nothing,AbstractString,AbstractDict,Vector} = nothing, change_inner_style::Union{Nothing,AbstractString,AbstractDict,Vector} = nothing, - + kwargs...) :: ParsedHTMLString if !isa(edit, Bool) || edit || cell_class !== nothing || cell_style !== nothing @@ -503,7 +540,7 @@ function table( fieldname::Symbol, startswith(String(p[1]), "inner_") ? p : nothing end - table_template = cell_template(; ref_table, ref_rows, rowkey, + table_template = cell_template(; ref_table, ref_rows, rowkey, edit, columns, class = cell_class, style = cell_style, type = cell_type, inner_class, inner_style, change_class, change_style, change_inner_class, change_inner_style, cell_kwargs..., inner_kwargs... ) From 09915f17cd95d0d682a0385e8d76b6520016f41b Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Thu, 29 Aug 2024 16:43:24 +0200 Subject: [PATCH 2/4] wip --- demos/tables/app.jl | 4 +-- demos/tables/app2.jl | 32 ++++--------------- demos/tables/ui2.jl | 7 ++++ demos/tables/ui2.jl.html | 11 +++++++ src/Tables.jl | 69 +++++++++++++++++++++++++++++----------- 5 files changed, 77 insertions(+), 46 deletions(-) create mode 100644 demos/tables/ui2.jl create mode 100644 demos/tables/ui2.jl.html diff --git a/demos/tables/app.jl b/demos/tables/app.jl index 38b57060..83375928 100644 --- a/demos/tables/app.jl +++ b/demos/tables/app.jl @@ -5,9 +5,9 @@ using DataFrames @genietools @app begin - NO_OF_ROWS = 1000 + NO_OF_ROWS = 1_000_000 DATA = sort!(DataFrame(rand(NO_OF_ROWS, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating - ROWS_PER_PAGE = 10 + ROWS_PER_PAGE = 100 @out df = DATA @out dt = DataTable(DataFrame([]), DataTableOptions(DATA)) diff --git a/demos/tables/app2.jl b/demos/tables/app2.jl index bf4410bc..f75e4f70 100644 --- a/demos/tables/app2.jl +++ b/demos/tables/app2.jl @@ -5,39 +5,21 @@ using DataFrames @genietools @app begin - NO_OF_ROWS = 1000 - DATA = sort!(DataFrame(rand(NO_OF_ROWS, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating - ROWS_PER_PAGE = 10 + DATA = sort!(DataFrame(rand(1_000_000, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating - @out df = DATA - @out dt = DataTable(DataFrame([]), DataTableOptions(DATA)) - @out pagination = DataTablePagination(rows_per_page = ROWS_PER_PAGE, rows_number = NO_OF_ROWS) - @out loading = false - @in filter = "" + @out dt = DataTable(DataFrame(x1=[], x2=[])) @onchange isready begin - dt = DataTable(DATA[1:ROWS_PER_PAGE, :]) - filter = "" + dt.data = DataFrame(DATA[1:StippleUI.Tables.DEFAULT_ROWS_PER_PAGE, :]) + @push end @event request begin - loading = true - state = process_request(DATA, dt, pagination, filter) - dt = state.datatable - pagination = state.pagination - loading = false - end - - @onchange filter begin - loading = true - pagination.page = 1 - state = process_request(DATA, dt, pagination[], filter) - dt = state.datatable - pagination = state.pagination - loading = false + dt = DataTable!(dt[]; data = DATA) + @push end end -@page("/", "ui.jl") +@page("/", "ui2.jl.html") end \ No newline at end of file diff --git a/demos/tables/ui2.jl b/demos/tables/ui2.jl new file mode 100644 index 00000000..19bf70f5 --- /dev/null +++ b/demos/tables/ui2.jl @@ -0,0 +1,7 @@ +table(:dt, + paginationsync = Symbol("dt.pagination"), + @on("request", :request), + loading = :loading, + filter = Symbol("dt.filter"), + title = "Random data" +) \ No newline at end of file diff --git a/demos/tables/ui2.jl.html b/demos/tables/ui2.jl.html new file mode 100644 index 00000000..41b55fc1 --- /dev/null +++ b/demos/tables/ui2.jl.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/src/Tables.jl b/src/Tables.jl index ed8939a2..207a39fb 100644 --- a/src/Tables.jl +++ b/src/Tables.jl @@ -13,6 +13,7 @@ register_normal_element("q__table", context = @__MODULE__) const ID = "__id" const DATAKEY = "data" # has to be changed to `rows` for Quasar 2 const DataTableSelection = Vector{Dict{String, Any}} +const DEFAULT_ROWS_PER_PAGE = 50 struct2dict(s::T) where T = Dict{Symbol, Any}(zip(fieldnames(T), getfield.(Ref(s), fieldnames(T)))) @@ -76,7 +77,7 @@ julia> DataTablePagination(rows_per_page=50) sort_by::Symbol = :desc descending::Bool = false page::Int = 1 - rows_per_page::Int = 10 + rows_per_page::Int = DEFAULT_ROWS_PER_PAGE rows_number::Union{Int,Nothing} = nothing _filter::AbstractString = "" # keep track of filter value for improving performance end @@ -139,23 +140,26 @@ julia> dt = DataTable(t1) mutable struct DataTable{T} data::T opts::DataTableOptions - filter::AbstractString pagination::DataTablePagination + filter::AbstractString end -# function DataTable{T}() where {T} -# DataTable{T}(T(), DataTableOptions(), "", DataTablePagination()) -# end +function DataTable{T}(data::T, opts::DataTableOptions, pagination::DataTablePagination) where {T} + DataTable{T}(data, opts, pagination, "") +end +function DataTable(data::T, opts::DataTableOptions, pagination::DataTablePagination) where {T} + DataTable{T}(data, opts, pagination) +end function DataTable{T}(data::T, opts::DataTableOptions) where {T} - DataTable{T}(data, opts, "", DataTablePagination()) + DataTable{T}(data, opts, DataTablePagination(), "") end function DataTable(data::T, opts::DataTableOptions) where {T} DataTable{T}(data, opts) end -function DataTable(data::T) where {T} - DataTable{T}(data, DataTableOptions()) +function DataTable(data::T; kwargs...) where {T} + DataTable{T}(data; kwargs...) end function DataTable{T}( data::T @@ -167,21 +171,26 @@ function DataTable{T}( addid::Bool = false, idcolumn::String = "ID", columns::Union{Vector{Column},Nothing} = nothing, - columnspecs::Dict{Union{String, Regex}, Dict{Symbol, Any}} = Dict(), + columnspecs::Dict = Dict(), # DataTablePagination sort_by::Symbol = :desc, descending::Bool = false, page::Int = 1, - rows_per_page::Int = 10, + rows_per_page::Int = DEFAULT_ROWS_PER_PAGE, rows_number::Union{Int,Nothing} = nothing, - - _filter::AbstractString = filter ) where {T} + try + isnothing(rows_number) && (rows_number = length(TablesInterface.rows(data))) + catch ex + @error ex + rows_number = nothing + end + DataTable{T}( data, DataTableOptions(addid, idcolumn, columns, columnspecs), - filter, - DataTablePagination(sort_by, descending, page, rows_per_page, rows_number, _filter) + DataTablePagination(sort_by, descending, page, rows_per_page, rows_number, filter), + filter ) end @@ -249,8 +258,10 @@ end function data(t::T; datakey = "data", columnskey = "columns")::Dict{String,Any} where {T<:DataTable} OrderedDict( - columnskey => columns(t), - datakey => rows(t) + columnskey => columns(t), + datakey => rows(t), + "pagination" => render(t.pagination), + "filter" => t.filter ) end @@ -631,11 +642,20 @@ function Base.parse(::Type{DataTablePagination}, d::Dict{String,Any}) dtp.sort_by = get!(d, "sortBy", "desc") |> Symbol dtp.page = get!(d, "page", 1) dtp.descending = get!(d, "descending", false) - dtp.rows_per_page = get!(d, "rowsPerPage", 10) + dtp.rows_per_page = get!(d, "rowsPerPage", DEFAULT_ROWS_PER_PAGE) dtp end +function Base.parse(::Type{DataTable}, d::Dict{String,Any}) + DataTable( + d["data"], + DataTableOptions(d["opts"]), + DataTablePagination(d["pagination"]), + d["filter"] + ) +end + function DataTableOptions(d::AbstractDict) DataTableOptions(d["addid"], d["idcolumn"], d["columns"], d["columnspecs"]) end @@ -658,6 +678,7 @@ function DataTableWithSelection(data::T) where {T} DataTableWithSelection(dt, DataTablePagination(), DataTableSelection()) end +Base.getindex(dt::DataTable) = dt Base.getindex(dt::DataTable, args...) = DataTable(dt.data[args...], dt.opts) Base.getindex(dt::DataTable, row::Int, col) = DataTable(dt.data[[row], col], dt.opts) Base.getindex(dt::DataTable, row, col::Int) = DataTable(dt.data[row, col::Int], dt.opts) @@ -778,6 +799,7 @@ function process_request(data, datatable::DataTable, pagination::DataTablePagina isa(get(event, "event", false), AbstractDict) && isa(get(event["event"], "name", false), AbstractString) && event["event"]["name"] == "request" + filter = get(event["event"]["event"], "filter", "") event = event["event"]["event"]["pagination"] else event = Dict() @@ -820,10 +842,19 @@ function process_request(data, datatable::DataTable, pagination::DataTablePagina start_row = (event["page"] - 1) * event["rowsPerPage"] + 1 end_row = event["page"] * event["rowsPerPage"] - datatable = typeof(datatable)(fd[(start_row <= pagination.rows_number ? start_row : pagination.rows_number) : (end_row <= pagination.rows_number ? end_row : pagination.rows_number), :], datatable.opts) pagination = typeof(pagination)(rows_per_page = event["rowsPerPage"], rows_number = pagination.rows_number, page = event["page"], sort_by = event["sortBy"], descending = event["descending"], _filter = pagination._filter) - return (data = fd, datatable = datatable, pagination = pagination) + datatable = typeof(datatable)(fd[(start_row <= pagination.rows_number ? start_row : pagination.rows_number) : (end_row <= pagination.rows_number ? end_row : pagination.rows_number), :], datatable.opts) + datatable.pagination = pagination + datatable.filter = filter + + return (data = fd, datatable = datatable, pagination = pagination, filter = filter) +end + +export DataTable! + +function DataTable!(datatable::DataTable; data = datatable.data, pagination = datatable.pagination, filter = "")::DataTable + process_request(data, datatable, pagination, filter).datatable end register_normal_element("q__td", context = @__MODULE__) From 6dff1f2b70f99d95e971443a1ed54e828615be84 Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Fri, 30 Aug 2024 21:09:12 +0200 Subject: [PATCH 3/4] Automatically handle too large payloads --- Project.toml | 4 ++-- demos/tables/app2.jl | 21 ++++++++++--------- demos/tables/ui2.jl | 16 ++++++++++----- src/Tables.jl | 48 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index 22f77e93..4cf7d609 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "StippleUI" uuid = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" authors = ["Adrian Salceanu "] -version = "0.23.4" +version = "0.23.3" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -25,7 +25,7 @@ Dates = "1.6" Genie = "5.30.4" OrderedCollections = "1" PrecompileTools = "1" -Stipple = "0.28.13" +Stipple = "0.28.14" Tables = "1" julia = "1.6" diff --git a/demos/tables/app2.jl b/demos/tables/app2.jl index f75e4f70..f5684bba 100644 --- a/demos/tables/app2.jl +++ b/demos/tables/app2.jl @@ -4,22 +4,25 @@ using GenieFramework using DataFrames @genietools +StippleUI.Tables.set_max_rows_client_side(2000) + @app begin - DATA = sort!(DataFrame(rand(1_000_000, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating + big_data = sort!(DataFrame(rand(1_000_000, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating + small_data = sort!(DataFrame(rand(1_001, 2), ["y1", "y2"]))::DataFrame - @out dt = DataTable(DataFrame(x1=[], x2=[])) + @out dt1 = DataTable(big_data; rows_per_page = 20) + @out dt2 = DataTable(small_data; rows_per_page = 20, server_side = false) - @onchange isready begin - dt.data = DataFrame(DATA[1:StippleUI.Tables.DEFAULT_ROWS_PER_PAGE, :]) - @push - end + @out loading = false - @event request begin - dt = DataTable!(dt[]; data = DATA) + @event paginate_dt1 begin + @paginate(dt1, big_data) @push end + end -@page("/", "ui2.jl.html") +# @page("/", "ui2.jl.html") +@page("/", "ui2.jl") end \ No newline at end of file diff --git a/demos/tables/ui2.jl b/demos/tables/ui2.jl index 19bf70f5..b7924bb6 100644 --- a/demos/tables/ui2.jl +++ b/demos/tables/ui2.jl @@ -1,7 +1,13 @@ -table(:dt, - paginationsync = Symbol("dt.pagination"), - @on("request", :request), +table(:dt1, + paginationsync = Symbol("dt1.pagination"), + @on("request", :paginate_dt1), loading = :loading, - filter = Symbol("dt.filter"), - title = "Random data" + filter = Symbol("dt1.filter"), + title = "Random big data" +) +table(:dt2, + paginationsync = Symbol("dt2.pagination"), + loading = :loading, + filter = Symbol("dt2.filter"), + title = "Random small data" ) \ No newline at end of file diff --git a/src/Tables.jl b/src/Tables.jl index 207a39fb..d34fc1ab 100644 --- a/src/Tables.jl +++ b/src/Tables.jl @@ -7,6 +7,7 @@ import Genie.Renderer.Html: HTMLString, normal_element, table, template, registe export Column, DataTablePagination, DataTableOptions, DataTable, DataTableSelection, DataTableWithSelection, rowselection, selectrows! export cell_template, qtd, qtr +export DataTable!, @paginate register_normal_element("q__table", context = @__MODULE__) @@ -14,6 +15,9 @@ const ID = "__id" const DATAKEY = "data" # has to be changed to `rows` for Quasar 2 const DataTableSelection = Vector{Dict{String, Any}} const DEFAULT_ROWS_PER_PAGE = 50 +const DEFAULT_MAX_ROWS_CLIENT_SIDE = Ref(1000) + +set_max_rows_client_side(n) = (DEFAULT_MAX_ROWS_CLIENT_SIDE[] = n) struct2dict(s::T) where T = Dict{Symbol, Any}(zip(fieldnames(T), getfield.(Ref(s), fieldnames(T)))) @@ -179,9 +183,30 @@ function DataTable{T}( page::Int = 1, rows_per_page::Int = DEFAULT_ROWS_PER_PAGE, rows_number::Union{Int,Nothing} = nothing, + + # options + server_side::Bool = true ) where {T} + if (isnothing(rows_number) && ! server_side) && size(data, 1) > DEFAULT_MAX_ROWS_CLIENT_SIDE[] + @warn """ + The number of rows exceeds the maximum number of rows that can be displayed client side. + This can have negative effects on performance, both in terms of loading time and responsiveness. + Automatically truncating your data to $(DEFAULT_MAX_ROWS_CLIENT_SIDE[]) rows. + + If you want to display more rows client side, + call `StippleUI.Tables.set_max_rows_client_side(n)` with `n` the number of rows you want to display. + Current maximum number of client side rows is: $(DEFAULT_MAX_ROWS_CLIENT_SIDE[]) + """ + try + data = data[1:DEFAULT_MAX_ROWS_CLIENT_SIDE[], :] + catch ex + @error "Failed to truncate data to $(DEFAULT_MAX_ROWS_CLIENT_SIDE[]) rows. Warning, this can have negative effects on performance." + @error ex + end + end + try - isnothing(rows_number) && (rows_number = length(TablesInterface.rows(data))) + isnothing(rows_number) && server_side && (rows_number = length(TablesInterface.rows(data))) catch ex @error ex rows_number = nothing @@ -257,9 +282,15 @@ function rows(t::T)::Vector{OrderedDict{String,Any}} where {T<:DataTable} end function data(t::T; datakey = "data", columnskey = "columns")::Dict{String,Any} where {T<:DataTable} + total_rows = size(t.data)[1] + max_rows = total_rows > t.pagination.rows_per_page && t.pagination.rows_number !== nothing ? + t.pagination.rows_per_page : + total_rows + 1 + data = total_rows >= max_rows ? t[1:max_rows, :] : t + OrderedDict( columnskey => columns(t), - datakey => rows(t), + datakey => data |> rows, "pagination" => render(t.pagination), "filter" => t.filter ) @@ -798,7 +829,9 @@ function process_request(data, datatable::DataTable, pagination::DataTablePagina if event !== nothing && isa(get(event, "event", false), AbstractDict) && isa(get(event["event"], "name", false), AbstractString) && - event["event"]["name"] == "request" + isa(get(event["event"], "event", false), AbstractDict) && + isa(get(event["event"]["event"], "pagination", false), AbstractDict) && + isa(get(event["event"]["event"], "filter", false), AbstractString) filter = get(event["event"]["event"], "filter", "") event = event["event"]["event"]["pagination"] else @@ -851,8 +884,6 @@ function process_request(data, datatable::DataTable, pagination::DataTablePagina return (data = fd, datatable = datatable, pagination = pagination, filter = filter) end -export DataTable! - function DataTable!(datatable::DataTable; data = datatable.data, pagination = datatable.pagination, filter = "")::DataTable process_request(data, datatable, pagination, filter).datatable end @@ -891,4 +922,11 @@ end Base.string(tr::Tr) = tr(tr.args...; tr.kwargs...) + +macro paginate(varname, data) + quote + $varname = DataTable!($varname[]; data = $data) + end |> esc +end + end From 545a0a24bf956137efc19f65d8c214fc838b7801 Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Mon, 2 Sep 2024 16:25:44 +0200 Subject: [PATCH 4/4] Improved support for server side tables, version bump --- Project.toml | 2 +- demos/tables/app2.jl | 12 +++++------- demos/tables/ui2.jl | 32 ++++++++++++++++++++++++-------- demos/tables/ui2.jl.html | 2 +- src/Tables.jl | 34 +++++++++++++++++++++++++--------- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/Project.toml b/Project.toml index 4cf7d609..5b81acce 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "StippleUI" uuid = "a3c5d34a-b254-4859-a8fa-b86abb7e84a3" authors = ["Adrian Salceanu "] -version = "0.23.3" +version = "0.23.4" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" diff --git a/demos/tables/app2.jl b/demos/tables/app2.jl index f5684bba..866d97b4 100644 --- a/demos/tables/app2.jl +++ b/demos/tables/app2.jl @@ -4,25 +4,23 @@ using GenieFramework using DataFrames @genietools -StippleUI.Tables.set_max_rows_client_side(2000) +StippleUI.Tables.set_default_rows_per_page(20) +StippleUI.Tables.set_max_rows_client_side(11_000) @app begin big_data = sort!(DataFrame(rand(1_000_000, 2), ["x1", "x2"]))::DataFrame # we only sort so that the changes are more visible when filtering and paginating - small_data = sort!(DataFrame(rand(1_001, 2), ["y1", "y2"]))::DataFrame - @out dt1 = DataTable(big_data; rows_per_page = 20) - @out dt2 = DataTable(small_data; rows_per_page = 20, server_side = false) + @out dt1 = DataTable(big_data; server_side = true) + @out dt2 = DataTable(big_data) @out loading = false - @event paginate_dt1 begin + @event dt1_request begin @paginate(dt1, big_data) @push end - end -# @page("/", "ui2.jl.html") @page("/", "ui2.jl") end \ No newline at end of file diff --git a/demos/tables/ui2.jl b/demos/tables/ui2.jl index b7924bb6..fe462863 100644 --- a/demos/tables/ui2.jl +++ b/demos/tables/ui2.jl @@ -1,13 +1,29 @@ table(:dt1, - paginationsync = Symbol("dt1.pagination"), - @on("request", :paginate_dt1), + server_side = true, loading = :loading, - filter = Symbol("dt1.filter"), - title = "Random big data" + title = "Server side data" ) table(:dt2, - paginationsync = Symbol("dt2.pagination"), loading = :loading, - filter = Symbol("dt2.filter"), - title = "Random small data" -) \ No newline at end of file + title = "Client side data" +) + +# ParsedHTMLString(""" +# +# +# +# """) diff --git a/demos/tables/ui2.jl.html b/demos/tables/ui2.jl.html index 41b55fc1..b49a255f 100644 --- a/demos/tables/ui2.jl.html +++ b/demos/tables/ui2.jl.html @@ -1,5 +1,5 @@