///|
/// APG Meter Pattern
/// https://www.w3.org/WAI/ARIA/apg/patterns/meter/
///
/// A meter is a graphical display of a numeric value that varies within a defined range.
/// Examples: battery percentage, fuel level, disk usage
///
/// Note: Do NOT use meter for:
/// - Values without meaningful maximum (use other representations)
/// - Progress/loading indicators (use progressbar role instead)
///
/// ARIA Roles, States, and Properties:
/// - role="meter"
/// - aria-valuenow: Current value (required)
/// - aria-valuemin: Minimum value (required)
/// - aria-valuemax: Maximum value (required)
/// - aria-valuetext: Human-readable value description (optional)
/// - aria-label/aria-labelledby: Accessible name (required)
///|
/// Create an accessible meter element.
///
/// Parameters:
/// - value: Current value (aria-valuenow)
/// - min: Minimum value (aria-valuemin), default 0
/// - max: Maximum value (aria-valuemax), default 100
/// - aria_label: Accessible label (required if no aria_labelledby)
/// - aria_labelledby: ID of labelling element
/// - aria_valuetext: Human-readable value (e.g., "50% (6 hours) remaining")
/// - children: Visual representation of the meter
///
/// Example:
/// ```
/// meter(75.0, aria_label="Battery level", [])
/// meter(50.0, aria_valuetext="50% (6 hours) remaining", aria_label="Battery", [])
/// ```
pub fn[E] meter(
value : Double,
min? : Double,
max? : Double,
aria_label? : String,
aria_labelledby? : String,
aria_valuetext? : String,
children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
let min_val = min.unwrap_or(0.0)
let max_val = max.unwrap_or(100.0)
let attrs : Array[(String, @luna.Attr[E, String])] = [
("role", @luna.attr_static("meter")),
("aria-valuenow", @luna.attr_static(value.to_string())),
("aria-valuemin", @luna.attr_static(min_val.to_string())),
("aria-valuemax", @luna.attr_static(max_val.to_string())),
]
if aria_label is Some(label) {
attrs.push(("aria-label", @luna.attr_static(label)))
}
if aria_labelledby is Some(id) {
attrs.push(("aria-labelledby", @luna.attr_static(id)))
}
if aria_valuetext is Some(text) {
attrs.push(("aria-valuetext", @luna.attr_static(text)))
}
@luna.h("div", attrs, children)
}
///|
/// Create a dynamic meter with signal-based value.
/// Automatically updates aria-valuenow when the signal changes.
///
/// Parameters:
/// - value: Signal containing current value
/// - min: Minimum value (default 0)
/// - max: Maximum value (default 100)
/// - aria_label: Accessible label
/// - aria_valuetext_fn: Function to generate aria-valuetext from current value
/// - children: Visual representation
pub fn[E] meter_dyn(
value : @resource.Signal[Double],
min? : Double,
max? : Double,
aria_label? : String,
aria_valuetext_fn? : (Double) -> String,
children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
let min_val = min.unwrap_or(0.0)
let max_val = max.unwrap_or(100.0)
let attrs : Array[(String, @luna.Attr[E, String])] = [
("role", @luna.attr_static("meter")),
("aria-valuenow", @luna.attr_dynamic(fn() { value.get().to_string() })),
("aria-valuemin", @luna.attr_static(min_val.to_string())),
("aria-valuemax", @luna.attr_static(max_val.to_string())),
]
if aria_label is Some(label) {
attrs.push(("aria-label", @luna.attr_static(label)))
}
if aria_valuetext_fn is Some(fn_) {
attrs.push(
("aria-valuetext", @luna.attr_dynamic(fn() { fn_(value.get()) })),
)
}
@luna.h("div", attrs, children)
}
///|
/// Create a meter using the native HTML element.
/// Native meter has better browser support but limited styling options.
///
/// Parameters:
/// - value: Current value
/// - min: Minimum value (default 0)
/// - max: Maximum value (default 100)
/// - low: Low threshold (values below are considered low)
/// - high: High threshold (values above are considered high)
/// - optimum: Optimal value
/// - aria_label: Accessible label
/// - children: Fallback content for browsers without meter support
pub fn[E] meter_native(
value : Double,
min? : Double,
max? : Double,
low? : Double,
high? : Double,
optimum? : Double,
aria_label? : String,
children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
let min_val = min.unwrap_or(0.0)
let max_val = max.unwrap_or(100.0)
let attrs : Array[(String, @luna.Attr[E, String])] = [
("value", @luna.attr_static(value.to_string())),
("min", @luna.attr_static(min_val.to_string())),
("max", @luna.attr_static(max_val.to_string())),
]
if low is Some(v) {
attrs.push(("low", @luna.attr_static(v.to_string())))
}
if high is Some(v) {
attrs.push(("high", @luna.attr_static(v.to_string())))
}
if optimum is Some(v) {
attrs.push(("optimum", @luna.attr_static(v.to_string())))
}
if aria_label is Some(label) {
attrs.push(("aria-label", @luna.attr_static(label)))
}
@luna.h("meter", attrs, children)
}