@crewlethq/ui 0.83.0 → 0.84.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.
Files changed (82) hide show
  1. package/README.md +38 -0
  2. package/dist/ActionCard/ActionCard.d.ts +7 -7
  3. package/dist/ActionCard/index.js +1 -1
  4. package/dist/ActionCard-YZKL3CKR.css +130 -0
  5. package/dist/AppShell/AppShell.d.ts +14 -11
  6. package/dist/AppShell/index.js +1 -1
  7. package/dist/{AppShell-U7D2MUUF.css → AppShell-NX5HCD4O.css} +84 -44
  8. package/dist/ArtifactBar/index.js +1 -1
  9. package/dist/ArtifactCard/index.js +3 -3
  10. package/dist/ArtifactKindIllustration/index.js +1 -1
  11. package/dist/{ArtifactKinds-3EH4KR3F.css → ArtifactKinds-27TE3KJL.css} +7 -7
  12. package/dist/ArtifactMakeRow/index.js +1 -1
  13. package/dist/ArtifactNameCell/index.js +1 -1
  14. package/dist/Avatar/index.js +1 -1
  15. package/dist/{Avatar-MNE3WBZO.css → Avatar-CRSLRUVT.css} +11 -10
  16. package/dist/BriefCard/index.js +2 -1
  17. package/dist/CatalogTile/CatalogTile.d.ts +23 -5
  18. package/dist/CatalogTile/index.d.ts +1 -1
  19. package/dist/CatalogTile/index.js +2 -1
  20. package/dist/{CatalogTile-6VF36JWA.css → CatalogTile-TMOSGSV3.css} +4 -3
  21. package/dist/CommentThread/index.js +1 -1
  22. package/dist/CompactNav/CompactNav.d.ts +10 -6
  23. package/dist/CompactNav/index.js +1 -1
  24. package/dist/DeepWorkCard/index.js +2 -2
  25. package/dist/DirectoryList/index.js +1 -1
  26. package/dist/{DirectoryList-XZOWYOA3.css → DirectoryList-IXQIMNFS.css} +8 -1
  27. package/dist/EntityChip/index.js +1 -1
  28. package/dist/ImageUpload/index.js +1 -1
  29. package/dist/InboxList/InboxList.d.ts +28 -2
  30. package/dist/InboxList/index.d.ts +1 -1
  31. package/dist/InboxList/index.js +6 -1
  32. package/dist/{InboxList-OFGSX5IH.css → InboxList-X5Q5WWAI.css} +63 -2
  33. package/dist/ListDetail/ListDetail.d.ts +6 -0
  34. package/dist/ListDetail/index.js +1 -1
  35. package/dist/{ListDetail-N3L7PB46.css → ListDetail-JGESKTR7.css} +38 -0
  36. package/dist/OrgLabel/index.js +1 -1
  37. package/dist/OrgNode/index.js +1 -1
  38. package/dist/OrgTable/index.js +1 -1
  39. package/dist/PeopleRail/index.js +1 -1
  40. package/dist/PresenceStack/index.js +1 -1
  41. package/dist/Principal/index.js +1 -1
  42. package/dist/PrincipalPicker/index.js +1 -1
  43. package/dist/ReadingPane/ReadingPane.d.ts +17 -1
  44. package/dist/ReadingPane/index.d.ts +1 -1
  45. package/dist/ReadingPane/index.js +2 -1
  46. package/dist/{ReadingPane-VLYOWMCM.css → ReadingPane-VFTRDPC5.css} +19 -0
  47. package/dist/SectionFrame/index.js +2 -2
  48. package/dist/ShareDialog/index.js +1 -1
  49. package/dist/SheetGrid/index.js +1 -1
  50. package/dist/SidebarNav/SidebarNav.d.ts +18 -2
  51. package/dist/SidebarNav/index.d.ts +1 -1
  52. package/dist/SidebarNav/index.js +1 -1
  53. package/dist/{SidebarNav-E7PZGGA6.css → SidebarNav-IKBKGXW4.css} +20 -0
  54. package/dist/TableCells/index.js +1 -1
  55. package/dist/ThreadSummary/index.js +2 -2
  56. package/dist/VersionTimeline/index.js +1 -1
  57. package/dist/{chunk-Q7N2ATMC.js → chunk-64JNKLLP.js} +57 -9
  58. package/dist/chunk-6C7GQOBV.js +10 -0
  59. package/dist/{chunk-YRNNUCQP.js → chunk-7BDBO2NP.js} +1 -1
  60. package/dist/{chunk-QM3432PH.js → chunk-7IWZN5Q2.js} +3 -3
  61. package/dist/{chunk-25JQNHTC.js → chunk-BRK3HXGZ.js} +8 -2
  62. package/dist/{chunk-QE4OIE3Q.js → chunk-DJIWCW5Z.js} +6 -4
  63. package/dist/{chunk-QPHD4UUG.js → chunk-F6ZUM66S.js} +160 -25
  64. package/dist/{chunk-MVMVMHYI.js → chunk-HDEKHP65.js} +1 -1
  65. package/dist/{chunk-BXGBGYKH.js → chunk-HKUZ2GYX.js} +1 -1
  66. package/dist/{chunk-ZB3E2Z4Y.js → chunk-J5T5TDM2.js} +1 -1
  67. package/dist/{chunk-C46DDVTK.js → chunk-LTD7KLOU.js} +1 -1
  68. package/dist/{chunk-YBC2CYPD.js → chunk-O4VTQMEF.js} +1 -1
  69. package/dist/{chunk-HTBTAIH6.js → chunk-OM2LABPS.js} +2 -2
  70. package/dist/{chunk-PGBKZSSQ.js → chunk-ORONAXSF.js} +10 -7
  71. package/dist/{chunk-YP2FS6SH.js → chunk-QPYGJ7R7.js} +1 -1
  72. package/dist/{chunk-ICE3D63E.js → chunk-QWRAWKB2.js} +50 -9
  73. package/dist/{chunk-SRDNGGZE.js → chunk-RKIJAXQA.js} +1 -1
  74. package/dist/{chunk-CF4M6N6A.js → chunk-XYSSFI5C.js} +1 -7
  75. package/dist/{chunk-IRONPJB4.js → chunk-YTLWOHOY.js} +1 -1
  76. package/dist/generated/index.js +33 -32
  77. package/dist/index.js +33 -32
  78. package/dist/styles.css +306 -99
  79. package/dist/utils/developing.d.ts +10 -0
  80. package/package.json +3 -3
  81. package/dist/ActionCard-47WSZ2O5.css +0 -110
  82. package/dist/{chunk-CV2LU2T2.js → chunk-IYI6HWKN.js} +1 -1
