ratatui_ruby 1.4.0-x86_64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +15 -0
- data/LICENSES/AGPL-3.0-or-later.txt +661 -0
- data/LICENSES/CC-BY-SA-4.0.txt +427 -0
- data/LICENSES/CC0-1.0.txt +121 -0
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/LICENSES/MIT.txt +21 -0
- data/REUSE.toml +42 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/.cargo/config.toml +13 -0
- data/ext/ratatui_ruby/.gitignore +4 -0
- data/ext/ratatui_ruby/Cargo.lock +1737 -0
- data/ext/ratatui_ruby/Cargo.toml +24 -0
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +21 -0
- data/ext/ratatui_ruby/src/color.rs +82 -0
- data/ext/ratatui_ruby/src/errors.rs +28 -0
- data/ext/ratatui_ruby/src/events.rs +700 -0
- data/ext/ratatui_ruby/src/frame.rs +241 -0
- data/ext/ratatui_ruby/src/lib.rs +343 -0
- data/ext/ratatui_ruby/src/lib_header.rs +11 -0
- data/ext/ratatui_ruby/src/rendering.rs +158 -0
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/style.rs +469 -0
- data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
- data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
- data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
- data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
- data/ext/ratatui_ruby/src/terminal/queries.rs +231 -0
- data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
- data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
- data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
- data/ext/ratatui_ruby/src/text.rs +225 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
- data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
- data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
- data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
- data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
- data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
- data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
- data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
- data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
- data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
- data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
- data/lib/ratatui_ruby/backend/window_size.rb +50 -0
- data/lib/ratatui_ruby/backend.rb +59 -0
- data/lib/ratatui_ruby/buffer/cell.rb +212 -0
- data/lib/ratatui_ruby/buffer.rb +149 -0
- data/lib/ratatui_ruby/cell.rb +208 -0
- data/lib/ratatui_ruby/debug.rb +215 -0
- data/lib/ratatui_ruby/draw.rb +63 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
- data/lib/ratatui_ruby/event/key/character.rb +53 -0
- data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
- data/lib/ratatui_ruby/event/key/media.rb +46 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
- data/lib/ratatui_ruby/event/key/system.rb +47 -0
- data/lib/ratatui_ruby/event/key.rb +479 -0
- data/lib/ratatui_ruby/event/mouse.rb +291 -0
- data/lib/ratatui_ruby/event/none.rb +53 -0
- data/lib/ratatui_ruby/event/paste.rb +130 -0
- data/lib/ratatui_ruby/event/resize.rb +221 -0
- data/lib/ratatui_ruby/event/sync.rb +52 -0
- data/lib/ratatui_ruby/event.rb +163 -0
- data/lib/ratatui_ruby/frame.rb +257 -0
- data/lib/ratatui_ruby/labs/a11y.rb +182 -0
- data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
- data/lib/ratatui_ruby/labs.rb +47 -0
- data/lib/ratatui_ruby/layout/alignment.rb +91 -0
- data/lib/ratatui_ruby/layout/constraint.rb +337 -0
- data/lib/ratatui_ruby/layout/layout.rb +258 -0
- data/lib/ratatui_ruby/layout/position.rb +81 -0
- data/lib/ratatui_ruby/layout/rect.rb +733 -0
- data/lib/ratatui_ruby/layout/size.rb +62 -0
- data/lib/ratatui_ruby/layout.rb +29 -0
- data/lib/ratatui_ruby/list_state.rb +201 -0
- data/lib/ratatui_ruby/output_guard.rb +171 -0
- data/lib/ratatui_ruby/ratatui_ruby.so +0 -0
- data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
- data/lib/ratatui_ruby/style/color.rb +149 -0
- data/lib/ratatui_ruby/style/style.rb +147 -0
- data/lib/ratatui_ruby/style.rb +19 -0
- data/lib/ratatui_ruby/symbols.rb +435 -0
- data/lib/ratatui_ruby/synthetic_events.rb +106 -0
- data/lib/ratatui_ruby/table_state.rb +251 -0
- data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
- data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
- data/lib/ratatui_ruby/terminal.rb +66 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +241 -0
- data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
- data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
- data/lib/ratatui_ruby/test_helper.rb +115 -0
- data/lib/ratatui_ruby/text/line.rb +245 -0
- data/lib/ratatui_ruby/text/span.rb +158 -0
- data/lib/ratatui_ruby/text.rb +99 -0
- data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
- data/lib/ratatui_ruby/tui/core.rb +67 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
- data/lib/ratatui_ruby/tui.rb +106 -0
- data/lib/ratatui_ruby/version.rb +12 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
- data/lib/ratatui_ruby/widgets/block.rb +266 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
- data/lib/ratatui_ruby/widgets/cell.rb +59 -0
- data/lib/ratatui_ruby/widgets/center.rb +71 -0
- data/lib/ratatui_ruby/widgets/chart.rb +172 -0
- data/lib/ratatui_ruby/widgets/clear.rb +66 -0
- data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
- data/lib/ratatui_ruby/widgets/list.rb +252 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
- data/lib/ratatui_ruby/widgets/row.rb +123 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
- data/lib/ratatui_ruby/widgets/table.rb +213 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
- data/lib/ratatui_ruby/widgets.rb +43 -0
- data/lib/ratatui_ruby.rb +555 -0
- data/sig/examples/app_all_events/app.rbs +11 -0
- data/sig/examples/app_all_events/model/app_model.rbs +23 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +23 -0
- data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
- data/sig/examples/app_all_events/view/app_view.rbs +8 -0
- data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
- data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
- data/sig/examples/app_all_events/view/live_view.rbs +6 -0
- data/sig/examples/app_all_events/view/log_view.rbs +6 -0
- data/sig/examples/app_all_events/view.rbs +14 -0
- data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
- data/sig/examples/app_color_picker/app.rbs +17 -0
- data/sig/examples/app_external_editor/app.rbs +12 -0
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_stateful_interaction/app.rbs +39 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
- data/sig/examples/verify_readme_usage/app.rbs +17 -0
- data/sig/examples/widget_block_demo/app.rbs +38 -0
- data/sig/examples/widget_box_demo/app.rbs +17 -0
- data/sig/examples/widget_calendar_demo/app.rbs +17 -0
- data/sig/examples/widget_cell_demo/app.rbs +17 -0
- data/sig/examples/widget_chart_demo/app.rbs +17 -0
- data/sig/examples/widget_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_layout_split/app.rbs +16 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_list_demo/app.rbs +17 -0
- data/sig/examples/widget_map_demo/app.rbs +17 -0
- data/sig/examples/widget_popup_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
- data/sig/examples/widget_rect/app.rbs +18 -0
- data/sig/examples/widget_render/app.rbs +16 -0
- data/sig/examples/widget_rich_text/app.rbs +17 -0
- data/sig/examples/widget_scroll_text/app.rbs +17 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
- data/sig/examples/widget_style_colors/app.rbs +20 -0
- data/sig/examples/widget_table_demo/app.rbs +17 -0
- data/sig/examples/widget_text_width/app.rbs +16 -0
- data/sig/generated/event_key_predicates.rbs +1348 -0
- data/sig/manifest.yaml +5 -0
- data/sig/patches/data.rbs +26 -0
- data/sig/patches/debugger__.rbs +8 -0
- data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
- data/sig/ratatui_ruby/backend.rbs +12 -0
- data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
- data/sig/ratatui_ruby/buffer.rbs +18 -0
- data/sig/ratatui_ruby/cell.rbs +44 -0
- data/sig/ratatui_ruby/clear.rbs +18 -0
- data/sig/ratatui_ruby/constraint.rbs +26 -0
- data/sig/ratatui_ruby/debug.rbs +45 -0
- data/sig/ratatui_ruby/draw.rbs +30 -0
- data/sig/ratatui_ruby/event.rbs +249 -0
- data/sig/ratatui_ruby/frame.rbs +23 -0
- data/sig/ratatui_ruby/interfaces.rbs +25 -0
- data/sig/ratatui_ruby/labs.rbs +90 -0
- data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
- data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
- data/sig/ratatui_ruby/layout/layout.rbs +45 -0
- data/sig/ratatui_ruby/layout/position.rbs +18 -0
- data/sig/ratatui_ruby/layout/rect.rbs +64 -0
- data/sig/ratatui_ruby/layout/size.rbs +18 -0
- data/sig/ratatui_ruby/list_state.rbs +23 -0
- data/sig/ratatui_ruby/output_guard.rbs +23 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
- data/sig/ratatui_ruby/rect.rbs +17 -0
- data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
- data/sig/ratatui_ruby/session.rbs +93 -0
- data/sig/ratatui_ruby/style/color.rbs +22 -0
- data/sig/ratatui_ruby/style/style.rbs +29 -0
- data/sig/ratatui_ruby/symbols.rbs +141 -0
- data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
- data/sig/ratatui_ruby/table_state.rbs +27 -0
- data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
- data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
- data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
- data/sig/ratatui_ruby/test_helper.rbs +18 -0
- data/sig/ratatui_ruby/text/line.rbs +27 -0
- data/sig/ratatui_ruby/text/span.rbs +23 -0
- data/sig/ratatui_ruby/text.rbs +12 -0
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
- data/sig/ratatui_ruby/tui/core.rbs +23 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
- data/sig/ratatui_ruby/tui.rbs +25 -0
- data/sig/ratatui_ruby/version.rbs +12 -0
- data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
- data/sig/ratatui_ruby/widgets/block.rbs +51 -0
- data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
- data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
- data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
- data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
- data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
- data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
- data/sig/ratatui_ruby/widgets/list.rbs +63 -0
- data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
- data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
- data/sig/ratatui_ruby/widgets/row.rbs +43 -0
- data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
- data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
- data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
- data/sig/ratatui_ruby/widgets/table.rbs +78 -0
- data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
- data/sig/ratatui_ruby/widgets.rbs +16 -0
- data/vendor/goodcop/base.yml +1047 -0
- metadata +729 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
module Widgets
|
|
10
|
+
# Displays categorical data as bars.
|
|
11
|
+
#
|
|
12
|
+
# Raw tables of numbers are hard to scan. Comparing magnitudes requires mental arithmetic, which slows down decision-making.
|
|
13
|
+
#
|
|
14
|
+
# This widget visualizes the data. It renders vertical bars proportional to their value.
|
|
15
|
+
#
|
|
16
|
+
# Use it to compare server loads, sales figures, or any discrete datasets.
|
|
17
|
+
#
|
|
18
|
+
# {rdoc-image:/doc/images/widget_barchart.png}[link:/examples/widget_barchart/app_rb.html]
|
|
19
|
+
#
|
|
20
|
+
# === Example
|
|
21
|
+
#
|
|
22
|
+
# Run the interactive demo from the terminal:
|
|
23
|
+
#
|
|
24
|
+
#--
|
|
25
|
+
# SPDX-SnippetBegin
|
|
26
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
27
|
+
# SPDX-License-Identifier: MIT-0
|
|
28
|
+
#++
|
|
29
|
+
# ruby examples/widget_barchart/app.rb
|
|
30
|
+
#
|
|
31
|
+
# # Grouped Bar Chart
|
|
32
|
+
# BarChart.new(
|
|
33
|
+
# data: [
|
|
34
|
+
# BarGroup.new(label: "Q1", bars: [Bar.new(value: 40), Bar.new(value: 45)]),
|
|
35
|
+
# BarGroup.new(label: "Q2", bars: [Bar.new(value: 50), Bar.new(value: 55)])
|
|
36
|
+
# ],
|
|
37
|
+
# bar_width: 5,
|
|
38
|
+
# group_gap: 3
|
|
39
|
+
# )
|
|
40
|
+
#--
|
|
41
|
+
# SPDX-SnippetEnd
|
|
42
|
+
#++
|
|
43
|
+
class BarChart < Data.define(:data, :bar_width, :bar_gap, :group_gap, :max, :style, :block, :direction, :label_style, :value_style, :bar_set)
|
|
44
|
+
include CoerceableWidget
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
##
|
|
48
|
+
##
|
|
49
|
+
##
|
|
50
|
+
# :attr_reader: data
|
|
51
|
+
# The data to display.
|
|
52
|
+
#
|
|
53
|
+
# Supports multiple formats:
|
|
54
|
+
# [<tt>Hash</tt>]
|
|
55
|
+
# Mapping labels (<tt>String</tt> or <tt>Symbol</tt>) to values (<tt>Integer</tt>).
|
|
56
|
+
# [<tt>Array</tt> of tuples]
|
|
57
|
+
# Ordered list of <tt>["Label", Value]</tt> or <tt>["Label", Value, Style]</tt> pairs.
|
|
58
|
+
# [<tt>Array</tt> of <tt>BarChart::BarGroup</tt>]
|
|
59
|
+
#--
|
|
60
|
+
# SPDX-SnippetBegin
|
|
61
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
62
|
+
# SPDX-License-Identifier: MIT-0
|
|
63
|
+
#++
|
|
64
|
+
# List of <tt>BarChart::BarGroup</tt> objects for grouped charts.
|
|
65
|
+
#
|
|
66
|
+
#--
|
|
67
|
+
# SPDX-SnippetEnd
|
|
68
|
+
#++
|
|
69
|
+
# === Examples
|
|
70
|
+
#
|
|
71
|
+
# Hash (Simple):
|
|
72
|
+
#--
|
|
73
|
+
# SPDX-SnippetBegin
|
|
74
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
75
|
+
# SPDX-License-Identifier: MIT-0
|
|
76
|
+
#++
|
|
77
|
+
# { "Apples" => 10, :Oranges => 15 }
|
|
78
|
+
#
|
|
79
|
+
#--
|
|
80
|
+
# SPDX-SnippetEnd
|
|
81
|
+
#++
|
|
82
|
+
# Array of Tuples (Ordered):
|
|
83
|
+
#--
|
|
84
|
+
# SPDX-SnippetBegin
|
|
85
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
86
|
+
# SPDX-License-Identifier: MIT-0
|
|
87
|
+
#++
|
|
88
|
+
# [["Mon", 20], ["Tue", 30], ["Wed", 25]]
|
|
89
|
+
#
|
|
90
|
+
#--
|
|
91
|
+
# SPDX-SnippetEnd
|
|
92
|
+
#++
|
|
93
|
+
# BarGroup (Grouped):
|
|
94
|
+
#--
|
|
95
|
+
# SPDX-SnippetBegin
|
|
96
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
97
|
+
# SPDX-License-Identifier: MIT-0
|
|
98
|
+
#++
|
|
99
|
+
# [
|
|
100
|
+
# RatatuiRuby::BarChart::BarGroup.new(label: "Q1", bars: [
|
|
101
|
+
# RatatuiRuby::BarChart::Bar.new(value: 50, label: "Rev"),
|
|
102
|
+
# RatatuiRuby::BarChart::Bar.new(value: 30, label: "Cost")
|
|
103
|
+
# ])
|
|
104
|
+
# ]
|
|
105
|
+
#--
|
|
106
|
+
# SPDX-SnippetEnd
|
|
107
|
+
#++
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
# :attr_reader: bar_width
|
|
111
|
+
# Width of each bar in characters.
|
|
112
|
+
|
|
113
|
+
##
|
|
114
|
+
# :attr_reader: bar_gap
|
|
115
|
+
# Spaces between bars.
|
|
116
|
+
|
|
117
|
+
##
|
|
118
|
+
# :attr_reader: group_gap
|
|
119
|
+
# Spaces between groups (for grouped bar charts).
|
|
120
|
+
|
|
121
|
+
##
|
|
122
|
+
# :attr_reader: max
|
|
123
|
+
# Maximum value for the Y-axis (optional).
|
|
124
|
+
#
|
|
125
|
+
# If nil, it is calculated from the data.
|
|
126
|
+
|
|
127
|
+
##
|
|
128
|
+
# :attr_reader: style
|
|
129
|
+
# Style for the bars.
|
|
130
|
+
|
|
131
|
+
##
|
|
132
|
+
# :attr_reader: block
|
|
133
|
+
# Optional wrapping block.
|
|
134
|
+
|
|
135
|
+
##
|
|
136
|
+
# :attr_reader: label_style
|
|
137
|
+
# Style for the bar labels (optional).
|
|
138
|
+
|
|
139
|
+
##
|
|
140
|
+
# :attr_reader: value_style
|
|
141
|
+
# Style for the bar values (optional).
|
|
142
|
+
|
|
143
|
+
##
|
|
144
|
+
# :attr_reader: bar_set
|
|
145
|
+
# Custom characters for the bars (optional).
|
|
146
|
+
#
|
|
147
|
+
# A Hash with keys defining the characters for the bars.
|
|
148
|
+
# Keys: <tt>:empty</tt>, <tt>:one_eighth</tt>, <tt>:one_quarter</tt>, <tt>:three_eighths</tt>, <tt>:half</tt>, <tt>:five_eighths</tt>, <tt>:three_quarters</tt>, <tt>:seven_eighths</tt>, <tt>:full</tt>.
|
|
149
|
+
#
|
|
150
|
+
# You can also use integers (0-8) as keys, where 0 is empty, 4 is half, and 8 is full.
|
|
151
|
+
#
|
|
152
|
+
# Alternatively, you can pass an Array of 9 strings, where index 0 is empty and index 8 is full.
|
|
153
|
+
#
|
|
154
|
+
# === Examples
|
|
155
|
+
#
|
|
156
|
+
#--
|
|
157
|
+
# SPDX-SnippetBegin
|
|
158
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
159
|
+
# SPDX-License-Identifier: MIT-0
|
|
160
|
+
#++
|
|
161
|
+
# bar_set: {
|
|
162
|
+
# empty: " ",
|
|
163
|
+
# one_eighth: " ",
|
|
164
|
+
# one_quarter: "▂",
|
|
165
|
+
# three_eighths: "▃",
|
|
166
|
+
# half: "▄",
|
|
167
|
+
# five_eighths: "▅",
|
|
168
|
+
# three_quarters: "▆",
|
|
169
|
+
# seven_eighths: "▇",
|
|
170
|
+
# full: "█"
|
|
171
|
+
# }
|
|
172
|
+
#
|
|
173
|
+
# # Numeric keys (0-8)
|
|
174
|
+
# bar_set: {
|
|
175
|
+
# 0 => " ", 1 => " ", 2 => "▂", 3 => "▃", 4 => "▄", 5 => "▅", 6 => "▆", 7 => "▇", 8 => "█"
|
|
176
|
+
# }
|
|
177
|
+
#
|
|
178
|
+
# # Array (9 items)
|
|
179
|
+
# bar_set: [" ", " ", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
|
|
180
|
+
#--
|
|
181
|
+
# SPDX-SnippetEnd
|
|
182
|
+
#++
|
|
183
|
+
|
|
184
|
+
BAR_KEYS = %i[empty one_eighth one_quarter three_eighths half five_eighths three_quarters seven_eighths full].freeze
|
|
185
|
+
|
|
186
|
+
# Creates a new BarChart widget.
|
|
187
|
+
#
|
|
188
|
+
# [data]
|
|
189
|
+
# Data to display. Hash, Array of arrays, or Array of BarGroup.
|
|
190
|
+
# [bar_width]
|
|
191
|
+
# Width of each bar (Integer).
|
|
192
|
+
# [bar_gap]
|
|
193
|
+
# Gap between bars (Integer).
|
|
194
|
+
# [group_gap]
|
|
195
|
+
# Gap between groups (Integer).
|
|
196
|
+
# [max]
|
|
197
|
+
# Maximum value of the bar chart (Integer).
|
|
198
|
+
# [style]
|
|
199
|
+
# Base style for the widget (Style).
|
|
200
|
+
# [block]
|
|
201
|
+
# Block to render around the chart (Block).
|
|
202
|
+
# [direction]
|
|
203
|
+
# Direction of the bars (:vertical or :horizontal).
|
|
204
|
+
# [label_style]
|
|
205
|
+
# Style object for labels (optional).
|
|
206
|
+
# [value_style]
|
|
207
|
+
# Style object for values (optional).
|
|
208
|
+
# [bar_set]
|
|
209
|
+
#--
|
|
210
|
+
# SPDX-SnippetBegin
|
|
211
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
212
|
+
# SPDX-License-Identifier: MIT-0
|
|
213
|
+
#++
|
|
214
|
+
# Symbol, Hash, or Array: Custom characters for bars.
|
|
215
|
+
# Symbols: <tt>:nine_levels</tt> (default gradient), <tt>:three_levels</tt> (simplified).
|
|
216
|
+
#--
|
|
217
|
+
# SPDX-SnippetEnd
|
|
218
|
+
#++
|
|
219
|
+
def initialize(data:, bar_width: 3, bar_gap: 1, group_gap: 0, max: nil, style: nil, block: nil, direction: :vertical, label_style: nil, value_style: nil, bar_set: nil)
|
|
220
|
+
# Normalize bar_set to Hash[Symbol, String] if provided as Array or Hash
|
|
221
|
+
bar_set = case bar_set
|
|
222
|
+
when Symbol, nil
|
|
223
|
+
bar_set
|
|
224
|
+
when Array
|
|
225
|
+
# Convert Array to Hash using BAR_KEYS order
|
|
226
|
+
BAR_KEYS.zip(bar_set).to_h
|
|
227
|
+
when Hash
|
|
228
|
+
# @type var raw_hash: Hash[untyped, untyped]
|
|
229
|
+
raw_hash = bar_set.dup
|
|
230
|
+
normalized = {} #: Hash[Symbol, String]
|
|
231
|
+
# Normalize numeric keys (0-8) to symbolic keys
|
|
232
|
+
BAR_KEYS.each_with_index do |key, i|
|
|
233
|
+
val = raw_hash.delete(i) || raw_hash.delete(i.to_s) || raw_hash.delete(key)
|
|
234
|
+
normalized[key] = val.to_s if val
|
|
235
|
+
end
|
|
236
|
+
normalized
|
|
237
|
+
else
|
|
238
|
+
bar_set
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Normalize data to Array of BarGroup
|
|
242
|
+
data = if data.is_a?(Hash)
|
|
243
|
+
if direction == :horizontal
|
|
244
|
+
bars = data.map do |label, value|
|
|
245
|
+
Bar.new(value:, label: label.to_s)
|
|
246
|
+
end
|
|
247
|
+
[BarGroup.new(label: "", bars:)]
|
|
248
|
+
else
|
|
249
|
+
data.map do |label, value|
|
|
250
|
+
BarGroup.new(label: label.to_s, bars: [Bar.new(value:)])
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
elsif data.is_a?(Array)
|
|
254
|
+
if data.empty?
|
|
255
|
+
[]
|
|
256
|
+
elsif data.first.is_a?(BarGroup)
|
|
257
|
+
data
|
|
258
|
+
elsif data.first.is_a?(Array)
|
|
259
|
+
# Tuples - use type assertion for Steep
|
|
260
|
+
if direction == :horizontal
|
|
261
|
+
bars = data.map do |item|
|
|
262
|
+
tuple = item #: Array[untyped]
|
|
263
|
+
label = tuple[0].to_s
|
|
264
|
+
value = tuple[1]
|
|
265
|
+
style = tuple[2]
|
|
266
|
+
|
|
267
|
+
bar = Bar.new(value:, label:)
|
|
268
|
+
bar = bar.with(style:) if style
|
|
269
|
+
bar
|
|
270
|
+
end
|
|
271
|
+
[BarGroup.new(label: "", bars:)]
|
|
272
|
+
else
|
|
273
|
+
data.map do |item|
|
|
274
|
+
tuple = item #: Array[untyped]
|
|
275
|
+
label = tuple[0].to_s
|
|
276
|
+
value = tuple[1]
|
|
277
|
+
style = tuple[2]
|
|
278
|
+
|
|
279
|
+
bar = Bar.new(value:)
|
|
280
|
+
bar = bar.with(style:) if style
|
|
281
|
+
BarGroup.new(label:, bars: [bar])
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
else
|
|
285
|
+
# Fallback
|
|
286
|
+
data
|
|
287
|
+
end
|
|
288
|
+
else
|
|
289
|
+
data
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
super(
|
|
293
|
+
data:,
|
|
294
|
+
bar_width: Integer(bar_width),
|
|
295
|
+
bar_gap: Integer(bar_gap),
|
|
296
|
+
group_gap: Integer(group_gap),
|
|
297
|
+
max: max.nil? ? nil : Integer(max),
|
|
298
|
+
style:,
|
|
299
|
+
block:,
|
|
300
|
+
direction:,
|
|
301
|
+
label_style:,
|
|
302
|
+
value_style:,
|
|
303
|
+
bar_set:
|
|
304
|
+
)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
module Widgets
|
|
10
|
+
# Defines the visual container for a widget.
|
|
11
|
+
#
|
|
12
|
+
# Widgets often float in void. Without boundaries, interfaces become a chaotic mess of text. Users need structure to parse information.
|
|
13
|
+
#
|
|
14
|
+
# This widget creates that structure. It wraps content in borders. It labels sections with titles. It paints the background.
|
|
15
|
+
#
|
|
16
|
+
# Use blocks to define distinct areas. Group related information. Create a visual hierarchy that guides the user's eye.
|
|
17
|
+
#
|
|
18
|
+
# {rdoc-image:/doc/images/widget_box.png}[link:/examples/widget_box/app_rb.html]
|
|
19
|
+
#
|
|
20
|
+
# === Example
|
|
21
|
+
#
|
|
22
|
+
# Run the interactive demo from the terminal:
|
|
23
|
+
#
|
|
24
|
+
# ruby examples/widget_box/app.rb
|
|
25
|
+
class Block < Data.define(:title, :titles, :title_alignment, :title_style, :borders, :border_style, :border_type, :border_set, :style, :padding, :children)
|
|
26
|
+
include CoerceableWidget
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# :attr_reader: title
|
|
30
|
+
# The main title displayed on the top border.
|
|
31
|
+
#
|
|
32
|
+
# === Example
|
|
33
|
+
#
|
|
34
|
+
# Block.new(title: "Main").title # => "Main"
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# :attr_reader: titles
|
|
38
|
+
# Additional titles for complex labeling.
|
|
39
|
+
#
|
|
40
|
+
# Each title can be a <tt>String</tt> or a <tt>Hash</tt> with keys <tt>:content</tt>, <tt>:alignment</tt>, <tt>:position</tt> (<tt>:top</tt> or <tt>:bottom</tt>), and <tt>:style</tt>.
|
|
41
|
+
#
|
|
42
|
+
# === Example
|
|
43
|
+
#
|
|
44
|
+
# Block.new(titles: ["Top", { content: "Bottom", position: :bottom }]).titles
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# :attr_reader: title_alignment
|
|
48
|
+
# Alignment of the main title.
|
|
49
|
+
#
|
|
50
|
+
# One of <tt>:left</tt>, <tt>:center</tt>, or <tt>:right</tt>.
|
|
51
|
+
#
|
|
52
|
+
# === Example
|
|
53
|
+
#
|
|
54
|
+
# Block.new(title_alignment: :center).title_alignment # => :center
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# :attr_reader: title_style
|
|
58
|
+
# Style applied to all titles if not overridden.
|
|
59
|
+
#
|
|
60
|
+
# === Example
|
|
61
|
+
#
|
|
62
|
+
# Block.new(title_style: Style.new(fg: :red)).title_style
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# :attr_reader: borders
|
|
66
|
+
# Visible borders.
|
|
67
|
+
#
|
|
68
|
+
# An array containing any of <tt>:top</tt>, <tt>:bottom</tt>, <tt>:left</tt>, <tt>:right</tt>, or <tt>:all</tt>.
|
|
69
|
+
#
|
|
70
|
+
# === Example
|
|
71
|
+
#
|
|
72
|
+
# Block.new(borders: [:left, :right]).borders # => [:left, :right]
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# :attr_reader: border_style
|
|
76
|
+
# Full style (colors/modifiers) for the border lines.
|
|
77
|
+
#
|
|
78
|
+
# A Style object or Hash with <tt>:fg</tt>, <tt>:bg</tt>, and <tt>:modifiers</tt>.
|
|
79
|
+
# This allows borders to be bold, italic, colored, etc.
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# :attr_reader: border_type
|
|
83
|
+
# Visual style of the border lines.
|
|
84
|
+
#
|
|
85
|
+
# One of <tt>:plain</tt>, <tt>:rounded</tt>, <tt>:double</tt>, <tt>:thick</tt>, etc.
|
|
86
|
+
|
|
87
|
+
##
|
|
88
|
+
# :attr_reader: border_set
|
|
89
|
+
# Custom characters for the border lines.
|
|
90
|
+
#
|
|
91
|
+
# A Hash with keys defining the characters for the borders.
|
|
92
|
+
# Keys: <tt>:top_left</tt>, <tt>:top_right</tt>, <tt>:bottom_left</tt>, <tt>:bottom_right</tt>,
|
|
93
|
+
# <tt>:vertical_left</tt>, <tt>:vertical_right</tt>, <tt>:horizontal_top</tt>, <tt>:horizontal_bottom</tt>.
|
|
94
|
+
#
|
|
95
|
+
# Providing this overrides <tt>border_type</tt>.
|
|
96
|
+
#
|
|
97
|
+
#
|
|
98
|
+
# === Example
|
|
99
|
+
#
|
|
100
|
+
# Block.new(border_set: { top_left: "1", top_right: "2", bottom_left: "3", bottom_right: "4", vertical_left: "5", vertical_right: "6", horizontal_top: "7", horizontal_bottom: "8" })
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# :attr_reader: style
|
|
104
|
+
# Base style (colors/modifiers) for the block content.
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
# :attr_reader: padding
|
|
108
|
+
# Inner padding.
|
|
109
|
+
#
|
|
110
|
+
# Can be a single <tt>Integer</tt> (uniform) or a 4-element <tt>Array</tt> (left, right, top, bottom).
|
|
111
|
+
#
|
|
112
|
+
# === Example
|
|
113
|
+
#
|
|
114
|
+
#--
|
|
115
|
+
# SPDX-SnippetBegin
|
|
116
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
117
|
+
# SPDX-License-Identifier: MIT-0
|
|
118
|
+
#++
|
|
119
|
+
# Block.new(padding: 2).padding # => 2
|
|
120
|
+
# Block.new(padding: [1, 1, 0, 0]).padding # => [1, 1, 0, 0]
|
|
121
|
+
#--
|
|
122
|
+
# SPDX-SnippetEnd
|
|
123
|
+
#++
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# :attr_reader: children
|
|
127
|
+
# Widgets to render inside the block (optional).
|
|
128
|
+
#
|
|
129
|
+
# When provided, each child widget is rendered within the block's area.
|
|
130
|
+
#
|
|
131
|
+
# === Example
|
|
132
|
+
#
|
|
133
|
+
#--
|
|
134
|
+
# SPDX-SnippetBegin
|
|
135
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
136
|
+
# SPDX-License-Identifier: MIT-0
|
|
137
|
+
#++
|
|
138
|
+
# Block.new(
|
|
139
|
+
# title: "Content",
|
|
140
|
+
# borders: [:all],
|
|
141
|
+
# children: [Paragraph.new(text: "Hello")]
|
|
142
|
+
# )
|
|
143
|
+
#--
|
|
144
|
+
# SPDX-SnippetEnd
|
|
145
|
+
#++
|
|
146
|
+
|
|
147
|
+
# Creates a new Block.
|
|
148
|
+
#
|
|
149
|
+
# [title]
|
|
150
|
+
# Main title string (optional).
|
|
151
|
+
# [titles]
|
|
152
|
+
# Array of additional titles (optional).
|
|
153
|
+
# [title_alignment]
|
|
154
|
+
# Alignment symbol: <tt>:left</tt> (default), <tt>:center</tt>, <tt>:right</tt>.
|
|
155
|
+
# [title_style]
|
|
156
|
+
# Base style for all titles (optional).
|
|
157
|
+
# [borders]
|
|
158
|
+
# Array of borders to show: <tt>:top</tt>, <tt>:bottom</tt>, <tt>:left</tt>, <tt>:right</tt>, or <tt>:all</tt> (default).
|
|
159
|
+
# [border_style]
|
|
160
|
+
# Style object or Hash for the border lines.
|
|
161
|
+
# [border_type]
|
|
162
|
+
# Symbol: <tt>:plain</tt> (default), <tt>:rounded</tt>, <tt>:double</tt>, <tt>:thick</tt>, <tt>:hidden</tt>, <tt>:quadrant_inside</tt>, <tt>:quadrant_outside</tt>.
|
|
163
|
+
# [border_set]
|
|
164
|
+
# Hash: Custom characters for the border lines. Unique characters are interned (leaked) permanently, so avoid infinite dynamic variations.
|
|
165
|
+
# [style]
|
|
166
|
+
# Style object or Hash for the block's content area.
|
|
167
|
+
# [padding]
|
|
168
|
+
# Integer (uniform) or Array[4] (left, right, top, bottom).
|
|
169
|
+
# [children]
|
|
170
|
+
# Array of widgets to render inside the block (optional).
|
|
171
|
+
def initialize(title: nil, titles: [], title_alignment: nil, title_style: nil, borders: [:all], border_style: nil, border_type: nil, border_set: nil, style: nil, padding: 0, children: [])
|
|
172
|
+
if border_set
|
|
173
|
+
border_set = border_set.dup
|
|
174
|
+
%i[top_left top_right bottom_left bottom_right vertical_left vertical_right horizontal_top horizontal_bottom].each do |long_key|
|
|
175
|
+
short_key = long_key.to_s.split("_").map { |s| s[0] }.join.to_sym
|
|
176
|
+
if (val = border_set.delete(short_key))
|
|
177
|
+
border_set[long_key] = val
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
coerced_padding = if padding.is_a?(Array)
|
|
182
|
+
padding.map { |v| Integer(v) }
|
|
183
|
+
else
|
|
184
|
+
Integer(padding)
|
|
185
|
+
end
|
|
186
|
+
super(
|
|
187
|
+
title:,
|
|
188
|
+
titles:,
|
|
189
|
+
title_alignment:,
|
|
190
|
+
title_style:,
|
|
191
|
+
borders:,
|
|
192
|
+
border_style:,
|
|
193
|
+
border_type:,
|
|
194
|
+
border_set:,
|
|
195
|
+
style:,
|
|
196
|
+
padding: coerced_padding,
|
|
197
|
+
children:
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Computes the inner content area given an outer area.
|
|
202
|
+
#
|
|
203
|
+
# This method calculates where content should be placed within a block,
|
|
204
|
+
# accounting for borders and padding. Essential for layout calculations
|
|
205
|
+
# when you need to know the usable space inside a block.
|
|
206
|
+
#
|
|
207
|
+
# === Example
|
|
208
|
+
#
|
|
209
|
+
#--
|
|
210
|
+
# SPDX-SnippetBegin
|
|
211
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
212
|
+
# SPDX-License-Identifier: MIT-0
|
|
213
|
+
#++
|
|
214
|
+
# block = Block.new(borders: [:all], padding: 1)
|
|
215
|
+
# outer = Layout::Rect.new(x: 0, y: 0, width: 20, height: 10)
|
|
216
|
+
# inner = block.inner(outer)
|
|
217
|
+
# # => Rect(x: 2, y: 2, width: 16, height: 6)
|
|
218
|
+
#
|
|
219
|
+
#--
|
|
220
|
+
# SPDX-SnippetEnd
|
|
221
|
+
#++
|
|
222
|
+
# [area]
|
|
223
|
+
#--
|
|
224
|
+
# SPDX-SnippetBegin
|
|
225
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
226
|
+
# SPDX-License-Identifier: MIT-0
|
|
227
|
+
#++
|
|
228
|
+
# The outer Rect to compute the inner area for.
|
|
229
|
+
#
|
|
230
|
+
#--
|
|
231
|
+
# SPDX-SnippetEnd
|
|
232
|
+
#++
|
|
233
|
+
# Returns a new Rect representing the inner content area.
|
|
234
|
+
def inner(area)
|
|
235
|
+
# Calculate border offsets
|
|
236
|
+
has_border = -> (side) { borders.include?(:all) || borders.include?(side) }
|
|
237
|
+
left_border = has_border.call(:left) ? 1 : 0
|
|
238
|
+
right_border = has_border.call(:right) ? 1 : 0
|
|
239
|
+
top_border = has_border.call(:top) ? 1 : 0
|
|
240
|
+
bottom_border = has_border.call(:bottom) ? 1 : 0
|
|
241
|
+
|
|
242
|
+
# Calculate padding offsets - ensure all are Integer
|
|
243
|
+
pad_left, pad_right, pad_top, pad_bottom = if padding.is_a?(Array)
|
|
244
|
+
# [left, right, top, bottom]
|
|
245
|
+
[
|
|
246
|
+
Integer(padding[0] || 0),
|
|
247
|
+
Integer(padding[1] || 0),
|
|
248
|
+
Integer(padding[2] || 0),
|
|
249
|
+
Integer(padding[3] || 0),
|
|
250
|
+
]
|
|
251
|
+
else
|
|
252
|
+
p = Integer(padding)
|
|
253
|
+
[p, p, p, p]
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Compute inner area
|
|
257
|
+
new_x = area.x + left_border + pad_left
|
|
258
|
+
new_y = area.y + top_border + pad_top
|
|
259
|
+
new_width = [area.width - left_border - right_border - pad_left - pad_right, 0].max
|
|
260
|
+
new_height = [area.height - top_border - bottom_border - pad_top - pad_bottom, 0].max
|
|
261
|
+
|
|
262
|
+
Layout::Rect.new(x: new_x, y: new_y, width: new_width, height: new_height)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
module Widgets
|
|
10
|
+
# Displays a monthly calendar grid.
|
|
11
|
+
#
|
|
12
|
+
# Dates are complex. Rendering them in a grid requires calculation of leap years, month lengths, and day-of-week offsets.
|
|
13
|
+
# Use this widget to skip the boilerplate.
|
|
14
|
+
#
|
|
15
|
+
# This widget renders a standard monthly view. It highlights the current date. It structures time.
|
|
16
|
+
#
|
|
17
|
+
# Use it for date pickers, schedulers, or logs.
|
|
18
|
+
#
|
|
19
|
+
# {rdoc-image:/doc/images/widget_calendar.png}[link:/examples/widget_calendar/app_rb.html]
|
|
20
|
+
#
|
|
21
|
+
# === Example
|
|
22
|
+
#
|
|
23
|
+
# Run the interactive demo from the terminal:
|
|
24
|
+
#
|
|
25
|
+
# ruby examples/widget_calendar/app.rb
|
|
26
|
+
class Calendar < Data.define(:year, :month, :events, :default_style, :header_style, :block, :show_weekdays_header, :show_surrounding, :show_month_header)
|
|
27
|
+
include CoerceableWidget
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# :attr_reader: year
|
|
31
|
+
# The year to display (Integer).
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# :attr_reader: month
|
|
35
|
+
# The month to display (1–12).
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# :attr_reader: events
|
|
39
|
+
# A Hash mapping Dates to Styles for event highlighting.
|
|
40
|
+
# Keys must be `Date` objects (or objects responding to `day`, `month`, `year`).
|
|
41
|
+
# Values must be `Style` objects.
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# :attr_reader: default_style
|
|
45
|
+
# Style for the days.
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# :attr_reader: header_style
|
|
49
|
+
# Style for the month name header.
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# :attr_reader: block
|
|
53
|
+
# Optional wrapping block.
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# :attr_reader: show_weekdays_header
|
|
57
|
+
# Whether to show the weekday header (Mon, Tue, etc.).
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# :attr_reader: show_surrounding
|
|
61
|
+
# Style for dates from surrounding months. If <tt>nil</tt>, surrounding dates are hidden.
|
|
62
|
+
|
|
63
|
+
# Creates a new Calendar.
|
|
64
|
+
#
|
|
65
|
+
# [year] Integer.
|
|
66
|
+
# [month] Integer.
|
|
67
|
+
# [events] Hash<Date, Style>. Optional.
|
|
68
|
+
# [default_style] Style.
|
|
69
|
+
# [header_style] Style.
|
|
70
|
+
# [block] Block.
|
|
71
|
+
# [show_weekdays_header] Boolean. Whether to show the weekday header.
|
|
72
|
+
# [show_surrounding] <tt>Style</tt> or <tt>nil</tt>. Style for surrounding month dates.
|
|
73
|
+
def initialize(year:, month:, events: {}, default_style: nil, header_style: nil, block: nil, show_weekdays_header: true, show_surrounding: nil, show_month_header: false)
|
|
74
|
+
super(
|
|
75
|
+
year: Integer(year),
|
|
76
|
+
month: Integer(month),
|
|
77
|
+
events:,
|
|
78
|
+
default_style:,
|
|
79
|
+
header_style:,
|
|
80
|
+
block:,
|
|
81
|
+
show_weekdays_header:,
|
|
82
|
+
show_surrounding:,
|
|
83
|
+
show_month_header:
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|