@crewlethq/ui 0.12.0 → 0.14.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 +82 -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 +5 -5
  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/{DateTimePicker-4AZBTODT.css → DateTimePicker-NJ6XH2TE.css} +4 -3
  22. package/dist/Document/Document.d.ts +56 -0
  23. package/dist/Document/index.d.ts +3 -0
  24. package/dist/Document/index.js +2 -0
  25. package/dist/Document-PQGXJHB7.css +288 -0
  26. package/dist/EntityChip/index.js +1 -1
  27. package/dist/Highlight/Highlight.d.ts +45 -0
  28. package/dist/Highlight/index.d.ts +3 -0
  29. package/dist/Highlight/index.js +2 -0
  30. package/dist/Highlight-DXLV2R6L.css +29 -0
  31. package/dist/HuePicker/index.js +3 -3
  32. package/dist/ImageUpload/index.js +2 -2
  33. package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
  34. package/dist/InboxList/InboxList.d.ts +116 -0
  35. package/dist/InboxList/index.d.ts +3 -0
  36. package/dist/InboxList/index.js +5 -0
  37. package/dist/InboxList-YMFWKGGS.css +252 -0
  38. package/dist/KeyChip/KeyChip.d.ts +21 -0
  39. package/dist/KeyChip/index.d.ts +3 -0
  40. package/dist/KeyChip/index.js +2 -0
  41. package/dist/KeyChip-4XJU4BBE.css +30 -0
  42. package/dist/Link/Link.d.ts +17 -2
  43. package/dist/Link/index.js +1 -1
  44. package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
  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-RQAYGG4L.css} +34 -1
  56. package/dist/OrgLabel/index.js +1 -1
  57. package/dist/OrgNode/index.js +1 -1
  58. package/dist/OrgTable/index.js +4 -4
  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-2P7AWUE4.css} +56 -6
  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 +2 -2
  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/{TimeWindowPicker-GBF4JDFI.css → TimeWindowPicker-BZ5HHATU.css} +5 -4
  99. package/dist/Toaster/index.js +1 -1
  100. package/dist/{Toaster-NVDCEIP5.css → Toaster-VZKISP2S.css} +81 -87
  101. package/dist/Tree/index.d.ts +1 -1
  102. package/dist/Tree/index.js +1 -1
  103. package/dist/Tree/useTreeState.d.ts +37 -7
  104. package/dist/TreeCanvas/index.js +2 -2
  105. package/dist/TreeGrid/TreeGrid.d.ts +22 -2
  106. package/dist/TreeGrid/index.js +2 -2
  107. package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
  108. package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
  109. package/dist/{chunk-GOUKOXJK.js → chunk-3VVKJ7AZ.js} +3 -3
  110. package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
  111. package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
  112. package/dist/{chunk-GXYNEDNA.js → chunk-4Y7WKXAW.js} +1 -1
  113. package/dist/{chunk-IINFVK3W.js → chunk-5IXMLWGW.js} +6 -6
  114. package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
  115. package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
  116. package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
  117. package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
  118. package/dist/chunk-77IEHFY4.js +299 -0
  119. package/dist/chunk-A5YKNRG6.js +94 -0
  120. package/dist/{chunk-JCNUQ3US.js → chunk-AXNQCAFC.js} +2 -2
  121. package/dist/{chunk-UG5ZMAXW.js → chunk-CBZSSKH3.js} +5 -5
  122. package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
  123. package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
  124. package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
  125. package/dist/chunk-FYTIJM2T.js +40 -0
  126. package/dist/chunk-H6VKCIWV.js +41 -0
  127. package/dist/{chunk-HLBRPES3.js → chunk-IFM63LAG.js} +3 -3
  128. package/dist/chunk-IG75YMUN.js +22 -0
  129. package/dist/{chunk-SEFMO2EV.js → chunk-IXUU5DLZ.js} +55 -6
  130. package/dist/{chunk-ZHOM65MI.js → chunk-JCO3SIST.js} +1 -1
  131. package/dist/chunk-KLIEPFEZ.js +95 -0
  132. package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
  133. package/dist/chunk-MNFU2RNT.js +74 -0
  134. package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
  135. package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
  136. package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
  137. package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
  138. package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
  139. package/dist/{chunk-TEEJSTFG.js → chunk-TFMJH2E6.js} +1 -1
  140. package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
  141. package/dist/chunk-VWNC2L6W.js +110 -0
  142. package/dist/chunk-VYY56AMX.js +2 -0
  143. package/dist/chunk-WI6K5M5X.js +24 -0
  144. package/dist/{chunk-XV65NTIT.js → chunk-WYQOHFML.js} +94 -41
  145. package/dist/chunk-XYCADWZN.js +100 -0
  146. package/dist/chunk-Y6NR7QJ3.js +336 -0
  147. package/dist/chunk-YSJC7AZC.js +62 -0
  148. package/dist/chunk-ZAJHGDAW.js +93 -0
  149. package/dist/generated/index.d.ts +12 -0
  150. package/dist/generated/index.js +64 -51
  151. package/dist/index.js +64 -51
  152. package/dist/styles.css +2711 -402
  153. package/dist/utils/highlight.d.ts +17 -0
  154. package/dist/utils/useMediaMatch.d.ts +12 -0
  155. package/package.json +3 -3
  156. package/dist/chunk-UAQIUSSK.js +0 -2
  157. /package/dist/{chunk-74OVQWA5.js → chunk-6AR2T5Y4.js} +0 -0
