export let chart_points = (b) => {
let points = filter(children(b), (item) => item.kind == "wdoc::draw::chart_point")
points
}
export let chart_point_value = (point) => {
let value = attr_or(point, "value", 0)
value
}
export let chart_point_label = (point) => {
let label = attr_or(point, "label", point.id)
label
}
export let chart_color = (point, i) => {
let color = attr_or(point, "color", "")
let palette = ["#2563eb", "#16a34a", "#f97316", "#dc2626", "#7c3aed", "#0891b2", "#ca8a04", "#64748b"]
let palette_i = min(i, len(palette) - 1)
let result = color != "" ? color : palette[palette_i]
result
}
export let chart_safe_span = (lo, hi) => {
let span = hi == lo ? 1 : hi - lo
span
}
export let chart_unit_y = (value, y_min, y_max) => {
let unit = max(0, min(1, ((value - y_min) * 1.0) / wdoc::chart_safe_span(y_min, y_max)))
unit
}
export let chart_plot_y = (value, y_min, y_max, plot_y, plot_h) => {
let y = plot_y + plot_h - (wdoc::chart_unit_y(value, y_min, y_max) * plot_h)
y
}
export let chart_tick_shapes = (i, tick_count, x, y, w, h, y_min, y_max, grid_stroke, axis_stroke, label_fill) => {
let denom = max(1, tick_count - 1)
let t = (i * 1.0) / denom
let ty = y + h - (t * h)
let value = y_min + (t * wdoc::chart_safe_span(y_min, y_max))
[
{ kind = "line", x1 = x, y1 = ty, x2 = x + w, y2 = ty,
stroke = i == 0 ? axis_stroke : grid_stroke, stroke_width = i == 0 ? 1.5 : 1, opacity = i == 0 ? 1 : 0.65 },
{ kind = "text", x = 0, y = ty - 7, width = x - 8, height = 14,
content = to_string(round(value)), font_size = 10, anchor = "end", fill = label_fill, opacity = 0.72 }
]
}
export let chart_title_shapes = (title, w, title_fill) => {
let shapes = title != "" ? [
{ kind = "text", x = 0, y = 8, width = w, height = 20,
content = title, font_size = 14, font_weight = "600", fill = title_fill }
] : []
shapes
}
export let chart_surface_shapes = (w, h, radius, surface_fill, border_stroke, border_width) => {
let shapes = [
{ kind = "rect", id = "surface", x = 0, y = 0, width = w, height = h, rx = radius,
fill = surface_fill, stroke = border_stroke, stroke_width = border_width,
_wdoc_full_container = "true" }
]
shapes
}
export let bar_chart_bar_shapes = (points, i, x, y, w, h, y_min, y_max, gap, axis_stroke, label_fill) => {
let n = len(points)
let point = points[i]
let value = wdoc::chart_point_value(point)
let slot_w = w / max(1, n)
let bar_w = max(2, slot_w - gap)
let bar_x = x + (i * slot_w) + (gap / 2)
let zero_y = wdoc::chart_plot_y(0, y_min, y_max, y, h)
let value_y = wdoc::chart_plot_y(value, y_min, y_max, y, h)
let bar_y = min(zero_y, value_y)
let bar_h = max(1, abs(zero_y - value_y))
[
{ kind = "rect", x = bar_x, y = bar_y, width = bar_w, height = bar_h, rx = 3,
fill = wdoc::chart_color(point, i), stroke = "none" },
{ kind = "text", x = x + (i * slot_w), y = y + h + 8, width = slot_w, height = 14,
content = wdoc::chart_point_label(point), font_size = 10, fill = label_fill, opacity = 0.78 }
]
}
export let widget_bar_chart = (b) => {
let points = wdoc::chart_points(b)
let n = len(points)
let values = n == 0 ? [0] : map(points, (point) => wdoc::chart_point_value(point))
let w = attr_or(b, "width", 360)
let h = attr_or(b, "height", 240)
let title = attr_or(b, "title", "")
let top_pad = title != "" ? 38 : 18
let left_pad = attr_or(b, "axis_width", 46)
let right_pad = attr_or(b, "right_padding", 18)
let bottom_pad = attr_or(b, "bottom_padding", 34)
let plot_x = left_pad
let plot_y = top_pad
let plot_w = w - left_pad - right_pad
let plot_h = h - top_pad - bottom_pad
let y_min = attr_or(b, "y_min", 0)
let raw_y_max = attr_or(b, "y_max", max(1, max_of(values)))
let y_max = raw_y_max <= y_min ? y_min + 1 : raw_y_max
let tick_count = attr_or(b, "tick_count", 5)
let surface_fill = attr_or(b, "surface_fill", "var(--color-bg)")
let border_stroke = attr_or(b, "border_stroke", "var(--color-nav-border)")
let axis_stroke = attr_or(b, "axis_stroke", border_stroke)
let grid_stroke = attr_or(b, "grid_stroke", border_stroke)
let label_fill = attr_or(b, "label_fill", "currentColor")
let title_fill = attr_or(b, "title_fill", label_fill)
let radius = attr_or(b, "radius", 8)
let gap = attr_or(b, "bar_gap", 10)
let ticks = flatten(map(range(0, tick_count), (i) => wdoc::chart_tick_shapes(i, tick_count, plot_x, plot_y, plot_w, plot_h, y_min, y_max, grid_stroke, axis_stroke, label_fill)))
let bars = flatten(map(range(0, n), (i) => wdoc::bar_chart_bar_shapes(points, i, plot_x, plot_y, plot_w, plot_h, y_min, y_max, gap, axis_stroke, label_fill)))
concat(concat(concat(concat(wdoc::chart_surface_shapes(w, h, radius, surface_fill, border_stroke, 1), wdoc::chart_title_shapes(title, w, title_fill)), ticks), [
{ kind = "line", x1 = plot_x, y1 = plot_y, x2 = plot_x, y2 = plot_y + plot_h,
stroke = axis_stroke, stroke_width = 1.5 },
{ kind = "line", x1 = plot_x, y1 = plot_y + plot_h, x2 = plot_x + plot_w, y2 = plot_y + plot_h,
stroke = axis_stroke, stroke_width = 1.5 }
]), bars)
}
export let line_chart_x_value = (points, i) => {
let value = has(points[i], "x") ? attr_or(points[i], "x", i) : i
value
}
export let line_chart_path = (points, n, x_min, x_max, y_min, y_max, plot_x, plot_y, plot_w, plot_h) => {
let d = n == 0 ? "" : join(" ", map(range(0, n), (i) => {
let px = plot_x + (((wdoc::line_chart_x_value(points, i) - x_min) * 1.0) / wdoc::chart_safe_span(x_min, x_max) * plot_w)
let py = wdoc::chart_plot_y(wdoc::chart_point_value(points[i]), y_min, y_max, plot_y, plot_h)
(i == 0 ? "M " : "L ") + to_string(px) + " " + to_string(py)
}))
d
}
export let line_chart_point_shapes = (points, i, x_min, x_max, y_min, y_max, plot_x, plot_y, plot_w, plot_h, point_radius, fill, label_fill) => {
let px = plot_x + (((wdoc::line_chart_x_value(points, i) - x_min) * 1.0) / wdoc::chart_safe_span(x_min, x_max) * plot_w)
let py = wdoc::chart_plot_y(wdoc::chart_point_value(points[i]), y_min, y_max, plot_y, plot_h)
[
{ kind = "circle", x = px - point_radius, y = py - point_radius, width = point_radius * 2, height = point_radius * 2,
fill = fill, stroke = "var(--color-bg)", stroke_width = 1.5 },
{ kind = "text", x = px - 30, y = plot_y + plot_h + 8, width = 60, height = 14,
content = wdoc::chart_point_label(points[i]), font_size = 10, fill = label_fill, opacity = 0.78 }
]
}
export let widget_line_chart = (b) => {
let points = wdoc::chart_points(b)
let n = len(points)
let values = n == 0 ? [0] : map(points, (point) => wdoc::chart_point_value(point))
let x_values = n == 0 ? [0] : map(range(0, n), (i) => wdoc::line_chart_x_value(points, i))
let w = attr_or(b, "width", 380)
let h = attr_or(b, "height", 240)
let title = attr_or(b, "title", "")
let top_pad = title != "" ? 38 : 18
let left_pad = attr_or(b, "axis_width", 46)
let right_pad = attr_or(b, "right_padding", 22)
let bottom_pad = attr_or(b, "bottom_padding", 34)
let plot_x = left_pad
let plot_y = top_pad
let plot_w = w - left_pad - right_pad
let plot_h = h - top_pad - bottom_pad
let y_min = attr_or(b, "y_min", 0)
let raw_y_max = attr_or(b, "y_max", max(1, max_of(values)))
let y_max = raw_y_max <= y_min ? y_min + 1 : raw_y_max
let x_min = attr_or(b, "x_min", min_of(x_values))
let raw_x_max = attr_or(b, "x_max", max_of(x_values))
let x_max = raw_x_max <= x_min ? x_min + 1 : raw_x_max
let tick_count = attr_or(b, "tick_count", 5)
let surface_fill = attr_or(b, "surface_fill", "var(--color-bg)")
let border_stroke = attr_or(b, "border_stroke", "var(--color-nav-border)")
let axis_stroke = attr_or(b, "axis_stroke", border_stroke)
let grid_stroke = attr_or(b, "grid_stroke", border_stroke)
let label_fill = attr_or(b, "label_fill", "currentColor")
let title_fill = attr_or(b, "title_fill", label_fill)
let line_stroke = attr_or(b, "line_stroke", "var(--color-link)")
let line_width = attr_or(b, "line_width", 2.5)
let show_points = attr_or(b, "show_points", true)
let point_radius = attr_or(b, "point_radius", 4)
let radius = attr_or(b, "radius", 8)
let ticks = flatten(map(range(0, tick_count), (i) => wdoc::chart_tick_shapes(i, tick_count, plot_x, plot_y, plot_w, plot_h, y_min, y_max, grid_stroke, axis_stroke, label_fill)))
let point_shapes = show_points ? flatten(map(range(0, n), (i) => wdoc::line_chart_point_shapes(points, i, x_min, x_max, y_min, y_max, plot_x, plot_y, plot_w, plot_h, point_radius, line_stroke, label_fill))) : []
concat(concat(concat(concat(concat(wdoc::chart_surface_shapes(w, h, radius, surface_fill, border_stroke, 1), wdoc::chart_title_shapes(title, w, title_fill)), ticks), [
{ kind = "line", x1 = plot_x, y1 = plot_y, x2 = plot_x, y2 = plot_y + plot_h,
stroke = axis_stroke, stroke_width = 1.5 },
{ kind = "line", x1 = plot_x, y1 = plot_y + plot_h, x2 = plot_x + plot_w, y2 = plot_y + plot_h,
stroke = axis_stroke, stroke_width = 1.5 },
{ kind = "path", x = 0, y = 0, width = w, height = h,
d = wdoc::line_chart_path(points, n, x_min, x_max, y_min, y_max, plot_x, plot_y, plot_w, plot_h),
fill = "none", stroke = line_stroke, stroke_width = line_width, stroke_linecap = "round", stroke_linejoin = "round" }
]), point_shapes), [])
}
export let pie_chart_slice_path = (cx, cy, r, start_angle, end_angle) => {
let sx = cx + (cos(start_angle) * r)
let sy = cy + (sin(start_angle) * r)
let ex = cx + (cos(end_angle) * r)
let ey = cy + (sin(end_angle) * r)
let large_arc = end_angle - start_angle > pi() ? 1 : 0
"M " + to_string(cx) + " " + to_string(cy) +
" L " + to_string(sx) + " " + to_string(sy) +
" A " + to_string(r) + " " + to_string(r) + " 0 " + to_string(large_arc) + " 1 " + to_string(ex) + " " + to_string(ey) + " Z"
}
export let pie_chart_slice_shapes = (points, i, values, total, cx, cy, r, start_offset, label_fill) => {
let start_value = sum(map(range(0, i), (j) => values[j]))
let end_value = start_value + values[i]
let start_angle = start_offset + (((start_value * 1.0) / total) * pi() * 2)
let end_angle = start_offset + (((end_value * 1.0) / total) * pi() * 2)
let point = points[i]
let mid_angle = (start_angle + end_angle) / 2
let label_x = cx + (cos(mid_angle) * r * 0.62) - 25
let label_y = cy + (sin(mid_angle) * r * 0.62) - 7
[
{ kind = "path", x = 0, y = 0, width = cx * 2, height = cy * 2,
d = wdoc::pie_chart_slice_path(cx, cy, r, start_angle, end_angle),
fill = wdoc::chart_color(point, i), stroke = "var(--color-bg)", stroke_width = 1.5 },
{ kind = "text", x = label_x, y = label_y, width = 50, height = 14,
content = wdoc::chart_point_label(point), font_size = 10, fill = label_fill }
]
}
export let pie_chart_legend_shapes = (points, i, x, y, label_fill) => {
let row_y = y + (i * 20)
[
{ kind = "rect", x = x, y = row_y + 4, width = 10, height = 10, rx = 2,
fill = wdoc::chart_color(points[i], i), stroke = "none" },
{ kind = "text", x = x + 16, y = row_y, width = 110, height = 18,
content = wdoc::chart_point_label(points[i]), font_size = 11, anchor = "start", fill = label_fill }
]
}
export let widget_pie_chart = (b) => {
let raw_points = wdoc::chart_points(b)
let points = filter(raw_points, (point) => wdoc::chart_point_value(point) > 0)
let n = len(points)
let values = n == 0 ? [0] : map(points, (point) => wdoc::chart_point_value(point))
let w = attr_or(b, "width", 360)
let h = attr_or(b, "height", 240)
let title = attr_or(b, "title", "")
let top_pad = title != "" ? 38 : 18
let surface_fill = attr_or(b, "surface_fill", "var(--color-bg)")
let border_stroke = attr_or(b, "border_stroke", "var(--color-nav-border)")
let label_fill = attr_or(b, "label_fill", "currentColor")
let title_fill = attr_or(b, "title_fill", label_fill)
let show_legend = attr_or(b, "show_legend", true)
let radius = attr_or(b, "radius", 8)
let pie_r = attr_or(b, "pie_radius", min((show_legend ? w - 150 : w - 34) / 2, (h - top_pad - 18) / 2))
let cx = 22 + pie_r
let cy = top_pad + pie_r
let total = attr_or(b, "total", max(1, sum(values)))
let start_offset = attr_or(b, "start_angle", -90) * pi() / 180
let slices = n == 1 ? [
{ kind = "circle", x = cx - pie_r, y = cy - pie_r, width = pie_r * 2, height = pie_r * 2,
fill = wdoc::chart_color(points[0], 0), stroke = "var(--color-bg)", stroke_width = 1.5 },
{ kind = "text", x = cx - 30, y = cy - 7, width = 60, height = 14,
content = wdoc::chart_point_label(points[0]), font_size = 10, fill = label_fill }
] : flatten(map(range(0, n), (i) => wdoc::pie_chart_slice_shapes(points, i, values, total, cx, cy, pie_r, start_offset, label_fill)))
let legend = show_legend ? flatten(map(range(0, n), (i) => wdoc::pie_chart_legend_shapes(points, i, cx + pie_r + 24, top_pad + 6, label_fill))) : []
concat(concat(concat(wdoc::chart_surface_shapes(w, h, radius, surface_fill, border_stroke, 1), wdoc::chart_title_shapes(title, w, title_fill)), slices), legend)
}