@@ -1,5 +1,6 @@
1
1
  import { type HTMLAttributes, type ReactNode } from 'react';
2
2
  import { type HeadingLevel } from '../utils/headingLevel.js';
3
+ import type { Tone } from '../utils/tone.js';
3
4
  export type CatalogTileElement = 'article' | 'li' | 'div';
4
5
  /**
5
6
  * How much room a tile takes. `comfortable` (the default) is the catalogue's
@@ -10,6 +11,23 @@ export type CatalogTileElement = 'article' | 'li' | 'div';
10
11
  * already in use rather than offers a choice.
11
12
  */
12
13
  export type CatalogTileDensity = 'comfortable' | 'compact';
14
+ /**
15
+ * An entry's state, as data: the tile draws it, the same way on every tile.
16
+ *
17
+ * ONE PRESENTATION, NOT A SLOT. Taken as any node, a service's In use came as
18
+ * a soft pill and an app's Connected as a dot and a word, so one row of tiles
19
+ * drew its states two ways and at two heights. A tone and a word cannot be
20
+ * drawn two ways.
21
+ */
22
+ export interface CatalogTileStatus {
23
+ /**
24
+ * What the state is: `success` for in use or connected, `warning` for
25
+ * something owed, `danger` for broken, `neutral` for coming soon.
26
+ */
27
+ tone: Tone;
28
+ /** The state in a word or two: In use, Connected, Needs attention. */
29
+ label: string;
30
+ }
13
31
  export interface CatalogTileProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
14
32
  /** The element the tile is: an `li` in a `TileGrid as="ul"`. */
15
33
  as?: CatalogTileElement | undefined;
@@ -24,12 +42,12 @@ export interface CatalogTileProps extends Omit<HTMLAttributes<HTMLElement>, 'tit
24
42
  /** Overrides the level the name is rendered at. */
25
43
  headingLevel?: HeadingLevel | undefined;
26
44
  /**
27
- * The entry's state, as a `Tag`, ONLY when the state is worth reading: On,
28
- * Connected, Needs attention, Coming soon. An entry in its resting state
29
- * (available, not set up) carries none, and its name stands centred beside
30
- * its mark; a pill under every name is a column a reader learns to skip.
45
+ * The entry's state, drawn as its tone's dot and its word, ONLY when the
46
+ * state is worth reading: In use, Connected, Needs attention, Coming soon.
47
+ * An entry in its resting state (available, not set up) carries none; a
48
+ * state under every name is a column a reader learns to skip.
31
49
  */
32
- status?: ReactNode;
50
+ status?: CatalogTileStatus | undefined;
33
51
  /** One sentence: what the entry is for, or what is owed when something is. */
34
52
  description?: ReactNode;
35
53
  /** A quiet fact at the foot (`2 agents · 14 deliveries`). */
@@ -1,3 +1,3 @@
1
1
  import './CatalogTile.css';
2
2
  export { CatalogTile } from './CatalogTile.js';
3
- export type { CatalogTileDensity, CatalogTileElement, CatalogTileProps } from './CatalogTile.js';
3
+ export type { CatalogTileDensity, CatalogTileElement, CatalogTileProps, CatalogTileStatus } from './CatalogTile.js';
@@ -1,3 +1,4 @@
1
- export { CatalogTile } from '../chunk-QE4OIE3Q.js';
1
+ export { CatalogTile } from '../chunk-DJIWCW5Z.js';
2
+ import '../chunk-CYQBDZ6M.js';
2
3
  import '../chunk-PQADV6P6.js';
3
4
  import '../chunk-ODEQTPFM.js';
@@ -79,9 +79,10 @@
79
79
  overflow-wrap: anywhere;
80
80
  }
81
81
 
