@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
package/README.md
CHANGED
|
@@ -41,9 +41,9 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
|
|
|
41
41
|
| Area | Components |
|
|
42
42
|
| ---- | ---------- |
|
|
43
43
|
| Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `ghost`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `DownloadButton`, `Kbd` |
|
|
44
|
-
| Content and text | `Text` (the eight registers and `inherit`), `Prose`, `Statement`, `Document`, `DocumentTitle` and `DocumentTitleField`, the Markdown grammar (`parseMarkdown`, `parseMarkdownSpans`, `markdownInline`, `safeHref` and the line readers, with no React in it), `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime`, `Note`, `Divider` |
|
|
45
|
-
| Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `TimeRangePicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `ChoiceCards`, `MemberPicker` (groups granted whole, with members left out or chosen on their own, and `chosenMembers`), `ChipField` (chosen items as chips inside the field), `PersonRow` (a person or an agent as one row of a list), `Composer`, `MarkdownEditor`, `RichEditor` (with `preloadRichEditor`, and the round trip `roundTripMarkdown` and `canonicalMarkdown` from `@crewlethq/ui/RichEditorMarkdown`) |
|
|
46
|
-
| Data display | `DataGrid` (with `useDataGridDriving`, `useAnyDataGridDriving` and `countDataGridRows`), `DataTable`, `CopyableCell`, `Table`, the read-only table cells (`TableKeyCell`, `TableNumberCell`, `TableDateCell`, `TableEntityCell`, `TableStatusCell`, `TableTagsCell`, `TableTextCell`), `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `QrCode`, `ObjectHeader`, `FactLine`, `SetByLine`, `Skeleton`, `Eyebrow`, `StatGroup`, `PinTile`, `IconTile`, `SettingsTable` (with `SettingsTable.Row`), `CatalogTile` |
|
|
44
|
+
| Content and text | `Text` (the eight registers and `inherit`; a line cut by `truncate` or `clamp` shows its whole words on hover), `Prose`, `Statement`, `Document`, `DocumentTitle` and `DocumentTitleField`, the Markdown grammar (`parseMarkdown`, `parseMarkdownSpans`, `markdownInline`, `safeHref` and the line readers, with no React in it), `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime`, `Note`, `Divider` |
|
|
45
|
+
| Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `TimeRangePicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `ChoiceCards`, `MemberPicker` (groups granted whole, with members left out or chosen on their own, and `chosenMembers`), `KeyValueRows` (keys and their values as rows under one column head, with `keyValueRowsLabels`), `ChipField` (chosen items as chips inside the field), `PersonRow` (a person or an agent as one row of a list), `Composer`, `MarkdownEditor`, `RichEditor` (with `preloadRichEditor`, and the round trip `roundTripMarkdown` and `canonicalMarkdown` from `@crewlethq/ui/RichEditorMarkdown`) |
|
|
46
|
+
| Data display | `DataGrid` (with `useDataGridDriving`, `useAnyDataGridDriving` and `countDataGridRows`), `DataTable`, `CopyableCell`, `Table`, the read-only table cells (`TableKeyCell`, `TableNumberCell`, `TableDateCell`, `TableEntityCell`, `TableStatusCell`, `TableTagsCell`, `TableTextCell`, `TableTwoLineCell`), `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `QrCode`, `ObjectHeader`, `FactLine`, `SetByLine`, `Skeleton`, `Eyebrow`, `StatGroup`, `PinTile`, `IconTile`, `SettingsTable` (with `SettingsTable.Row`), `CatalogTile` |
|
|
47
47
|
| List screens | `DataView`, and its parts on their own: `DataViewToolbar`, `FilterAxisBar`, `FilterAxisChip`, `FilterPill`, plus the filter model (`filterDefsFromColumns`, `applyColumnFilters`, `filterPredicate`, `blankFilterValue`) |
|
|
48
48
|
| Feedback and overlays | `Callout`, `AttentionList` (what needs a person, as rows in one card), `ConflictNote`, `EditingStatus`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
|
|
49
49
|
| Documents and review | `DiffView` and `DiffSummary` with the line diff they draw (`diffLines`, `diffStat`, `collapseDiff`, `firstChangedLines`, `splitLines`, `MAX_DIFF_LINES`, with no React in it), `DiffList`, `VersionCompare`, `CommentThread` |
|
|
@@ -51,7 +51,7 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
|
|
|
51
51
|
| Charts | `Legend`, `BarList`, `StackedBar`, `StackedColumns`, `ActivityStrip`, `TimeSeries`, `Sparkline`, the reading layer they share (`ChartTooltip`, `useChartHover`), and the ramp (`DATA_COLORS`, `DATA_COLOR_OTHER`, `dataColor`) |
|
|
52
52
|
| Marks | `Count` (with `tone="attention"`), `StatusDot`, `Spinner`, `EmptyValue`, `VisuallyHidden` |
|
|
53
53
|
| Hooks and seams | `useModalLayer`, `usePopupLayer`, `LayerHost`, `useListbox`, `useOptionKeys`, `useHighlightRest` and `HIGHLIGHT_REST_MS`, `useClipboard`, `useDownload`, `downloadText`, `downloadBlob`, `safeFilename`, `toCsv`, `elementToPng` and `ElementToPngError`, `useOutcome`, `useAnnouncer`, `useNow`, `formatRelative`, `HeadingLevelProvider`, `cx`, the node hues (`NODE_HUES`, `NODE_HUE_NAMES`, `isNodeHue`) |
|
|
54
|
-
| Layout | `Container`, `Section`, `Card`, `TileGrid`, `ListDetail`, `AsideLayout`, `Tabs`, `Accordion`, `Disclosure`, `Outline` (with `useActiveHeading` and `scrollToHeading`), `TreeGrid` |
|
|
54
|
+
| Layout | `Container`, `Section`, `Card`, `TileGrid`, `ListDetail`, `RailList` (a directory's groups as a rail beside its list, folding into a picker), `AsideLayout`, `Tabs`, `Accordion`, `Disclosure`, `Outline` (with `useActiveHeading` and `scrollToHeading`), `TreeGrid` |
|
|
55
55
|
| Arranging a page | `ArrangeBoard`, `ArrangeGrid`, `ArrangeFrame` (with `useGridResize`), `ArrangeGroup`, `ArrangeList`, and the reorder hooks under them (`useReorder`, `useReorderLists`, `moveKey`) |
|
|
56
56
|
| Application shell | `AppShell` with `AppShell.Rail`, the compact rail (`rail="compact"`, the one fold control from `onRailChange`, and `useAppShell().rail` to pick the shape) and `fill="flush"`, the `aside` track and the `peek` slot (`useAppShellAside`, `useAppShellPeek`), `AppShell.ResizeHandle` (also `ResizeHandle`) and `useStoredWidth`, `SidePanel` and `SidePanelFrame`, `PeekRail` and `PeekHost` (with `PeekNeighbours`, `usePeekNeighbours`, `usePeekStep`, `peekClickHandler`, `peekRowActivation`), `SectionFrame` (with `SectionFrame.Nav` and `SectionFrame.Body`), `DockColumn` and `usePresence`, `LockedLabel`, `AppShell.Topbar`, `AppShell.AccountRow` and `useAppShell`, `AuthLayout`, `SidebarNav` and `NavItem` (with `dense` lists, `unread` and `muted` rows and `collapsible` groups), `CompactNav` with `CompactNavItem`, `CompactNavGroup` and `CompactNavSeparator` (the rail folded to its marks, fed from the same places), `DirectoryList`, `BrandLockup`, `SearchTrigger`, `PageHeader`, `Toolbar`, `Stack`, `Inline`, `Spacer`, `AutoGrid`, `ErrorBoundary`, `useShortcut`, `useFullscreen`, `useIdle`, `useMediaMatch` |
|
|
57
57
|
| Preferences | `ThemeToggle`, `ThemeSwitcher`, `DensitySwitcher`, `ThemeGallery`, `ThemeSwatch`, `useThemePreference`, `useDensityPreference`, `useSystemTheme`, `resolveTheme`, `applyStoredPreferences`, and the framework-free `applyTheme`, `applyPalette`, `applyDensity` with `THEME_PREFERENCES`, `DENSITY_PREFERENCES` and their guards |
|
|
@@ -205,6 +205,20 @@ places:
|
|
|
205
205
|
- A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
|
|
206
206
|
when the content beneath it moves, and every panel anchored inside follows.
|
|
207
207
|
|
|
208
|
+
## Changes in 0.86.0
|
|
209
|
+
|
|
210
|
+
### Forms, tables and directories
|
|
211
|
+
|
|
212
|
+
**`KeyValueRows` is new**: keys and their values edited as rows under one column head, for request headers, environment variables and any list of names and what each is set to. The key and value columns are named once (`keyLabel`, `valueLabel`), and each field is named on its own for a screen reader ("Header 2", "Value for Authorization"), so every row is the same row. Every row stands on its fields' top edge, so a refused key or value (`keyError`, `valueError`) is said under its own field, linked to it, and the controls beside it stay level. The remove button is a square the fields' height, and Add is a flush quiet action under the rows. Add puts the focus in the new row's key; Remove moves it to the row that took that row's place, else the one before, else Add, and says what went. Keys take the `reference` appearance and are never spell-checked; `secret` hides each value and marks it `new-password`, so no browser offers a saved password into it. `keyReadOnly` holds a row's key, and `valuePlaceholder` may differ per row. Controlled: every edit hands back a new list with each untouched row as it was. Rows may carry fields of their own (`KeyValueRows<T extends KeyValueRow>`), and then `createRow` makes a blank one, which the type requires. The group is a fieldset named by `label` (`hideLabel` keeps it for assistive technology only); `empty` is what an empty list says; `helper`, `error` and `disabled` are as on a field. Every word is the caller's (`labels`), with English defaults made from the column names by `keyValueRowsLabels(keyLabel, valueLabel)`. Types: `KeyValueRow`, `KeyValueRowsLabels`, `KeyValueRowsProps`. Stories: `KeyValueRows`.
|
|
213
|
+
|
|
214
|
+
**`TableTwoLineCell` is new**: a value over a quieter line, each cut on one line, among the read-only table cells. The `value` is set in the cell's own register and the `detail` under it is a caption in the secondary ink, so a row of these is exactly two lines tall whatever they hold. `mono` (`TableTwoLineCellMono`: `value`, `detail` or `both`) sets a literal in the mono face. A mark stands centred against both lines, as a column's hidden `icon` or a row's read `mark`, and in a grid's stacked rows the lines give the row its baseline. A cut line shows its whole words on hover; `title` speaks for the whole cell instead, and `titleWhenCut={false}` leaves the words to a `Tooltip` around the cell. Types: `TableTwoLineCellProps`, `TableTwoLineCellMono`. Stories: `TableCells` Two Line Cells, and its right to left, stacked and both themes variants.
|
|
215
|
+
|
|
216
|
+
**`RailList` is new**: a directory in a card, the groups its items are filed in as a rail beside the chosen group's list, so a long catalogue reads as a few short lists. The rail is a dense `SidebarNav`, each group (`RailListItem`) a row with its `count`, read as part of its name with `labels.counted` ("Chat, 6 apps"), the chosen one (`value`) raised and `aria-current`, with an optional quiet `note` under the rows. A group with an `href` is a link a reader can open in a new tab, and a plain click chooses it in place (`onChange`); without one it is a button. The chosen group's `description` stands over the list on a hairline, and a grid at the top of the list draws no second rule. Under 44rem of the card's own width the rail goes and a `Select` of the same groups, named by the same `label`, joins the bar across the top, so the same directory in a page and in a sheet needs nothing set. `toolbar` is the bar's content: given as a function, it is handed the picker to place among its own controls; given as content, the picker leads it. `renderLink` draws each row's link for a router. Every word is the caller's (`labels`, with `RAIL_LIST_LABELS` as the defaults). Types: `RailListItem`, `RailListLabels`, `RailListProps`. Stories: `RailList`.
|
|
217
|
+
|
|
218
|
+
### Text
|
|
219
|
+
|
|
220
|
+
**A `Text` cut by `truncate` or `clamp` shows its whole words on hover.** While the words are cut, the element's title carries them whole, read from the element as it is shown; while they fit, it carries none. The cut is measured when the pointer arrives, so the title never goes stale after a resize or a new value, and nothing is measured until then. A `title` the caller passes always wins, and `titleWhenCut={false}` leaves the title to whatever already shows the words, such as a `Tooltip`. Story: `Text` Whole Words When Cut.
|
|
221
|
+
|
|
208
222
|
## Changes in 0.85.0
|
|
209
223
|
|
|
210
224
|
### What needs a person, and who gets something
|
package/dist/DataView/index.js
CHANGED
|
@@ -4,11 +4,11 @@ import '../chunk-K4LZXSKZ.js';
|
|
|
4
4
|
import '../chunk-7K6LVM5I.js';
|
|
5
5
|
import '../chunk-WANKINXG.js';
|
|
6
6
|
import '../chunk-ZXXZESCD.js';
|
|
7
|
+
import '../chunk-YGRTX67J.js';
|
|
7
8
|
import '../chunk-STJ7QBC3.js';
|
|
8
9
|
import '../chunk-DTIW4CHZ.js';
|
|
9
|
-
import '../chunk-YGRTX67J.js';
|
|
10
|
-
import '../chunk-EE3DJMSQ.js';
|
|
11
10
|
import '../chunk-NXFTCPF6.js';
|
|
11
|
+
import '../chunk-EE3DJMSQ.js';
|
|
12
12
|
import '../chunk-EC6EUIAD.js';
|
|
13
13
|
import '../chunk-EJNJMHQG.js';
|
|
14
14
|
import '../chunk-RE3FBGQQ.js';
|
|
@@ -73,13 +73,23 @@ export type InboxRowTone = 'default' | 'danger';
|
|
|
73
73
|
export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'onClick' | 'title'> {
|
|
74
74
|
/** Who the row is from: a seat's or a person's name. */
|
|
75
75
|
title: ReactNode;
|
|
76
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* After the name, in the quiet ink: a word where the row is not a plain
|
|
78
|
+
* question or update ("coding run", "stopped"), or what the person did
|
|
79
|
+
* ("replied to a thread in Design"). The name and the qualifier read as one
|
|
80
|
+
* run of words that wraps to a second line before it stops, so a long
|
|
81
|
+
* qualifier never hides the name.
|
|
82
|
+
*/
|
|
77
83
|
qualifier?: ReactNode;
|
|
78
84
|
/** `danger` sets the qualifier in the danger ink, for a seat that has stopped. */
|
|
79
85
|
qualifierTone?: InboxRowTone | undefined;
|
|
80
86
|
/** At the end of the first line: when it arrived. */
|
|
81
87
|
meta?: ReactNode;
|
|
82
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Before the words: an avatar or a mark, at the target size. Its slot is at
|
|
90
|
+
* least a target square and grows to fit a larger mark, such as a 32px face,
|
|
91
|
+
* with the name's line beside its top.
|
|
92
|
+
*/
|
|
83
93
|
leading?: ReactNode;
|
|
84
94
|
/** What the row says, in up to three lines. */
|
|
85
95
|
children?: ReactNode;
|
|
@@ -159,7 +169,8 @@ export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'on
|
|
|
159
169
|
* caller names in the head (`status`): every word stays legible, and the
|
|
160
170
|
* state is never carried by a decoration alone.
|
|
161
171
|
*
|
|
162
|
-
* NOTHING IS CUT AT THE EDGE. The name
|
|
172
|
+
* NOTHING IS CUT AT THE EDGE. The name and its qualifier wrap to two lines
|
|
173
|
+
* before they stop, the time keeps a column of its own and never wraps, and
|
|
163
174
|
* the words take up to three lines before they stop, so a row says what it is
|
|
164
175
|
* rather than its first few words.
|
|
165
176
|
*/
|
package/dist/InboxList/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { INBOX_ROW_LEAVE, InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-
|
|
1
|
+
export { INBOX_ROW_LEAVE, InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-VKNZQB2G.js';
|
|
2
2
|
import '../chunk-RK4XPHVG.js';
|
|
3
3
|
import '../chunk-6C7GQOBV.js';
|
|
4
4
|
import '../chunk-PCAT2EFB.js';
|
|
@@ -223,19 +223,36 @@
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* row's edge
|
|
226
|
+
* THE LEADING SLOT FITS WHAT IT HOLDS. It is at least a target square (and at
|
|
227
|
+
* least a line tall), with its mark centred in it, and it grows to a larger
|
|
228
|
+
* mark, such as a 32px face, rather than letting it spill: a fixed one-line
|
|
229
|
+
* box let a 32px face overflow it by 8px, up and to the left, until it all but
|
|
230
|
+
* touched the row's edge.
|
|
231
|
+
*
|
|
232
|
+
* THE NAME'S LINE IS CENTRED ON A TARGET-SIZED MARK by moving the words down
|
|
233
|
+
* rather than the mark up: the words start half the difference between the
|
|
234
|
+
* target and a line lower, and the control's top pad is that much shorter, so
|
|
235
|
+
* a row with a target-sized avatar keeps its height and its name where they
|
|
236
|
+
* were, with the avatar inside the control instead of over its pad. A larger
|
|
237
|
+
* mark hangs from the same top, beside the name's line and the one under it.
|
|
231
238
|
*/
|
|
239
|
+
.crewlet-inbox-row__control--leading {
|
|
240
|
+
--crewlet-inbox-row-lift: max(0px, (var(--size-target-min) - 1lh) / 2);
|
|
241
|
+
|
|
242
|
+
padding-top: calc(var(--spacing-2) + var(--spacing-1) / 2 - var(--crewlet-inbox-row-lift));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.crewlet-inbox-row__control--leading > .crewlet-inbox-row__main {
|
|
246
|
+
padding-top: var(--crewlet-inbox-row-lift);
|
|
247
|
+
}
|
|
248
|
+
|
|
232
249
|
.crewlet-inbox-row__leading {
|
|
233
250
|
display: inline-flex;
|
|
234
251
|
flex: none;
|
|
235
252
|
align-items: center;
|
|
236
253
|
justify-content: center;
|
|
237
|
-
width: var(--size-target-min);
|
|
238
|
-
height: 1lh;
|
|
254
|
+
min-width: var(--size-target-min);
|
|
255
|
+
min-height: max(var(--size-target-min), 1lh);
|
|
239
256
|
}
|
|
240
257
|
|
|
241
258
|
.crewlet-inbox-row__main {
|
|
@@ -245,24 +262,49 @@
|
|
|
245
262
|
min-width: 0;
|
|
246
263
|
}
|
|
247
264
|
|
|
265
|
+
/*
|
|
266
|
+
* THE HEAD IS ONE RUN OF WORDS: who, then what they did, wrapping as words do
|
|
267
|
+
* and stopping at two lines with an ellipsis. The name comes first, so it is
|
|
268
|
+
* the last thing a long qualifier can push out of sight. As one line of boxes
|
|
269
|
+
* the name was the only one allowed to shrink, so a qualifier longer than a
|
|
270
|
+
* word ("replied to a thread in Product Manager") squeezed the name to
|
|
271
|
+
* nothing and pushed the time past the row's edge.
|
|
272
|
+
*
|
|
273
|
+
* THE TIME FLOATS AT THE END OF THE FIRST LINE, so only that line gives up
|
|
274
|
+
* room to it and the second runs the row's full width: "replied to a thread
|
|
275
|
+
* in #engineering" keeps the room's name, the part of the line most worth
|
|
276
|
+
* reading, where a column of its own took the time's width from both lines.
|
|
277
|
+
* The float sits in a block of its own inside the clamp, which is where every
|
|
278
|
+
* engine lays a float out and still counts the clamped lines.
|
|
279
|
+
*/
|
|
248
280
|
.crewlet-inbox-row__head {
|
|
249
|
-
display:
|
|
250
|
-
|
|
251
|
-
|
|
281
|
+
display: -webkit-box;
|
|
282
|
+
-webkit-box-orient: vertical;
|
|
283
|
+
-webkit-line-clamp: 2;
|
|
284
|
+
line-clamp: 2;
|
|
252
285
|
min-width: 0;
|
|
286
|
+
overflow: hidden;
|
|
287
|
+
overflow-wrap: anywhere;
|
|
288
|
+
color: var(--color-text-tertiary);
|
|
253
289
|
}
|
|
254
290
|
|
|
255
|
-
.crewlet-inbox-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
291
|
+
.crewlet-inbox-row__head-text {
|
|
292
|
+
display: block;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* The time (and any status), in the head's own line box, so the time sits on the name's baseline. */
|
|
296
|
+
.crewlet-inbox-row__head-end {
|
|
297
|
+
float: inline-end;
|
|
298
|
+
margin-inline-start: var(--spacing-2);
|
|
259
299
|
white-space: nowrap;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.crewlet-inbox-row__title {
|
|
260
303
|
font-weight: var(--font-weight-medium);
|
|
261
304
|
color: var(--color-text-primary);
|
|
262
305
|
}
|
|
263
306
|
|
|
264
307
|
.crewlet-inbox-row__qualifier {
|
|
265
|
-
flex: none;
|
|
266
308
|
font-size: var(--font-size-xs);
|
|
267
309
|
color: var(--color-text-tertiary);
|
|
268
310
|
}
|
|
@@ -272,8 +314,6 @@
|
|
|
272
314
|
}
|
|
273
315
|
|
|
274
316
|
.crewlet-inbox-row__meta {
|
|
275
|
-
flex: none;
|
|
276
|
-
margin-inline-start: auto;
|
|
277
317
|
font-size: var(--font-size-xs);
|
|
278
318
|
font-variant-numeric: tabular-nums;
|
|
279
319
|
white-space: nowrap;
|
|
@@ -321,21 +361,12 @@
|
|
|
321
361
|
color: var(--color-text-secondary);
|
|
322
362
|
}
|
|
323
363
|
|
|
364
|
+
/* At the end of the first line, before the time when the row has both. */
|
|
324
365
|
.crewlet-inbox-row__status {
|
|
325
366
|
display: inline-flex;
|
|
326
|
-
flex: none;
|
|
327
367
|
align-items: center;
|
|
328
368
|
}
|
|
329
369
|
|
|
330
|
-
/* With a status, the time stays at the end and the status sits beside it. */
|
|
331
|
-
.crewlet-inbox-row__status + .crewlet-inbox-row__meta {
|
|
332
|
-
margin-inline-start: 0;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
.crewlet-inbox-row__head > .crewlet-inbox-row__status {
|
|
336
|
-
margin-inline-start: auto;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
370
|
/*
|
|
340
371
|
* What came of the row: a glyph in the tone's ink, then words in the
|
|
341
372
|
* secondary ink, on one line that wraps rather than cuts.
|
|
@@ -391,7 +422,9 @@
|
|
|
391
422
|
|
|
392
423
|
/*
|
|
393
424
|
* The task, under the words and lined up with them: the control's start pad,
|
|
394
|
-
*
|
|
425
|
+
* a target-sized leading slot and the gap after it. The task sits outside the
|
|
426
|
+
* control, so it lines up with the words for a mark of the target size, the
|
|
427
|
+
* size `leading` asks for.
|
|
395
428
|
*/
|
|
396
429
|
.crewlet-inbox-row__aside {
|
|
397
430
|
min-width: 0;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/** One key and its value: a header and what it carries, a variable and what it is set to. */
|
|
3
|
+
export interface KeyValueRow {
|
|
4
|
+
/**
|
|
5
|
+
* Stable across edits, and unique in the list: the row's identity while its
|
|
6
|
+
* key is typed, emptied or repeated. Two rows may share a key; never an id.
|
|
7
|
+
*/
|
|
8
|
+
id: string;
|
|
9
|
+
key: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
/** Every word the rows say, beyond the names of their two columns. */
|
|
13
|
+
export interface KeyValueRowsLabels {
|
|
14
|
+
/** Names a row's key field: "Header 2". */
|
|
15
|
+
key: (position: number) => string;
|
|
16
|
+
/** Names a row's value field by its key: "Value for Authorization", or "Value for header 2" before one is typed. */
|
|
17
|
+
value: (key: string, position: number) => string;
|
|
18
|
+
/** Names a row's remove button: "Remove Authorization". */
|
|
19
|
+
remove: (key: string, position: number) => string;
|
|
20
|
+
/** The add action's words: "Add header". */
|
|
21
|
+
add: string;
|
|
22
|
+
/** Said once a row is removed, since the row's going is otherwise silent: "Removed Authorization". */
|
|
23
|
+
removed: (key: string, position: number) => string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The rows' words in English, made from the two column names: `Header` and
|
|
27
|
+
* `Value` give "Header 2", "Value for Authorization", "Remove Authorization"
|
|
28
|
+
* and "Add header". A caller whose key column is an acronym (`URL`) or not in
|
|
29
|
+
* English passes its own `labels`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function keyValueRowsLabels(keyLabel: string, valueLabel?: string): KeyValueRowsLabels;
|
|
32
|
+
interface KeyValueRowsBaseProps<T extends KeyValueRow> {
|
|
33
|
+
/**
|
|
34
|
+
* What the list is: "Headers", "Environment variables". The group's legend,
|
|
35
|
+
* read on entering it.
|
|
36
|
+
*/
|
|
37
|
+
label: string;
|
|
38
|
+
/**
|
|
39
|
+
* The legend is kept for assistive technology and drawn for nobody, where
|
|
40
|
+
* the column head or a heading right above already names the list.
|
|
41
|
+
*/
|
|
42
|
+
hideLabel?: boolean | undefined;
|
|
43
|
+
/** The key column's head, and the name each key field is called by: "Header", "Variable". */
|
|
44
|
+
keyLabel: string;
|
|
45
|
+
/** The value column's head. "Value" by default. */
|
|
46
|
+
valueLabel?: string | undefined;
|
|
47
|
+
value: readonly T[];
|
|
48
|
+
onChange: (next: T[]) => void;
|
|
49
|
+
keyPlaceholder?: string | undefined;
|
|
50
|
+
/** The value field's placeholder, the same for every row or per row (a stored secret says so). */
|
|
51
|
+
valuePlaceholder?: string | ((row: T) => string | undefined) | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* The values are secrets (a token, a password): each value field hides what
|
|
54
|
+
* is typed, and no browser offers a saved password for it.
|
|
55
|
+
*/
|
|
56
|
+
secret?: boolean | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* A row whose key may not change, such as one already saved: its key field
|
|
59
|
+
* is read only, and its value and its remove button still work.
|
|
60
|
+
*/
|
|
61
|
+
keyReadOnly?: ((row: T) => boolean) | undefined;
|
|
62
|
+
/** Why a row's key was refused, said under that key and linked to it. */
|
|
63
|
+
keyError?: ((row: T, index: number) => ReactNode) | undefined;
|
|
64
|
+
/** Why a row's value was refused, said under that value and linked to it. */
|
|
65
|
+
valueError?: ((row: T, index: number) => ReactNode) | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* What the list says while it holds no row, in place of the rows. Left out,
|
|
68
|
+
* an empty list is its legend and its add action.
|
|
69
|
+
*/
|
|
70
|
+
empty?: ReactNode;
|
|
71
|
+
/** Where the keys come from, or what shape they take, under the add action. */
|
|
72
|
+
helper?: ReactNode;
|
|
73
|
+
/** Why the list as a whole was refused. */
|
|
74
|
+
error?: ReactNode;
|
|
75
|
+
disabled?: boolean | undefined;
|
|
76
|
+
/** Words in place of the ones `keyValueRowsLabels` makes from `keyLabel` and `valueLabel`. */
|
|
77
|
+
labels?: Partial<KeyValueRowsLabels> | undefined;
|
|
78
|
+
className?: string | undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A blank row for the add action. Optional while a row is nothing but its id,
|
|
82
|
+
* key and value, when the list makes `{ id, key: '', value: '' }` itself;
|
|
83
|
+
* required once the caller's rows carry a field of their own that a blank row
|
|
84
|
+
* must set.
|
|
85
|
+
*
|
|
86
|
+
* AN EMPTY LIST NAMES NO ROW TYPE. `value={[]}` infers `never`, which no row
|
|
87
|
+
* is assignable to, so a test of assignability alone asked an empty list for
|
|
88
|
+
* a `createRow` it has no use for. A list whose row type was never inferred
|
|
89
|
+
* makes its own blank rows too.
|
|
90
|
+
*/
|
|
91
|
+
type RowMaker<T extends KeyValueRow> = [T] extends [never] ? {
|
|
92
|
+
createRow?: (() => T) | undefined;
|
|
93
|
+
} : KeyValueRow extends T ? {
|
|
94
|
+
createRow?: (() => T) | undefined;
|
|
95
|
+
} : {
|
|
96
|
+
createRow: () => T;
|
|
97
|
+
};
|
|
98
|
+
export type KeyValueRowsProps<T extends KeyValueRow = KeyValueRow> = KeyValueRowsBaseProps<T> & RowMaker<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Keys and their values, edited as rows under one column head: request
|
|
101
|
+
* headers, environment variables, any list of names and what each is set to.
|
|
102
|
+
*
|
|
103
|
+
* ONE HEAD OVER THE COLUMNS, SO EVERY ROW IS THE SAME ROW. The key and value
|
|
104
|
+
* columns are named once, above the first row, and each field's own name is
|
|
105
|
+
* read rather than drawn ("Header 2", "Value for Authorization"). A label
|
|
106
|
+
* drawn on the first row only stood that row taller than the rest, and the
|
|
107
|
+
* value and the remove button beside it lost the line they shared.
|
|
108
|
+
*
|
|
109
|
+
* AN ERROR MOVES NOTHING BESIDE IT. Every row stands on its fields' top edge,
|
|
110
|
+
* so a refused key grows its own cell downward and the value and the remove
|
|
111
|
+
* button stay level with the key's field.
|
|
112
|
+
*
|
|
113
|
+
* THE REMOVE BUTTON IS THE FIELDS' HEIGHT, a square at the control step, so a
|
|
114
|
+
* row is one line of three controls. Add is a flush quiet action under the
|
|
115
|
+
* rows, its words on the fields' own edge.
|
|
116
|
+
*
|
|
117
|
+
* FOCUS NEVER FALLS TO THE PAGE. Add puts it in the new row's key; Remove
|
|
118
|
+
* moves it to the row that took that row's place, else the one before, else
|
|
119
|
+
* Add, and says what went.
|
|
120
|
+
*
|
|
121
|
+
* Controlled: the rows are the caller's, and every edit hands back a new
|
|
122
|
+
* list with each untouched row as it was.
|
|
123
|
+
*/
|
|
124
|
+
export declare function KeyValueRows<T extends KeyValueRow = KeyValueRow>(props: KeyValueRowsProps<T>): import("react").JSX.Element;
|
|
125
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { KeyValueRows, keyValueRowsLabels } from '../chunk-TRZS7DLB.js';
|
|
2
|
+
import '../chunk-O7N6W52M.js';
|
|
3
|
+
import '../chunk-PGCLHYVP.js';
|
|
4
|
+
import '../chunk-DGXZ635L.js';
|
|
5
|
+
import '../chunk-XSPCITPN.js';
|
|
6
|
+
import '../chunk-TUEM2XIW.js';
|
|
7
|
+
import '../chunk-4ITETIGZ.js';
|
|
8
|
+
import '../chunk-UAUP6XGK.js';
|
|
9
|
+
import '../chunk-PQADV6P6.js';
|
|
10
|
+
import '../chunk-2G355KDJ.js';
|
|
11
|
+
import '../chunk-UT6CW3C7.js';
|
|
12
|
+
import '../chunk-ODEQTPFM.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* KeyValueRows: keys and their values as rows under one column head
|
|
3
|
+
* (KeyValueRows.tsx).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* The rows, then Add under them, a row's gap apart. */
|
|
7
|
+
.crewlet-key-value-rows__body,
|
|
8
|
+
.crewlet-key-value-rows__table {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: var(--spacing-2);
|
|
12
|
+
min-width: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* EVERY ROW STANDS ON ITS FIELDS' TOP EDGE, so a key's error grows its own
|
|
17
|
+
* cell downward and nothing beside it moves. The last track is the remove
|
|
18
|
+
* button's, a square the fields' height, and the head keeps it empty so the
|
|
19
|
+
* value's name stands over the value column.
|
|
20
|
+
*/
|
|
21
|
+
.crewlet-key-value-rows__head,
|
|
22
|
+
.crewlet-key-value-rows__row {
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) var(--size-control-md);
|
|
25
|
+
align-items: start;
|
|
26
|
+
gap: var(--spacing-2);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The head stands over the first row at a field label's distance from its field. */
|
|
30
|
+
.crewlet-key-value-rows__head {
|
|
31
|
+
margin-block-end: calc(var(--spacing-1) - var(--spacing-2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.crewlet-key-value-rows__empty {
|
|
35
|
+
margin: 0;
|
|
36
|
+
font-size: var(--font-size-compact);
|
|
37
|
+
line-height: var(--font-line-height-normal);
|
|
38
|
+
color: var(--color-text-secondary);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Add hugs its words, on the fields' start edge, rather than spanning the list. */
|
|
42
|
+
.crewlet-key-value-rows__add {
|
|
43
|
+
display: flex;
|
|
44
|
+
}
|
package/dist/ListDetail/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ListDetail } from '../chunk-
|
|
1
|
+
export { ListDetail } from '../chunk-P33VNG7Q.js';
|
|
2
2
|
import '../chunk-ODEQTPFM.js';
|
|
@@ -65,6 +65,12 @@
|
|
|
65
65
|
min-height: 0;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/* A LIST THAT GROWS SCROLLS IN ITS OWN COLUMN, whatever it holds. The pane
|
|
69
|
+
left the scrolling to its content, which a DirectoryList does for its
|
|
70
|
+
groups and an InboxList does not: an activity list taller than the screen
|
|
71
|
+
ran past the pane's end, where the screen clipped its later rows and its
|
|
72
|
+
"older" link, with nothing to scroll them into view. A list that scrolls
|
|
73
|
+
itself fills the pane and never makes it overflow. */
|
|
68
74
|
.crewlet-list-detail__list {
|
|
69
75
|
display: flex;
|
|
70
76
|
flex-direction: column;
|
|
@@ -72,6 +78,7 @@
|
|
|
72
78
|
min-height: 0;
|
|
73
79
|
padding-right: var(--crewlet-list-detail-gutter);
|
|
74
80
|
border-right: 1px solid var(--color-border-default);
|
|
81
|
+
overflow-y: auto;
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
/* A DIRECTORY RUNS TO THE HAIRLINE. A DirectoryList is navigation: its lines
|
|
@@ -106,7 +113,6 @@
|
|
|
106
113
|
.crewlet-list-detail__list {
|
|
107
114
|
padding-right: 0;
|
|
108
115
|
border-right: 0;
|
|
109
|
-
overflow-y: auto;
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
/* The list is the pane that scrolls now, and takes the detail's end where
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { type NavItemProps } from '../SidebarNav/index.js';
|
|
3
|
+
/** One of the groups a directory files its items in, and the rail's row for it. */
|
|
4
|
+
export interface RailListItem {
|
|
5
|
+
/** Unique among the items, and what `value` names. An empty key is a fine "All". */
|
|
6
|
+
key: string;
|
|
7
|
+
label: string;
|
|
8
|
+
/** How many items the group holds: a quiet figure at the row's end, and after the label in the folded picker. */
|
|
9
|
+
count?: number | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* The group's own address, where the chosen group is kept in the page's
|
|
12
|
+
* address: the row is then a link a reader can open in a new tab, and a
|
|
13
|
+
* plain click still chooses it in place.
|
|
14
|
+
*/
|
|
15
|
+
href?: string | undefined;
|
|
16
|
+
/** One quiet line about the group, said over the list while it is chosen: what the group holds. */
|
|
17
|
+
description?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
/** Every word the rail and its picker say, beyond the items' own labels. */
|
|
20
|
+
export interface RailListLabels {
|
|
21
|
+
/** What a count counts, read after the figure as part of the row's name: "apps" makes "Chat, 6 apps". */
|
|
22
|
+
counted: string;
|
|
23
|
+
/** A group as the folded picker names it: "Chat (6)". */
|
|
24
|
+
option: (label: string, count: number | undefined) => string;
|
|
25
|
+
}
|
|
26
|
+
export declare const RAIL_LIST_LABELS: RailListLabels;
|
|
27
|
+
export interface RailListProps {
|
|
28
|
+
/** Names the rail's navigation and the picker it folds into: "Categories", "Template kinds". */
|
|
29
|
+
label: string;
|
|
30
|
+
/** The groups, in the order the rail lists them; an "All" group is one of them, usually the first. */
|
|
31
|
+
items: readonly RailListItem[];
|
|
32
|
+
/** The chosen group's key. */
|
|
33
|
+
value: string;
|
|
34
|
+
onChange: (key: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* The bar across the top of the card, over the rail and the list: a search
|
|
37
|
+
* and filter chips. Where the card is too narrow for the rail, the picker
|
|
38
|
+
* it folds into joins this bar. Given as a function, it is handed the
|
|
39
|
+
* picker to place among its own controls; given as content, the picker
|
|
40
|
+
* leads the bar. Left out, the bar is drawn only to hold the picker.
|
|
41
|
+
*/
|
|
42
|
+
toolbar?: ReactNode | ((picker: ReactNode) => ReactNode);
|
|
43
|
+
/** One quiet fact about the whole directory, under the rail's rows: what every item in it has in common. */
|
|
44
|
+
note?: ReactNode;
|
|
45
|
+
/** The list the chosen group shows: a grid, a list of tiles. */
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
/** Draws each rail row's link, for a router that owns navigation (`SidebarNav`'s `renderLink`). */
|
|
48
|
+
renderLink?: NavItemProps['renderLink'];
|
|
49
|
+
labels?: Partial<RailListLabels> | undefined;
|
|
50
|
+
className?: string | undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A directory in a card: a rail of the groups its items are filed in, beside
|
|
54
|
+
* the list of the chosen group, so a long catalogue reads as a few short
|
|
55
|
+
* lists: apps in what they are for, templates in their kinds, reports in
|
|
56
|
+
* the teams that read them.
|
|
57
|
+
*
|
|
58
|
+
* THE RAIL IS THE APPLICATION'S RAIL, DENSER (`SidebarNav` `dense`), each
|
|
59
|
+
* group a row with its count, the chosen one raised and `aria-current`, on a
|
|
60
|
+
* hairline of its own beside the list, with an optional quiet note under its
|
|
61
|
+
* rows. The chosen group's description, where it has one, stands over the
|
|
62
|
+
* list on a hairline, so a reader knows what the short list is.
|
|
63
|
+
*
|
|
64
|
+
* IT FOLDS BY ITS OWN WIDTH, NOT THE WINDOW'S. Under 44rem of the card's
|
|
65
|
+
* width the rail does not leave the list a readable column, so the rail goes
|
|
66
|
+
* and a `Select` of the same groups, named the same, joins the bar across the
|
|
67
|
+
* top. The same directory in a page and in a 560px sheet is one component
|
|
68
|
+
* with nothing to set: the sheet is simply narrow.
|
|
69
|
+
*
|
|
70
|
+
* Controlled: the chosen group is the caller's, often kept in the page's
|
|
71
|
+
* address (`href` on each group), so a group opened in a new tab and Back
|
|
72
|
+
* both keep it.
|
|
73
|
+
*/
|
|
74
|
+
export declare function RailList({ label, items, value, onChange, toolbar, note, children, renderLink, labels: overrides, className, }: RailListProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { RAIL_LIST_LABELS, RailList } from '../chunk-REWKR3S4.js';
|
|
2
|
+
import '../chunk-BRK3HXGZ.js';
|
|
3
|
+
import '../chunk-3IWZVEXA.js';
|
|
4
|
+
import '../chunk-O5CEG7TQ.js';
|
|
5
|
+
import '../chunk-YGRTX67J.js';
|
|
6
|
+
import '../chunk-TEV5REGB.js';
|
|
7
|
+
import '../chunk-HBDA76EZ.js';
|
|
8
|
+
import '../chunk-U2IQNDDR.js';
|
|
9
|
+
import '../chunk-AV2DXAEL.js';
|
|
10
|
+
import '../chunk-OMV5W53A.js';
|
|
11
|
+
import '../chunk-2YTOOH3A.js';
|
|
12
|
+
import '../chunk-TUEM2XIW.js';
|
|
13
|
+
import '../chunk-4ITETIGZ.js';
|
|
14
|
+
import '../chunk-D2GT3CR4.js';
|
|
15
|
+
import '../chunk-PQADV6P6.js';
|
|
16
|
+
import '../chunk-UT6CW3C7.js';
|
|
17
|
+
import '../chunk-ODEQTPFM.js';
|