improve echart exporting

This commit is contained in:
timurgordon
2025-02-20 06:40:19 +03:00
parent 0704c9421d
commit eb80294b0a
7 changed files with 277 additions and 151 deletions

View File

@@ -92,7 +92,7 @@ pub fn playmacro(action Action) !string {
content = sh.wiki(args) or { panic(err) }
}
'graph_title_row' {
content = sh.wiki_title_chart(args)
content = sh.wiki_title_chart(args)!
}
'graph_line_row' {
content = sh.wiki_line_chart(args)!

View File

@@ -260,7 +260,9 @@ pub fn (mut s Sheet) json() string {
// find row, report error if not found
pub fn (s Sheet) row_get(name string) !&Row {
row := s.rows[name] or { return error('could not find row with name: ${name}, available rows: ${s.rows.keys()}') }
row := s.rows[name] or {
return error('could not find row with name: ${name}, available rows: ${s.rows.keys()}')
}
return row
}

View File

@@ -4,7 +4,7 @@ import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console
// format a sheet properly in wiki format
pub fn (mut s Sheet) wiki(args_ RowGetArgs) !string {
pub fn (s Sheet) wiki(args_ RowGetArgs) !string {
mut args := args_
_ := match args.period_type {

View File

@@ -3,22 +3,12 @@ module spreadsheet
import freeflowuniverse.herolib.data.markdownparser.elements
import freeflowuniverse.herolib.ui.console
pub fn (mut s Sheet) wiki_title_chart(args RowGetArgs) string {
if args.title.len > 0 {
titletxt := "
title: {
text: '${args.title}',
subtext: '${args.title_sub}',
left: 'center'
},
"
return titletxt
}
return ''
pub fn (s Sheet) wiki_title_chart(args RowGetArgs) !string {
return s.title_chart(args).markdown()
}
pub fn (mut s_ Sheet) wiki_row_overview(args RowGetArgs) !string {
mut s := s_.filter(args)!
pub fn (s_ Sheet) wiki_row_overview(args RowGetArgs) !string {
s := s_.filter(args)!
rows_values := s.rows.values().map([it.name, it.description, it.tags])
mut rows := []elements.Row{}
@@ -43,146 +33,18 @@ pub fn (mut s_ Sheet) wiki_row_overview(args RowGetArgs) !string {
// produce a nice looking bar chart see
// https://echarts.apache.org/examples/en/editor.html?c=line-stack
pub fn (mut s Sheet) wiki_line_chart(args_ RowGetArgs) !string {
mut args := args_
rownames := s.rownames_get(args)!
header := s.header_get_as_string(args.period_type)!
mut series_lines := []string{}
for rowname in rownames {
data := s.data_get_as_string(RowGetArgs{
...args
rowname: rowname
})!
series_lines << '{
name: \'${rowname}\',
type: \'line\',
stack: \'Total\',
data: [${data}]
}'
}
// TODO: need to implement the multiple results which can come back from the args, can be more than 1
// header := s.header_get_as_string(args.period_type)!
// data := s.data_get_as_string(args)!
// console.print_debug('HERE! ${header}')
// console.print_debug('HERE!! ${data}')
template := "
${s.wiki_title_chart(args)}
tooltip: {
trigger: 'axis'
},
legend: {
data: ${rownames}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [${header}]
},
yAxis: {
type: 'value'
},
series: [${series_lines.join(',')}]
"
out := remove_empty_line('```echarts\n{${template}\n};\n```\n')
return out
pub fn (s Sheet) wiki_line_chart(args_ RowGetArgs) !string {
return s.line_chart(args_)!.markdown()
}
// produce a nice looking bar chart see
// https://echarts.apache.org/examples/en/index.html#chart-type-bar
pub fn (mut s Sheet) wiki_bar_chart(args_ RowGetArgs) !string {
mut args := args_
args.rowname = s.rowname_get(args)!
header := s.header_get_as_string(args.period_type)!
data := s.data_get_as_string(args)!
bar1 := "
${s.wiki_title_chart(args)}
xAxis: {
type: 'category',
data: [${header}]
},
yAxis: {
type: 'value'
},
series: [
{
data: [${data}],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}
]
"
out := remove_empty_line('```echarts\n{${bar1}\n};\n```\n')
return out
pub fn (s Sheet) wiki_bar_chart(args_ RowGetArgs) !string {
return s.bar_chart(args_)!.markdown()
}
// produce a nice looking bar chart see
// https://echarts.apache.org/examples/en/index.html#chart-type-bar
pub fn (mut s Sheet) wiki_pie_chart(args_ RowGetArgs) !string {
mut args := args_
args.rowname = s.rowname_get(args)!
header := s.header_get_as_list(args.period_type)!
data := s.data_get_as_list(args)!
mut radius := ''
if args.size.len > 0 {
radius = "radius: '${args.size}',"
}
if header.len != data.len {
return error('data and header lengths must match.\n${header}\n${data}')
}
mut data_lines := []string{}
for i, _ in data {
data_lines << '{ value: ${data[i]}, name: ${header[i]}}'
}
data_str := '[${data_lines.join(',')}]'
bar1 := "
${s.wiki_title_chart(args)}
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: 'Access From',
type: 'pie',
${radius}
data: ${data_str},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
"
out := remove_empty_line('```echarts\n{${bar1}\n};\n```\n')
return out
pub fn (s Sheet) wiki_pie_chart(args_ RowGetArgs) !string {
return s.pie_chart(args_)!.markdown()
}

87
lib/web/echarts/echarts.v Normal file
View File

@@ -0,0 +1,87 @@
module echarts
import json
import x.json2
pub struct Title {
pub:
text string @[json: 'text'; omitempty]
subtext string @[json: 'subtext'; omitempty]
left string @[json: 'left'; omitempty]
}
pub struct Tooltip {
pub:
trigger string @[json: 'trigger'; omitempty]
}
pub struct Legend {
pub:
data []string @[json: 'data'; omitempty]
orient string @[omitempty]
left string @[omitempty]
}
pub struct Grid {
pub:
left string @[json: 'left'; omitempty]
right string @[json: 'right'; omitempty]
bottom string @[json: 'bottom'; omitempty]
contain_label bool @[json: 'containLabel'; omitempty]
}
pub struct ToolboxFeature {
pub:
save_as_image map[string]string @[json: 'saveAsImage'; omitempty]
}
pub struct Toolbox {
pub:
feature ToolboxFeature @[json: 'feature'; omitempty]
}
pub struct XAxis {
pub:
type_ string @[json: 'type'; omitempty]
boundary_gap bool @[json: 'boundaryGap'; omitempty]
data []string @[json: 'data'; omitempty]
}
pub struct YAxis {
pub:
type_ string @[json: 'type'; omitempty]
}
pub struct Series {
pub:
name string @[json: 'name'; omitempty]
type_ string @[json: 'type'; omitempty]
stack string @[json: 'stack'; omitempty]
data []string @[json: 'data'; omitempty]
radius int @[omitempty]
emphasis Emphasis @[omitempty]
}
pub struct Emphasis {
pub:
item_style ItemStyle @[json: 'itemStyle'; omitempty]
}
pub struct ItemStyle {
pub:
shadow_blur int @[json: 'shadowBlur'; omitempty]
shadow_offset_x int @[json: 'shadowOffsetX'; omitempty]
shadow_color string @[json: 'shadowColor'; omitempty]
}
pub struct EChartsOption {
pub:
title Title @[json: 'title'; omitempty]
tooltip Tooltip @[json: 'tooltip'; omitempty]
legend Legend @[json: 'legend'; omitempty]
grid Grid @[json: 'grid'; omitempty]
toolbox Toolbox @[json: 'toolbox'; omitempty]
x_axis XAxis @[json: 'xAxis'; omitempty]
y_axis YAxis @[json: 'yAxis'; omitempty]
series []Series @[json: 'series'; omitempty]
}

View File

@@ -0,0 +1,55 @@
module echarts
import json
const option_json = '{"title":{"text":"Main Title","subtext":"Subtitle","left":"center"},"tooltip":{"trigger":"axis"},"legend":{"data":["Example1","Example2"]},"grid":{"left":"3%","right":"4%","bottom":"3%","containLabel":true},"xAxis":{"type":"category","data":["Jan","Feb","Mar"]},"yAxis":{"type":"value"},"series":[{"name":"Example1","type":"line","stack":"Total","data":["10","20","30"]},{"name":"Example2","type":"line","stack":"Total","data":["15","25","35"]}]}'
fn test_echarts() {
option := EChartsOption{
title: Title{
text: 'Main Title'
subtext: 'Subtitle'
left: 'center'
}
tooltip: Tooltip{
trigger: 'axis'
}
legend: Legend{
data: ['Example1', 'Example2']
}
grid: Grid{
left: '3%'
right: '4%'
bottom: '3%'
contain_label: true
}
toolbox: Toolbox{
feature: ToolboxFeature{
save_as_image: {}
}
}
x_axis: XAxis{
type_: 'category'
boundary_gap: false
data: ['Jan', 'Feb', 'Mar']
}
y_axis: YAxis{
type_: 'value'
}
series: [
Series{
name: 'Example1'
type_: 'line'
stack: 'Total'
data: ['10', '20', '30']
},
Series{
name: 'Example2'
type_: 'line'
stack: 'Total'
data: ['15', '25', '35']
},
]
}
assert json.encode(option) == option_json
}

120
lib/web/echarts/encode.v Normal file
View File

@@ -0,0 +1,120 @@
module echarts
import x.json2 as json
pub fn (o EChartsOption) json() string {
return json.encode(o)
}
pub fn (o EChartsOption) mdx() string {
option := format_js_object(o, true)
return '<EChart option={${option}} />'
}
pub fn (o EChartsOption) markdown() string {
option := format_js_object(o, true)
return '```echarts\n{${option}\n};\n```\n'
}
// Generic function to format JavaScript-like objects
fn format_js_object[T](obj T, omitempty bool) string {
mut result := ''
result += '{'
$for field in T.fields {
field_name := if field.attrs.any(it.starts_with('json:')) {
field.attrs.filter(it.starts_with('json'))[0].all_after('json:').trim_space()
} else {
field.name
}
value := obj.$(field.name)
formatted_value := format_js_value(value, field.attrs.contains('omitempty'))
if formatted_value.trim_space() != '' || !omitempty {
result += '${field_name}: ${formatted_value.trim_space()}, '
}
}
result += '}'
if result == '{}' && omitempty {
return ''
}
return result.str().replace(', }', '}') // Remove trailing comma
}
// Fully generic function to format any JS value
// TODO: improve code below, far from cleanest implementation
// currently is sufficient since only used in echart mdx export
fn format_js_value[T](value T, omitempty bool) string {
return $if T is string {
// is actually map
if value.str().starts_with('{') && value.str().ends_with('}') {
value
// map_any := json2.raw_decode(value.str()) or {'{}'}.as_map()
// println('debugzo21 ${map_any}')
// mut val := '{'
// for k, v in map_any {
// val += '${k}: ${format_js_value(v.str(), false)}'
// }
// val += '}'
// if val == '{}' && omitempty {
// return ''
// }
// val
} else {
val := '"${value}"'
if val == '""' && omitempty {
return ''
}
val
}
} $else $if T is int {
if '${value}' == '0' && omitempty {
''
} else {
'${value}'
}
} $else $if T is f64 {
if '${value}' == '0.0' && omitempty {
''
} else {
'${value}'
}
} $else $if T is bool {
if '${value}' == 'false' && omitempty {
''
} else {
'${value}'
}
} $else $if T is $struct {
val := format_js_object(value, omitempty)
if val == '' && omitempty {
return ''
}
val
} $else $if T is $array {
mut arr := '['
for i in 0 .. value.len {
if i != 0 {
arr += ', '
}
val := format_js_value(value[i], omitempty)
if val.starts_with('"{') && val.ends_with('}"') {
arr += val.trim('"')
} else if val.starts_with('"\'') && val.ends_with('\'"') {
arr += val.trim('"')
} else if val.trim('"').trim_space().f64() != 0 {
arr += val.trim('"').trim_space()
} else if val.trim('"').trim_space() == '0' || val.trim('"').trim_space() == '0.0' {
arr += '0'
} else {
arr += val
}
}
arr += ']'
if omitempty && arr == '[]' {
return ''
}
arr
} $else {
'null'
}
}