@crewlethq/ui 0.12.0 → 0.13.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.
Files changed (157) hide show
  1. package/README.md +66 -5
  2. package/dist/AppShell/index.js +2 -1
  3. package/dist/Avatar/index.js +1 -1
  4. package/dist/{Avatar-QHYOFID3.css → Avatar-BPJ36QZR.css} +1 -1
  5. package/dist/AvatarPicker/index.js +6 -6
  6. package/dist/Breadcrumb/Breadcrumb.d.ts +68 -0
  7. package/dist/Breadcrumb/index.d.ts +3 -0
  8. package/dist/Breadcrumb/index.js +3 -0
  9. package/dist/Breadcrumb-YMWVM3SV.css +193 -0
  10. package/dist/ChoiceButtons/ChoiceButtons.d.ts +43 -0
  11. package/dist/ChoiceButtons/index.d.ts +3 -0
  12. package/dist/ChoiceButtons/index.js +3 -0
  13. package/dist/ChoiceButtons-DJ5EOGUA.css +127 -0
  14. package/dist/Combobox/index.js +2 -2
  15. package/dist/CommandPalette/CommandPalette.d.ts +48 -1
  16. package/dist/CommandPalette/index.js +3 -3
  17. package/dist/{CommandPalette-C5AMKGN2.css → CommandPalette-HBTNKHZQ.css} +55 -2
  18. package/dist/DataTable/index.js +4 -4
  19. package/dist/DataView/index.js +11 -11
  20. package/dist/DateTimePicker/index.js +2 -2
  21. package/dist/Document/Document.d.ts +56 -0
  22. package/dist/Document/index.d.ts +3 -0
  23. package/dist/Document/index.js +2 -0
  24. package/dist/Document-PQGXJHB7.css +288 -0
  25. package/dist/EntityChip/index.js +1 -1
  26. package/dist/Highlight/Highlight.d.ts +45 -0
  27. package/dist/Highlight/index.d.ts +3 -0
  28. package/dist/Highlight/index.js +2 -0
  29. package/dist/Highlight-DXLV2R6L.css +29 -0
  30. package/dist/HuePicker/index.js +3 -3
  31. package/dist/ImageUpload/index.js +2 -2
  32. package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
  33. package/dist/InboxList/InboxList.d.ts +116 -0
  34. package/dist/InboxList/index.d.ts +3 -0
  35. package/dist/InboxList/index.js +5 -0
  36. package/dist/InboxList-YMFWKGGS.css +252 -0
  37. package/dist/KeyChip/KeyChip.d.ts +21 -0
  38. package/dist/KeyChip/index.d.ts +3 -0
  39. package/dist/KeyChip/index.js +2 -0
  40. package/dist/KeyChip-4XJU4BBE.css +30 -0
  41. package/dist/Link/Link.d.ts +17 -2
  42. package/dist/Link/index.js +1 -1
  43. package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
  44. package/dist/ListInput/index.js +2 -2
  45. package/dist/Listbox/index.d.ts +2 -0
  46. package/dist/Listbox/index.js +1 -1
  47. package/dist/Listbox/useHighlightRest.d.ts +35 -0
  48. package/dist/MarkdownEditor/MarkdownEditor.d.ts +93 -0
  49. package/dist/MarkdownEditor/format.d.ts +34 -0
  50. package/dist/MarkdownEditor/index.d.ts +5 -0
  51. package/dist/MarkdownEditor/index.js +12 -0
  52. package/dist/MarkdownEditor-ZLSHTOIZ.css +146 -0
  53. package/dist/Modal/Modal.d.ts +5 -1
  54. package/dist/Modal/index.js +1 -1
  55. package/dist/{Modal-BNT3TDV2.css → Modal-3YJ2GG2T.css} +33 -0
  56. package/dist/OrgLabel/index.js +1 -1
  57. package/dist/OrgNode/index.js +1 -1
  58. package/dist/OrgTable/index.js +5 -5
  59. package/dist/Outline/Outline.d.ts +72 -0
  60. package/dist/Outline/index.d.ts +3 -0
  61. package/dist/Outline/index.js +2 -0
  62. package/dist/Outline-VCXKKU3E.css +78 -0
  63. package/dist/Popover/Popover.d.ts +21 -1
  64. package/dist/Popover/index.js +1 -1
  65. package/dist/ReadingPane/ReadingPane.d.ts +95 -0
  66. package/dist/ReadingPane/index.d.ts +3 -0
  67. package/dist/ReadingPane/index.js +4 -0
  68. package/dist/ReadingPane-5UOLPWY5.css +194 -0
  69. package/dist/SegmentedControl/SegmentedControl.d.ts +9 -1
  70. package/dist/SegmentedControl/index.js +3 -3
  71. package/dist/{SegmentedControl-76HODRBT.css → SegmentedControl-PKBLJVSA.css} +18 -0
  72. package/dist/Select/Select.d.ts +22 -0
  73. package/dist/Select/index.js +2 -2
  74. package/dist/{Select-4ANVLHUY.css → Select-XMZ44RWY.css} +24 -0
  75. package/dist/SidebarNav/index.js +2 -1
  76. package/dist/{SidebarNav-X5NLZNWA.css → SidebarNav-KF66MLOM.css} +4 -19
  77. package/dist/Switcher/Switcher.d.ts +151 -0
  78. package/dist/Switcher/index.d.ts +3 -0
  79. package/dist/Switcher/index.js +16 -0
  80. package/dist/Switcher-B44Q4WF4.css +346 -0
  81. package/dist/Tabs/Tabs.d.ts +7 -1
  82. package/dist/Tabs/index.js +1 -1
  83. package/dist/{Tabs-5I5Q5KKM.css → Tabs-ITJYJFMU.css} +49 -0
  84. package/dist/TagsInput/index.js +3 -3
  85. package/dist/ThemeGallery/ThemeGallery.d.ts +65 -0
  86. package/dist/ThemeGallery/index.d.ts +3 -0
  87. package/dist/ThemeGallery/index.js +6 -0
  88. package/dist/ThemeGallery-NITASULQ.css +93 -0
  89. package/dist/ThemeSwatch/ThemeSwatch.d.ts +71 -0
  90. package/dist/ThemeSwatch/index.d.ts +3 -0
  91. package/dist/ThemeSwatch/index.js +2 -0
  92. package/dist/ThemeSwatch-4DL457ZB.css +256 -0
  93. package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +5 -1
  94. package/dist/ThemeSwitcher/index.js +4 -4
  95. package/dist/ThemeToggle/ThemeToggle.d.ts +1 -1
  96. package/dist/ThemeToggle/index.js +1 -1
  97. package/dist/TimeWindowPicker/index.js +4 -4
  98. package/dist/Toaster/index.js +1 -1
  99. package/dist/{Toaster-NVDCEIP5.css → Toaster-KZ7YE2HA.css} +1 -0
  100. package/dist/Tree/index.d.ts +1 -1
  101. package/dist/Tree/index.js +1 -1
  102. package/dist/Tree/useTreeState.d.ts +37 -7
  103. package/dist/TreeCanvas/index.js +2 -2
  104. package/dist/TreeGrid/TreeGrid.d.ts +22 -2
  105. package/dist/TreeGrid/index.js +2 -2
  106. package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
  107. package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
  108. package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
  109. package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
  110. package/dist/{chunk-GXYNEDNA.js → chunk-4Y7WKXAW.js} +1 -1
  111. package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
  112. package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
  113. package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
  114. package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
  115. package/dist/chunk-77IEHFY4.js +299 -0
  116. package/dist/chunk-A5YKNRG6.js +94 -0
  117. package/dist/{chunk-HLBRPES3.js → chunk-B2CITO6R.js} +2 -2
  118. package/dist/{chunk-TEEJSTFG.js → chunk-CF6DTETT.js} +1 -1
  119. package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
  120. package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
  121. package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
  122. package/dist/chunk-FYTIJM2T.js +40 -0
  123. package/dist/{chunk-ZHOM65MI.js → chunk-GG2QEZO2.js} +2 -2
  124. package/dist/chunk-H6VKCIWV.js +41 -0
  125. package/dist/{chunk-XV65NTIT.js → chunk-HKGS67VG.js} +94 -41
  126. package/dist/chunk-IG75YMUN.js +22 -0
  127. package/dist/{chunk-JCNUQ3US.js → chunk-JDPAA2JU.js} +1 -1
  128. package/dist/chunk-KLIEPFEZ.js +95 -0
  129. package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
  130. package/dist/{chunk-UG5ZMAXW.js → chunk-LELD5KPA.js} +1 -1
  131. package/dist/chunk-MNFU2RNT.js +74 -0
  132. package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
  133. package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
  134. package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
  135. package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
  136. package/dist/chunk-S3IENQ3E.js +336 -0
  137. package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
  138. package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
  139. package/dist/{chunk-IINFVK3W.js → chunk-UZPHPW3Z.js} +6 -6
  140. package/dist/chunk-VWNC2L6W.js +110 -0
  141. package/dist/chunk-VYY56AMX.js +2 -0
  142. package/dist/chunk-WI6K5M5X.js +24 -0
  143. package/dist/{chunk-GOUKOXJK.js → chunk-WY2NLRZN.js} +3 -3
  144. package/dist/{chunk-SEFMO2EV.js → chunk-XFE5W2I4.js} +44 -5
  145. package/dist/chunk-XYCADWZN.js +100 -0
  146. package/dist/chunk-YSJC7AZC.js +62 -0
  147. package/dist/chunk-ZAJHGDAW.js +93 -0
  148. package/dist/generated/index.d.ts +12 -0
  149. package/dist/generated/index.js +65 -52
  150. package/dist/index.js +65 -52
  151. package/dist/styles.css +2449 -161
  152. package/dist/utils/highlight.d.ts +17 -0
  153. package/dist/utils/useMediaMatch.d.ts +12 -0
  154. package/package.json +3 -3
  155. package/dist/chunk-UAQIUSSK.js +0 -2
  156. package/dist/{chunk-74OVQWA5.js → chunk-6AR2T5Y4.js} +0 -0
  157. package/dist/{chunk-GARACZAH.js → chunk-LZP7FGT7.js} +1 -1
@@ -1,8 +1,8 @@
1
1
  import '../chunk-EXHQ2QVW.js';
2
- export { HuePicker } from '../chunk-K5Q2DK3P.js';
2
+ export { HuePicker } from '../chunk-NXDEPUVG.js';
3
3
  import '../chunk-AYKXMIOR.js';
4
- import '../chunk-EBBAAQDY.js';
5
- import '../chunk-JHQUT4WJ.js';
4
+ import '../chunk-26PEPONN.js';
5
+ import '../chunk-4LDTQWS7.js';
6
6
  import '../chunk-CBOICT64.js';
7
7
  import '../chunk-BHHIIK3M.js';
8
8
  import '../chunk-BZFWDIV6.js';
@@ -1,4 +1,4 @@
1
- export { ImageUpload } from '../chunk-LAQK4V3Z.js';
2
- import '../chunk-3UK4VFSK.js';
1
+ export { ImageUpload } from '../chunk-DJMLHCLD.js';
2
+ import '../chunk-6NIZCQUM.js';
3
3
  import '../chunk-HI6Q2UXQ.js';
4
4
  import '../chunk-ODEQTPFM.js';
@@ -1,10 +1,13 @@
1
1
  /*
2
2
  * ImageUpload, the click-to-upload affordance built on Avatar.
3
3
  *
4
- * WHITE ON THE SCRIM, from tokens, in both palettes. The overlay used to spell
4
+ * WHITE ON THE SCRIM, from tokens, in every theme. The overlay used to spell
5
5
  * its own rgba(0, 0, 0, 0.5) and three shades of white; the scrim and the
6
- * on-accent step are the two values the palette already measures for exactly
7
- * this, which is a label drawn on something dark whatever is behind it.
6
+ * on-scrim step are the two values the palette measures for exactly this,
7
+ * which is a label drawn on something dark whatever is behind it. The
8
+ * on-accent step is not: a theme whose accent is light labels it dark. The
9
+ * remove button is the danger fill, and takes the on-danger step for the
10
+ * same reason.
8
11
  *
9
12
  * NO LITERAL FALLBACKS INSIDE var(). A fallback is what renders whenever the
10
13
  * token name is misspelt, so it hides the very failure it looks like
@@ -91,7 +94,7 @@
91
94
  justify-content: center;
92
95
  gap: 2px;
93
96
  background: var(--color-surface-scrim);
94
- color: var(--color-text-on-accent);
97
+ color: var(--color-text-on-scrim);
95
98
  opacity: 0;
96
99
  transition: opacity var(--motion-duration-base) var(--motion-easing-default);
97
100
  pointer-events: none;
@@ -136,7 +139,7 @@
136
139
  height: calc(var(--crewlet-image-upload-size) * 0.3);
137
140
  border-radius: var(--radius-circle);
138
141
  border: 2px solid var(--color-surface-scrim);
139
- border-top-color: var(--color-text-on-accent);
142
+ border-top-color: var(--color-text-on-scrim);
140
143
  animation: crewlet-image-upload-spin calc(var(--motion-duration-slow) * 2.5) linear infinite;
141
144
  }
142
145
 
@@ -166,7 +169,7 @@
166
169
  border-radius: var(--radius-circle);
167
170
  background: var(--color-feedback-danger);
168
171
  border: 2px solid var(--color-surface-background);
169
- color: var(--color-text-on-accent);
172
+ color: var(--color-text-on-danger);
170
173
  cursor: pointer;
171
174
  opacity: 0;
172
175
  transition: opacity var(--motion-duration-fast) var(--motion-easing-default);
@@ -0,0 +1,116 @@
1
+ import { type ButtonHTMLAttributes, type HTMLAttributes, type LiHTMLAttributes, type MouseEvent, type ReactNode } from 'react';
2
+ import { type HeadingLevel } from '../utils/headingLevel.js';
3
+ export interface InboxListProps extends HTMLAttributes<HTMLElement> {
4
+ /** Names the list as a landmark: "Inbox". */
5
+ label: string;
6
+ }
7
+ /**
8
+ * The column of an inbox: what reached a person, grouped, one row each, read
9
+ * in full in a pane beside it.
10
+ *
11
+ * A NAVIGATION, NOT A TABLE. Every row opens something, and a reader moves
12
+ * between them to read one at a time. It promises no columns, so it draws
13
+ * none, and its rows are as tall as their words rather than one fixed height.
14
+ */
15
+ export declare function InboxList({ label, className, children, ...rest }: InboxListProps): import("react").JSX.Element;
16
+ export declare namespace InboxList {
17
+ var Group: typeof InboxGroup;
18
+ var Row: typeof InboxRow;
19
+ var More: typeof InboxMore;
20
+ var Toggle: typeof InboxToggle;
21
+ }
22
+ export interface InboxGroupProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
23
+ /** The group's name: "Needs you", "Updates". */
24
+ label: ReactNode;
25
+ /**
26
+ * Names the group for a screen reader only. For rows a control above them
27
+ * already introduces, such as the snoozed ones under their own toggle.
28
+ */
29
+ hideLabel?: boolean | undefined;
30
+ /** Overrides the level the label is rendered at. */
31
+ headingLevel?: HeadingLevel | undefined;
32
+ }
33
+ /**
34
+ * A labelled run of rows. The label is a heading set in the micro-label
35
+ * register, so a reader navigating by heading lands on each group and a
36
+ * reader scanning sees where one ends.
37
+ */
38
+ export declare function InboxGroup({ label, hideLabel, headingLevel, className, children, ...rest }: InboxGroupProps): import("react").JSX.Element;
39
+ export type InboxRowTone = 'default' | 'danger';
40
+ export interface InboxRowProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'onClick' | 'title'> {
41
+ /** Who the row is from: a seat's or a person's name. */
42
+ title: ReactNode;
43
+ /** A word after the name where the row is not a plain question or update: "coding run", "stopped". */
44
+ qualifier?: ReactNode;
45
+ /** `danger` sets the qualifier in the danger ink, for a seat that has stopped. */
46
+ qualifierTone?: InboxRowTone | undefined;
47
+ /** At the end of the first line: when it arrived. */
48
+ meta?: ReactNode;
49
+ /** Before the words: an avatar, drawn at the target size. */
50
+ leading?: ReactNode;
51
+ /** What the row says, in up to three lines. */
52
+ children?: ReactNode;
53
+ /** Drawn after the words in the quiet ink, inside the same three lines: a comment's own text. */
54
+ excerpt?: ReactNode;
55
+ /**
56
+ * Under the row and OUTSIDE its control, so it can be a control of its own:
57
+ * the task the row is about, as a link.
58
+ */
59
+ aside?: ReactNode;
60
+ /** This row is the one open beside the list. */
61
+ selected?: boolean | undefined;
62
+ /** Not read yet: the name is heavier, the words take the primary ink, and the state is read out. */
63
+ unread?: boolean | undefined;
64
+ /** Read after the row's words when `unread`. */
65
+ unreadLabel?: string | undefined;
66
+ /** It waits on the reader: the words take the primary ink. */
67
+ actionable?: boolean | undefined;
68
+ /** Settled: the words are struck through in the quiet ink, and the state is read out. */
69
+ done?: boolean | undefined;
70
+ /** Read after the row's words when `done`: "Answered", "Resolved". */
71
+ doneLabel?: string | undefined;
72
+ /** Opens the row as a link. */
73
+ href?: string | undefined;
74
+ /**
75
+ * Opens the row as a button, or, beside `href`, takes the press first: a
76
+ * router calls `preventDefault` on a plain click and lets a modified one
77
+ * open the link in a tab.
78
+ */
79
+ onClick?: ((event: MouseEvent<HTMLElement>) => void) | undefined;
80
+ }
81
+ /**
82
+ * One row: WHO, then WHAT in up to three lines, then the task it is about.
83
+ *
84
+ * THE ROW IS ONE CONTROL and the task is another. A link nested inside a
85
+ * button is invalid and unreachable by keyboard, so the task sits under the
86
+ * control, in the same item, and lines up with the words above it. The item
87
+ * takes the hover and the selected fill as a whole, so the two read as one row.
88
+ *
89
+ * THE SELECTED FILL IS NEUTRAL. An inbox is read one row after another, and an
90
+ * accent tint on the open row made the column read as a column of alerts; the
91
+ * pressed fill says "here" without saying "look". The control carries
92
+ * `aria-current`, so the row is announced as the open one where focus is.
93
+ *
94
+ * NOTHING IS CUT AT THE EDGE. The name ellipsizes, the time never wraps, and
95
+ * the words take up to three lines before they stop, so a row says what it is
96
+ * rather than its first few words.
97
+ */
98
+ export declare function InboxRow({ title, qualifier, qualifierTone, meta, leading, excerpt, aside, selected, unread, unreadLabel, actionable, done, doneLabel, href, onClick, className, children, ...rest }: InboxRowProps): import("react").JSX.Element;
99
+ export type InboxMoreProps = HTMLAttributes<HTMLDivElement>;
100
+ /**
101
+ * The end of the list: the quiet toggles that open what is not in it by
102
+ * default, the snoozed rows and the earlier ones. Two words at the bottom
103
+ * rather than a filter bar at the top, because a reader opens them rarely
104
+ * and a filter bar is read every time.
105
+ */
106
+ export declare function InboxMore({ className, children, ...rest }: InboxMoreProps): import("react").JSX.Element;
107
+ export interface InboxToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {
108
+ /** Whether the rows it opens are shown. */
109
+ expanded: boolean;
110
+ }
111
+ /**
112
+ * One of those toggles: a word in the quiet ink that opens rows under it.
113
+ * Its own `aria-expanded` says whether they are open, and the ink steps up to
114
+ * the primary while they are.
115
+ */
116
+ export declare function InboxToggle({ expanded, className, children, ...rest }: InboxToggleProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './InboxList.css';
2
+ export { InboxList, InboxGroup, InboxRow, InboxMore, InboxToggle } from './InboxList.js';
3
+ export type { InboxListProps, InboxGroupProps, InboxRowProps, InboxRowTone, InboxMoreProps, InboxToggleProps, } from './InboxList.js';
@@ -0,0 +1,5 @@
1
+ export { InboxGroup, InboxList, InboxMore, InboxRow, InboxToggle } from '../chunk-XYCADWZN.js';
2
+ import '../chunk-RK4XPHVG.js';
3
+ import '../chunk-PQADV6P6.js';
4
+ import '../chunk-BZFWDIV6.js';
5
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,252 @@
1
+ /*
2
+ * The inbox's column: groups of rows, each row a control with the task it is
3
+ * about under it, and the quiet toggles at the end.
4
+ *
5
+ * EVERY TRACK IS minmax(0, 1fr). An `auto` track grows to its widest content,
6
+ * and a row holding a long unbroken word pushed the whole column past its
7
+ * edge, cutting every row off at the right.
8
+ */
9
+
10
+ .crewlet-inbox-list {
11
+ display: grid;
12
+ grid-template-columns: minmax(0, 1fr);
13
+ align-content: start;
14
+ gap: 2px;
15
+ min-width: 0;
16
+ font-size: var(--font-size-compact);
17
+ line-height: var(--font-line-height-body);
18
+ }
19
+
20
+ .crewlet-inbox-list__group {
21
+ display: grid;
22
+ grid-template-columns: minmax(0, 1fr);
23
+ gap: 2px;
24
+ min-width: 0;
25
+ }
26
+
27
+ /* The heading's own line box is the label's, so the label sits where it is drawn. */
28
+ .crewlet-inbox-list__label {
29
+ margin: 0;
30
+ padding: var(--spacing-3) var(--spacing-2) var(--spacing-1);
31
+ font: inherit;
32
+ font-size: var(--font-size-2xs);
33
+ line-height: var(--font-line-height-snug);
34
+ }
35
+
36
+ .crewlet-inbox-list__rows {
37
+ display: grid;
38
+ grid-template-columns: minmax(0, 1fr);
39
+ gap: 2px;
40
+ margin: 0;
41
+ padding: 0;
42
+ list-style: none;
43
+ }
44
+
45
+ /* ---- The row ------------------------------------------------------------ */
46
+
47
+ /*
48
+ * The ITEM takes the hover and the selected fill, so the control and the task
49
+ * under it read as one row. The selected fill is the neutral pressed step, and
50
+ * a selected row takes no hover on top of it: both are a background on the
51
+ * same element, and a composite of the two is a colour nothing has measured.
52
+ */
53
+ .crewlet-inbox-row {
54
+ min-width: 0;
55
+ border-radius: var(--radius-md);
56
+ transition: background var(--motion-duration-fast) var(--motion-easing-default);
57
+ }
58
+
59
+ .crewlet-inbox-row:hover {
60
+ background: var(--color-surface-hover);
61
+ }
62
+
63
+ .crewlet-inbox-row--selected,
64
+ .crewlet-inbox-row--selected:hover {
65
+ background: var(--color-surface-pressed);
66
+ }
67
+
68
+ /*
69
+ * FORCED COLORS repaints every author background to Canvas, which takes the
70
+ * open row's fill with it, so the row the pane is showing would look like
71
+ * every other. An outline is the one mark the mode keeps, drawn inside the row
72
+ * so the scroller does not clip it, in the system's colour for exactly this.
73
+ */
74
+ @media (forced-colors: active) {
75
+ .crewlet-inbox-row--selected {
76
+ outline: 2px solid Highlight;
77
+ outline-offset: -2px;
78
+ }
79
+ }
80
+
81
+ .crewlet-inbox-row__control {
82
+ display: flex;
83
+ align-items: flex-start;
84
+ gap: calc(var(--spacing-2) + var(--spacing-1) / 2);
85
+ width: 100%;
86
+ min-width: 0;
87
+ padding: calc(var(--spacing-2) + var(--spacing-1) / 2) var(--spacing-2) var(--spacing-1);
88
+ border: 0;
89
+ border-radius: var(--radius-md);
90
+ background: none;
91
+ color: inherit;
92
+ font: inherit;
93
+ text-align: left;
94
+ text-decoration: none;
95
+ cursor: pointer;
96
+ }
97
+
98
+ /* With no task under it, the control closes the row at the same padding it opened with. */
99
+ .crewlet-inbox-row__control:only-child {
100
+ padding-bottom: calc(var(--spacing-2) + var(--spacing-1) / 2);
101
+ }
102
+
103
+ /*
104
+ * The ring is drawn INSIDE: the list sits in a clipping scroller, where an
105
+ * outset ring is cut off on the first and last rows and painted over by the
106
+ * row below.
107
+ */
108
+ .crewlet-inbox-row__control:focus-visible {
109
+ outline: 2px solid var(--color-focus);
110
+ outline-offset: var(--size-focus-ring-inset-offset);
111
+ }
112
+
113
+ .crewlet-inbox-row__leading {
114
+ display: inline-flex;
115
+ flex: none;
116
+ justify-content: center;
117
+ width: var(--size-target-min);
118
+ margin-top: 1px;
119
+ }
120
+
121
+ .crewlet-inbox-row__main {
122
+ display: grid;
123
+ flex: 1;
124
+ gap: 2px;
125
+ min-width: 0;
126
+ }
127
+
128
+ .crewlet-inbox-row__head {
129
+ display: flex;
130
+ align-items: baseline;
131
+ gap: var(--spacing-1);
132
+ min-width: 0;
133
+ }
134
+
135
+ .crewlet-inbox-row__title {
136
+ min-width: 0;
137
+ overflow: hidden;
138
+ text-overflow: ellipsis;
139
+ white-space: nowrap;
140
+ font-weight: var(--font-weight-medium);
141
+ color: var(--color-text-primary);
142
+ }
143
+
144
+ .crewlet-inbox-row__qualifier {
145
+ flex: none;
146
+ font-size: var(--font-size-xs);
147
+ color: var(--color-text-tertiary);
148
+ }
149
+
150
+ .crewlet-inbox-row__qualifier--danger {
151
+ color: var(--color-feedback-danger-ink);
152
+ }
153
+
154
+ .crewlet-inbox-row__meta {
155
+ flex: none;
156
+ margin-inline-start: auto;
157
+ font-size: var(--font-size-xs);
158
+ font-variant-numeric: tabular-nums;
159
+ white-space: nowrap;
160
+ color: var(--color-text-tertiary);
161
+ }
162
+
163
+ .crewlet-inbox-row__text {
164
+ display: -webkit-box;
165
+ -webkit-box-orient: vertical;
166
+ -webkit-line-clamp: 3;
167
+ line-clamp: 3;
168
+ overflow: hidden;
169
+ overflow-wrap: anywhere;
170
+ line-height: var(--font-line-height-snug);
171
+ color: var(--color-text-secondary);
172
+ }
173
+
174
+ .crewlet-inbox-row__excerpt {
175
+ color: var(--color-text-tertiary);
176
+ }
177
+
178
+ /* Unread: a heavier name, and the words in the primary ink. */
179
+ .crewlet-inbox-row__control--unread .crewlet-inbox-row__title {
180
+ font-weight: var(--font-weight-semibold);
181
+ }
182
+
183
+ .crewlet-inbox-row__control--unread .crewlet-inbox-row__text,
184
+ .crewlet-inbox-row__control--actionable .crewlet-inbox-row__text {
185
+ color: var(--color-text-primary);
186
+ }
187
+
188
+ /*
189
+ * Settled. The strike is drawn in the strong hairline rather than the text's
190
+ * own ink, so the words under it stay legible; the state is also read out,
191
+ * because a line through a word says nothing to somebody listening.
192
+ */
193
+ .crewlet-inbox-row__control--done .crewlet-inbox-row__text {
194
+ text-decoration: line-through;
195
+ text-decoration-color: var(--color-border-strong);
196
+ color: var(--color-text-tertiary);
197
+ }
198
+
199
+ /*
200
+ * The task, under the words and lined up with them: the control's start pad,
201
+ * the leading slot and the gap after it.
202
+ */
203
+ .crewlet-inbox-row__aside {
204
+ min-width: 0;
205
+ padding:
206
+ 0 var(--spacing-2) calc(var(--spacing-2) + var(--spacing-1) / 2)
207
+ calc(var(--spacing-2) + var(--size-target-min) + var(--spacing-2) + var(--spacing-1) / 2);
208
+ overflow-wrap: anywhere;
209
+ font-size: var(--font-size-xs);
210
+ line-height: var(--font-line-height-snug);
211
+ }
212
+
213
+ /* ---- The end of the list ---------------------------------------------- */
214
+
215
+ .crewlet-inbox-list__more {
216
+ display: flex;
217
+ flex-wrap: wrap;
218
+ gap: calc(var(--spacing-4) + var(--spacing-1) / 2);
219
+ padding: calc(var(--spacing-3) + var(--spacing-1) / 2) var(--spacing-2) 0;
220
+ }
221
+
222
+ .crewlet-inbox-list__toggle {
223
+ padding: 0;
224
+ border: 0;
225
+ border-radius: var(--radius-xs);
226
+ background: none;
227
+ font: inherit;
228
+ font-size: var(--font-size-xs);
229
+ color: var(--color-text-tertiary);
230
+ cursor: pointer;
231
+ transition: color var(--motion-duration-fast) var(--motion-easing-default);
232
+ }
233
+
234
+ .crewlet-inbox-list__toggle:hover,
235
+ .crewlet-inbox-list__toggle[aria-expanded='true'] {
236
+ color: var(--color-text-primary);
237
+ }
238
+
239
+ .crewlet-inbox-list__toggle:focus-visible {
240
+ outline: 2px solid var(--color-focus);
241
+ outline-offset: 2px;
242
+ }
243
+
244
+ @media (prefers-reduced-motion: reduce) {
245
+ .crewlet-inbox-row {
246
+ transition: none;
247
+ }
248
+
249
+ .crewlet-inbox-list__toggle {
250
+ transition: none;
251
+ }
252
+ }
@@ -0,0 +1,21 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ export interface KeyChipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
3
+ /** The key as the system mints it: `FAIR`, `ENG`, `SPLIT`. */
4
+ children: string;
5
+ /**
6
+ * What a search typed. The letters it matched are drawn bold: the start of
7
+ * the key first, then anywhere in it.
8
+ */
9
+ highlight?: string | undefined;
10
+ }
11
+ /**
12
+ * A project's key, drawn as the small mono chip every surface draws it as: a
13
+ * row in the rail, a crumb, a row in a switcher, a cell in a directory.
14
+ *
15
+ * ONE CHIP. It was drawn by hand twice, by the rail's row and by the
16
+ * dashboard's own stylesheet, with two radii and two paddings for one fact.
17
+ *
18
+ * It is text, not a control, and carries no role: the key is read as part of
19
+ * whatever it labels, so a row reads "FAIR Product and Engineering".
20
+ */
21
+ export declare function KeyChip({ children, highlight, className, ...rest }: KeyChipProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './KeyChip.css';
2
+ export { KeyChip } from './KeyChip.js';
3
+ export type { KeyChipProps } from './KeyChip.js';
@@ -0,0 +1,2 @@
1
+ export { KeyChip } from '../chunk-FYTIJM2T.js';
2
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,30 @@
1
+ /*
2
+ * A project's key as a chip: the raised rung inside the plain hairline, in the
3
+ * mono face at the micro step and the secondary ink.
4
+ *
5
+ * The rail's suite measures this ink on this fill against every ground a rail
6
+ * row can have (SidebarNav.test.tsx), because the rail is where the chip sits
7
+ * on the most grounds: the frame, a pointed row and the current row.
8
+ */
9
+ .crewlet-key-chip {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ flex: none;
13
+ padding: 0 calc(var(--spacing-1) + 1px);
14
+ border: 1px solid var(--color-border-default);
15
+ border-radius: var(--radius-xs);
16
+ background: var(--color-surface-elevated);
17
+ color: var(--color-text-secondary);
18
+ font-family: var(--font-family-mono);
19
+ font-size: var(--font-size-2xs);
20
+ font-weight: var(--font-weight-medium);
21
+ letter-spacing: var(--font-letter-spacing-normal);
22
+ line-height: var(--font-line-height-body);
23
+ white-space: nowrap;
24
+ }
25
+
26
+ /* The letters a search matched: the weight carries it, in the primary ink. */
27
+ .crewlet-key-chip__match {
28
+ font-weight: var(--font-weight-bold);
29
+ color: var(--color-text-primary);
30
+ }
@@ -3,8 +3,16 @@ export type LinkVariant =
3
3
  /** A navigation: the accent ink, underlined on hover. */
4
4
  'default'
5
5
  /** An identity a reader clicks through to: neutral ink, underlined on hover. */
6
- | 'subtle';
7
- export type LinkSize = 'body' | 'caption';
6
+ | 'subtle'
7
+ /**
8
+ * A reference set among other words, the task a question is about, say: a
9
+ * softer ink than a navigation, never underlined, stepping up to the full
10
+ * accent ink on hover.
11
+ */
12
+ | 'quiet';
13
+ export type LinkSize = 'body' | 'caption'
14
+ /** The size of the words around it, for a link set inside a sentence. */
15
+ | 'inherit';
8
16
  export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
9
17
  variant?: LinkVariant | undefined;
10
18
  size?: LinkSize | undefined;
@@ -41,5 +49,12 @@ export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
41
49
  * instruction, a seat's name in a row, say. It is still underlined on hover
42
50
  * and still takes the focus ring, because a link a reader cannot find is not
43
51
  * quieter, it is hidden.
52
+ *
53
+ * `quiet` is for a reference that sits in a column of rows or inside a
54
+ * sentence, where every row drawing a full accent link would turn the column
55
+ * into a column of links. It keeps an ink of its own, measured to 4.5:1 on
56
+ * every surface and pressed row, so it is still found by colour and by its
57
+ * place, and it keeps the focus ring. It draws no underline even on hover:
58
+ * the ink stepping up to the accent is the signal that it is live.
44
59
  */
45
60
  export declare const Link: import("react").ForwardRefExoticComponent<LinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
@@ -1,3 +1,3 @@
1
- export { Link } from '../chunk-ZPSOF6VL.js';
1
+ export { Link } from '../chunk-FBSEJO2C.js';
2
2
  import '../chunk-BZFWDIV6.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -1,5 +1,6 @@
1
1
  /*
2
- * A link, in the two registers a console needs: a navigation and an identity.
2
+ * A link, in the three registers a console needs: a navigation, an identity
3
+ * and a quiet reference.
3
4
  *
4
5
  * The ink is the accent's INK step, not the accent fill. The fill is a mark
5
6
  * and clears 3:1; text has to clear 4.5:1, and at caption size it is exactly
@@ -32,10 +33,44 @@
32
33
  color: var(--color-brand-accent-ink);
33
34
  }
34
35
 
36
+ /*
37
+ * A reference among other words. Its own ink sits between the accent ink and
38
+ * the secondary text, so a column of rows that each name a task does not read
39
+ * as a column of links, and the hover steps it up to the full accent ink with
40
+ * no underline. The ink is a token rather than a mix of two, because a mix is
41
+ * a colour nobody measured; this one clears 4.5:1 on every surface and on a
42
+ * pressed row.
43
+ */
44
+ .crewlet-link--quiet {
45
+ color: var(--color-text-link-quiet);
46
+ }
47
+
48
+ .crewlet-link--quiet:hover {
49
+ color: var(--color-brand-accent-ink);
50
+ text-decoration: none;
51
+ }
52
+
53
+ /*
54
+ * FORCED COLORS paints every link in LinkText whatever its state, so the step
55
+ * up to the accent ink cannot be seen there, and a hover with no underline is
56
+ * no hover at all. The rule under the words is the one mark the mode leaves to
57
+ * the author, so it is where the quiet variant's hover goes in that mode.
58
+ */
59
+ @media (forced-colors: active) {
60
+ .crewlet-link--quiet:hover {
61
+ text-decoration: underline;
62
+ }
63
+ }
64
+
35
65
  .crewlet-link--body {
36
66
  font-size: var(--font-size-sm);
37
67
  }
38
68
 
69
+ /* Set inside a sentence: the sentence's own size, whatever it is. */
70
+ .crewlet-link--inherit {
71
+ font-size: inherit;
72
+ }
73
+
39
74
  /*
40
75
  * A caption-sized link takes the caption's SIZE and keeps the link's ink. It
41
76
  * is medium weight and does not wrap, because the thing it names (an event id,
@@ -1,9 +1,9 @@
1
- export { ListInput } from '../chunk-GARACZAH.js';
1
+ export { ListInput } from '../chunk-LZP7FGT7.js';
2
2
  import '../chunk-SMBKXWML.js';
3
3
  import '../chunk-FTEJPHBI.js';
4
4
  import '../chunk-PQADV6P6.js';
5
- import '../chunk-RSAQOKH3.js';
6
5
  import '../chunk-4FQT6ZAY.js';
6
+ import '../chunk-RSAQOKH3.js';
7
7
  import '../chunk-7RPGY3JE.js';
8
8
  import '../chunk-R25RGPUB.js';
9
9
  import '../chunk-BZFWDIV6.js';
@@ -3,3 +3,5 @@ export { useListbox } from './useListbox.js';
3
3
  export type { Listbox, ListboxOptions } from './useListbox.js';
4
4
  export { TYPE_AHEAD_WINDOW, useOptionKeys } from './useOptionKeys.js';
5
5
  export type { OptionKeys, OptionKeysOptions } from './useOptionKeys.js';
6
+ export { HIGHLIGHT_REST_MS, useHighlightRest } from './useHighlightRest.js';
7
+ export type { HighlightRest } from './useHighlightRest.js';
@@ -1,2 +1,2 @@
1
- export { TYPE_AHEAD_WINDOW, useListbox, useOptionKeys } from '../chunk-6MU7HNB4.js';
1
+ export { HIGHLIGHT_REST_MS, TYPE_AHEAD_WINDOW, useHighlightRest, useListbox, useOptionKeys } from '../chunk-F73MC7EZ.js';
2
2
  import '../chunk-R25RGPUB.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Where a list's highlight COMES TO REST, told once per resting place.
3
+ *
4
+ * WHY IT EXISTS. A list that previews what its highlight is on (a theme the
5
+ * whole screen repaints in while the reader decides) has to know where the
6
+ * reader STOPPED, not every row they passed. A key moves the highlight on
7
+ * purpose, so where a key lands is told at once. A pointer crosses rows on
8
+ * its way somewhere else, so a row it passes over is told only once the
9
+ * pointer has rested on it for `HIGHLIGHT_REST_MS`; told on every row it
10
+ * crossed, a list over a screen that repaints would flash every theme between
11
+ * the pointer's start and its goal.
12
+ *
13
+ * THE RULES IT KEEPS.
14
+ *
15
+ * - A RESTING PLACE IS TOLD ONCE. Telling the same row twice is a second
16
+ * repaint of a screen already showing it, so a re-render, or a list handed
17
+ * new option objects for the same rows, says nothing.
18
+ * - A KEY CANCELS A POINTER STILL ON ITS WAY. Where the arrows put the
19
+ * highlight wins over a row the pointer had not yet rested on.
20
+ * - `null` IS NOWHERE, and it is told when the list stops offering anything:
21
+ * it closed, it emptied, the pointer left it, or the component went away.
22
+ * A preview never outlives the list that started it.
23
+ *
24
+ * It owns no markup and no highlight: the component keeps its own, and hands
25
+ * this the KEY of each row the highlight reaches (a value, an id).
26
+ */
27
+ /** How long a pointer rests on a row before the row is where the highlight rests, in milliseconds. */
28
+ export declare const HIGHLIGHT_REST_MS = 120;
29
+ export interface HighlightRest {
30
+ /** The highlight moved by a key or by the list itself, or left the list (`null`): told now. */
31
+ now: (key: string | null) => void;
32
+ /** The pointer came onto a row: told once it rests there. */
33
+ rest: (key: string) => void;
34
+ }
35
+ export declare function useHighlightRest(onRest: ((key: string | null) => void) | undefined): HighlightRest;