@crewlethq/ui 0.11.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 (165) hide show
  1. package/README.md +76 -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/ChoiceList/ChoiceList.d.ts +45 -0
  15. package/dist/ChoiceList/index.d.ts +3 -0
  16. package/dist/ChoiceList/index.js +2 -0
  17. package/dist/ChoiceList-2PQJS4DX.css +109 -0
  18. package/dist/Combobox/index.js +2 -2
  19. package/dist/CommandPalette/CommandPalette.d.ts +48 -1
  20. package/dist/CommandPalette/index.js +3 -3
  21. package/dist/{CommandPalette-C5AMKGN2.css → CommandPalette-HBTNKHZQ.css} +55 -2
  22. package/dist/Composer/Composer.d.ts +27 -0
  23. package/dist/Composer/index.d.ts +3 -0
  24. package/dist/Composer/index.js +7 -0
  25. package/dist/Composer-ABGJIAIR.css +88 -0
  26. package/dist/DataTable/index.js +3 -3
  27. package/dist/DataView/index.js +11 -11
  28. package/dist/DateTimePicker/index.js +2 -2
  29. package/dist/Document/Document.d.ts +56 -0
  30. package/dist/Document/index.d.ts +3 -0
  31. package/dist/Document/index.js +2 -0
  32. package/dist/Document-PQGXJHB7.css +288 -0
  33. package/dist/EntityChip/index.js +1 -1
  34. package/dist/Highlight/Highlight.d.ts +45 -0
  35. package/dist/Highlight/index.d.ts +3 -0
  36. package/dist/Highlight/index.js +2 -0
  37. package/dist/Highlight-DXLV2R6L.css +29 -0
  38. package/dist/HuePicker/index.js +3 -3
  39. package/dist/ImageUpload/index.js +2 -2
  40. package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
  41. package/dist/InboxList/InboxList.d.ts +116 -0
  42. package/dist/InboxList/index.d.ts +3 -0
  43. package/dist/InboxList/index.js +5 -0
  44. package/dist/InboxList-YMFWKGGS.css +252 -0
  45. package/dist/KeyChip/KeyChip.d.ts +21 -0
  46. package/dist/KeyChip/index.d.ts +3 -0
  47. package/dist/KeyChip/index.js +2 -0
  48. package/dist/KeyChip-4XJU4BBE.css +30 -0
  49. package/dist/Link/Link.d.ts +17 -2
  50. package/dist/Link/index.js +1 -1
  51. package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
  52. package/dist/Listbox/index.d.ts +2 -0
  53. package/dist/Listbox/index.js +1 -1
  54. package/dist/Listbox/useHighlightRest.d.ts +35 -0
  55. package/dist/MarkdownEditor/MarkdownEditor.d.ts +93 -0
  56. package/dist/MarkdownEditor/format.d.ts +34 -0
  57. package/dist/MarkdownEditor/index.d.ts +5 -0
  58. package/dist/MarkdownEditor/index.js +12 -0
  59. package/dist/MarkdownEditor-ZLSHTOIZ.css +146 -0
  60. package/dist/Modal/Modal.d.ts +5 -1
  61. package/dist/Modal/index.js +1 -1
  62. package/dist/{Modal-BNT3TDV2.css → Modal-3YJ2GG2T.css} +33 -0
  63. package/dist/OrgLabel/index.js +1 -1
  64. package/dist/OrgNode/index.js +1 -1
  65. package/dist/OrgTable/index.js +5 -5
  66. package/dist/Outline/Outline.d.ts +72 -0
  67. package/dist/Outline/index.d.ts +3 -0
  68. package/dist/Outline/index.js +2 -0
  69. package/dist/Outline-VCXKKU3E.css +78 -0
  70. package/dist/Popover/Popover.d.ts +21 -1
  71. package/dist/Popover/index.js +1 -1
  72. package/dist/ReadingPane/ReadingPane.d.ts +95 -0
  73. package/dist/ReadingPane/index.d.ts +3 -0
  74. package/dist/ReadingPane/index.js +4 -0
  75. package/dist/ReadingPane-5UOLPWY5.css +194 -0
  76. package/dist/SegmentedControl/SegmentedControl.d.ts +9 -1
  77. package/dist/SegmentedControl/index.js +3 -3
  78. package/dist/{SegmentedControl-76HODRBT.css → SegmentedControl-PKBLJVSA.css} +18 -0
  79. package/dist/Select/Select.d.ts +22 -0
  80. package/dist/Select/index.js +2 -2
  81. package/dist/{Select-4ANVLHUY.css → Select-XMZ44RWY.css} +24 -0
  82. package/dist/SidebarNav/index.js +2 -1
  83. package/dist/{SidebarNav-X5NLZNWA.css → SidebarNav-KF66MLOM.css} +4 -19
  84. package/dist/Switcher/Switcher.d.ts +151 -0
  85. package/dist/Switcher/index.d.ts +3 -0
  86. package/dist/Switcher/index.js +16 -0
  87. package/dist/Switcher-B44Q4WF4.css +346 -0
  88. package/dist/Tabs/Tabs.d.ts +7 -1
  89. package/dist/Tabs/index.js +1 -1
  90. package/dist/{Tabs-5I5Q5KKM.css → Tabs-ITJYJFMU.css} +49 -0
  91. package/dist/TagsInput/index.js +2 -2
  92. package/dist/ThemeGallery/ThemeGallery.d.ts +65 -0
  93. package/dist/ThemeGallery/index.d.ts +3 -0
  94. package/dist/ThemeGallery/index.js +6 -0
  95. package/dist/ThemeGallery-NITASULQ.css +93 -0
  96. package/dist/ThemeSwatch/ThemeSwatch.d.ts +71 -0
  97. package/dist/ThemeSwatch/index.d.ts +3 -0
  98. package/dist/ThemeSwatch/index.js +2 -0
  99. package/dist/ThemeSwatch-4DL457ZB.css +256 -0
  100. package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +5 -1
  101. package/dist/ThemeSwitcher/index.js +4 -4
  102. package/dist/ThemeToggle/ThemeToggle.d.ts +1 -1
  103. package/dist/ThemeToggle/index.js +1 -1
  104. package/dist/TimeWindowPicker/index.js +4 -4
  105. package/dist/Toaster/index.js +1 -1
  106. package/dist/{Toaster-NVDCEIP5.css → Toaster-KZ7YE2HA.css} +1 -0
  107. package/dist/Tree/index.d.ts +1 -1
  108. package/dist/Tree/index.js +1 -1
  109. package/dist/Tree/useTreeState.d.ts +37 -7
  110. package/dist/TreeCanvas/index.js +2 -2
  111. package/dist/TreeGrid/TreeGrid.d.ts +22 -2
  112. package/dist/TreeGrid/index.js +2 -2
  113. package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
  114. package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
  115. package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
  116. package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
  117. package/dist/{chunk-ELZKOVEC.js → chunk-4Y7WKXAW.js} +1 -1
  118. package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
  119. package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
  120. package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
  121. package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
  122. package/dist/chunk-77IEHFY4.js +299 -0
  123. package/dist/chunk-A5YKNRG6.js +94 -0
  124. package/dist/{chunk-HLBRPES3.js → chunk-B2CITO6R.js} +2 -2
  125. package/dist/{chunk-TEEJSTFG.js → chunk-CF6DTETT.js} +1 -1
  126. package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
  127. package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
  128. package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
  129. package/dist/chunk-FYTIJM2T.js +40 -0
  130. package/dist/{chunk-7JGWSOLM.js → chunk-GG2QEZO2.js} +1 -1
  131. package/dist/chunk-H6VKCIWV.js +41 -0
  132. package/dist/{chunk-6SDGRKQ7.js → chunk-HKGS67VG.js} +94 -41
  133. package/dist/chunk-IG75YMUN.js +22 -0
  134. package/dist/{chunk-JCNUQ3US.js → chunk-JDPAA2JU.js} +1 -1
  135. package/dist/chunk-KLIEPFEZ.js +95 -0
  136. package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
  137. package/dist/{chunk-UG5ZMAXW.js → chunk-LELD5KPA.js} +1 -1
  138. package/dist/chunk-MNFU2RNT.js +74 -0
  139. package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
  140. package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
  141. package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
  142. package/dist/chunk-QBXZLGK2.js +40 -0
  143. package/dist/chunk-QSLIN64M.js +47 -0
  144. package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
  145. package/dist/chunk-S3IENQ3E.js +336 -0
  146. package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
  147. package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
  148. package/dist/{chunk-CIOKUMVC.js → chunk-UZPHPW3Z.js} +7 -7
  149. package/dist/chunk-VWNC2L6W.js +110 -0
  150. package/dist/chunk-VYY56AMX.js +2 -0
  151. package/dist/chunk-WI6K5M5X.js +24 -0
  152. package/dist/{chunk-R4ZJB5YX.js → chunk-WY2NLRZN.js} +2 -2
  153. package/dist/{chunk-SEFMO2EV.js → chunk-XFE5W2I4.js} +44 -5
  154. package/dist/chunk-XYCADWZN.js +100 -0
  155. package/dist/chunk-YSJC7AZC.js +62 -0
  156. package/dist/chunk-ZAJHGDAW.js +93 -0
  157. package/dist/generated/index.d.ts +14 -0
  158. package/dist/generated/index.js +65 -50
  159. package/dist/index.js +65 -50
  160. package/dist/styles.css +4456 -1965
  161. package/dist/utils/highlight.d.ts +17 -0
  162. package/dist/utils/useMediaMatch.d.ts +12 -0
  163. package/package.json +3 -3
  164. package/dist/chunk-UAQIUSSK.js +0 -2
  165. /package/dist/{chunk-PQLMLG5K.js → chunk-6AR2T5Y4.js} +0 -0
@@ -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,
@@ -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;
@@ -0,0 +1,93 @@
1
+ import { type ReactNode, type Ref } from 'react';
2
+ import { type MarkdownFormat } from './format.js';
3
+ export type MarkdownEditorMode = 'write' | 'preview';
4
+ /** Every string the editor draws on its own. */
5
+ export interface MarkdownEditorLabels {
6
+ /** The formatting bar's name. */
7
+ toolbar: string;
8
+ /** The Write and Preview switch's name. */
9
+ view: string;
10
+ write: string;
11
+ preview: string;
12
+ /** What Preview shows for an empty body. */
13
+ empty: string;
14
+ /** Each control's name, which is also its tooltip. */
15
+ formats: Record<MarkdownFormat, string>;
16
+ }
17
+ /** The strings a caller may change; each format's name can be changed alone. */
18
+ export type MarkdownEditorLabelOverrides = Partial<Omit<MarkdownEditorLabels, 'formats'>> & {
19
+ formats?: Partial<MarkdownEditorLabels['formats']> | undefined;
20
+ };
21
+ /** What a control the caller adds to the bar may do to the editor. */
22
+ export interface MarkdownEditorApi {
23
+ /**
24
+ * Which view is showing. A control that edits the source is disabled in
25
+ * Preview, as the bar's own controls are, because there is no caret there.
26
+ */
27
+ readonly mode: MarkdownEditorMode;
28
+ /** Replaces the selection with `text` and leaves the caret after it, as a paste would. */
29
+ insert(text: string): void;
30
+ /** Applies a format, exactly as the bar's own control for it does. */
31
+ format(format: MarkdownFormat): void;
32
+ /** Moves focus into the field. */
33
+ focus(): void;
34
+ }
35
+ export interface MarkdownEditorProps {
36
+ /** The markdown being edited. */
37
+ value: string;
38
+ onChange: (value: string) => void;
39
+ /** The field's accessible name: "The page, in markdown". */
40
+ label: string;
41
+ /**
42
+ * Draws the preview of `value`. The renderer is the application's, because
43
+ * what a body may contain is a decision about its own content; wrap what it
44
+ * returns in `Document` and the preview is set exactly as the page will be.
45
+ */
46
+ renderPreview: (value: string) => ReactNode;
47
+ placeholder?: string | undefined;
48
+ /** The view, when the caller holds it. Uncontrolled, the editor opens on Write. */
49
+ mode?: MarkdownEditorMode | undefined;
50
+ defaultMode?: MarkdownEditorMode | undefined;
51
+ onModeChange?: ((mode: MarkdownEditorMode) => void) | undefined;
52
+ /**
53
+ * Controls of the caller's own, drawn on the bar after the formatting
54
+ * controls: a "Link a page" picker, say. They are handed the editor, so a
55
+ * picker inserts at the writer's caret rather than at the end.
56
+ */
57
+ actions?: ((editor: MarkdownEditorApi) => ReactNode) | undefined;
58
+ /** The strip under the field: a word count, a size against a limit. */
59
+ footer?: ReactNode;
60
+ /** Command+Enter (Control+Enter elsewhere), the shortcut every composer in the system saves on. */
61
+ onSubmit?: (() => void) | undefined;
62
+ disabled?: boolean | undefined;
63
+ /** Draws the field as refused, for a body over its limit. */
64
+ invalid?: boolean | undefined;
65
+ /** How many lines the field stands at before it scrolls. */
66
+ rows?: number | undefined;
67
+ /** The field's id, for a label or a description elsewhere on the screen. */
68
+ id?: string | undefined;
69
+ 'aria-describedby'?: string | undefined;
70
+ labels?: MarkdownEditorLabelOverrides | undefined;
71
+ ref?: Ref<MarkdownEditorApi> | undefined;
72
+ className?: string | undefined;
73
+ }
74
+ /**
75
+ * Writing markdown: a formatting bar, the source in a monospace field, and a
76
+ * Preview that draws it as the reader will see it.
77
+ *
78
+ * THE SOURCE IS WHAT IS EDITED, never a rich-text model of it. The engine
79
+ * stores markdown and nothing else, so a writer sees exactly what will be
80
+ * saved, a paste of markdown stays markdown, and nothing converts between two
81
+ * formats on the way to a save.
82
+ *
83
+ * THE BAR WRITES MARKDOWN AT THE CARET, the way a person would type it, and
84
+ * every control is a toggle (see format.ts). A press keeps the field's own
85
+ * selection: the control never takes focus from the field, and the field gets
86
+ * focus back with the selection where the format left it, so pressing Bold
87
+ * and carrying on typing types inside the bold.
88
+ *
89
+ * ONE TAB STOP FOR THE BAR. It is a `Toolbar`: Tab reaches it once, the arrows
90
+ * walk its controls, and Write and Preview are the last stop, a tab list whose
91
+ * panel is the field or the preview.
92
+ */
93
+ export declare function MarkdownEditor({ value, onChange, label, renderPreview, placeholder, mode: controlled, defaultMode, onModeChange, actions, footer, onSubmit, disabled, invalid, rows, id, 'aria-describedby': describedBy, labels, ref, className, }: MarkdownEditorProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * What a formatting control does to markdown source, as pure functions over a
3
+ * value and a selection.
4
+ *
5
+ * WHY PURE. A formatting bar that edits a textarea by hand is a bar whose
6
+ * rules can only be tested by driving a browser, and the rules are where it
7
+ * goes wrong: a bold applied to " word " that CommonMark refuses to read as
8
+ * bold, a list applied twice that nests a marker inside a marker, a heading
9
+ * that stacks `## ### `. Each control here answers the new value and where
10
+ * the selection lands, and the component only applies the answer.
11
+ *
12
+ * EVERY CONTROL IS A TOGGLE. Pressed on text that already carries its format,
13
+ * it takes the format off, which is what a writer expects of a bar and what
14
+ * keeps a second press from writing `****word****`.
15
+ */
16
+ /** The formats the bar offers, in the order it draws them. */
17
+ export declare const MARKDOWN_FORMATS: readonly ["heading2", "heading3", "bold", "italic", "code", "bulletList", "numberedList", "checklist", "table"];
18
+ export type MarkdownFormat = (typeof MARKDOWN_FORMATS)[number];
19
+ /** A selection in the source, as the textarea reports it. `start === end` is a caret. */
20
+ export interface MarkdownSelection {
21
+ start: number;
22
+ end: number;
23
+ }
24
+ /** What a control answers: the new source, and the selection to leave in it. */
25
+ export interface MarkdownEdit {
26
+ value: string;
27
+ selection: MarkdownSelection;
28
+ }
29
+ /** Replaces the selection with `text` and leaves the caret after it. */
30
+ export declare function insertMarkdown(value: string, { start, end }: MarkdownSelection, text: string): MarkdownEdit;
31
+ /** The format applied to the selection, or taken off it when it is already there. */
32
+ export declare function formatMarkdown(value: string, selection: MarkdownSelection, format: MarkdownFormat): MarkdownEdit;
33
+ /** How many words a body holds, counted the way a writer would. */
34
+ export declare function markdownWords(value: string): number;
@@ -0,0 +1,5 @@
1
+ import './MarkdownEditor.css';
2
+ export { MarkdownEditor } from './MarkdownEditor.js';
3
+ export type { MarkdownEditorApi, MarkdownEditorLabelOverrides, MarkdownEditorLabels, MarkdownEditorMode, MarkdownEditorProps, } from './MarkdownEditor.js';
4
+ export { MARKDOWN_FORMATS, formatMarkdown, insertMarkdown, markdownWords } from './format.js';
5
+ export type { MarkdownEdit, MarkdownFormat, MarkdownSelection } from './format.js';
@@ -0,0 +1,12 @@
1
+ export { MARKDOWN_FORMATS, MarkdownEditor, formatMarkdown, insertMarkdown, markdownWords } from '../chunk-77IEHFY4.js';
2
+ import '../chunk-VYY56AMX.js';
3
+ import '../chunk-PGRPNDWM.js';
4
+ import '../chunk-4FQT6ZAY.js';
5
+ import '../chunk-26PEPONN.js';
6
+ import '../chunk-4LDTQWS7.js';
7
+ import '../chunk-CBOICT64.js';
8
+ import '../chunk-BHHIIK3M.js';
9
+ import '../chunk-7RPGY3JE.js';
10
+ import '../chunk-R25RGPUB.js';
11
+ import '../chunk-BZFWDIV6.js';
12
+ import '../chunk-ODEQTPFM.js';