@@ -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';
@@ -0,0 +1,146 @@
1
+ /*
2
+ * Writing markdown (MarkdownEditor.tsx): the bar, the source or its preview,
3
+ * and the footer, in ONE box.
4
+ *
5
+ * THE BOX TAKES THE RING, and only for the field. It is the package's one ring
6
+ * for a field: the box's own hairline drawn in the focus colour, laid on the
7
+ * boundary (see Input.css). It is asked of the FIELD rather than of
8
+ * :focus-within, so a reader walking the bar's controls sees each control's
9
+ * own ring and not a second one round the whole editor.
10
+ */
11
+
12
+ .crewlet-markdown-editor {
13
+ /* The height the field and the preview stand at, so switching between them
14
+ does not move what is under the editor. */
15
+ --crewlet-markdown-editor-height: calc(
16
+ var(--crewlet-markdown-editor-rows, 14) * var(--font-size-compact) * var(--font-line-height-relaxed) +
17
+ var(--spacing-3) * 2
18
+ );
19
+ display: flex;
20
+ flex-direction: column;
21
+ min-width: 0;
22
+ border: 1px solid var(--color-border-control);
23
+ border-radius: var(--radius-md);
24
+ background: var(--color-surface-subtle);
25
+ transition: border-color var(--motion-duration-fast) var(--motion-easing-default);
26
+ }
27
+
28
+ .crewlet-markdown-editor:hover {
29
+ border-color: var(--color-border-hover);
30
+ }
31
+
32
+ .crewlet-markdown-editor:has(.crewlet-markdown-editor__field:focus-visible) {
33
+ outline: 1px solid var(--color-focus);
34
+ outline-offset: -1px;
35
+ }
36
+
37
+ /* A refused body keeps its colour while the writer is in it: the ring takes
38
+ the danger step, so "here" and "refused" stay one edge. */
39
+ .crewlet-markdown-editor--invalid {
40
+ border-color: var(--color-feedback-danger);
41
+ }
42
+
43
+ .crewlet-markdown-editor--invalid:has(.crewlet-markdown-editor__field:focus-visible) {
44
+ outline-color: var(--color-feedback-danger);
45
+ }
46
+
47
+ .crewlet-markdown-editor--disabled {
48
+ opacity: 0.6;
49
+ }
50
+
51
+ /* The bar sits on the box's top edge, a hairline under it. Two classes, so it
52
+ outranks the toolbar's own padding whichever stylesheet loads first. */
53
+ .crewlet-markdown-editor .crewlet-markdown-editor__bar {
54
+ gap: var(--spacing-1);
55
+ padding: var(--spacing-1) var(--spacing-2);
56
+ border-block-end: 1px solid var(--color-border-default);
57
+ }
58
+
59
+ .crewlet-markdown-editor__group {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ gap: calc(var(--spacing-1) / 2);
63
+ }
64
+
65
+ /* A hairline between groups, so nine controls read as three ideas. */
66
+ .crewlet-markdown-editor__group + .crewlet-markdown-editor__group {
67
+ padding-inline-start: var(--spacing-1);
68
+ border-inline-start: 1px solid var(--color-border-default);
69
+ }
70
+
71
+ .crewlet-markdown-editor__spacer {
72
+ flex: 1;
73
+ }
74
+
75
+ .crewlet-markdown-editor__panel {
76
+ display: flex;
77
+ flex-direction: column;
78
+ min-width: 0;
79
+ }
80
+
81
+ /*
82
+ * THE SOURCE IS SET IN THE MONOSPACE FACE: a writer counts the hashes of a
83
+ * heading and lines up the bars of a table, and a proportional face makes
84
+ * both a guess.
85
+ */
86
+ .crewlet-markdown-editor__field {
87
+ display: block;
88
+ box-sizing: border-box;
89
+ width: 100%;
90
+ min-height: var(--crewlet-markdown-editor-height);
91
+ margin: 0;
92
+ padding: var(--spacing-3) var(--spacing-4);
93
+ border: 0;
94
+ border-radius: 0;
95
+ background: transparent;
96
+ font-family: var(--font-family-mono);
97
+ font-size: var(--font-size-compact);
98
+ line-height: var(--font-line-height-relaxed);
99
+ color: var(--color-text-primary);
100
+ resize: vertical;
101
+ }
102
+
103
+ .crewlet-markdown-editor__field::placeholder {
104
+ color: var(--color-text-tertiary);
105
+ }
106
+
107
+ /* The ring is the box's. A transparent outline stays, because forced colours
108
+ paints an outline where this would otherwise draw none. */
109
+ .crewlet-markdown-editor__field:focus-visible {
110
+ outline: 2px solid transparent;
111
+ outline-offset: -2px;
112
+ }
113
+
114
+ .crewlet-markdown-editor__field:disabled {
115
+ cursor: not-allowed;
116
+ }
117
+
118
+ .crewlet-markdown-editor__preview {
119
+ min-height: var(--crewlet-markdown-editor-height);
120
+ padding: var(--spacing-3) var(--spacing-4);
121
+ min-width: 0;
122
+ }
123
+
124
+ .crewlet-markdown-editor__empty {
125
+ margin: 0;
126
+ font-size: var(--font-size-compact);
127
+ color: var(--color-text-tertiary);
128
+ }
129
+
130
+ .crewlet-markdown-editor__footer {
131
+ display: flex;
132
+ flex-wrap: wrap;
133
+ align-items: center;
134
+ gap: var(--spacing-1) var(--spacing-3);
135
+ padding: var(--spacing-1) var(--spacing-3);
136
+ border-block-start: 1px solid var(--color-border-default);
137
+ font-size: var(--font-size-xs);
138
+ font-variant-numeric: tabular-nums;
139
+ color: var(--color-text-tertiary);
140
+ }
141
+
142
+ @media (prefers-reduced-motion: reduce) {
143
+ .crewlet-markdown-editor {
144
+ transition: none;
145
+ }
146
+ }
@@ -21,8 +21,12 @@ export type ModalVariant = 'dialog' | 'sheet';
21
21
  *
22
22
  * `center` is kept for a surface that is the whole of what the reader is doing
23
23
  * and has nothing above it to stay in touch with, such as a full-bleed viewer.
24
+ *
25
+ * `bottom` is a sheet that rises from the bottom edge, the full width of the
26
+ * window, rounded on its top corners only: a list a phone's thumb reaches,
27
+ * such as a switcher opened on a narrow screen.
24
28
  */
25
- export type ModalPlacement = 'center' | 'top';
29
+ export type ModalPlacement = 'center' | 'top' | 'bottom';
26
30
  /**
27
31
  * How much chrome the surface carries.
28
32
  *
@@ -1,4 +1,4 @@
1
- export { ConfirmModal, Modal } from '../chunk-TEEJSTFG.js';
1
+ export { ConfirmModal, Modal } from '../chunk-TFMJH2E6.js';
2
2
  import '../chunk-OG6CL3EB.js';
3
3
  import '../chunk-PQADV6P6.js';
4
4
  import '../chunk-4FQT6ZAY.js';
@@ -84,6 +84,28 @@
84
84
  padding-top: var(--spacing-10);
85
85
  }
86
86
 
87
+ /*
88
+ * THE BOTTOM SHEET: pinned to the bottom edge with no inset, so it rises from
89
+ * where a thumb already is. Its frame takes the window's width and keeps a
90
+ * corner on its top edge only, and its foot stays clear of a phone's home
91
+ * indicator.
92
+ */
93
+ .crewlet-modal-overlay--dialog.crewlet-modal-overlay--bottom {
94
+ align-items: flex-end;
95
+ padding: 0;
96
+ }
97
+
98
+ .crewlet-modal-overlay--bottom > .crewlet-modal--dialog {
99
+ width: 100%;
100
+ max-height: calc(100dvh - var(--spacing-10));
101
+ border-bottom: none;
102
+ border-radius: var(--radius-xl) var(--radius-xl) 0 0;
103
+ padding-bottom: env(safe-area-inset-bottom, 0px);
104
+ /* It rises further and sooner than a dialog drops in: a sheet arrives from
105
+ the edge it is pinned to, and a thumb is waiting on it. */
106
+ animation: crewlet-modal-lift var(--motion-duration-moderate) var(--motion-easing-emphasized);
107
+ }
108
+
87
109
  /*
88
110
  * The sheet's veil fades over the same span the sheet itself travels, so the
89
111
  * two are one movement rather than a ground that lands and a panel that
@@ -267,7 +289,7 @@
267
289
  * padding is only the least it keeps when a title wraps. It was the 16px step
268
290
  * while the bar was 64px, which a full control step filled exactly; at 52px
269
291
  * the same padding stood the head 12px past the bar behind it. At the 4px step
270
- * the head is the bar's height with the control step at every density, 36.48px
292
+ * the head is the bar's height with the control step at every density, 37.12px
271
293
  * at comfortable included, and with a subtitle under the title.
272
294
  *
273
295
  * It used to be the tighter band, 49px with a close control alone, and the
@@ -479,6 +501,13 @@
479
501
  }
480
502
  }
481
503
 
504
+ @keyframes crewlet-modal-lift {
505
+ from {
506
+ opacity: 0;
507
+ transform: translateY(var(--spacing-6));
508
+ }
509
+ }
510
+
482
511
  /*
483
512
  * A SHEET TRAVELS ITS OWN WIDTH, which is what makes the motion read as a
484
513
  * sheet rather than as a box appearing beside the page: the frame starts
@@ -502,4 +531,8 @@
502
531
  .crewlet-modal--sheet {
503
532
  animation: none;
504
533
  }
534
+
535
+ .crewlet-modal-overlay--bottom > .crewlet-modal--dialog {
536
+ animation: none;
537
+ }
505
538
  }
@@ -1,4 +1,4 @@
1
1
  export { OrgLabel } from '../chunk-6OF2DBKY.js';
2
- import '../chunk-3UK4VFSK.js';
2
+ import '../chunk-6NIZCQUM.js';
3
3
  import '../chunk-HI6Q2UXQ.js';
4
4
  import '../chunk-ODEQTPFM.js';
@@ -1,5 +1,5 @@
1
1
  export { OrgNodeDisclosure, OrgNodeLabel, OrgNodeLead } from '../chunk-IHGKPEEV.js';
2
2
  import '../chunk-6OF2DBKY.js';
3
- import '../chunk-3UK4VFSK.js';
3
+ import '../chunk-6NIZCQUM.js';
4
4
  import '../chunk-HI6Q2UXQ.js';
5
5
  import '../chunk-ODEQTPFM.js';
@@ -1,11 +1,11 @@
1
- export { OrgTable, OrgTableActions, OrgTableAdd, OrgTableName } from '../chunk-ZMJHK7DR.js';
2
- import '../chunk-MCPKHYMC.js';
3
- import '../chunk-S3BCVWTU.js';
1
+ export { OrgTable, OrgTableActions, OrgTableAdd, OrgTableName } from '../chunk-75WOCYGC.js';
2
+ import '../chunk-6LXMLQUM.js';
3
+ import '../chunk-MY374UUD.js';
4
4
  import '../chunk-6OF2DBKY.js';
5
5
  import '../chunk-53DVJCVW.js';
6
6
  import '../chunk-74WGARJE.js';
7
7
  import '../chunk-4FQT6ZAY.js';
8
- import '../chunk-3UK4VFSK.js';
8
+ import '../chunk-6NIZCQUM.js';
9
9
  import '../chunk-7RPGY3JE.js';
10
10
  import '../chunk-R25RGPUB.js';
11
11
  import '../chunk-BZFWDIV6.js';
@@ -0,0 +1,72 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ /** One heading of a document, as an outline lists it. */
3
+ export interface OutlineItem {
4
+ /** The `id` of the heading this entry moves to. */
5
+ id: string;
6
+ /** What the reader sees: the heading's own text. */
7
+ label: string;
8
+ /** The heading's level, from 1. Each level deeper than the shallowest entry is indented one step. */
9
+ level: number;
10
+ }
11
+ export interface OutlineProps extends Omit<HTMLAttributes<HTMLElement>, 'onSelect'> {
12
+ /** The landmark's name, drawn as the outline's own heading: "On this page". */
13
+ label: string;
14
+ items: readonly OutlineItem[];
15
+ /**
16
+ * The entry whose section the reader is in, usually `useActiveHeading`'s
17
+ * answer. Drawn with the accent edge and `aria-current="location"`.
18
+ */
19
+ activeId?: string | null | undefined;
20
+ /**
21
+ * Called with an entry's id when it is pressed. `scrollToHeading` by default,
22
+ * which brings the heading into view and moves focus to it.
23
+ */
24
+ onSelect?: ((id: string) => void) | undefined;
25
+ }
26
+ /**
27
+ * The headings of the document beside it, as a list a reader moves through
28
+ * the document with: "On this page".
29
+ *
30
+ * BUTTONS, NOT ANCHORS. An application with a hash router keeps its own route
31
+ * in the fragment, so `href="#section"` would navigate away from the page the
32
+ * outline belongs to. A press moves to the heading instead, and the address
33
+ * stays the page's.
34
+ *
35
+ * THE CURRENT ENTRY IS MARKED BY ITS OWN EDGE, the accent rule every current
36
+ * row in the system takes, and by `aria-current="location"`, so a screen reader
37
+ * hears where in the document the reader is rather than a colour standing for
38
+ * it.
39
+ */
40
+ export declare function Outline({ label, items, activeId, onSelect, className, ...rest }: OutlineProps): import("react").JSX.Element;
41
+ /**
42
+ * Brings the element with `id` into view and moves focus to it, so a keyboard
43
+ * carries on reading from the heading it chose rather than from the outline.
44
+ *
45
+ * A heading is not focusable, so it is given `tabindex="-1"`: focusable by
46
+ * script, never a tab stop. The scroll is smooth unless the reader asked for
47
+ * less motion, and focus is moved without a second, instant scroll. Answers
48
+ * false when nothing has that id.
49
+ */
50
+ export declare function scrollToHeading(id: string, ownerDocument?: Document): boolean;
51
+ export interface ActiveHeadingOptions {
52
+ /** The element that scrolls the headings, or `null` for the window. */
53
+ root?: HTMLElement | null | undefined;
54
+ /**
55
+ * How far below the top of the scroller a heading counts as reached, in
56
+ * pixels. The heading the reader has scrolled past last is the current one.
57
+ */
58
+ offset?: number | undefined;
59
+ }
60
+ /**
61
+ * Which of the headings `ids` the reader is in: the last one scrolled past
62
+ * the top of `root`, the first while none has been, and the LAST once the
63
+ * scroller can go no further, because a closing section shorter than the
64
+ * view would otherwise never become current at all.
65
+ *
66
+ * MEASURED ON A FRAME, never on every scroll event: a scroll fires faster than
67
+ * a screen draws, and reading every heading's box on each one is a layout per
68
+ * event. A heading the document does not hold is skipped rather than fatal,
69
+ * so an outline built a moment before its body renders settles on the next
70
+ * frame instead of throwing.
71
+ */
72
+ export declare function useActiveHeading(ids: readonly string[], { root, offset }?: ActiveHeadingOptions): string | null;
@@ -0,0 +1,3 @@
1
+ import './Outline.css';
2
+ export { Outline, scrollToHeading, useActiveHeading } from './Outline.js';
3
+ export type { ActiveHeadingOptions, OutlineItem, OutlineProps } from './Outline.js';
@@ -0,0 +1,2 @@
1
+ export { Outline, scrollToHeading, useActiveHeading } from '../chunk-A5YKNRG6.js';
2
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,78 @@
1
+ /*
2
+ * A document's outline, "On this page" (Outline.tsx).
3
+ *
4
+ * QUIET UNTIL IT IS CURRENT. The entries are the tertiary ink at the caption
5
+ * step, so the column beside a page reads as a map rather than as a second
6
+ * page, and the entry the reader is in takes the primary ink and the accent
7
+ * edge every current row in the system takes.
8
+ */
9
+
10
+ .crewlet-outline {
11
+ display: flex;
12
+ flex-direction: column;
13
+ gap: var(--spacing-2);
14
+ min-width: 0;
15
+ }
16
+
17
+ /* The outline's name in the register of a group label: sentence case at the
18
+ caption step, the 4.5:1 ink. */
19
+ .crewlet-outline__title {
20
+ margin: 0;
21
+ font-family: var(--font-family-sans);
22
+ font-size: var(--font-size-xs);
23
+ font-weight: var(--font-weight-medium);
24
+ line-height: var(--font-line-height-tight);
25
+ color: var(--color-text-tertiary);
26
+ }
27
+
28
+ .crewlet-outline__list {
29
+ display: flex;
30
+ flex-direction: column;
31
+ margin: 0;
32
+ padding: 0;
33
+ list-style: none;
34
+ }
35
+
36
+ /*
37
+ * One entry: the heading's text, wrapped rather than cut, because a heading
38
+ * cut to fit is a section the reader cannot tell from its neighbour. The edge
39
+ * the current entry draws is held on every entry in its resting colour, so
40
+ * nothing moves when the current entry changes.
41
+ */
42
+ .crewlet-outline__item {
43
+ display: block;
44
+ width: 100%;
45
+ min-height: var(--size-target-min);
46
+ margin: 0;
47
+ padding: calc(var(--spacing-1) / 2) var(--spacing-2);
48
+ padding-inline-start: calc(var(--spacing-3) + var(--spacing-3) * var(--crewlet-outline-depth, 0));
49
+ border: 0;
50
+ border-inline-start: 2px solid var(--color-border-default);
51
+ border-radius: 0;
52
+ background: none;
53
+ font-family: var(--font-family-sans);
54
+ font-size: var(--font-size-xs);
55
+ font-weight: var(--font-weight-regular);
56
+ line-height: var(--font-line-height-snug);
57
+ text-align: start;
58
+ color: var(--color-text-tertiary);
59
+ overflow-wrap: anywhere;
60
+ cursor: pointer;
61
+ }
62
+
63
+ .crewlet-outline__item:hover {
64
+ color: var(--color-text-primary);
65
+ }
66
+
67
+ /* The ring lies on the entry's own edge, never a second line beside it. */
68
+ .crewlet-outline__item:focus-visible {
69
+ outline: 2px solid var(--color-focus);
70
+ outline-offset: var(--size-focus-ring-inset-offset);
71
+ }
72
+
73
+ /* The weight stays the resting one: a bolder entry is a wider one, and a
74
+ wider entry can wrap onto a second line as the reader scrolls past it. */
75
+ .crewlet-outline__item--current {
76
+ border-inline-start-color: var(--color-brand-accent);
77
+ color: var(--color-text-primary);
78
+ }
@@ -19,6 +19,26 @@ interface PopoverBaseProps {
19
19
  side?: PopoverSide | undefined;
20
20
  /** `match` sizes the panel to the anchor, which is what a Select wants. */
21
21
  width?: PopoverWidth | undefined;
22
+ /**
23
+ * Moves the panel along the anchor's edge after it is aligned, in pixels; a
24
+ * negative shift moves it toward the start. A function is asked on every
25
+ * placement, with the panel and the anchor, for a shift measured from what
26
+ * the two draw: lining a list's first column up under the anchor's own text
27
+ * is a distance in the panel's padding and the anchor's, both of which move
28
+ * with the density.
29
+ */
30
+ alignOffset?: number | ((panel: HTMLElement, anchor: HTMLElement) => number) | undefined;
31
+ /**
32
+ * How far the panel stands off the anchor's edge, in pixels: the layer's own
33
+ * gap unless a design measures the space under its anchor differently.
34
+ */
35
+ sideOffset?: number | undefined;
36
+ /**
37
+ * Where a dialog panel's focus starts, if not its first control. A search
38
+ * field focused on a touch screen raises a keyboard over the list it
39
+ * searches, so a panel opened by a tap can start on itself instead.
40
+ */
41
+ initialFocus?: (() => HTMLElement | null) | undefined;
22
42
  className?: string | undefined;
23
43
  /** Controlled. Leave it out for a panel that owns its own open state. */
24
44
  open?: boolean | undefined;
@@ -87,5 +107,5 @@ export type PopoverReason = DismissReason | 'trigger' | 'content' | 'anchor-gone
87
107
  * on them at the document, and so does a chord with Control, Command or Alt,
88
108
  * which is an application shortcut rather than a key of the panel's.
89
109
  */
90
- export declare function Popover({ trigger, children, align, side, width, role, label, className, open: controlled, defaultOpen, onOpenChange, }: PopoverProps): import("react").JSX.Element;
110
+ export declare function Popover({ trigger, children, align, side, width, alignOffset, sideOffset, initialFocus, role, label, className, open: controlled, defaultOpen, onOpenChange, }: PopoverProps): import("react").JSX.Element;
91
111
  export {};
@@ -1,4 +1,4 @@
1
- export { Popover } from '../chunk-PDUZO35I.js';
1
+ export { Popover } from '../chunk-O2ZDZZS7.js';
2
2
  import '../chunk-7RPGY3JE.js';
3
3
  import '../chunk-R25RGPUB.js';
4
4
  import '../chunk-ODEQTPFM.js';