git-stack-cli 0.5.0 → 0.5.1

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.
@@ -53,6 +53,10 @@ export function MultiSelect(props) {
53
53
  props.onSelect({ item, selected, state });
54
54
  }, [selected_set]);
55
55
  Ink.useInput((_input, key) => {
56
+ if (props.disabled) {
57
+ console.debug("[MultiSelect] disabled, ignoring input");
58
+ return;
59
+ }
56
60
  if (key.return) {
57
61
  selectRef.current = true;
58
62
  const item = props.items[index];
@@ -135,12 +135,8 @@ function SelectCommitRangesInternal(props) {
135
135
  max_item_width -= left_arrow.length + right_arrow.length;
136
136
  return (React.createElement(Ink.Box, { flexDirection: "column" },
137
137
  React.createElement(Ink.Box, { height: 1 }),
138
- React.createElement(MultiSelect, { key: group.id, items: items, maxWidth: max_item_width, onSelect: (args) => {
138
+ React.createElement(MultiSelect, { key: group.id, items: items, maxWidth: max_item_width, disabled: group_input, onSelect: (args) => {
139
139
  // console.debug("onSelect", args);
140
- if (group_input) {
141
- // console.debug("group_input is true, ignoring onSelect");
142
- return;
143
- }
144
140
  const key = args.item.sha;
145
141
  let value;
146
142
  if (args.selected) {
@@ -1,7 +1,10 @@
1
1
  import * as React from "react";
2
2
  import * as Ink from "ink";
3
+ import { assertNever } from "../core/assertNever.js";
3
4
  import { invariant } from "../core/invariant.js";
4
5
  import { Store } from "./Store.js";
6
+ import { Table } from "./Table.js";
7
+ import { Url } from "./Url.js";
5
8
  export function StatusTable() {
6
9
  const commit_range = Store.useState((state) => state.commit_range);
7
10
  invariant(commit_range, "commit_range must exist");
@@ -10,10 +13,9 @@ export function StatusTable() {
10
13
  const row = {
11
14
  icon: "",
12
15
  count: "",
13
- status: "",
16
+ status: "NEW",
14
17
  title: "",
15
18
  url: "",
16
- id: group.id,
17
19
  };
18
20
  if (group.id === commit_range.UNASSIGNED) {
19
21
  row.icon = "⭑";
@@ -41,69 +43,65 @@ export function StatusTable() {
41
43
  row.url = group.pr.url;
42
44
  }
43
45
  else {
44
- row.title = group.id;
46
+ row.title = group.title || group.id;
45
47
  row.count = `0/${group.commits.length}`;
46
48
  }
47
49
  }
48
50
  row_list.push(row);
49
51
  }
50
- if (!row_list.length) {
51
- return (React.createElement(Container, null,
52
- React.createElement(Ink.Text, { dimColor: true }, "No data found.")));
53
- }
54
- // walk data and discover max width for each column
55
- const sample_row = row_list[0];
56
- const col_list = Object.keys(sample_row);
57
- const max_col_width = {};
58
- for (const col of col_list) {
59
- max_col_width[col] = 0;
60
- }
61
- for (const row of row_list) {
62
- for (const col of col_list) {
63
- const value = row[col];
64
- max_col_width[col] = Math.max(value.length, max_col_width[col]);
65
- }
52
+ return (React.createElement(Table, { data: row_list, fillColumn: "title",
53
+ // maxWidth={{
54
+ // title: 30,
55
+ // }}
56
+ columnGap: 3, columns: {
57
+ icon: IconColumn,
58
+ status: StatusColumn,
59
+ count: CountColumn,
60
+ title: TitleColumn,
61
+ url: UrlColumn,
62
+ } }));
63
+ }
64
+ function IconColumn(props) {
65
+ const value = props.row[props.column];
66
+ return (React.createElement(Ink.Text, { color: get_status_color(props.row), bold: get_status_bold(props.row) }, value));
67
+ }
68
+ function StatusColumn(props) {
69
+ const value = props.row[props.column];
70
+ return (React.createElement(Ink.Text, { color: get_status_color(props.row), bold: get_status_bold(props.row) }, value));
71
+ }
72
+ function CountColumn(props) {
73
+ const value = props.row[props.column];
74
+ return React.createElement(Ink.Text, { dimColor: true }, value);
75
+ }
76
+ function TitleColumn(props) {
77
+ const value = props.row[props.column];
78
+ return React.createElement(Ink.Text, { wrap: "truncate-end" }, value);
79
+ }
80
+ function UrlColumn(props) {
81
+ const value = props.row[props.column];
82
+ return React.createElement(Url, { dimColor: true }, value);
83
+ }
84
+ function get_status_color(row) {
85
+ switch (row.status) {
86
+ case "NEW":
87
+ return "yellow";
88
+ case "OUTDATED":
89
+ return "red";
90
+ case "MERGED":
91
+ return "purple";
92
+ case "SYNCED":
93
+ return "green";
94
+ default:
95
+ assertNever(row.status);
96
+ return "gray";
66
97
  }
67
- const { stdout } = Ink.useStdout();
68
- const available_width = stdout.columns;
69
- const columnGap = 2;
70
- const breathing_room = 0;
71
- const remaining_space = available_width -
72
- // icon
73
- max_col_width.icon -
74
- // status
75
- max_col_width.status -
76
- // commits
77
- max_col_width.count -
78
- // url
79
- max_col_width.url -
80
- // gap * col count (minus one for row.id which is not shown but used at key)
81
- columnGap * (col_list.length - 1) -
82
- // remove some extra space
83
- breathing_room;
84
- // add one for ellipsis character
85
- const title_width = Math.min(max_col_width.title, remaining_space + 1);
86
- // prettier-ignore
87
- // console.debug({ available_width, remaining_space, title_width, max_col_width });
88
- return (React.createElement(Container, null, row_list.map((row) => {
89
- return (React.createElement(Ink.Box, { key: row.id,
90
- // borderStyle="round"
91
- flexDirection: "row", columnGap: columnGap, width: available_width },
92
- React.createElement(Ink.Box, { width: max_col_width.icon },
93
- React.createElement(Ink.Text, null, row.icon)),
94
- React.createElement(Ink.Box, { width: max_col_width.status },
95
- React.createElement(Ink.Text, null, row.status)),
96
- React.createElement(Ink.Box, { width: max_col_width.count },
97
- React.createElement(Ink.Text, null, row.count)),
98
- React.createElement(Ink.Box, { width: title_width },
99
- React.createElement(Ink.Text, { wrap: "truncate-end" }, row.title)),
100
- React.createElement(Ink.Box, { width: max_col_width.url },
101
- React.createElement(Ink.Text, null, row.url))));
102
- })));
103
98
  }
104
- function Container(props) {
105
- return (React.createElement(Ink.Box, { flexDirection: "column" },
106
- React.createElement(Ink.Box, { height: 1 }),
107
- props.children,
108
- React.createElement(Ink.Box, { height: 1 })));
99
+ function get_status_bold(row) {
100
+ switch (row.status) {
101
+ case "NEW":
102
+ case "OUTDATED":
103
+ return true;
104
+ default:
105
+ return false;
106
+ }
109
107
  }
@@ -0,0 +1,63 @@
1
+ import * as React from "react";
2
+ import * as Ink from "ink";
3
+ import { is_finite_value } from "../core/is_finite_value.js";
4
+ export function Table(props) {
5
+ if (!props.data.length) {
6
+ return (React.createElement(Container, null,
7
+ React.createElement(Ink.Text, { dimColor: true }, "No data found.")));
8
+ }
9
+ const sample_row = props.data[0];
10
+ const RowColumnList = Object.keys(sample_row);
11
+ // walk data and discover max width for each column
12
+ const max_col_width = {};
13
+ for (const col of RowColumnList) {
14
+ max_col_width[col] = 0;
15
+ }
16
+ for (const row of props.data) {
17
+ for (const col of RowColumnList) {
18
+ const row_col = row[col];
19
+ max_col_width[col] = Math.max(String(row_col).length, max_col_width[col]);
20
+ const maxWidth = props.maxWidth?.[col];
21
+ if (is_finite_value(maxWidth)) {
22
+ max_col_width[col] = Math.min(maxWidth - 1, max_col_width[col]);
23
+ }
24
+ }
25
+ }
26
+ const { stdout } = Ink.useStdout();
27
+ const available_width = stdout.columns;
28
+ const columnGap = is_finite_value(props.columnGap) ? props.columnGap : 2;
29
+ const breathing_room = 0;
30
+ if (props.fillColumn) {
31
+ let remaining_space = available_width;
32
+ for (const col of RowColumnList) {
33
+ // skip fill column from calculation
34
+ if (props.fillColumn === col) {
35
+ continue;
36
+ }
37
+ remaining_space -= max_col_width[col];
38
+ }
39
+ // gap * col count
40
+ remaining_space -= columnGap * (RowColumnList.length - 1);
41
+ // remove some extra space
42
+ remaining_space -= breathing_room;
43
+ if (props.fillColumn) {
44
+ max_col_width[props.fillColumn] = Math.min(max_col_width[props.fillColumn], remaining_space);
45
+ }
46
+ }
47
+ // console.debug({ available_width, remaining_space, max_col_width });
48
+ return (React.createElement(Container, null, props.data.map((row, i) => {
49
+ return (React.createElement(Ink.Box, { key: i,
50
+ // borderStyle="round"
51
+ flexDirection: "row", columnGap: columnGap, width: available_width }, RowColumnList.map((column) => {
52
+ const ColumnComponent = props.columns[column];
53
+ return (React.createElement(Ink.Box, { key: String(column), width: max_col_width[column] },
54
+ React.createElement(ColumnComponent, { row: row, column: column })));
55
+ })));
56
+ })));
57
+ }
58
+ function Container(props) {
59
+ return (React.createElement(Ink.Box, { flexDirection: "column" },
60
+ React.createElement(Ink.Box, { height: 1 }),
61
+ props.children,
62
+ React.createElement(Ink.Box, { height: 1 })));
63
+ }
package/dist/app/Url.js CHANGED
@@ -2,5 +2,5 @@ import * as React from "react";
2
2
  import * as Ink from "ink";
3
3
  export function Url(props) {
4
4
  const text_color = "#38bdf8";
5
- return (React.createElement(Ink.Text, { bold: true, color: text_color }, props.children));
5
+ return (React.createElement(Ink.Text, { bold: true, color: text_color, ...props }, props.children));
6
6
  }
@@ -0,0 +1,5 @@
1
+ export function invariant(condition, message) {
2
+ if (!condition) {
3
+ throw new Error(message);
4
+ }
5
+ }
@@ -0,0 +1,3 @@
1
+ export function isFiniteValue(value) {
2
+ return typeof value === "number" && Number.isFinite(value);
3
+ }
@@ -0,0 +1,3 @@
1
+ export function is_finite_value(value) {
2
+ return typeof value === "number" && Number.isFinite(value);
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",