82
- /* A state in words (a StatusDot's label) at a Tag's caption step, so a row
83
- reads the same whichever one a tile carries. It inherited the page's size,
84
- and on a 16px page "Connected" stood taller than the name before it. */
82
+ /* THE STATE, the tone's dot and its word, at the caption step the fact
83
+ beside it takes, so a compact tile's line reads name, state, fact at one
84
+ height. It inherited the page's size, and on a 16px page "Connected" stood
85
+ taller than the name before it. */
85
86
  .crewlet-catalog-tile__status {
86
87
  display: inline-flex;
87
88
  font-size: var(--font-size-xs);
@@ -2,7 +2,7 @@ export { COMMENT_THREAD_LABELS, CommentThread } from '../chunk-IAP7APOX.js';
2
2
  import '../chunk-HF5L56YQ.js';
3
3
  import '../chunk-EJNJMHQG.js';
4
4
  import '../chunk-RE3FBGQQ.js';
5
- import '../chunk-IRONPJB4.js';
5
+ import '../chunk-YTLWOHOY.js';
6
6
  import '../chunk-XZY6S3UC.js';
7
7
  import '../chunk-I4PBFJGX.js';
8
8
  import '../chunk-EBVZHVPD.js';
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import { type MouseEventHandler, type ReactNode } from 'react';
26
26
  import { type PlacementRect } from '../Layer/index.js';
27
- import type { NavFigure, NavLinkProps } from '../SidebarNav/index.js';
27
+ import type { NavBadge, NavFigure, NavLinkProps } from '../SidebarNav/index.js';
28
28
  export interface CompactNavProps {
29
29
  /** Names the navigation region, as `SidebarNav`'s label does. */
30
30
  label: string;
@@ -45,9 +45,11 @@ interface CompactPlace {
45
45
  current?: boolean | undefined;
46
46
  /**
47
47
  * How many things here are waiting on the reader: the accent figure on the
48
- * tile's corner, as `SidebarNav.Item`'s badge.
48
+ * tile's corner, as `SidebarNav.Item`'s badge. `tone: 'quiet'` draws it as
49
+ * the quiet figure, as the full rail's quiet badge, and keeps it out of a
50
+ * group's sum.
49
51
  */
50
- badge?: NavFigure | undefined;
52
+ badge?: NavBadge | undefined;
51
53
  /** How many of something the place holds: the quiet figure, as `SidebarNav.Item`'s count. */
52
54
  count?: NavFigure | undefined;
53
55
  /**
@@ -123,7 +125,8 @@ export interface CompactNavGroupProps {
123
125
  * badge's own value with a `+`, since there is more than it says.
124
126
  *
125
127
  * The quiet counts are not carried: a count says how much a place holds, and
126
- * a sum of how much six places hold is a figure about nothing.
128
+ * a sum of how much six places hold is a figure about nothing. Nor is a quiet
129
+ * badge: the group's figure is the accent, and only what calls is added to it.
127
130
  */
128
131
  export declare function groupBadge(links: readonly Pick<CompactNavLink, 'badge'>[]): (number | string) | undefined;
129
132
  /**
@@ -157,8 +160,9 @@ lift?: number): {
157
160
  * whatever came next in the document.
158
161
  *
159
162
  * EVERY PLACE IS A REAL LINK, so a modified press opens a tab as it does in
160
- * the full rail; choosing one closes the flyout and, where the keyboard chose
161
- * it, hands focus back to the tile.
163
+ * the full rail; choosing one closes the flyout, and focus follows the
164
+ * navigation: where the page it opened took focus it stays there, and where
165
+ * it is still in the flyout it goes back to the tile, by a press as by a key.
162
166
  *
163
167
  * WHAT OPENED IT DECIDES WHAT CLOSES IT. Opened by the pointer resting on the
164
168
  * tile, it closes when the pointer leaves both; opened by a press or the keys,
@@ -1,4 +1,4 @@
1
- export { CompactNav, CompactNavGroup, CompactNavItem, CompactNavSeparator, groupBadge, placeFlyout } from '../chunk-ICE3D63E.js';
1
+ export { CompactNav, CompactNavGroup, CompactNavItem, CompactNavSeparator, groupBadge, placeFlyout } from '../chunk-QWRAWKB2.js';
2
2
  import '../chunk-TEV5REGB.js';
3
3
  import '../chunk-HTS5RJ7S.js';
4
4
  import '../chunk-OMV5W53A.js';
@@ -1,7 +1,7 @@
1
- export { DEEP_WORK_CARD_LABELS, DEEP_WORK_STATES, DEEP_WORK_STATE_LABELS, DeepWorkCard } from '../chunk-CV2LU2T2.js';
1
+ export { DEEP_WORK_CARD_LABELS, DEEP_WORK_STATES, DEEP_WORK_STATE_LABELS, DeepWorkCard } from '../chunk-IYI6HWKN.js';
2
2
  import '../chunk-RJWO2PC5.js';
3
- import '../chunk-ZWCK4N4L.js';
4
3
  import '../chunk-RQFRRPV6.js';
4
+ import '../chunk-ZWCK4N4L.js';
5
5
  import '../chunk-FLW5U7YS.js';
6
6
  import '../chunk-LBGKLYBY.js';
7
7
  import '../chunk-U2IQNDDR.js';
@@ -1,3 +1,3 @@
1
- export { DirectoryList } from '../chunk-SRDNGGZE.js';
1
+ export { DirectoryList } from '../chunk-RKIJAXQA.js';
2
2
  import '../chunk-PQADV6P6.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -17,7 +17,12 @@
17
17
  font-family: var(--font-family-sans);
18
18
  }
19
19
 
20
- /* The groups take the room and scroll on their own, so the foot stays put. */
20
+ /* The groups take the room and scroll on their own, so the foot stays put.
21
+
22
+ THEIR INSET IS INSIDE THE SCROLLER. The gutter is reserved whether or not
23
+ they scroll, and the lines end a step before it: a line's state pill stood
24
+ against the scrollbar, and moved by its width when the list grew long
25
+ enough to scroll. */
21
26
  .crewlet-directory__groups {
22
27
  display: flex;
23
28
  flex: 1 1 auto;
@@ -26,7 +31,9 @@
26
31
  under it rather than a band between two runs of them. */
27
32
  gap: var(--spacing-3);
28
33
  min-height: 0;
34
+ padding-right: var(--spacing-2);
29
35
  overflow-y: auto;
36
+ scrollbar-gutter: stable;
30
37
  }
31
38
 
32
39
  .crewlet-directory__group {
@@ -1,4 +1,4 @@
1
1
  export { EntityChip } from '../chunk-65RFU7LF.js';
2
- import '../chunk-IRONPJB4.js';
2
+ import '../chunk-YTLWOHOY.js';
3
3
  import '../chunk-XZY6S3UC.js';
4
4
  import '../chunk-ODEQTPFM.js';
@@ -1,5 +1,5 @@
1
1
  export { ImageUpload } from '../chunk-K6Y6CDEZ.js';
2
- import '../chunk-IRONPJB4.js';
2
+ import '../chunk-YTLWOHOY.js';
3
3
  import '../chunk-XZY6S3UC.js';
4
4
  import '../chunk-XSPCITPN.js';
5
5
  import '../chunk-ODEQTPFM.js';
@@ -48,13 +48,27 @@ export interface InboxGroupProps extends Omit<HTMLAttributes<HTMLElement>, 'titl
48
48
  /** Whether a collapsible group starts open, when the group holds it. */
49
49
  defaultOpen?: boolean | undefined;
50
50
  onOpenChange?: ((open: boolean) => void) | undefined;
51
+ /**
52
+ * The latest row to arrive in the group, as a token (the row's key): each
53
+ * new value plays a brief wash of the accent over the group's label and
54
+ * count, so a row that lands in a closed group (Answered) is seen to land.
55
+ * Pass it only for a row that has just arrived, never on a first draw.
56
+ */
57
+ arrived?: string | number | undefined;
51
58
  }
59
+ /**
60
+ * The motion a leaving row waits for: its height closing, a transition of the
61
+ * row's own `grid-template-rows` (InboxList.css). A suite fakes exactly this.
62
+ */
63
+ export declare const INBOX_ROW_LEAVE: {
64
+ readonly transitions: readonly ["grid-template-rows"];
65
+ };
52
66
  /**
53
67
  * A labelled run of rows. The label is a heading set in the micro-label
54
68
  * register, so a reader navigating by heading lands on each group and a
55
69
  * reader scanning sees where one ends.
56
70
  */
57
- export declare function InboxGroup({ label, hideLabel, headingLevel, count, collapsible, open: controlledOpen, defaultOpen, onOpenChange, className, children, ...rest }: InboxGroupProps): import("react").JSX.Element;
71
+ export declare function InboxGroup({ label, hideLabel, headingLevel, count, collapsible, open: controlledOpen, defaultOpen, onOpenChange, arrived, className, children, ...rest }: InboxGroupProps): import("react").JSX.Element;
58
72
  export type InboxRowTone = 'default' | 'danger';
59
73
  export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'onClick' | 'title'> {
60
74
  /** Who the row is from: a seat's or a person's name. */
@@ -114,6 +128,18 @@ export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'on
114
128
  * open the link in a tab.
115
129
  */
116
130
  onClick?: ((event: MouseEvent<HTMLElement>) => void) | undefined;
131
+ /**
132
+ * On its way out: the row stays drawn, inert, while its height closes, so
133
+ * the rows under it slide up rather than jump, and is drawn no more once it
134
+ * has closed. An `InboxGroup` does this itself for a keyed row that leaves
135
+ * its children, and a wrapper the host draws its rows through forwards
136
+ * this and `onLeft` to its `InboxRow`; pass it for a list of your own.
137
+ */
138
+ leaving?: boolean | undefined;
139
+ /** Runs once a leaving row has closed and is drawn no more, once for each leave. */
140
+ onLeft?: (() => void) | undefined;
141
+ /** Just arrived: a brief wash of the accent that fades back to the row's own ground. */
142
+ arrived?: boolean | undefined;
117
143
  }
118
144
  /**
119
145
  * One row: WHO, then WHAT in up to three lines, then the task it is about.
@@ -137,7 +163,7 @@ export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'on
137
163
  * the words take up to three lines before they stop, so a row says what it is
138
164
  * rather than its first few words.
139
165
  */
140
- export declare function InboxRow({ title, qualifier, qualifierTone, meta, leading, excerpt, aside, selected, unread, unreadLabel, actionable, done, doneLabel, status, outcome, outcomeIcon, outcomeTone, href, onClick, className, children, ...rest }: InboxRowProps): import("react").JSX.Element;
166
+ export declare function InboxRow({ title, qualifier, qualifierTone, meta, leading, excerpt, aside, selected, unread, unreadLabel, actionable, done, doneLabel, status, outcome, outcomeIcon, outcomeTone, href, onClick, leaving, onLeft, arrived, className, children, ...rest }: InboxRowProps): import("react").JSX.Element | null;
141
167
  export type InboxMoreProps = HTMLAttributes<HTMLDivElement>;
142
168
  /**
143
169
  * The end of the list: the quiet toggles that open what is not in it by
@@ -1,3 +1,3 @@
1
1
  import './InboxList.css';
2
- export { InboxList, InboxGroup, InboxRow, InboxMore, InboxToggle } from './InboxList.js';
2
+ export { InboxList, InboxGroup, InboxRow, InboxMore, InboxToggle, INBOX_ROW_LEAVE } from './InboxList.js';
3
3
  export type { InboxListProps, InboxGroupProps, InboxRowProps, InboxRowTone, InboxMoreProps, InboxToggleProps, } from './InboxList.js';
@@ -1,5 +1,10 @@
1
- export { InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-QPHD4UUG.js';
1
+ export { INBOX_ROW_LEAVE, InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-F6ZUM66S.js';
2
2
  import '../chunk-RK4XPHVG.js';
3
+ import '../chunk-6C7GQOBV.js';
4
+ import '../chunk-PCAT2EFB.js';
5
+ import '../chunk-YJQXESOJ.js';
6
+ import '../chunk-HTS5RJ7S.js';
7
+ import '../chunk-4ITETIGZ.js';
3
8
  import '../chunk-PQADV6P6.js';
4
9
  import '../chunk-UT6CW3C7.js';
5
10
  import '../chunk-ODEQTPFM.js';
@@ -26,6 +26,9 @@
26
26
 
27
27
  /* The heading's own line box is the label's, so the label sits where it is drawn. */
28
28
  .crewlet-inbox-list__label {
29
+ /* The arrival's wash stands behind the label's words, inside it. */
30
+ position: relative;
31
+ isolation: isolate;
29
32
  margin: 0;
30
33
  padding: var(--spacing-3) var(--spacing-2) var(--spacing-1);
31
34
  font: inherit;
@@ -33,6 +36,32 @@
33
36
  line-height: var(--font-line-height-snug);
34
37
  }
35
38
 
39
+ /*
40
+ * A ROW ARRIVED: a wash of the accent that fades back to the ground, over the
41
+ * row itself, or over a group's label and count where the row landed in a
42
+ * closed group. The row's keyframes name no last step, so the wash returns
43
+ * to whatever the row draws of its own (hover, selected).
44
+ */
45
+ .crewlet-inbox-list__arrival {
46
+ position: absolute;
47
+ inset: 0;
48
+ z-index: -1;
49
+ border-radius: var(--radius-xs);
50
+ pointer-events: none;
51
+ animation: crewlet-inbox-arrive calc(var(--motion-duration-moderate) * 8) var(--motion-easing-out) both;
52
+ }
53
+
54
+ .crewlet-inbox-row--arrived {
55
+ animation: crewlet-inbox-arrive calc(var(--motion-duration-moderate) * 8) var(--motion-easing-out);
56
+ }
57
+
58
+ @keyframes crewlet-inbox-arrive {
59
+ 0%,
60
+ 40% {
61
+ background: var(--color-brand-accent-soft-strong);
62
+ }
63
+ }
64
+
36
65
  /* The count after a label, in the quiet ink and the label's own register. */
37
66
  .crewlet-inbox-list__count {
38
67
  margin-inline-start: var(--spacing-1);
@@ -107,12 +136,38 @@
107
136
  * a selected row takes no hover on top of it: both are a background on the
108
137
  * same element, and a composite of the two is a colour nothing has measured.
109
138
  */
139
+ /* ONE TRACK, so a leaving row can close its height (below). */
110
140
  .crewlet-inbox-row {
141
+ display: grid;
142
+ grid-template-rows: 1fr;
111
143
  min-width: 0;
112
144
  border-radius: var(--radius-md);
113
145
  transition: background var(--motion-duration-fast) var(--motion-easing-default);
114
146
  }
115
147
 
148
+ .crewlet-inbox-row__body {
149
+ min-width: 0;
150
+ min-height: 0;
151
+ }
152
+
153
+ /*
154
+ * A ROW LEAVING closes its one track to nothing and fades, on the slow step
155
+ * with the emphasized curve, the motion a column opens and shuts by, so the
156
+ * rows under it slide up into its place. The shell's presence waits on the
157
+ * track's transition (`INBOX_ROW_LEAVE`), and the row is inert all the while.
158
+ */
159
+ .crewlet-inbox-row--leaving {
160
+ grid-template-rows: 0fr;
161
+ opacity: 0;
162
+ transition:
163
+ grid-template-rows var(--motion-duration-slow) var(--motion-easing-emphasized),
164
+ opacity var(--motion-duration-moderate) var(--motion-easing-out);
165
+ }
166
+
167
+ .crewlet-inbox-row--leaving .crewlet-inbox-row__body {
168
+ overflow: hidden;
169
+ }
170
+
116
171
  .crewlet-inbox-row:hover {
117
172
  background: var(--color-surface-hover);
118
173
  }
@@ -349,7 +404,7 @@
349
404
  }
350
405
 
351
406
  /* A row with no leading slot: the words start at the control's own pad, and so does the task. */
352
- .crewlet-inbox-row:not(:has(.crewlet-inbox-row__leading)) > .crewlet-inbox-row__aside {
407
+ .crewlet-inbox-row:not(:has(.crewlet-inbox-row__leading)) .crewlet-inbox-row__aside {
353
408
  padding-inline-start: var(--spacing-2);
354
409
  }
355
410
 
@@ -385,10 +440,16 @@
385
440
  }
386
441
 
387
442
  @media (prefers-reduced-motion: reduce) {
388
- .crewlet-inbox-row {
443
+ .crewlet-inbox-row,
444
+ .crewlet-inbox-row--leaving {
389
445
  transition: none;
390
446
  }
391
447
 
448
+ .crewlet-inbox-row--arrived,
449
+ .crewlet-inbox-list__arrival {
450
+ animation: none;
451
+ }
452
+
392
453
  .crewlet-inbox-list__toggle,
393
454
  .crewlet-inbox-list__chevron {
394
455
  transition: none;
@@ -21,6 +21,12 @@ export interface ListDetailProps extends Omit<HTMLAttributes<HTMLDivElement>, 'c
21
21
  * column. The list column is `--crewlet-list-detail-list` wide (17rem by
22
22
  * default), with a hairline between the two.
23
23
  *
24
+ * THE DETAIL'S SCROLLBAR IS ON THE SHEET'S EDGE. Placed directly in a fill
25
+ * screen's column, it reaches past the column's end pad, and the detail
26
+ * keeps the screen's side inset inside itself, with its scrollbar's gutter
27
+ * always reserved: its content ends under the bar's last control, and a
28
+ * trailing control never touches the scrollbar.
29
+ *
24
30
  * BELOW 640px OF ITS OWN WIDTH IT IS ONE PANE, the one `showing` names: the
25
31
  * list and a reading detail do not both fit, whether the window is a phone or
26
32
  * an aside beside the screen has taken the room. The application gives its
@@ -1,2 +1,2 @@
1
- export { ListDetail } from '../chunk-YBC2CYPD.js';
1
+ export { ListDetail } from '../chunk-O4VTQMEF.js';
2
2
  import '../chunk-ODEQTPFM.js';
@@ -10,6 +10,17 @@
10
10
  * drew the detail 130px wide beside a 17rem list. The phone rule below is the
11
11
  * one decision that stays the window's: on a phone the page scrolls as a
12
12
  * whole rather than each pane on its own.
13
+ *
14
+ * ON A SCREEN THAT FILLS THE WINDOW, THE PANE THAT SCROLLS IS ITS OUTERMOST
15
+ * BOX, and its inset is padding inside it. Standing directly in the fill
16
+ * column, the ListDetail reaches past the column's end pad to the sheet's
17
+ * edge (below), so the scrollbar is the sheet's last pixel; the detail then
18
+ * reserves its scrollbar's gutter whether or not it scrolls, and keeps the
19
+ * screen's side inset between its content and that gutter, so a trailing
20
+ * control (a header's Disconnect, a tile's Open) never touches the scrollbar,
21
+ * does not move when the detail grows long enough to scroll, and ends under
22
+ * the bar's last control. Anywhere else (a scrolling or a flush screen, or
23
+ * outside a shell) it is as wide as its box, with nothing reserved.
13
24
  */
14
25
 
15
26
  /* THE HAIRLINE STANDS MIDWAY between the two panes: the list's inset before it
@@ -17,6 +28,9 @@
17
28
  the list than to the detail it divides it from; the two still add to 64. */
18
29
  .crewlet-list-detail {
19
30
  --crewlet-list-detail-gutter: var(--spacing-7);
31
+ /* The inset at a scrolling pane's end: the screen's own side inset in a
32
+ shell, so the page's two edges match. */
33
+ --crewlet-list-detail-end: var(--crewlet-app-shell-inset, var(--spacing-5));
20
34
 
21
35
  container: crewlet-list-detail / inline-size;
22
36
  display: flex;
@@ -27,6 +41,21 @@
27
41
  min-height: 0;
28
42
  }
29
43
 
44
+ /* STANDING IN A FILL SCREEN'S COLUMN, IT TAKES THE COLUMN'S END PAD BACK: the
45
+ inset and the scrollbar's width, which the shell publishes for exactly
46
+ this (`--crewlet-app-shell-bleed-end`, AppShell.css). The detail keeps the
47
+ same inset inside itself, and reserves its gutter, there and only there. */
48
+ :where(.crewlet-app-shell--fill:not(.crewlet-app-shell--fill-flush) .crewlet-app-shell__content) > .crewlet-list-detail {
49
+ margin-right: calc(var(--crewlet-app-shell-bleed-end, 0px) * -1);
50
+ }
51
+
52
+ :where(.crewlet-app-shell--fill:not(.crewlet-app-shell--fill-flush) .crewlet-app-shell__content)
53
+ > .crewlet-list-detail
54
+ .crewlet-list-detail__detail {
55
+ padding-right: var(--crewlet-list-detail-end);
56
+ scrollbar-gutter: stable;
57
+ }
58
+
30
59
  .crewlet-list-detail__panes {
31
60
  display: grid;
32
61
  flex: 1 1 auto;
@@ -68,6 +97,15 @@
68
97
  overflow-y: auto;
69
98
  }
70
99
 
100
+ /* The list is the pane that scrolls now, and takes the detail's end where
101
+ the ListDetail reaches the sheet. */
102
+ :where(.crewlet-app-shell--fill:not(.crewlet-app-shell--fill-flush) .crewlet-app-shell__content)
103
+ > .crewlet-list-detail
104
+ .crewlet-list-detail__list {
105
+ padding-right: var(--crewlet-list-detail-end);
106
+ scrollbar-gutter: stable;
107
+ }
108
+
71
109
  .crewlet-list-detail--showing-list .crewlet-list-detail__detail,
72
110
  .crewlet-list-detail--showing-detail .crewlet-list-detail__list {
73
111
  display: none;
@@ -1,4 +1,4 @@
1
1
  export { OrgLabel } from '../chunk-5YXMKZUA.js';
2
- import '../chunk-IRONPJB4.js';
2
+ import '../chunk-YTLWOHOY.js';
3
3
  import '../chunk-XZY6S3UC.js';
4
4
  import '../chunk-ODEQTPFM.js';
@@ -1,5 +1,5 @@
1
1
  export { OrgNodeDisclosure, OrgNodeLabel, OrgNodeLead } from '../chunk-NKBKHOMU.js';
2
2
  import '../chunk-5YXMKZUA.js';
3
- import '../chunk-IRONPJB4.js';
3
+ import '../chunk-YTLWOHOY.js';
4
4
  import '../chunk-XZY6S3UC.js';
5
5
  import '../chunk-ODEQTPFM.js';
@@ -3,7 +3,7 @@ import '../chunk-ZDWAIVWL.js';
3
3
  import '../chunk-MY374UUD.js';
4
4
  import '../chunk-5YXMKZUA.js';
5
5
  import '../chunk-LM3ISVJA.js';
6
- import '../chunk-IRONPJB4.js';
6
+ import '../chunk-YTLWOHOY.js';
7
7
  import '../chunk-XZY6S3UC.js';
8
8
  import '../chunk-CVKANYHD.js';
9
9
  import '../chunk-OMV5W53A.js';
@@ -2,7 +2,7 @@ export { PEOPLE_RAIL_LABELS, PeopleRail, peopleRailKey } from '../chunk-QGEDXYLB
2
2
  import '../chunk-CENOOTAV.js';
3
3
  import '../chunk-Z7JYG5TF.js';
4
4
  import '../chunk-AYKXMIOR.js';
5
- import '../chunk-IRONPJB4.js';
5
+ import '../chunk-YTLWOHOY.js';
6
6
  import '../chunk-XZY6S3UC.js';
7
7
  import '../chunk-DGXZ635L.js';
8
8
  import '../chunk-XSPCITPN.js';
@@ -3,7 +3,7 @@ import '../chunk-CENOOTAV.js';
3
3
  import '../chunk-EFX2YC5T.js';
4
4
  import '../chunk-Z7JYG5TF.js';
5
5
  import '../chunk-AYKXMIOR.js';
6
- import '../chunk-IRONPJB4.js';
6
+ import '../chunk-YTLWOHOY.js';
7
7
  import '../chunk-XZY6S3UC.js';
8
8
  import '../chunk-UT6CW3C7.js';
9
9
  import '../chunk-ODEQTPFM.js';
@@ -1,4 +1,4 @@
1
1
  export { PRINCIPAL_KINDS, PrincipalAvatar } from '../chunk-HF5L56YQ.js';
2
- import '../chunk-IRONPJB4.js';
2
+ import '../chunk-YTLWOHOY.js';
3
3
  import '../chunk-XZY6S3UC.js';
4
4
  import '../chunk-ODEQTPFM.js';
@@ -2,7 +2,7 @@ export { PrincipalPicker } from '../chunk-Q6TCUSQA.js';
2
2
  import '../chunk-NNZRTR5M.js';
3
3
  import '../chunk-YGRTX67J.js';
4
4
  import '../chunk-HF5L56YQ.js';
5
- import '../chunk-IRONPJB4.js';
5
+ import '../chunk-YTLWOHOY.js';
6
6
  import '../chunk-XZY6S3UC.js';
7
7
  import '../chunk-O7N6W52M.js';
8
8
  import '../chunk-PGCLHYVP.js';
@@ -3,7 +3,23 @@ import { type HeadingLevel } from '../utils/headingLevel.js';
3
3
  export interface ReadingPaneProps extends HTMLAttributes<HTMLElement> {
4
4
  /** Overrides the level the title is rendered at. */
5
5
  headingLevel?: HeadingLevel | undefined;
6
+ /**
7
+ * What the pane shows, as a key: the item's id. A new value draws the new
8
+ * item afresh and plays it in, a short fade and rise on the moderate step
9
+ * (`READING_PANE_ENTER` on the column; nothing moves under reduced motion),
10
+ * and moves focus to its title, without scrolling the page, where the reader
11
+ * was last in the pane (focus came into it, or a press landed in it): the
12
+ * control they answered with went with the old item. A reader in the list
13
+ * beside the pane, or nowhere (a live update), keeps their place.
14
+ */
15
+ contentKey?: string | number | undefined;
16
+ /** Where focus goes after a change of `contentKey`, in place of the title. */
17
+ focusTarget?: (() => HTMLElement | null | undefined) | undefined;
6
18
  }
19
+ /** The keyframes a new item plays in by, on the column: the one motion a suite fakes. */
20
+ export declare const READING_PANE_ENTER: {
21
+ readonly animations: readonly ["crewlet-reading-pane-enter"];
22
+ };
7
23
  /**
8
24
  * One item read in full, top to bottom, in one column: the line saying who is
9
25
  * asking and about what, the question in large type, its context, the
@@ -19,7 +35,7 @@ export interface ReadingPaneProps extends HTMLAttributes<HTMLElement> {
19
35
  * The article is named by its title, so a reader moving by landmark hears the
20
36
  * question rather than "article".
21
37
  */
22
- export declare function ReadingPane({ headingLevel, className, children, ...rest }: ReadingPaneProps): import("react").JSX.Element;
38
+ export declare function ReadingPane({ headingLevel, contentKey, focusTarget, className, children, ...rest }: ReadingPaneProps): import("react").JSX.Element;
23
39
  export declare namespace ReadingPane {
24
40
  var Byline: typeof ReadingPaneByline;
25
41
  var Title: typeof ReadingPaneTitle;
@@ -1,3 +1,3 @@
1
1
  import './ReadingPane.css';
2
- export { ReadingPane, ReadingPaneByline, ReadingPaneTitle, ReadingPaneLead, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneOutcome, ReadingPaneSection, } from './ReadingPane.js';
2
+ export { ReadingPane, ReadingPaneByline, ReadingPaneTitle, ReadingPaneLead, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneOutcome, ReadingPaneSection, READING_PANE_ENTER, } from './ReadingPane.js';
3
3
  export type { ReadingPaneProps, ReadingPaneBylineProps, ReadingPaneTitleProps, ReadingPaneLeadProps, ReadingPaneQuoteProps, ReadingPaneReasoningProps, ReadingPaneOutcomeProps, ReadingPaneSectionProps, } from './ReadingPane.js';
@@ -1,4 +1,5 @@
1
- export { ReadingPane, ReadingPaneByline, ReadingPaneLead, ReadingPaneOutcome, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneSection, ReadingPaneTitle } from '../chunk-Q7N2ATMC.js';
1
+ export { READING_PANE_ENTER, ReadingPane, ReadingPaneByline, ReadingPaneLead, ReadingPaneOutcome, ReadingPaneQuote, ReadingPaneReasoning, ReadingPaneSection, ReadingPaneTitle } from '../chunk-64JNKLLP.js';
2
2
  import '../chunk-RK4XPHVG.js';
3
+ import '../chunk-4ITETIGZ.js';
3
4
  import '../chunk-PQADV6P6.js';
4
5
  import '../chunk-ODEQTPFM.js';
@@ -32,6 +32,25 @@
32
32
  }
33
33
  }
34
34
 
35
+ /* A NEW ITEM PLAYS IN (`contentKey`): a short fade and rise, so the reader
36
+ sees the pane move on rather than its words change under them. */
37
+ .crewlet-reading-pane__column--entering {
38
+ animation: crewlet-reading-pane-enter var(--motion-duration-moderate) var(--motion-easing-out);
39
+ }
40
+
41
+ @keyframes crewlet-reading-pane-enter {
42
+ from {
43
+ opacity: 0;
44
+ transform: translateY(var(--spacing-2));
45
+ }
46
+ }
47
+
48
+ @media (prefers-reduced-motion: reduce) {
49
+ .crewlet-reading-pane__column--entering {
50
+ animation: none;
51
+ }
52
+ }
53
+
35
54
  /* ---- The byline -------------------------------------------------------- */
36
55
 
37
56
  .crewlet-reading-pane__byline {
@@ -1,5 +1,5 @@
1
- export { SectionFrame, SectionFrameBody, SectionFrameNav } from '../chunk-YRNNUCQP.js';
2
- import '../chunk-25JQNHTC.js';
1
+ export { SectionFrame, SectionFrameBody, SectionFrameNav } from '../chunk-7BDBO2NP.js';
2
+ import '../chunk-BRK3HXGZ.js';
3
3
  import '../chunk-TEV5REGB.js';
4
4
  import '../chunk-HBDA76EZ.js';
5
5
  import '../chunk-2YTOOH3A.js';
@@ -7,7 +7,7 @@ import '../chunk-DTIW4CHZ.js';
7
7
  import '../chunk-YGRTX67J.js';
8
8
  import '../chunk-BPCY7ZID.js';
9
9
  import '../chunk-HF5L56YQ.js';
10
- import '../chunk-IRONPJB4.js';
10
+ import '../chunk-YTLWOHOY.js';
11
11
  import '../chunk-XZY6S3UC.js';
12
12
  import '../chunk-YJQXESOJ.js';
13
13
  import '../chunk-L72URIHR.js';
@@ -9,7 +9,7 @@ import '../chunk-Z7JYG5TF.js';
9
9
  import '../chunk-LBGKLYBY.js';
10
10
  import '../chunk-EC6EUIAD.js';
11
11
  import '../chunk-AYKXMIOR.js';
12
- import '../chunk-IRONPJB4.js';
12
+ import '../chunk-YTLWOHOY.js';
13
13
  import '../chunk-XZY6S3UC.js';
14
14
  import '../chunk-CYQBDZ6M.js';
15
15
  import '../chunk-7ZSEUO5Q.js';