-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(leaves): Add a table component (#31)
* feat(leaves): add a table component * refactor(leaves): make keybindings responsibility of parent component
- Loading branch information
Showing
9 changed files
with
307 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
_build | ||
_opam | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
Output demo.gif | ||
|
||
Require echo | ||
|
||
Set Shell "bash" | ||
Set Framerate 24 | ||
Set FontSize 32 | ||
Set Width 1200 | ||
Set Height 600 | ||
|
||
Type "dune exec --no-print-directory ./main.exe" | ||
Enter | ||
Sleep 1s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Space | ||
Sleep 0.2s | ||
Space | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.3s | ||
Up | ||
Sleep 0.3s | ||
Up | ||
Sleep 0.3s | ||
Up | ||
Sleep 0.3s | ||
Up | ||
Sleep 0.3s | ||
Up | ||
Sleep 0.2s | ||
Down | ||
Sleep 0.2s | ||
Down | ||
Sleep 2s |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
(executable | ||
(name main) | ||
(libraries minttea spices leaves)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
open Minttea | ||
open Leaves | ||
|
||
type model = { table : Table.t } | ||
|
||
let initial_model = | ||
{ | ||
table = | ||
{ | ||
columns = | ||
[| | ||
{ title = "Rank"; width = 4 }; | ||
{ title = "City"; width = 10 }; | ||
{ title = "Country"; width = 10 }; | ||
{ title = "Population"; width = 10 }; | ||
|]; | ||
rows = | ||
[ | ||
[ "1"; "Tokyo"; "Japan"; "37,274,000" ]; | ||
[ "2"; "Delhi"; "India"; "32,065,760" ]; | ||
[ "3"; "Shanghai"; "China"; "28,516,904" ]; | ||
[ "4"; "Dhaka"; "Bangladesh"; "22,478,116" ]; | ||
[ "5"; "São Paulo"; "Brazil"; "22,429,800" ]; | ||
[ "6"; "Mexico City"; "Mexico"; "22,085,140" ]; | ||
[ "7"; "Cairo"; "Egypt"; "21,750,020" ]; | ||
[ "8"; "Beijing"; "China"; "21,333,332" ]; | ||
[ "9"; "Mumbai"; "India"; "20,961,472" ]; | ||
[ "10"; "Osaka"; "Japan"; "19,059,856" ]; | ||
[ "11"; "Chongqing"; "China"; "16,874,740" ]; | ||
[ "12"; "Karachi"; "Pakistan"; "16,839,950" ]; | ||
[ "13"; "Istanbul"; "Turkey"; "15,636,243" ]; | ||
[ "14"; "Kinshasa"; "DR Congo"; "15,628,085" ]; | ||
[ "15"; "Lagos"; "Nigeria"; "15,387,639" ]; | ||
[ "16"; "Buenos Aires"; "Argentina"; "15,369,919" ]; | ||
[ "17"; "Kolkata"; "India"; "15,133,888" ]; | ||
[ "18"; "Manila"; "Philippines"; "14,406,059" ]; | ||
[ "19"; "Tianjin"; "China"; "14,011,828" ]; | ||
[ "20"; "Guangzhou"; "China"; "13,964,637" ]; | ||
[ "21"; "Rio De Janeiro"; "Brazil"; "13,634,274" ]; | ||
[ "22"; "Lahore"; "Pakistan"; "13,541,764" ]; | ||
[ "23"; "Bangalore"; "India"; "13,193,035" ]; | ||
[ "24"; "Shenzhen"; "China"; "12,831,330" ]; | ||
[ "25"; "Moscow"; "Russia"; "12,640,818" ]; | ||
[ "26"; "Chennai"; "India"; "11,503,293" ]; | ||
[ "27"; "Bogota"; "Colombia"; "11,344,312" ]; | ||
[ "28"; "Paris"; "France"; "11,142,303" ]; | ||
[ "29"; "Jakarta"; "Indonesia"; "11,074,811" ]; | ||
[ "30"; "Lima"; "Peru"; "11,044,607" ]; | ||
[ "31"; "Bangkok"; "Thailand"; "10,899,698" ]; | ||
[ "32"; "Hyderabad"; "India"; "10,534,418" ]; | ||
[ "33"; "Seoul"; "South Korea"; "9,975,709" ]; | ||
[ "34"; "Nagoya"; "Japan"; "9,571,596" ]; | ||
[ "35"; "London"; "United Kingdom"; "9,540,576" ]; | ||
[ "36"; "Chengdu"; "China"; "9,478,521" ]; | ||
[ "37"; "Nanjing"; "China"; "9,429,381" ]; | ||
[ "38"; "Tehran"; "Iran"; "9,381,546" ]; | ||
[ "39"; "Ho Chi Minh City"; "Vietnam"; "9,077,158" ]; | ||
[ "40"; "Luanda"; "Angola"; "8,952,496" ]; | ||
[ "41"; "Wuhan"; "China"; "8,591,611" ]; | ||
[ "42"; "Xi An Shaanxi"; "China"; "8,537,646" ]; | ||
[ "43"; "Ahmedabad"; "India"; "8,450,228" ]; | ||
[ "44"; "Kuala Lumpur"; "Malaysia"; "8,419,566" ]; | ||
[ "45"; "New York City"; "United States"; "8,177,020" ]; | ||
[ "46"; "Hangzhou"; "China"; "8,044,878" ]; | ||
[ "47"; "Surat"; "India"; "7,784,276" ]; | ||
[ "48"; "Suzhou"; "China"; "7,764,499" ]; | ||
[ "49"; "Hong Kong"; "Hong Kong"; "7,643,256" ]; | ||
[ "50"; "Riyadh"; "Saudi Arabia"; "7,538,200" ]; | ||
[ "51"; "Shenyang"; "China"; "7,527,975" ]; | ||
[ "52"; "Baghdad"; "Iraq"; "7,511,920" ]; | ||
[ "53"; "Dongguan"; "China"; "7,511,851" ]; | ||
[ "54"; "Foshan"; "China"; "7,497,263" ]; | ||
[ "55"; "Dar Es Salaam"; "Tanzania"; "7,404,689" ]; | ||
[ "56"; "Pune"; "India"; "6,987,077" ]; | ||
[ "57"; "Santiago"; "Chile"; "6,856,939" ]; | ||
[ "58"; "Madrid"; "Spain"; "6,713,557" ]; | ||
[ "59"; "Haerbin"; "China"; "6,665,951" ]; | ||
[ "60"; "Toronto"; "Canada"; "6,312,974" ]; | ||
[ "61"; "Belo Horizonte"; "Brazil"; "6,194,292" ]; | ||
[ "62"; "Khartoum"; "Sudan"; "6,160,327" ]; | ||
[ "63"; "Johannesburg"; "South Africa"; "6,065,354" ]; | ||
[ "64"; "Singapore"; "Singapore"; "6,039,577" ]; | ||
[ "65"; "Dalian"; "China"; "5,930,140" ]; | ||
[ "66"; "Qingdao"; "China"; "5,865,232" ]; | ||
[ "67"; "Zhengzhou"; "China"; "5,690,312" ]; | ||
[ "68"; "Ji Nan Shandong"; "China"; "5,663,015" ]; | ||
[ "69"; "Barcelona"; "Spain"; "5,658,472" ]; | ||
[ "70"; "Saint Petersburg"; "Russia"; "5,535,556" ]; | ||
[ "71"; "Abidjan"; "Ivory Coast"; "5,515,790" ]; | ||
[ "72"; "Yangon"; "Myanmar"; "5,514,454" ]; | ||
[ "73"; "Fukuoka"; "Japan"; "5,502,591" ]; | ||
[ "74"; "Alexandria"; "Egypt"; "5,483,605" ]; | ||
[ "75"; "Guadalajara"; "Mexico"; "5,339,583" ]; | ||
[ "76"; "Ankara"; "Turkey"; "5,309,690" ]; | ||
[ "77"; "Chittagong"; "Bangladesh"; "5,252,842" ]; | ||
[ "78"; "Addis Ababa"; "Ethiopia"; "5,227,794" ]; | ||
[ "79"; "Melbourne"; "Australia"; "5,150,766" ]; | ||
[ "80"; "Nairobi"; "Kenya"; "5,118,844" ]; | ||
[ "81"; "Hanoi"; "Vietnam"; "5,067,352" ]; | ||
[ "82"; "Sydney"; "Australia"; "5,056,571" ]; | ||
[ "83"; "Monterrey"; "Mexico"; "5,036,535" ]; | ||
[ "84"; "Changsha"; "China"; "4,809,887" ]; | ||
[ "85"; "Brasilia"; "Brazil"; "4,803,877" ]; | ||
[ "86"; "Cape Town"; "South Africa"; "4,800,954" ]; | ||
[ "87"; "Jiddah"; "Saudi Arabia"; "4,780,740" ]; | ||
[ "88"; "Urumqi"; "China"; "4,710,203" ]; | ||
[ "89"; "Kunming"; "China"; "4,657,381" ]; | ||
[ "90"; "Changchun"; "China"; "4,616,002" ]; | ||
[ "91"; "Hefei"; "China"; "4,496,456" ]; | ||
[ "92"; "Shantou"; "China"; "4,490,411" ]; | ||
[ "93"; "Xinbei"; "Taiwan"; "4,470,672" ]; | ||
[ "94"; "Kabul"; "Afghanistan"; "4,457,882" ]; | ||
[ "95"; "Ningbo"; "China"; "4,405,292" ]; | ||
[ "96"; "Tel Aviv"; "Israel"; "4,343,584" ]; | ||
[ "97"; "Yaounde"; "Cameroon"; "4,336,670" ]; | ||
[ "98"; "Rome"; "Italy"; "4,297,877" ]; | ||
[ "99"; "Shijiazhuang"; "China"; "4,285,135" ]; | ||
[ "100"; "Montreal"; "Canada"; "4,276,526" ]; | ||
]; | ||
cursor = 0; | ||
styles = Table.default_styles; | ||
start_of_frame = 0; | ||
height_of_frame = 5; | ||
}; | ||
} | ||
|
||
let init _ = Command.Noop | ||
|
||
let update event model = | ||
match event with | ||
| Event.KeyDown (Key "q") -> (model, Command.Quit) | ||
| Event.KeyDown (Key "b") -> | ||
({ table = Table.update model.table Table.PageUp }, Command.Noop) | ||
| Event.KeyDown (Key "f") | Event.KeyDown Space -> | ||
({ table = Table.update model.table Table.PageDown }, Command.Noop) | ||
| Event.KeyDown Up -> | ||
({ table = Table.update model.table Table.Up }, Command.Noop) | ||
| Event.KeyDown Down -> | ||
({ table = Table.update model.table Table.Down }, Command.Noop) | ||
| _ -> (model, Command.Noop) | ||
|
||
let view model = Table.view model.table ^ "\n\nhint: up/down b/f/space, quit: q" | ||
let app = Minttea.app ~init ~update ~view () | ||
let () = Minttea.start ~initial_model app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
type column = { title : string; width : int } | ||
type row = string list | ||
|
||
type styles = { | ||
cursor : Spices.style; | ||
header : Spices.style; | ||
cell : Spices.style; | ||
} | ||
|
||
let default_styles = | ||
{ | ||
cursor = | ||
Spices.( | ||
default |> bold true |> fg (color "#FFFFFF") |> bg (color "#053d37")); | ||
header = | ||
Spices.( | ||
default |> bold true | ||
|> fg (color "#FFFFFF") | ||
|> padding_right 1 |> padding_left 1); | ||
cell = Spices.(default |> padding_right 1 |> padding_left 1); | ||
} | ||
|
||
type t = { | ||
columns : column array; | ||
rows : row list; | ||
styles : styles; | ||
cursor : int; | ||
start_of_frame : int; | ||
height_of_frame : int; | ||
} | ||
|
||
type action = Up | Down | PageUp | PageDown | ||
|
||
let update t (e : action) = | ||
let go_up t steps = | ||
let cursor = max (t.cursor - steps) 0 in | ||
let start_of_frame = | ||
if cursor < t.start_of_frame then cursor else t.start_of_frame | ||
in | ||
{ t with cursor; start_of_frame } | ||
in | ||
let go_down t steps = | ||
let cursor = min (t.cursor + steps) (List.length t.rows - 1) in | ||
let start_of_frame = | ||
if cursor > t.start_of_frame + t.height_of_frame - 1 then | ||
cursor - t.height_of_frame + 1 | ||
else t.start_of_frame | ||
in | ||
{ t with cursor; start_of_frame } | ||
in | ||
match e with | ||
| PageUp -> go_up t t.height_of_frame | ||
| PageDown -> go_down t t.height_of_frame | ||
| Up -> go_up t 1 | ||
| Down -> go_down t 1 | ||
|
||
let truncate_or_pad_unicode ~width str = | ||
(* Truncates a Unicode string to a target width with an ellipsis | ||
or pads it with spaces on the right. *) | ||
let uuseg_string_length s = | ||
(* TODO(@sabine): we probably don't want the uuseg dependency | ||
or move this somewhere where it actually belongs. Also for uuseg_string_sub, | ||
we need to write actually good code. *) | ||
Uuseg_string.fold_utf_8 `Grapheme_cluster (fun len _ -> len + 1) 0 s | ||
in | ||
let uuseg_string_sub s start n = | ||
let inner s start n acc = | ||
Uuseg_string.fold_utf_8 `Grapheme_cluster | ||
(fun (pos, collected, s) c -> | ||
if pos >= start && collected < n then (pos + 1, collected + 1, s ^ c) | ||
else (pos + 1, collected, s)) | ||
acc s | ||
in | ||
let _, _, r = inner s start n (0, 0, "") in | ||
r | ||
in | ||
let len = uuseg_string_length str in | ||
if len > width then uuseg_string_sub str 0 (width - 1) ^ "…" | ||
else str ^ String.make (width - len) ' ' | ||
|
||
let view t = | ||
let column_titles = | ||
let render_title col = | ||
truncate_or_pad_unicode ~width:col.width col.title | ||
|> (t.styles.header |> Spices.build) "%s" | ||
in | ||
t.columns |> Array.to_list |> List.map render_title |> String.concat "" | ||
in | ||
let rows = | ||
let get_visible_rows t = | ||
t.rows |> List.to_seq |> Seq.drop t.start_of_frame | ||
|> Seq.take t.height_of_frame |> List.of_seq | ||
in | ||
let render_row i row = | ||
let render_cell i item = | ||
truncate_or_pad_unicode ~width:t.columns.(i).width item | ||
|> (t.styles.cell |> Spices.build) "%s" | ||
in | ||
let row = row |> List.mapi render_cell |> String.concat "" in | ||
if i = t.cursor - t.start_of_frame then | ||
(t.styles.cursor |> Spices.build) "%s" row | ||
else row | ||
in | ||
t |> get_visible_rows |> List.mapi render_row |> String.concat "\n" | ||
in | ||
Printf.sprintf {|%s | ||
|
||
%s|} column_titles rows |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters