@crewlethq/ui 0.64.1 → 0.65.0
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/dist/AppShell/index.d.ts +1 -0
- package/dist/AppShell/index.js +1 -1
- package/dist/AppShell/screenWidth.d.ts +66 -0
- package/dist/DataGrid/DataGrid.d.ts +35 -3
- package/dist/DataGrid/index.js +1 -1
- package/dist/{DataGrid-QYORTTHI.css → DataGrid-NHIN2I42.css} +96 -93
- package/dist/ListDetail/ListDetail.d.ts +10 -5
- package/dist/ListDetail/index.js +1 -1
- package/dist/{ListDetail-5NXERLRJ.css → ListDetail-N3L7PB46.css} +38 -9
- package/dist/{chunk-AJLMINJU.js → chunk-E55DKVY7.js} +61 -3
- package/dist/{chunk-4W7F3JEK.js → chunk-ELLRZ5XR.js} +20 -9
- package/dist/{chunk-RXG543EB.js → chunk-YBC2CYPD.js} +4 -4
- package/dist/generated/index.js +16 -16
- package/dist/index.js +16 -16
- package/dist/styles.css +135 -103
- package/package.json +3 -3
package/dist/AppShell/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { APP_SHELL_ASIDE_WIDTH, AppShell, AppShellRail, AppShellRailRow, AppShel
|
|
|
5
5
|
export { RESIZE_HANDLE_LARGE_STEP, RESIZE_HANDLE_STEP, ResizeHandle, clampSize } from './ResizeHandle.js';
|
|
6
6
|
export type { ResizeHandleProps } from './ResizeHandle.js';
|
|
7
7
|
export { readStoredWidth, useStoredWidth } from './storedWidth.js';
|
|
8
|
+
export { useScreenBelow, useScreenWidth } from './screenWidth.js';
|
|
8
9
|
export type { StoredWidth, StoredWidthOptions, WidthStorage } from './storedWidth.js';
|
|
9
10
|
export { AccountRow } from './AccountRow.js';
|
|
10
11
|
export type { AccountRowMenu, AccountRowProps } from './AccountRow.js';
|
package/dist/AppShell/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { APP_SHELL_ASIDE_WIDTH, AccountRow, AppShell, AppShellRail, AppShellRailRow, AppShellRailStatus, AppShellToggle, AppShellTopbar, readStoredWidth, useAppShell, useStoredWidth } from '../chunk-
|
|
1
|
+
export { APP_SHELL_ASIDE_WIDTH, AccountRow, AppShell, AppShellRail, AppShellRailRow, AppShellRailStatus, AppShellToggle, AppShellTopbar, readStoredWidth, useAppShell, useScreenBelow, useScreenWidth, useStoredWidth } from '../chunk-E55DKVY7.js';
|
|
2
2
|
import '../chunk-RTPQDNW3.js';
|
|
3
3
|
import '../chunk-T73DMDU4.js';
|
|
4
4
|
export { RESIZE_HANDLE_LARGE_STEP, RESIZE_HANDLE_STEP, ResizeHandle, clampSize, useAppShellAside, useAppShellPeek } from '../chunk-PIYJNCN7.js';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SCREEN'S width: the content column the shell draws a screen in, which is
|
|
3
|
+
* what a screen's layout has to fit, rather than the window's.
|
|
4
|
+
*
|
|
5
|
+
* WHY NOT THE WINDOW. The window reports what the frame takes from it only
|
|
6
|
+
* while that never changes. The rail is a fixed share, but an aside the reader
|
|
7
|
+
* opens (the assistant) and drags wider takes width the window never reports:
|
|
8
|
+
* at a 1375px window with a 640px aside the screen is 443px wide while a
|
|
9
|
+
* media query still answers "desktop". A split screen that asks the window
|
|
10
|
+
* keeps its two columns and draws its reading pane 130px wide. Stylesheets
|
|
11
|
+
* already answer this with a container query on the content column; this is
|
|
12
|
+
* the same answer for the decisions a stylesheet cannot make, such as which
|
|
13
|
+
* pane a screen renders or whether it fills the window.
|
|
14
|
+
*
|
|
15
|
+
* ONE MEASUREMENT FOR EVERY CONSUMER. The shell measures its content column
|
|
16
|
+
* once and publishes the width through a store, so twenty screens asking do
|
|
17
|
+
* not attach twenty observers, and every one of them reads the same number in
|
|
18
|
+
* the same frame.
|
|
19
|
+
*
|
|
20
|
+
* RE-RENDERS ON A CROSSING, NOT ON A PIXEL. [useScreenBelow] subscribes to the
|
|
21
|
+
* answer to its own question, so dragging the aside re-renders a screen only
|
|
22
|
+
* when the screen crosses that screen's threshold.
|
|
23
|
+
*
|
|
24
|
+
* OUTSIDE A SHELL IT IS THE WINDOW. A primitive drawn in a story, a test or an
|
|
25
|
+
* application without the shell still gets an answer: the window's, through
|
|
26
|
+
* the same media query it would have asked before. The first render inside a
|
|
27
|
+
* shell answers the same way, and the shell's measurement replaces it in a
|
|
28
|
+
* layout effect, before anything is painted.
|
|
29
|
+
*/
|
|
30
|
+
/** Where the shell keeps the content column's width, and who listens. */
|
|
31
|
+
export interface ScreenWidthStore {
|
|
32
|
+
/** The content column's inner width in px, or null before it is measured. */
|
|
33
|
+
get(): number | null;
|
|
34
|
+
/** Record a new width, telling every listener when it changed. */
|
|
35
|
+
set(width: number | null): void;
|
|
36
|
+
subscribe(listener: () => void): () => void;
|
|
37
|
+
}
|
|
38
|
+
/** A store for one shell's content column. */
|
|
39
|
+
export declare function createScreenWidthStore(): ScreenWidthStore;
|
|
40
|
+
export declare const ScreenWidthContext: import("react").Context<ScreenWidthStore | null>;
|
|
41
|
+
/**
|
|
42
|
+
* The width of an element's content box: its client width less its inline
|
|
43
|
+
* padding, which is what a container query on it measures.
|
|
44
|
+
*
|
|
45
|
+
* NULL FOR AN ELEMENT WITH NO BOX (hidden, detached, or a document without
|
|
46
|
+
* layout such as a test's): a width of zero there is not a measurement, and
|
|
47
|
+
* answering "narrower than everything" would fold every screen. The window's
|
|
48
|
+
* answer stands instead.
|
|
49
|
+
*/
|
|
50
|
+
export declare function contentInlineSize(element: HTMLElement): number | null;
|
|
51
|
+
/**
|
|
52
|
+
* The width of the screen's content column in px, re-read whenever it
|
|
53
|
+
* changes; null outside a shell, or before the shell has measured it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function useScreenWidth(): number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the screen's content column is narrower than `width` px: the
|
|
58
|
+
* question a screen asks before it chooses its narrow shape. Inside a shell it
|
|
59
|
+
* is answered from the shell's measured content column, so an aside the
|
|
60
|
+
* reader opens or drags counts; outside one, by the window, exactly as
|
|
61
|
+
* `useMediaMatch(`(width < ${width}px)`)` would.
|
|
62
|
+
*
|
|
63
|
+
* STRICTLY UNDER, like the stylesheets' `(width < 640px)`: at exactly the
|
|
64
|
+
* threshold the wide shape fits.
|
|
65
|
+
*/
|
|
66
|
+
export declare function useScreenBelow(width: number): boolean;
|
|
@@ -243,26 +243,58 @@ export interface GridFit {
|
|
|
243
243
|
readonly width: number;
|
|
244
244
|
/** The keys it dropped, in the order it dropped them. */
|
|
245
245
|
readonly dropped: readonly string[];
|
|
246
|
+
/**
|
|
247
|
+
* Whether the grid has STACKED into card rows: it still did not fit with
|
|
248
|
+
* every give-way column gone, so its rows are cards, as on a phone.
|
|
249
|
+
*/
|
|
250
|
+
readonly stacked?: boolean | undefined;
|
|
246
251
|
}
|
|
247
252
|
/**
|
|
248
253
|
* The next fit, or null when this one stands. PURE, so the rules are held
|
|
249
254
|
* without a layout engine:
|
|
250
255
|
*
|
|
251
|
-
* - the grid GREW past the width columns were dropped at: start
|
|
252
|
-
* of them (the next pass drops
|
|
256
|
+
* - the grid GREW past the width columns were dropped or it stacked at: start
|
|
257
|
+
* again from all of them as columns (the next pass drops, and stacks, again
|
|
258
|
+
* only what still does not fit);
|
|
259
|
+
* - a stacked grid that did not grow stays stacked: its rows are cards and
|
|
260
|
+
* there is no column left to measure;
|
|
253
261
|
* - the tracks OVERRUN the grid: drop the visible column with the lowest
|
|
254
262
|
* `drop`, if there is one;
|
|
263
|
+
* - nothing is left to drop and the grid still cannot draw its values
|
|
264
|
+
* (`stack`: the tracks overrun, or a value is cut short): STACK into card
|
|
265
|
+
* rows, the shape a phone draws, rather than clip;
|
|
255
266
|
* - otherwise remember a narrower width, so growing back past it re-tries.
|
|
256
267
|
*/
|
|
257
|
-
export declare function fitGridColumns({ fit, width, overflows, visible, }: {
|
|
268
|
+
export declare function fitGridColumns({ fit, width, overflows, stack, visible, }: {
|
|
258
269
|
fit: GridFit;
|
|
259
270
|
width: number;
|
|
271
|
+
/** The columns do not fit: overrun, or a value or a head cut short. */
|
|
260
272
|
overflows: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* The grid cannot draw its VALUES: the tracks overrun, or a value is cut
|
|
275
|
+
* short. A head alone cut short drops a give-way column but never stacks.
|
|
276
|
+
*/
|
|
277
|
+
stack?: boolean;
|
|
261
278
|
visible: readonly {
|
|
262
279
|
key: string;
|
|
263
280
|
drop?: number | undefined;
|
|
264
281
|
}[];
|
|
265
282
|
}): GridFit | null;
|
|
283
|
+
/**
|
|
284
|
+
* Whether a content-sized column has been SQUEEZED: drawn narrower than its
|
|
285
|
+
* cap AND cutting a value short, or its HEAD. The quieter way a grid fails to
|
|
286
|
+
* fit: rather than overrun, it shrinks a status pill to a letter, or every
|
|
287
|
+
* head of a row of counts to its first letter over values that still fit,
|
|
288
|
+
* while the columns that could have gone stay. Read over the head and the
|
|
289
|
+
* first screen of rows. `leading` is how many of the grid's own columns (the
|
|
290
|
+
* selection column) are drawn before the caller's.
|
|
291
|
+
*
|
|
292
|
+
* `'values'` where a value is cut short (the grid cannot be read: drop, then
|
|
293
|
+
* stack), `'heads'` where only a head is (drop a column if one may go, but a
|
|
294
|
+
* grid whose every value reads is not turned into cards for a head), and
|
|
295
|
+
* null where nothing is.
|
|
296
|
+
*/
|
|
297
|
+
export declare function squeezed<T>(wrap: HTMLElement, visible: readonly DataGridColumn<T>[], leading?: number): 'values' | 'heads' | null;
|
|
266
298
|
/**
|
|
267
299
|
* A list as columns: sortable heads, optional and give-way columns, bands for
|
|
268
300
|
* a grouped answer, a row link stretched over each row, and a card per row on
|
package/dist/DataGrid/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ChooserList, ColumnChooser, DataGrid, columnChoicesOf, countDataGridRows, fitGridColumns, parseGridSort, useAnyDataGridDriving, useDataGridDriving } from '../chunk-
|
|
1
|
+
export { ChooserList, ColumnChooser, DataGrid, columnChoicesOf, countDataGridRows, fitGridColumns, parseGridSort, useAnyDataGridDriving, useDataGridDriving } from '../chunk-ELLRZ5XR.js';
|
|
2
2
|
import '../chunk-PBCNINMB.js';
|
|
3
3
|
import '../chunk-PPUR46MK.js';
|
|
4
4
|
import '../chunk-ETSF7SU6.js';
|
|
@@ -467,7 +467,7 @@ button.crewlet-data-grid__row-link {
|
|
|
467
467
|
}
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
-
/* ---- A ROW IS A CARD ON A PHONE -----------------------------------------
|
|
470
|
+
/* ---- A ROW IS A CARD ON A PHONE, OR WHERE COLUMNS DO NOT FIT -----------------------------------------
|
|
471
471
|
A GRID IS COLUMNS, AND A PHONE DOES NOT HAVE THEM: the grid clips (for its
|
|
472
472
|
sticky head), so the columns past the edge were cut off with nothing to
|
|
473
473
|
scroll to, and a sideways scroller resolves every flexible track to zero. So
|
|
@@ -477,111 +477,114 @@ button.crewlet-data-grid__row-link {
|
|
|
477
477
|
(`:empty`), and a cell that draws a marked absence has content and stays.
|
|
478
478
|
The sort is in the application's state, so a sorted list still arrives
|
|
479
479
|
sorted. */
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
480
|
+
/* Card rows are the grid's own decision (`data-stacked`, DataGrid.tsx): set
|
|
481
|
+
on a phone, and wherever the grid's own width cannot hold its columns once
|
|
482
|
+
every give-way column has gone. `:where()` keeps each rule's weight what it
|
|
483
|
+
was when this block was a media query, so a rule written against the card
|
|
484
|
+
shape still wins where it did. */
|
|
485
|
+
.crewlet-data-grid:where([data-stacked]) {
|
|
486
|
+
/* No ghost control is pulled out here (see above): every line of a card
|
|
487
|
+
starts at the grid's edge, and a compact row's marks run on one line,
|
|
488
|
+
where the hover ground swallowed the gap before it. */
|
|
489
|
+
--crewlet-data-grid-control-outdent: 0px;
|
|
489
490
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
491
|
+
display: block;
|
|
492
|
+
}
|
|
493
493
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
494
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__head {
|
|
495
|
+
display: none;
|
|
496
|
+
}
|
|
498
497
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
498
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__body,
|
|
499
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__band {
|
|
500
|
+
display: block;
|
|
501
|
+
}
|
|
504
502
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
gap: var(--spacing-3);
|
|
511
|
-
padding: 1px 0;
|
|
512
|
-
min-width: 0;
|
|
513
|
-
}
|
|
503
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__row {
|
|
504
|
+
display: block;
|
|
505
|
+
padding: var(--spacing-2) var(--crewlet-data-grid-edge);
|
|
506
|
+
min-height: 0;
|
|
507
|
+
}
|
|
514
508
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
509
|
+
/* A FIXED BASIS lines the names up without a track, and the name wraps
|
|
510
|
+
inside it rather than pushing its value past every other card line. */
|
|
511
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__cell {
|
|
512
|
+
display: flex;
|
|
513
|
+
align-items: baseline;
|
|
514
|
+
gap: var(--spacing-3);
|
|
515
|
+
padding: 1px 0;
|
|
516
|
+
min-width: 0;
|
|
517
|
+
}
|
|
518
518
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
overflow-wrap: anywhere;
|
|
523
|
-
font-size: var(--font-size-2xs);
|
|
524
|
-
text-transform: uppercase;
|
|
525
|
-
letter-spacing: var(--font-letter-spacing-wide);
|
|
526
|
-
color: var(--color-text-tertiary);
|
|
527
|
-
}
|
|
519
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__cell:empty {
|
|
520
|
+
display: none;
|
|
521
|
+
}
|
|
528
522
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
523
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__cell[data-label]::before {
|
|
524
|
+
content: attr(data-label);
|
|
525
|
+
flex: 0 0 7rem;
|
|
526
|
+
overflow-wrap: anywhere;
|
|
527
|
+
font-size: var(--font-size-2xs);
|
|
528
|
+
text-transform: uppercase;
|
|
529
|
+
letter-spacing: var(--font-letter-spacing-wide);
|
|
530
|
+
color: var(--color-text-tertiary);
|
|
531
|
+
}
|
|
535
532
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
533
|
+
/* `shrink` and `right` are column shapes, and there are no columns here. */
|
|
534
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__cell--shrink,
|
|
535
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__cell--right {
|
|
536
|
+
white-space: normal;
|
|
537
|
+
justify-content: flex-start;
|
|
538
|
+
}
|
|
542
539
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
flex-wrap: wrap;
|
|
550
|
-
align-items: center;
|
|
551
|
-
gap: var(--spacing-1) var(--spacing-2);
|
|
552
|
-
}
|
|
540
|
+
:where(.crewlet-data-grid[data-stacked]) .crewlet-data-grid__cell .crewlet-data-grid__truncate {
|
|
541
|
+
white-space: normal;
|
|
542
|
+
overflow: visible;
|
|
543
|
+
text-overflow: clip;
|
|
544
|
+
overflow-wrap: anywhere;
|
|
545
|
+
}
|
|
553
546
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
547
|
+
/* ---- THE COMPACT ROW (`phoneRows="compact"`) ---------------------------
|
|
548
|
+
TWO LINES, NO LABELS: what the row is, then its marks. One flex run that
|
|
549
|
+
breaks once: the lead cells first, a zero-height full-width `::after`
|
|
550
|
+
between them and the rest, then every other cell in column order. */
|
|
551
|
+
.crewlet-data-grid[data-phone-rows='compact']:where([data-stacked]) .crewlet-data-grid__row {
|
|
552
|
+
display: flex;
|
|
553
|
+
flex-wrap: wrap;
|
|
554
|
+
align-items: center;
|
|
555
|
+
gap: var(--spacing-1) var(--spacing-2);
|
|
556
|
+
}
|
|
560
557
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
558
|
+
.crewlet-data-grid[data-phone-rows='compact']:where([data-stacked]) .crewlet-data-grid__row::after {
|
|
559
|
+
content: '';
|
|
560
|
+
order: 1;
|
|
561
|
+
flex-basis: 100%;
|
|
562
|
+
height: 0;
|
|
563
|
+
}
|
|
567
564
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
565
|
+
.crewlet-data-grid[data-phone-rows='compact']:where([data-stacked]) .crewlet-data-grid__cell {
|
|
566
|
+
order: 2;
|
|
567
|
+
padding: 0;
|
|
568
|
+
align-items: center;
|
|
569
|
+
gap: var(--spacing-1);
|
|
570
|
+
}
|
|
571
571
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
flex: 1 1 0;
|
|
576
|
-
min-width: 0;
|
|
577
|
-
font-weight: var(--font-weight-medium);
|
|
578
|
-
}
|
|
572
|
+
.crewlet-data-grid[data-phone-rows='compact']:where([data-stacked]) .crewlet-data-grid__cell[data-lead] {
|
|
573
|
+
order: 0;
|
|
574
|
+
}
|
|
579
575
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
576
|
+
/* The title takes the rest of the first line, beside the key. */
|
|
577
|
+
.crewlet-data-grid[data-phone-rows='compact']
|
|
578
|
+
.crewlet-data-grid__cell[data-lead]:not(.crewlet-data-grid__cell--shrink) {
|
|
579
|
+
flex: 1 1 0;
|
|
580
|
+
min-width: 0;
|
|
581
|
+
font-weight: var(--font-weight-medium);
|
|
582
|
+
}
|
|
583
583
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
584
|
+
.crewlet-data-grid[data-phone-rows='compact']:where([data-stacked]) .crewlet-data-grid__cell[data-label]::before {
|
|
585
|
+
content: none;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.crewlet-data-grid[data-phone-rows='compact']:where([data-stacked]) .crewlet-data-grid__cell[data-phone-omit] {
|
|
589
|
+
display: none;
|
|
587
590
|
}
|
|
@@ -6,8 +6,9 @@ export interface ListDetailProps extends Omit<HTMLAttributes<HTMLDivElement>, 'c
|
|
|
6
6
|
/** The chosen item's detail. */
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
/**
|
|
9
|
-
* Which pane
|
|
10
|
-
* something is chosen, then the
|
|
9
|
+
* Which pane shows where the two are one column (under 640px of the
|
|
10
|
+
* component's own width): the list until something is chosen, then the
|
|
11
|
+
* detail. Wider, both are always shown.
|
|
11
12
|
*/
|
|
12
13
|
showing?: ListDetailPane | undefined;
|
|
13
14
|
}
|
|
@@ -20,8 +21,12 @@ export interface ListDetailProps extends Omit<HTMLAttributes<HTMLDivElement>, 'c
|
|
|
20
21
|
* column. The list column is `--crewlet-list-detail-list` wide (17rem by
|
|
21
22
|
* default), with a hairline between the two.
|
|
22
23
|
*
|
|
23
|
-
* BELOW 640px IT IS ONE PANE, the one `showing` names
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* BELOW 640px OF ITS OWN WIDTH IT IS ONE PANE, the one `showing` names: the
|
|
25
|
+
* list and a reading detail do not both fit, whether the window is a phone or
|
|
26
|
+
* an aside beside the screen has taken the room. The application gives its
|
|
27
|
+
* detail a way back to the list, and asks the same question to decide when to
|
|
28
|
+
* draw it (`useScreenBelow(640)` for a ListDetail that spans the screen, or a
|
|
29
|
+
* container query on it). On a phone the page scrolls as any other, each pane
|
|
30
|
+
* as tall as its content.
|
|
26
31
|
*/
|
|
27
32
|
export declare function ListDetail({ list, children, showing, className, ...rest }: ListDetailProps): import("react").JSX.Element;
|
package/dist/ListDetail/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ListDetail } from '../chunk-
|
|
1
|
+
export { ListDetail } from '../chunk-YBC2CYPD.js';
|
|
2
2
|
import '../chunk-ODEQTPFM.js';
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* ListDetail: a list beside the detail of what is chosen in it, each scrolling
|
|
3
|
-
* on its own in the height it is given, one pane
|
|
3
|
+
* on its own in the height it is given, one pane where it is narrow
|
|
4
|
+
* (ListDetail.tsx).
|
|
5
|
+
*
|
|
6
|
+
* IT FOLDS BY ITS OWN WIDTH, NOT THE WINDOW'S. The root is an inline-size
|
|
7
|
+
* container and the panes are its child, so the fold answers how wide THIS
|
|
8
|
+
* list and detail are: beside an aside the reader opened (the assistant) a
|
|
9
|
+
* 1375px window can leave the screen 443px, and a fold that asked the window
|
|
10
|
+
* drew the detail 130px wide beside a 17rem list. The phone rule below is the
|
|
11
|
+
* one decision that stays the window's: on a phone the page scrolls as a
|
|
12
|
+
* whole rather than each pane on its own.
|
|
4
13
|
*/
|
|
5
14
|
|
|
6
15
|
/* THE HAIRLINE STANDS MIDWAY between the two panes: the list's inset before it
|
|
@@ -9,11 +18,20 @@
|
|
|
9
18
|
.crewlet-list-detail {
|
|
10
19
|
--crewlet-list-detail-gutter: var(--spacing-7);
|
|
11
20
|
|
|
21
|
+
container: crewlet-list-detail / inline-size;
|
|
22
|
+
display: flex;
|
|
23
|
+
flex: 1 1 auto;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
height: 100%;
|
|
26
|
+
min-width: 0;
|
|
27
|
+
min-height: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.crewlet-list-detail__panes {
|
|
12
31
|
display: grid;
|
|
13
32
|
flex: 1 1 auto;
|
|
14
33
|
grid-template-columns: var(--crewlet-list-detail-list, 17rem) minmax(0, 1fr);
|
|
15
34
|
gap: var(--crewlet-list-detail-gutter);
|
|
16
|
-
height: 100%;
|
|
17
35
|
min-width: 0;
|
|
18
36
|
min-height: 0;
|
|
19
37
|
}
|
|
@@ -36,19 +54,18 @@
|
|
|
36
54
|
overflow-y: auto;
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
/* ONE PANE UNDER 640px OF ITS OWN WIDTH: the list and a reading detail do not
|
|
58
|
+
both fit, so the pane `showing` names takes the whole width and keeps its
|
|
59
|
+
own scroller, which is what lets it sit in a screen that fills the window. */
|
|
60
|
+
@container crewlet-list-detail (width < 640px) {
|
|
61
|
+
.crewlet-list-detail__panes {
|
|
41
62
|
grid-template-columns: minmax(0, 1fr);
|
|
42
|
-
height: auto;
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
.crewlet-list-detail__list {
|
|
46
66
|
padding-right: 0;
|
|
47
67
|
border-right: 0;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.crewlet-list-detail__detail {
|
|
51
|
-
overflow: visible;
|
|
68
|
+
overflow-y: auto;
|
|
52
69
|
}
|
|
53
70
|
|
|
54
71
|
.crewlet-list-detail--showing-list .crewlet-list-detail__detail,
|
|
@@ -56,3 +73,15 @@
|
|
|
56
73
|
display: none;
|
|
57
74
|
}
|
|
58
75
|
}
|
|
76
|
+
|
|
77
|
+
/* ON A PHONE THE PAGE SCROLLS AS A WHOLE, as tall as its content. */
|
|
78
|
+
@media (width < 640px) {
|
|
79
|
+
.crewlet-list-detail {
|
|
80
|
+
height: auto;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.crewlet-list-detail__list,
|
|
84
|
+
.crewlet-list-detail__detail {
|
|
85
|
+
overflow: visible;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -10,7 +10,7 @@ import { cx } from './chunk-ODEQTPFM.js';
|
|
|
10
10
|
import './HitArea-QUOS6ZDJ.css';
|
|
11
11
|
import './AppShell-LZQA3XU4.css';
|
|
12
12
|
import './ResizeHandle-2UNSZ4ZQ.css';
|
|
13
|
-
import { createContext, forwardRef, useContext, useId, useState, useRef,
|
|
13
|
+
import { createContext, forwardRef, useContext, useSyncExternalStore, useCallback, useId, useState, useRef, useEffect, useLayoutEffect } from 'react';
|
|
14
14
|
import { breakpoint } from '@crewlethq/tokens';
|
|
15
15
|
import { ChevronUpGlyph, ChevronsUpDownGlyph, ChevronRightGlyph, MenuGlyph } from '@crewlethq/icons/glyphs';
|
|
16
16
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
@@ -79,6 +79,49 @@ function AccountRow({
|
|
|
79
79
|
actions ? /* @__PURE__ */ jsx("div", { className: "crewlet-account-row__actions", children: actions }) : null
|
|
80
80
|
] });
|
|
81
81
|
}
|
|
82
|
+
function createScreenWidthStore() {
|
|
83
|
+
let width = null;
|
|
84
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
85
|
+
return {
|
|
86
|
+
get: () => width,
|
|
87
|
+
set(next) {
|
|
88
|
+
if (next === width) return;
|
|
89
|
+
width = next;
|
|
90
|
+
for (const listener of listeners) listener();
|
|
91
|
+
},
|
|
92
|
+
subscribe(listener) {
|
|
93
|
+
listeners.add(listener);
|
|
94
|
+
return () => listeners.delete(listener);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
var ScreenWidthContext = createContext(null);
|
|
99
|
+
var noSubscription = () => () => {
|
|
100
|
+
};
|
|
101
|
+
function contentInlineSize(element) {
|
|
102
|
+
if (element.getClientRects().length === 0) return null;
|
|
103
|
+
const style = getComputedStyle(element);
|
|
104
|
+
const padding = (Number.parseFloat(style.paddingLeft) || 0) + (Number.parseFloat(style.paddingRight) || 0);
|
|
105
|
+
return Math.max(0, Math.round(element.clientWidth - padding));
|
|
106
|
+
}
|
|
107
|
+
function useScreenWidth() {
|
|
108
|
+
const store = useContext(ScreenWidthContext);
|
|
109
|
+
return useSyncExternalStore(
|
|
110
|
+
store ? store.subscribe : noSubscription,
|
|
111
|
+
() => store ? store.get() : null,
|
|
112
|
+
() => null
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function useScreenBelow(width) {
|
|
116
|
+
const store = useContext(ScreenWidthContext);
|
|
117
|
+
const window2 = useMediaMatch(`(width < ${width}px)`);
|
|
118
|
+
const read = useCallback(() => {
|
|
119
|
+
const measured2 = store ? store.get() : null;
|
|
120
|
+
return measured2 === null ? null : measured2 < width;
|
|
121
|
+
}, [store, width]);
|
|
122
|
+
const measured = useSyncExternalStore(store ? store.subscribe : noSubscription, read, () => null);
|
|
123
|
+
return measured ?? window2;
|
|
124
|
+
}
|
|
82
125
|
var APP_SHELL_ASIDE_WIDTH = 420;
|
|
83
126
|
var NARROW = `(width < ${breakpoint.shell})`;
|
|
84
127
|
var AppShellContext = createContext(null);
|
|
@@ -167,6 +210,21 @@ function AppShell({
|
|
|
167
210
|
observer.observe(frame);
|
|
168
211
|
return () => observer.disconnect();
|
|
169
212
|
}, [asideColumn]);
|
|
213
|
+
const content = useRef(null);
|
|
214
|
+
const [screenWidth] = useState(createScreenWidthStore);
|
|
215
|
+
useLayoutEffect(() => {
|
|
216
|
+
const column = content.current;
|
|
217
|
+
if (!column) return void 0;
|
|
218
|
+
const measure = () => screenWidth.set(contentInlineSize(column));
|
|
219
|
+
measure();
|
|
220
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
221
|
+
const observer = new ResizeObserver(measure);
|
|
222
|
+
observer.observe(column);
|
|
223
|
+
return () => {
|
|
224
|
+
observer.disconnect();
|
|
225
|
+
screenWidth.set(null);
|
|
226
|
+
};
|
|
227
|
+
}, [screenWidth]);
|
|
170
228
|
const shell = {
|
|
171
229
|
narrow,
|
|
172
230
|
drawerOpen,
|
|
@@ -238,7 +296,7 @@ function AppShell({
|
|
|
238
296
|
topbar,
|
|
239
297
|
banner ? /* @__PURE__ */ jsx("div", { className: "crewlet-app-shell__banner", children: banner }) : null,
|
|
240
298
|
/* @__PURE__ */ jsxs("div", { className: "crewlet-app-shell__body", children: [
|
|
241
|
-
/* @__PURE__ */ jsx("main", { id: main, ref: setMain, className: "crewlet-app-shell__main", tabIndex: -1, children: /* @__PURE__ */ jsx("div", { className: "crewlet-app-shell__content", children }) }),
|
|
299
|
+
/* @__PURE__ */ jsx("main", { id: main, ref: setMain, className: "crewlet-app-shell__main", tabIndex: -1, children: /* @__PURE__ */ jsx("div", { ref: content, className: "crewlet-app-shell__content", children: /* @__PURE__ */ jsx(ScreenWidthContext.Provider, { value: screenWidth, children }) }) }),
|
|
242
300
|
hasPeek ? /* @__PURE__ */ jsx(PeekContext.Provider, { value: { shape: peekShape }, children: peek }) : null
|
|
243
301
|
] }),
|
|
244
302
|
footer
|
|
@@ -598,4 +656,4 @@ function useStoredWidth({ key, min, max, storage }) {
|
|
|
598
656
|
return { width, setWidth, commitWidth };
|
|
599
657
|
}
|
|
600
658
|
|
|
601
|
-
export { APP_SHELL_ASIDE_WIDTH, AccountRow, AppShell, AppShellRail, AppShellRailRow, AppShellRailStatus, AppShellToggle, AppShellTopbar, readStoredWidth, useAppShell, useStoredWidth };
|
|
659
|
+
export { APP_SHELL_ASIDE_WIDTH, AccountRow, AppShell, AppShellRail, AppShellRailRow, AppShellRailStatus, AppShellToggle, AppShellTopbar, readStoredWidth, useAppShell, useScreenBelow, useScreenWidth, useStoredWidth };
|