@crewlethq/ui 0.85.0 → 0.86.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.
- package/README.md +18 -4
- package/dist/DataView/index.js +2 -2
- package/dist/InboxList/InboxList.d.ts +14 -3
- package/dist/InboxList/index.js +1 -1
- package/dist/{InboxList-X5Q5WWAI.css → InboxList-NVPIL3WU.css} +61 -28
- package/dist/KeyValueRows/KeyValueRows.d.ts +125 -0
- package/dist/KeyValueRows/index.d.ts +3 -0
- package/dist/KeyValueRows/index.js +12 -0
- package/dist/KeyValueRows-US5KW7JK.css +44 -0
- package/dist/ListDetail/index.js +1 -1
- package/dist/{ListDetail-Q7SFVKSO.css → ListDetail-WXBCLONL.css} +7 -1
- package/dist/RailList/RailList.d.ts +74 -0
- package/dist/RailList/index.d.ts +3 -0
- package/dist/RailList/index.js +17 -0
- package/dist/RailList-N2ZBPRM2.css +97 -0
- package/dist/RelatedRow/index.js +2 -2
- package/dist/SetupWizard/index.js +2 -2
- package/dist/ShareDialog/index.js +1 -1
- package/dist/TableCells/TableCells.d.ts +60 -0
- package/dist/TableCells/index.d.ts +2 -2
- package/dist/TableCells/index.js +2 -1
- package/dist/{TableCells-TKSNLRK3.css → TableCells-ZALZLLEF.css} +28 -0
- package/dist/Text/Text.d.ts +25 -3
- package/dist/Text/index.js +1 -1
- package/dist/{chunk-AFLS26AC.js → chunk-3CALM534.js} +36 -2
- package/dist/{chunk-EAHEJNVJ.js → chunk-CL3FWLDO.js} +1 -1
- package/dist/{chunk-QP2P6GDQ.js → chunk-O5CEG7TQ.js} +14 -0
- package/dist/{chunk-SNJM5KPV.js → chunk-P33VNG7Q.js} +1 -1
- package/dist/chunk-REWKR3S4.js +73 -0
- package/dist/chunk-TRZS7DLB.js +159 -0
- package/dist/{chunk-F6ZUM66S.js → chunk-VKNZQB2G.js} +44 -23
- package/dist/generated/index.d.ts +2 -0
- package/dist/generated/index.js +65 -63
- package/dist/index.js +65 -63
- package/dist/styles.css +246 -32
- package/package.json +3 -3
- package/dist/{chunk-VIX3GZCD.js → chunk-PAGITACP.js} +0 -0
- package/dist/{chunk-3BVNNQZY.js → chunk-WRZPO4YL.js} +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* RailList: a directory in a card, a rail of groups beside the chosen
|
|
3
|
+
* group's list, folding into a picker where the card is narrow
|
|
4
|
+
* (RailList.tsx).
|
|
5
|
+
*
|
|
6
|
+
* SIZED BY ITS OWN WIDTH, NOT THE WINDOW'S, because the same directory stands
|
|
7
|
+
* in a page and in a sheet: the root is an inline-size container, and under
|
|
8
|
+
* the rail's room (44rem) the rail goes and the picker shows. Both are always
|
|
9
|
+
* rendered and the stylesheet decides, so the fold costs no measurement and
|
|
10
|
+
* never draws the wrong one first.
|
|
11
|
+
*/
|
|
12
|
+
.crewlet-rail-list {
|
|
13
|
+
container: crewlet-rail-list / inline-size;
|
|
14
|
+
min-width: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* The bar across the card's top: the caller's search and chips, and the folded picker. */
|
|
18
|
+
.crewlet-rail-list__bar {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-wrap: wrap;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: var(--spacing-2);
|
|
23
|
+
padding: var(--spacing-3);
|
|
24
|
+
border-block-end: 1px solid var(--color-border-default);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Wide, the rail is the choice: the picker and a bar that holds nothing else are not drawn. */
|
|
28
|
+
.crewlet-rail-list__picker,
|
|
29
|
+
.crewlet-rail-list__bar--picker-only {
|
|
30
|
+
display: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* The rail at a fixed width (13rem by default), the list taking the rest. */
|
|
34
|
+
.crewlet-rail-list__body {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: var(--crewlet-rail-list-rail, 13rem) minmax(0, 1fr);
|
|
37
|
+
min-width: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* The rail on a hairline of its own, its note a step under its rows. */
|
|
41
|
+
.crewlet-rail-list__rail {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: var(--spacing-3);
|
|
45
|
+
min-width: 0;
|
|
46
|
+
padding: var(--spacing-2);
|
|
47
|
+
border-inline-end: 1px solid var(--color-border-default);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* The note starts on the rows' words, inside the dense rail's own inset. */
|
|
51
|
+
.crewlet-rail-list__note {
|
|
52
|
+
margin: 0;
|
|
53
|
+
padding-inline: var(--spacing-2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.crewlet-rail-list__list {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* What the chosen group holds, over its list on a hairline, at the list's own inset. */
|
|
63
|
+
.crewlet-rail-list__about {
|
|
64
|
+
margin: 0;
|
|
65
|
+
padding: var(--spacing-2) var(--spacing-4);
|
|
66
|
+
border-block-end: 1px solid var(--color-border-default);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* A GRID AT THE LIST'S TOP TAKES NO RULE OF ITS OWN. A grid in a card draws
|
|
71
|
+
* one rule above its head to part it from the card's head (DataGrid.css);
|
|
72
|
+
* here the bar's hairline, the description's or the card's own edge is
|
|
73
|
+
* already that rule, and the grid's drew a second line under it.
|
|
74
|
+
*/
|
|
75
|
+
.crewlet-rail-list__list > .crewlet-data-grid:first-child,
|
|
76
|
+
.crewlet-rail-list__about + .crewlet-data-grid {
|
|
77
|
+
border-top: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Under the rail's room the list takes the card, and the picker takes the rail's place in the bar. */
|
|
81
|
+
@container crewlet-rail-list (width < 44rem) {
|
|
82
|
+
.crewlet-rail-list__body {
|
|
83
|
+
grid-template-columns: minmax(0, 1fr);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.crewlet-rail-list__rail {
|
|
87
|
+
display: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.crewlet-rail-list__picker {
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.crewlet-rail-list__bar--picker-only {
|
|
95
|
+
display: flex;
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/RelatedRow/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { RelatedRow } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { RelatedRow } from '../chunk-CL3FWLDO.js';
|
|
2
|
+
import '../chunk-O5CEG7TQ.js';
|
|
3
3
|
import '../chunk-ZAC7SKUN.js';
|
|
4
4
|
import '../chunk-U2IQNDDR.js';
|
|
5
5
|
import '../chunk-AV2DXAEL.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { SetupWizard } from '../chunk-
|
|
2
|
-
import '../chunk-FPDIHEM4.js';
|
|
1
|
+
export { SetupWizard } from '../chunk-WRZPO4YL.js';
|
|
3
2
|
import '../chunk-TMML7BHT.js';
|
|
3
|
+
import '../chunk-FPDIHEM4.js';
|
|
4
4
|
import '../chunk-YJQXESOJ.js';
|
|
5
5
|
import '../chunk-CYQBDZ6M.js';
|
|
6
6
|
import '../chunk-NAHA62WD.js';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { ARTIFACT_ROLES, SHAREABLE_ROLES, ShareDialog, roleAtLeast } from '../chunk-Y7PPKQFQ.js';
|
|
2
2
|
import '../chunk-3IWZVEXA.js';
|
|
3
3
|
import '../chunk-ZXXZESCD.js';
|
|
4
|
+
import '../chunk-YGRTX67J.js';
|
|
4
5
|
import '../chunk-ETNQYT5O.js';
|
|
5
6
|
import '../chunk-STJ7QBC3.js';
|
|
6
7
|
import '../chunk-DTIW4CHZ.js';
|
|
7
|
-
import '../chunk-YGRTX67J.js';
|
|
8
8
|
import '../chunk-BPCY7ZID.js';
|
|
9
9
|
import '../chunk-HF5L56YQ.js';
|
|
10
10
|
import '../chunk-YTLWOHOY.js';
|
|
@@ -151,3 +151,63 @@ export type TableTextCellProps = {
|
|
|
151
151
|
* would draw the column's answer over the row's.
|
|
152
152
|
*/
|
|
153
153
|
export declare function TableTextCell({ children, icon, mark }: TableTextCellProps): import("react").JSX.Element;
|
|
154
|
+
/** Which of a two-line cell's lines is a literal, set in the mono face. */
|
|
155
|
+
export type TableTwoLineCellMono = 'value' | 'detail' | 'both';
|
|
156
|
+
export type TableTwoLineCellProps = {
|
|
157
|
+
/** The value, in the cell's own register: a name, a key, a short state. */
|
|
158
|
+
value: ReactNode;
|
|
159
|
+
/**
|
|
160
|
+
* The quieter line under it, a caption in the secondary ink: where it runs,
|
|
161
|
+
* the id it resolves to, when it last answered. Left out, the cell is the
|
|
162
|
+
* value's one line, at the same place a row of two-line cells puts it.
|
|
163
|
+
*/
|
|
164
|
+
detail?: ReactNode;
|
|
165
|
+
/**
|
|
166
|
+
* Which line is a literal (a command, an id, a key), set in the mono face:
|
|
167
|
+
* in the sans face `--` and `=` read as prose.
|
|
168
|
+
*/
|
|
169
|
+
mono?: TableTwoLineCellMono | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* The whole cell's hover text, where the two lines are not the whole story
|
|
172
|
+
* (a model's CLI, key and id in one line). Given, neither line adds its own.
|
|
173
|
+
*/
|
|
174
|
+
title?: string | undefined;
|
|
175
|
+
/**
|
|
176
|
+
* Whether a cut line carries its whole words as its title (`Text`'s
|
|
177
|
+
* `titleWhenCut`). On by default. Off where something else already shows
|
|
178
|
+
* them, a `Tooltip` around the cell.
|
|
179
|
+
*/
|
|
180
|
+
titleWhenCut?: boolean | undefined;
|
|
181
|
+
} & ({
|
|
182
|
+
/**
|
|
183
|
+
* A mark every row of the column wears, centred against the two lines.
|
|
184
|
+
* The header carries the meaning, so it is drawn as decoration.
|
|
185
|
+
*/
|
|
186
|
+
icon?: ReactNode;
|
|
187
|
+
mark?: never;
|
|
188
|
+
} | {
|
|
189
|
+
icon?: never;
|
|
190
|
+
/**
|
|
191
|
+
* A mark that varies per row and states a fact of its own (a vendor, the
|
|
192
|
+
* CLI a model runs on), centred against the two lines. It brings its own
|
|
193
|
+
* accessible name and is read.
|
|
194
|
+
*/
|
|
195
|
+
mark: ReactNode;
|
|
196
|
+
});
|
|
197
|
+
/**
|
|
198
|
+
* A value over a quieter line, each cut on one line: a name over where it
|
|
199
|
+
* runs, a key over the id it resolves to, a short state over when it began.
|
|
200
|
+
*
|
|
201
|
+
* TWO LINES IN TWO REGISTERS, ALWAYS THE SAME TWO, so every two-line cell in a
|
|
202
|
+
* table reads alike: the value in the cell's own register (it inherits the
|
|
203
|
+
* grid's size and ink), the line under it a caption in the secondary ink. A
|
|
204
|
+
* row of them is as tall as two lines whatever they hold, because each line
|
|
205
|
+
* is cut with an ellipsis rather than wrapped, and its whole words are on its
|
|
206
|
+
* title while it is cut (`Text`'s `truncate`).
|
|
207
|
+
*
|
|
208
|
+
* A MARK STANDS CENTRED AGAINST BOTH LINES, the way a vendor's mark stands
|
|
209
|
+
* beside a service's name and host. In a grid's stacked rows the lines, not
|
|
210
|
+
* the mark, give the row its baseline, so a column's label stands on the
|
|
211
|
+
* value's line (DataGrid.css).
|
|
212
|
+
*/
|
|
213
|
+
export declare function TableTwoLineCell({ value, detail, mono, title, titleWhenCut, icon, mark, }: TableTwoLineCellProps): import("react").JSX.Element;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import './TableCells.css';
|
|
2
|
-
export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell, } from './TableCells.js';
|
|
3
|
-
export type { TableDateCellProps, TableEntityCellProps, TableKeyCellProps, TableNumberCellProps, TableStatusCellProps, TableTagsCellLabels, TableTagsCellProps, TableTextCellProps, } from './TableCells.js';
|
|
2
|
+
export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell, TableTwoLineCell, } from './TableCells.js';
|
|
3
|
+
export type { TableDateCellProps, TableEntityCellProps, TableKeyCellProps, TableNumberCellProps, TableStatusCellProps, TableTagsCellLabels, TableTagsCellProps, TableTextCellProps, TableTwoLineCellMono, TableTwoLineCellProps, } from './TableCells.js';
|
package/dist/TableCells/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell } from '../chunk-
|
|
1
|
+
export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell, TableTwoLineCell } from '../chunk-3CALM534.js';
|
|
2
|
+
import '../chunk-O5CEG7TQ.js';
|
|
2
3
|
import '../chunk-QF5UNP4T.js';
|
|
3
4
|
import '../chunk-LBGKLYBY.js';
|
|
4
5
|
import '../chunk-EJNJMHQG.js';
|
|
@@ -112,3 +112,31 @@
|
|
|
112
112
|
font-size: var(--font-size-xs);
|
|
113
113
|
font-variant-numeric: tabular-nums;
|
|
114
114
|
}
|
|
115
|
+
|
|
116
|
+
/*
|
|
117
|
+
* A VALUE OVER A QUIETER LINE (TableTwoLineCell), each line its own block cut
|
|
118
|
+
* on one line. A column of boxes rather than two runs of text, so neither
|
|
119
|
+
* line takes the line box of the words around it, and the cell is exactly
|
|
120
|
+
* the two lines tall. `min-width: 0` lets the column shrink inside a grid's
|
|
121
|
+
* cell, which is what cuts the lines rather than pushing the column wider.
|
|
122
|
+
*/
|
|
123
|
+
.crewlet-table-cell__lines {
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
min-width: 0;
|
|
127
|
+
max-width: 100%;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* The mark is centred against both lines, as an entity's badge is against its name. */
|
|
131
|
+
.crewlet-table-cell__two-line {
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
gap: var(--spacing-2);
|
|
135
|
+
min-width: 0;
|
|
136
|
+
max-width: 100%;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* The mark keeps its size: the lines give way to the column, never the mark. */
|
|
140
|
+
.crewlet-table-cell__two-line > :first-child {
|
|
141
|
+
flex: none;
|
|
142
|
+
}
|
package/dist/Text/Text.d.ts
CHANGED
|
@@ -57,10 +57,22 @@ export interface TextProps extends HTMLAttributes<HTMLElement> {
|
|
|
57
57
|
* it on every push, because the proportional digits have different widths.
|
|
58
58
|
*/
|
|
59
59
|
numeric?: boolean | undefined;
|
|
60
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* One line, cut with an ellipsis: a name in a narrow column, a caption
|
|
62
|
+
* under it. While the words are cut, the pointer's title carries them whole
|
|
63
|
+
* (see `titleWhenCut`).
|
|
64
|
+
*/
|
|
61
65
|
truncate?: boolean | undefined;
|
|
62
|
-
/** Two or three lines, then an ellipsis. */
|
|
66
|
+
/** Two or three lines, then an ellipsis, with the whole words on the title while they are cut. */
|
|
63
67
|
clamp?: 2 | 3 | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Whether words cut by `truncate` or `clamp` carry their whole text as the
|
|
70
|
+
* element's title while they are cut. On by default. Turn it off where
|
|
71
|
+
* something else already shows the whole words, a `Tooltip` around the
|
|
72
|
+
* text or a title on the element around it, so the reader is not shown
|
|
73
|
+
* them twice. A `title` the caller passes always wins.
|
|
74
|
+
*/
|
|
75
|
+
titleWhenCut?: boolean | undefined;
|
|
64
76
|
/** Bounds the line length, for anything a reader reads rather than scans. */
|
|
65
77
|
measure?: TextMeasure | undefined;
|
|
66
78
|
}
|
|
@@ -77,5 +89,15 @@ export interface TextProps extends HTMLAttributes<HTMLElement> {
|
|
|
77
89
|
* `variant` sets the size, the weight, the tracking and the ink together,
|
|
78
90
|
* because those four move as one. `tone` overrides only the ink, for the case
|
|
79
91
|
* where the same register carries a quieter fact.
|
|
92
|
+
*
|
|
93
|
+
* CUT WORDS ARE STILL READABLE WHOLE. A line cut with an ellipsis hid the
|
|
94
|
+
* end of a name from a sighted reader, who had no way to read it short of
|
|
95
|
+
* widening the window, while a screen reader read it whole (the cut is
|
|
96
|
+
* paint, not content). So a cut Text puts its whole words on its title. It
|
|
97
|
+
* is MEASURED AT THE MOMENT THE POINTER ARRIVES, which is the only moment a
|
|
98
|
+
* title is read: the words and the column are what they are then, so a title
|
|
99
|
+
* never goes stale after a resize or a new value, and a table of a thousand
|
|
100
|
+
* cut cells costs nothing until one is hovered. Words that fit carry no
|
|
101
|
+
* title, since a tooltip that repeats what is on the screen is noise.
|
|
80
102
|
*/
|
|
81
|
-
export declare function Text({ as: Tag, variant, tone, mono, numeric, truncate, clamp, measure, className, children, ...rest }: TextProps): import("react").JSX.Element;
|
|
103
|
+
export declare function Text({ as: Tag, variant, tone, mono, numeric, truncate, clamp, measure, titleWhenCut, className, children, ...rest }: TextProps): import("react").JSX.Element;
|
package/dist/Text/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Text } from '../chunk-
|
|
1
|
+
export { Text } from '../chunk-O5CEG7TQ.js';
|
|
2
2
|
import '../chunk-ODEQTPFM.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Text } from './chunk-O5CEG7TQ.js';
|
|
1
2
|
import { Link } from './chunk-QF5UNP4T.js';
|
|
2
3
|
import { Tag } from './chunk-LBGKLYBY.js';
|
|
3
4
|
import { RelativeTime } from './chunk-EJNJMHQG.js';
|
|
@@ -5,7 +6,7 @@ import { EmptyValue } from './chunk-RE3FBGQQ.js';
|
|
|
5
6
|
import { Avatar } from './chunk-XZY6S3UC.js';
|
|
6
7
|
import { VisuallyHidden } from './chunk-UT6CW3C7.js';
|
|
7
8
|
import { cx } from './chunk-ODEQTPFM.js';
|
|
8
|
-
import './TableCells-
|
|
9
|
+
import './TableCells-ZALZLLEF.css';
|
|
9
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
11
|
|
|
11
12
|
function TableKeyCell({ value, text, href, emptyLabel = "Not set" }) {
|
|
@@ -84,5 +85,38 @@ function TableTextCell({ children, icon, mark }) {
|
|
|
84
85
|
/* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__truncate", children })
|
|
85
86
|
] });
|
|
86
87
|
}
|
|
88
|
+
function TableTwoLineCell({
|
|
89
|
+
value,
|
|
90
|
+
detail,
|
|
91
|
+
mono,
|
|
92
|
+
title,
|
|
93
|
+
titleWhenCut = true,
|
|
94
|
+
icon,
|
|
95
|
+
mark
|
|
96
|
+
}) {
|
|
97
|
+
const ownTitles = titleWhenCut && title === void 0;
|
|
98
|
+
const hasDetail = detail !== void 0 && detail !== null && detail !== false && detail !== "";
|
|
99
|
+
const leading = mark ?? (icon !== void 0 && icon !== null ? /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__icon", "aria-hidden": "true", children: icon }) : null);
|
|
100
|
+
const leads = leading !== null && leading !== void 0 && leading !== false;
|
|
101
|
+
const lines = /* @__PURE__ */ jsxs("span", { className: "crewlet-table-cell__lines", title: leads ? void 0 : title, children: [
|
|
102
|
+
/* @__PURE__ */ jsx(Text, { variant: "inherit", truncate: true, mono: mono === "value" || mono === "both", titleWhenCut: ownTitles, children: value }),
|
|
103
|
+
hasDetail ? /* @__PURE__ */ jsx(
|
|
104
|
+
Text,
|
|
105
|
+
{
|
|
106
|
+
variant: "caption",
|
|
107
|
+
tone: "secondary",
|
|
108
|
+
truncate: true,
|
|
109
|
+
mono: mono === "detail" || mono === "both",
|
|
110
|
+
titleWhenCut: ownTitles,
|
|
111
|
+
children: detail
|
|
112
|
+
}
|
|
113
|
+
) : null
|
|
114
|
+
] });
|
|
115
|
+
if (!leads) return lines;
|
|
116
|
+
return /* @__PURE__ */ jsxs("span", { className: "crewlet-table-cell__two-line", title, children: [
|
|
117
|
+
leading,
|
|
118
|
+
lines
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
87
121
|
|
|
88
|
-
export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell };
|
|
122
|
+
export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell, TableTwoLineCell };
|
|
@@ -11,14 +11,22 @@ function Text({
|
|
|
11
11
|
truncate = false,
|
|
12
12
|
clamp,
|
|
13
13
|
measure,
|
|
14
|
+
titleWhenCut = true,
|
|
14
15
|
className,
|
|
15
16
|
children,
|
|
16
17
|
...rest
|
|
17
18
|
}) {
|
|
19
|
+
const cuts = truncate || clamp !== void 0;
|
|
20
|
+
const measures = cuts && titleWhenCut && rest.title === void 0;
|
|
21
|
+
const { onMouseEnter } = rest;
|
|
18
22
|
return /* @__PURE__ */ jsx(
|
|
19
23
|
Tag,
|
|
20
24
|
{
|
|
21
25
|
...rest,
|
|
26
|
+
onMouseEnter: measures ? (event) => {
|
|
27
|
+
titleIfCut(event.currentTarget, truncate ? "inline" : "block");
|
|
28
|
+
onMouseEnter?.(event);
|
|
29
|
+
} : onMouseEnter,
|
|
22
30
|
className: cx(
|
|
23
31
|
"crewlet-text",
|
|
24
32
|
`crewlet-text--${variant}`,
|
|
@@ -34,5 +42,11 @@ function Text({
|
|
|
34
42
|
}
|
|
35
43
|
);
|
|
36
44
|
}
|
|
45
|
+
function titleIfCut(element, axis) {
|
|
46
|
+
const cut = axis === "inline" ? element.scrollWidth > element.clientWidth : element.scrollHeight > element.clientHeight;
|
|
47
|
+
const words = (element.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
48
|
+
if (cut && words !== "") element.setAttribute("title", words);
|
|
49
|
+
else element.removeAttribute("title");
|
|
50
|
+
}
|
|
37
51
|
|
|
38
52
|
export { Text };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SidebarNav, NavItem } from './chunk-BRK3HXGZ.js';
|
|
2
|
+
import { Select } from './chunk-3IWZVEXA.js';
|
|
3
|
+
import { Text } from './chunk-O5CEG7TQ.js';
|
|
4
|
+
import { Card } from './chunk-U2IQNDDR.js';
|
|
5
|
+
import './RailList-N2ZBPRM2.css';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var RAIL_LIST_LABELS = {
|
|
9
|
+
counted: "items",
|
|
10
|
+
option: (label, count) => count === void 0 ? label : `${label} (${count.toLocaleString()})`
|
|
11
|
+
};
|
|
12
|
+
var elsewhere = (event) => event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0;
|
|
13
|
+
function RailList({
|
|
14
|
+
label,
|
|
15
|
+
items,
|
|
16
|
+
value,
|
|
17
|
+
onChange,
|
|
18
|
+
toolbar,
|
|
19
|
+
note,
|
|
20
|
+
children,
|
|
21
|
+
renderLink,
|
|
22
|
+
labels: overrides,
|
|
23
|
+
className
|
|
24
|
+
}) {
|
|
25
|
+
const words = overrides ? { ...RAIL_LIST_LABELS, ...overrides } : RAIL_LIST_LABELS;
|
|
26
|
+
const chosen = items.find((item) => item.key === value);
|
|
27
|
+
const about = chosen?.description;
|
|
28
|
+
const picker = /* @__PURE__ */ jsx("span", { className: "crewlet-rail-list__picker", children: /* @__PURE__ */ jsx(
|
|
29
|
+
Select,
|
|
30
|
+
{
|
|
31
|
+
ariaLabel: label,
|
|
32
|
+
size: "sm",
|
|
33
|
+
width: "auto",
|
|
34
|
+
value,
|
|
35
|
+
options: items.map((item) => ({ value: item.key, label: words.option(item.label, item.count), text: item.label })),
|
|
36
|
+
onChange: (next) => onChange(String(next))
|
|
37
|
+
}
|
|
38
|
+
) });
|
|
39
|
+
const bar = toolbar === void 0 || toolbar === null ? /* @__PURE__ */ jsx("div", { className: "crewlet-rail-list__bar crewlet-rail-list__bar--picker-only", children: picker }) : /* @__PURE__ */ jsx("div", { className: "crewlet-rail-list__bar", children: typeof toolbar === "function" ? toolbar(picker) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
40
|
+
picker,
|
|
41
|
+
toolbar
|
|
42
|
+
] }) });
|
|
43
|
+
return /* @__PURE__ */ jsx(Card, { padding: "none", className, children: /* @__PURE__ */ jsxs("div", { className: "crewlet-rail-list", children: [
|
|
44
|
+
bar,
|
|
45
|
+
/* @__PURE__ */ jsxs("div", { className: "crewlet-rail-list__body", children: [
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "crewlet-rail-list__rail", children: [
|
|
47
|
+
/* @__PURE__ */ jsx(SidebarNav, { label, dense: true, children: items.map((item) => /* @__PURE__ */ jsx(
|
|
48
|
+
NavItem,
|
|
49
|
+
{
|
|
50
|
+
label: item.label,
|
|
51
|
+
href: item.href,
|
|
52
|
+
current: item.key === value,
|
|
53
|
+
count: item.count === void 0 ? void 0 : { value: item.count.toLocaleString(), label: words.counted },
|
|
54
|
+
renderLink,
|
|
55
|
+
onClick: (event) => {
|
|
56
|
+
if (item.href !== void 0 && elsewhere(event)) return;
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
onChange(item.key);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
item.key
|
|
62
|
+
)) }),
|
|
63
|
+
note !== void 0 && note !== null && note !== false ? /* @__PURE__ */ jsx(Text, { as: "p", variant: "caption", tone: "tertiary", className: "crewlet-rail-list__note", children: note }) : null
|
|
64
|
+
] }),
|
|
65
|
+
/* @__PURE__ */ jsxs("div", { className: "crewlet-rail-list__list", children: [
|
|
66
|
+
about !== void 0 && about !== null && about !== false && about !== "" ? /* @__PURE__ */ jsx(Text, { as: "p", variant: "caption", tone: "secondary", className: "crewlet-rail-list__about", children: about }) : null,
|
|
67
|
+
children
|
|
68
|
+
] })
|
|
69
|
+
] })
|
|
70
|
+
] }) });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { RAIL_LIST_LABELS, RailList };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { FormField, Label, Input } from './chunk-O7N6W52M.js';
|
|
2
|
+
import { Button } from './chunk-DGXZ635L.js';
|
|
3
|
+
import { IconButton } from './chunk-UAUP6XGK.js';
|
|
4
|
+
import { announce } from './chunk-2G355KDJ.js';
|
|
5
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
6
|
+
import './KeyValueRows-US5KW7JK.css';
|
|
7
|
+
import { useId, useRef, useEffect } from 'react';
|
|
8
|
+
import { TrashGlyph, PlusGlyph } from '@crewlethq/icons/glyphs';
|
|
9
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
function keyValueRowsLabels(keyLabel, valueLabel = "Value") {
|
|
12
|
+
const item = keyLabel.toLocaleLowerCase();
|
|
13
|
+
return {
|
|
14
|
+
key: (position) => `${keyLabel} ${position}`,
|
|
15
|
+
value: (key, position) => key ? `${valueLabel} for ${key}` : `${valueLabel} for ${item} ${position}`,
|
|
16
|
+
remove: (key, position) => key ? `Remove ${key}` : `Remove ${item} ${position}`,
|
|
17
|
+
add: `Add ${item}`,
|
|
18
|
+
removed: (key, position) => key ? `Removed ${key}` : `Removed ${item} ${position}`
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function KeyValueRows(props) {
|
|
22
|
+
const {
|
|
23
|
+
label,
|
|
24
|
+
hideLabel = false,
|
|
25
|
+
keyLabel,
|
|
26
|
+
valueLabel = "Value",
|
|
27
|
+
value: rows,
|
|
28
|
+
onChange,
|
|
29
|
+
keyPlaceholder,
|
|
30
|
+
valuePlaceholder,
|
|
31
|
+
secret = false,
|
|
32
|
+
keyReadOnly,
|
|
33
|
+
keyError,
|
|
34
|
+
valueError,
|
|
35
|
+
empty,
|
|
36
|
+
helper,
|
|
37
|
+
error,
|
|
38
|
+
disabled = false,
|
|
39
|
+
labels: overrides,
|
|
40
|
+
className,
|
|
41
|
+
createRow
|
|
42
|
+
} = props;
|
|
43
|
+
const words = { ...keyValueRowsLabels(keyLabel, valueLabel), ...overrides };
|
|
44
|
+
const base = useId();
|
|
45
|
+
const minted = useRef(0);
|
|
46
|
+
const keyFields = useRef(/* @__PURE__ */ new Map());
|
|
47
|
+
const addButton = useRef(null);
|
|
48
|
+
const focusNext = useRef(null);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const want = focusNext.current;
|
|
51
|
+
if (want === null) return;
|
|
52
|
+
focusNext.current = null;
|
|
53
|
+
if (want === "add") addButton.current?.focus();
|
|
54
|
+
else keyFields.current.get(want.id)?.focus();
|
|
55
|
+
});
|
|
56
|
+
const blank = () => createRow ? createRow() : { id: `${base}-${minted.current += 1}`, key: "", value: "" };
|
|
57
|
+
const edit = (id, change) => onChange(rows.map((row) => row.id === id ? { ...row, ...change } : row));
|
|
58
|
+
const add = () => {
|
|
59
|
+
const row = blank();
|
|
60
|
+
focusNext.current = { id: row.id };
|
|
61
|
+
onChange([...rows, row]);
|
|
62
|
+
};
|
|
63
|
+
const remove = (index) => {
|
|
64
|
+
const gone = rows[index];
|
|
65
|
+
const next = rows.filter((_, i) => i !== index);
|
|
66
|
+
const neighbour = next[index] ?? next[index - 1];
|
|
67
|
+
focusNext.current = neighbour ? { id: neighbour.id } : "add";
|
|
68
|
+
onChange(next);
|
|
69
|
+
announce(words.removed(gone.key.trim(), index + 1));
|
|
70
|
+
};
|
|
71
|
+
const placeholderFor = (row) => typeof valuePlaceholder === "function" ? valuePlaceholder(row) : valuePlaceholder;
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
FormField,
|
|
74
|
+
{
|
|
75
|
+
as: "fieldset",
|
|
76
|
+
label,
|
|
77
|
+
hideLabel,
|
|
78
|
+
helper,
|
|
79
|
+
error,
|
|
80
|
+
className: cx("crewlet-key-value-rows", className),
|
|
81
|
+
children: /* @__PURE__ */ jsxs("div", { className: "crewlet-key-value-rows__body", children: [
|
|
82
|
+
rows.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "crewlet-key-value-rows__table", children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { className: "crewlet-key-value-rows__head", "aria-hidden": "true", children: [
|
|
84
|
+
/* @__PURE__ */ jsx(Label, { as: "span", children: keyLabel }),
|
|
85
|
+
/* @__PURE__ */ jsx(Label, { as: "span", children: valueLabel })
|
|
86
|
+
] }),
|
|
87
|
+
rows.map((row, index) => {
|
|
88
|
+
const position = index + 1;
|
|
89
|
+
const key = row.key.trim();
|
|
90
|
+
return /* @__PURE__ */ jsxs("div", { className: "crewlet-key-value-rows__row", children: [
|
|
91
|
+
/* @__PURE__ */ jsx(FormField, { label: words.key(position), hideLabel: true, error: keyError?.(row, index), children: (field) => /* @__PURE__ */ jsx(
|
|
92
|
+
Input,
|
|
93
|
+
{
|
|
94
|
+
ref: (element) => {
|
|
95
|
+
if (element) keyFields.current.set(row.id, element);
|
|
96
|
+
else keyFields.current.delete(row.id);
|
|
97
|
+
},
|
|
98
|
+
id: field.id,
|
|
99
|
+
"aria-describedby": field.describedBy,
|
|
100
|
+
"aria-invalid": field.invalid || void 0,
|
|
101
|
+
error: field.invalid,
|
|
102
|
+
appearance: "reference",
|
|
103
|
+
value: row.key,
|
|
104
|
+
placeholder: keyPlaceholder,
|
|
105
|
+
readOnly: keyReadOnly?.(row) ?? false,
|
|
106
|
+
disabled,
|
|
107
|
+
autoComplete: "off",
|
|
108
|
+
spellCheck: false,
|
|
109
|
+
onChange: (event) => edit(row.id, { key: event.target.value })
|
|
110
|
+
}
|
|
111
|
+
) }),
|
|
112
|
+
/* @__PURE__ */ jsx(FormField, { label: words.value(key, position), hideLabel: true, error: valueError?.(row, index), children: (field) => /* @__PURE__ */ jsx(
|
|
113
|
+
Input,
|
|
114
|
+
{
|
|
115
|
+
id: field.id,
|
|
116
|
+
"aria-describedby": field.describedBy,
|
|
117
|
+
"aria-invalid": field.invalid || void 0,
|
|
118
|
+
error: field.invalid,
|
|
119
|
+
type: secret ? "password" : "text",
|
|
120
|
+
autoComplete: secret ? "new-password" : "off",
|
|
121
|
+
spellCheck: false,
|
|
122
|
+
value: row.value,
|
|
123
|
+
placeholder: placeholderFor(row),
|
|
124
|
+
disabled,
|
|
125
|
+
onChange: (event) => edit(row.id, { value: event.target.value })
|
|
126
|
+
}
|
|
127
|
+
) }),
|
|
128
|
+
/* @__PURE__ */ jsx(
|
|
129
|
+
IconButton,
|
|
130
|
+
{
|
|
131
|
+
variant: "ghost-danger",
|
|
132
|
+
label: words.remove(key, position),
|
|
133
|
+
icon: /* @__PURE__ */ jsx(TrashGlyph, { size: "sm" }),
|
|
134
|
+
disabled,
|
|
135
|
+
onClick: () => remove(index)
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
] }, row.id);
|
|
139
|
+
})
|
|
140
|
+
] }) : empty !== void 0 && empty !== null && empty !== false ? /* @__PURE__ */ jsx("p", { className: "crewlet-key-value-rows__empty", children: empty }) : null,
|
|
141
|
+
/* @__PURE__ */ jsx("div", { className: "crewlet-key-value-rows__add", children: /* @__PURE__ */ jsx(
|
|
142
|
+
Button,
|
|
143
|
+
{
|
|
144
|
+
ref: addButton,
|
|
145
|
+
variant: "ghost",
|
|
146
|
+
size: "small",
|
|
147
|
+
flush: true,
|
|
148
|
+
leadingIcon: /* @__PURE__ */ jsx(PlusGlyph, { size: "sm" }),
|
|
149
|
+
disabled,
|
|
150
|
+
onClick: add,
|
|
151
|
+
children: words.add
|
|
152
|
+
}
|
|
153
|
+
) })
|
|
154
|
+
] })
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export { KeyValueRows, keyValueRowsLabels };
|