@lovett/ui 0.0.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-RBYWGBQ2.js +2752 -0
- package/dist/chunk-RBYWGBQ2.js.map +1 -0
- package/dist/index.d.ts +5574 -57
- package/dist/index.js +21650 -11206
- package/dist/index.js.map +1 -1
- package/dist/rich-composer-impl-5NO443A6.js +1859 -0
- package/dist/rich-composer-impl-5NO443A6.js.map +1 -0
- package/dist/styles.css +1570 -0
- package/dist/tokens.css +112 -0
- package/package.json +8 -1
- package/src/__tests__/avatar.test.tsx +272 -0
- package/src/__tests__/bar-chart.test.tsx +809 -0
- package/src/__tests__/board.test.tsx +420 -0
- package/src/__tests__/chart-math.test.ts +922 -0
- package/src/__tests__/chart-series.test.ts +339 -0
- package/src/__tests__/code-block.test.tsx +134 -0
- package/src/__tests__/display-popover.test.tsx +195 -0
- package/src/__tests__/display-store.test.tsx +307 -0
- package/src/__tests__/donut-chart.test.tsx +397 -0
- package/src/__tests__/dropdown-menu.test.tsx +156 -2
- package/src/__tests__/filter-menu.test.tsx +175 -0
- package/src/__tests__/gauge-ring.test.tsx +233 -0
- package/src/__tests__/line-chart.test.tsx +612 -0
- package/src/__tests__/ranked-bars.test.tsx +343 -0
- package/src/__tests__/remark-underline.test.ts +194 -0
- package/src/__tests__/sortable.test.tsx +394 -0
- package/src/__tests__/sparkline.test.tsx +368 -0
- package/src/__tests__/stat-layer.test.tsx +271 -0
- package/src/__tests__/stat-strip.test.tsx +175 -0
- package/src/__tests__/status.test.tsx +293 -0
- package/src/__tests__/tabs.test.tsx +303 -0
- package/src/__tests__/token-shape.test.ts +132 -2
- package/src/avatar.tsx +352 -0
- package/src/bar-chart.tsx +1214 -0
- package/src/board.tsx +658 -0
- package/src/chart-frame.tsx +960 -0
- package/src/chart-legend.tsx +304 -0
- package/src/chart-tooltip.tsx +267 -0
- package/src/code-block.tsx +62 -8
- package/src/delta-chip.tsx +263 -0
- package/src/detail/__tests__/activity-pane.test.tsx +369 -0
- package/src/detail/__tests__/detail-chrome.test.tsx +134 -0
- package/src/detail/__tests__/detail-surface.test.tsx +529 -0
- package/src/detail/__tests__/field-row.test.tsx +357 -0
- package/src/detail/activity-pane.tsx +611 -0
- package/src/detail/calendar.tsx +355 -0
- package/src/detail/detail-divider.tsx +261 -0
- package/src/detail/detail-header.tsx +287 -0
- package/src/detail/detail-menu.tsx +254 -0
- package/src/detail/detail-surface.tsx +1110 -0
- package/src/detail/field-list.tsx +196 -0
- package/src/detail/field-row.tsx +1131 -0
- package/src/detail/index.ts +58 -0
- package/src/detail/segmented-choice.tsx +94 -0
- package/src/detail/types.ts +129 -0
- package/src/display-popover.tsx +487 -0
- package/src/display-store.tsx +301 -0
- package/src/donut-chart.tsx +988 -0
- package/src/dropdown-menu.tsx +290 -19
- package/src/filter-core/EXPORTS.md +85 -0
- package/src/filter-core/__tests__/columns.test.ts +159 -0
- package/src/filter-core/__tests__/faceting.test.ts +193 -0
- package/src/filter-core/__tests__/filter-fns.test.ts +519 -0
- package/src/filter-core/__tests__/operators.test.ts +235 -0
- package/src/filter-core/__tests__/state.test.ts +268 -0
- package/src/filter-core/__tests__/url.test.ts +350 -0
- package/src/filter-core/columns.ts +134 -0
- package/src/filter-core/date-utils.ts +38 -0
- package/src/filter-core/examples/task-filter-columns.ts +121 -0
- package/src/filter-core/faceting.ts +120 -0
- package/src/filter-core/filter-fns.ts +335 -0
- package/src/filter-core/index.ts +105 -0
- package/src/filter-core/operators.ts +433 -0
- package/src/filter-core/state.ts +280 -0
- package/src/filter-core/types.ts +247 -0
- package/src/filter-core/url.ts +261 -0
- package/src/filter-dropdown.tsx +12 -0
- package/src/filter-menu.tsx +649 -0
- package/src/floating-drawer.tsx +19 -1
- package/src/gauge-ring.tsx +435 -0
- package/src/hue.ts +52 -0
- package/src/index.ts +303 -0
- package/src/kbd.tsx +27 -4
- package/src/lib/chart.ts +866 -0
- package/src/lib/focus.ts +43 -1
- package/src/lib/layer-stack.ts +30 -3
- package/src/lib/remark-underline.ts +443 -0
- package/src/lib/series.ts +169 -0
- package/src/line-chart.tsx +1176 -0
- package/src/markdown.tsx +26 -7
- package/src/modal.tsx +42 -18
- package/src/progress-ledger.tsx +304 -0
- package/src/ranked-bars.tsx +386 -0
- package/src/segmented-pill.tsx +32 -9
- package/src/sortable.tsx +520 -1
- package/src/sparkline.tsx +416 -0
- package/src/stat-card.tsx +376 -0
- package/src/stat-strip.tsx +327 -0
- package/src/status.tsx +215 -0
- package/src/styles.css +1570 -0
- package/src/tabs.tsx +206 -25
- package/src/task-card.tsx +610 -0
- package/src/thread/__tests__/comment-body-hostile.test.tsx +331 -0
- package/src/thread/__tests__/comment-tree.test.ts +151 -0
- package/src/thread/__tests__/emoji.test.ts +187 -0
- package/src/thread/__tests__/fixtures/thread-fixture.ts +252 -0
- package/src/thread/__tests__/link-preview-source.test.ts +120 -0
- package/src/thread/__tests__/link-preview.test.tsx +600 -0
- package/src/thread/__tests__/markdown-format.test.ts +82 -0
- package/src/thread/__tests__/markdown-spec.test.ts +469 -0
- package/src/thread/__tests__/relative-time.test.ts +71 -0
- package/src/thread/__tests__/rich-composer.test.tsx +799 -0
- package/src/thread/__tests__/scroll-caret.test.ts +58 -0
- package/src/thread/__tests__/suggestion-list.test.tsx +648 -0
- package/src/thread/__tests__/thread-scroll-ownership.test.tsx +88 -0
- package/src/thread/__tests__/thread.test.tsx +742 -0
- package/src/thread/__tests__/use-attachments.test.tsx +679 -0
- package/src/thread/actions.tsx +196 -0
- package/src/thread/attachments.tsx +1071 -0
- package/src/thread/comment-body.tsx +148 -0
- package/src/thread/comment-tree.ts +182 -0
- package/src/thread/comment.tsx +967 -0
- package/src/thread/composer-footer.tsx +125 -0
- package/src/thread/composer.tsx +319 -0
- package/src/thread/emoji.ts +283 -0
- package/src/thread/index.ts +153 -0
- package/src/thread/link-preview.tsx +341 -0
- package/src/thread/markdown-format.ts +155 -0
- package/src/thread/markdown-spec.ts +754 -0
- package/src/thread/rail.tsx +372 -0
- package/src/thread/reactions.tsx +415 -0
- package/src/thread/relative-time.tsx +94 -0
- package/src/thread/rich-composer-impl.tsx +1601 -0
- package/src/thread/rich-composer.tsx +195 -0
- package/src/thread/scroll-caret.ts +37 -0
- package/src/thread/suggestion-list.tsx +182 -0
- package/src/thread/thread.tsx +718 -0
- package/src/thread/types.ts +232 -0
- package/src/thread/use-attachments.ts +598 -0
- package/src/thread/use-now.ts +73 -0
- package/src/thread/use-thread.ts +316 -0
- package/src/tokens.css +112 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread primitive — the data shapes.
|
|
3
|
+
*
|
|
4
|
+
* ADR-147 T3. This module is deliberately entity-agnostic: it knows nothing
|
|
5
|
+
* about tasks, boards, posts, brands, or the wire. It takes already-shaped
|
|
6
|
+
* data and reports intent through callbacks. The `entity_type` registry that
|
|
7
|
+
* makes threads safe (ADR-147 D2) lives server-side; nothing here is an
|
|
8
|
+
* authorization boundary and nothing here should ever be treated as one.
|
|
9
|
+
*
|
|
10
|
+
* Timestamps are integer epoch MILLISECONDS. The D1 schema stores epoch
|
|
11
|
+
* seconds (ADR-147 D16); converting at the API seam is T4's job, not this
|
|
12
|
+
* layer's — a component that guesses at units is a component that renders
|
|
13
|
+
* "56 years ago" the first time someone changes the column.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { MentionTargetType } from './comment-body'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ADR-147 D8 — reactions are a bounded set of glyph KEYS, never emoji and
|
|
20
|
+
* never a free string. The bound is what makes the column enumerable, the
|
|
21
|
+
* rendering consistent across platforms, and the no-emoji rule enforceable.
|
|
22
|
+
*/
|
|
23
|
+
export const THREAD_REACTION_KEYS = [
|
|
24
|
+
'up',
|
|
25
|
+
'heart',
|
|
26
|
+
'celebrate',
|
|
27
|
+
'eyes',
|
|
28
|
+
'check',
|
|
29
|
+
'question',
|
|
30
|
+
] as const
|
|
31
|
+
|
|
32
|
+
export type ThreadReactionKey = (typeof THREAD_REACTION_KEYS)[number]
|
|
33
|
+
|
|
34
|
+
/** One reaction row as the API groups it: key, tally, and the viewer's own state. */
|
|
35
|
+
export interface ThreadReactionCount {
|
|
36
|
+
readonly key: ThreadReactionKey
|
|
37
|
+
readonly count: number
|
|
38
|
+
readonly viewerReacted: boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ADR-147 D17 — optimistic send is a first-class state, not a nicety.
|
|
43
|
+
* A comment silently lost to a bad connection is the worst thing this surface
|
|
44
|
+
* can do to someone, and it is the most likely.
|
|
45
|
+
*/
|
|
46
|
+
export type CommentDeliveryState = 'sent' | 'pending' | 'failed'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* ADR-147 D5 — every field here can be absent. `users.name` / `given_name` /
|
|
50
|
+
* `family_name` are nullable by standing policy (do not fabricate a display
|
|
51
|
+
* name), `author_id` is ON DELETE SET NULL, and no avatar column is populated
|
|
52
|
+
* yet. The display fallback lives in the component (D6), never in the data.
|
|
53
|
+
*/
|
|
54
|
+
export interface ThreadAuthor {
|
|
55
|
+
readonly id?: string | null | undefined
|
|
56
|
+
readonly name?: string | null | undefined
|
|
57
|
+
/** Used as the display fallback when `name` is absent — D5's "only an email" case. */
|
|
58
|
+
readonly email?: string | null | undefined
|
|
59
|
+
/** May 404 or be blocked. The avatar keeps its initial behind the photo (D14). */
|
|
60
|
+
readonly avatarUrl?: string | null | undefined
|
|
61
|
+
/** Identity colour as DATA (D6). Omitted falls back to Avatar's documented hash. */
|
|
62
|
+
readonly tint?: string | null | undefined
|
|
63
|
+
/** A short role chip beside the name, e.g. "Client". */
|
|
64
|
+
readonly role?: string | null | undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* `gif` joined `image` and `file` with ADR-151 D4.
|
|
69
|
+
*
|
|
70
|
+
* It is its own kind rather than a flavour of `image` because it identifies a
|
|
71
|
+
* Giphy reference whose stored rendition is an mp4. That is what lets its
|
|
72
|
+
* renderer genuinely pause on the first frame. Uploaded GIFs deliberately stay
|
|
73
|
+
* `image` and are distinguished by `mime: 'image/gif'`; their renderer swaps a
|
|
74
|
+
* captured canvas still for the live image under reduced motion.
|
|
75
|
+
*/
|
|
76
|
+
export type ThreadAttachmentKind = 'image' | 'gif' | 'file'
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* ADR-147 D9. `aspectRatio` is captured at attach time so the thread reserves
|
|
80
|
+
* the image slot before it loads — the layout-stability rule. An attachment
|
|
81
|
+
* without one gets a declared 16/10 box rather than a collapsing one.
|
|
82
|
+
*/
|
|
83
|
+
export interface ThreadAttachment {
|
|
84
|
+
readonly id: string
|
|
85
|
+
readonly kind: ThreadAttachmentKind
|
|
86
|
+
readonly name: string
|
|
87
|
+
readonly url?: string | undefined
|
|
88
|
+
readonly mime?: string | undefined
|
|
89
|
+
readonly sizeBytes?: number | undefined
|
|
90
|
+
readonly aspectRatio?: number | undefined
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* One unfurled link card on one comment (ADR-150 D8).
|
|
95
|
+
*
|
|
96
|
+
* This is the WIRE shape, and it is deliberately narrower than the cache row
|
|
97
|
+
* behind it. `thread_link_previews` also carries `status` (`ok` / `failed` /
|
|
98
|
+
* `blocked`) and internal failure codes; a thread read returns only `ok`,
|
|
99
|
+
* unhidden rows, so neither ever crosses the wire. A client cannot tell a
|
|
100
|
+
* blocked URL from one nobody has fetched yet, which is the intent.
|
|
101
|
+
*
|
|
102
|
+
* `title` is NOT nullable, unlike everything else here: D5 makes a non-empty
|
|
103
|
+
* title the condition for writing an `ok` row at all, so a card without one
|
|
104
|
+
* does not exist. `domain` is derived by the WORKER from the URL that was
|
|
105
|
+
* actually fetched — never from `og:site_name`, which the target page chooses.
|
|
106
|
+
*
|
|
107
|
+
* `href` is per-OCCURRENCE and keeps its fragment, so clicking the card lands
|
|
108
|
+
* on the anchor the person meant; the cache is keyed on a canonical fetch URL
|
|
109
|
+
* with the fragment removed, because a fragment is never sent to an origin.
|
|
110
|
+
*
|
|
111
|
+
* `canHide` reflects "the viewer authored this comment" for rendering only. It
|
|
112
|
+
* is never an authorization decision — D7 re-checks authorship server-side on
|
|
113
|
+
* every hide, and a flipped flag earns a 404.
|
|
114
|
+
*/
|
|
115
|
+
export interface ThreadLinkPreview {
|
|
116
|
+
/** SHA-256 of the canonical fetch URL, 64 lowercase hex characters. */
|
|
117
|
+
readonly urlHash: string
|
|
118
|
+
/** The normalized href for THIS occurrence, fragment included. */
|
|
119
|
+
readonly href: string
|
|
120
|
+
readonly title: string
|
|
121
|
+
/** Worker-derived display host. Not `og:site_name`. */
|
|
122
|
+
readonly domain: string
|
|
123
|
+
readonly description?: string | null | undefined
|
|
124
|
+
readonly imageUrl?: string | null | undefined
|
|
125
|
+
readonly siteName?: string | null | undefined
|
|
126
|
+
/**
|
|
127
|
+
* `og:image:width / og:image:height`, already clamped to `0.1..10` by the
|
|
128
|
+
* worker (ADR-150 D4). Absent means the card declares a 16:9 slot — the point
|
|
129
|
+
* being that it declares SOMETHING before the image loads, and keeps it if
|
|
130
|
+
* the image never does.
|
|
131
|
+
*/
|
|
132
|
+
readonly imageAspect?: number | null | undefined
|
|
133
|
+
readonly canHide: boolean
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** -1 | 0 | 1. The community/roadmap variant (D8) — never combined with reactions. */
|
|
137
|
+
export type ThreadVote = -1 | 0 | 1
|
|
138
|
+
|
|
139
|
+
export interface ThreadComment {
|
|
140
|
+
readonly id: string
|
|
141
|
+
/** Absent or null = a root comment. */
|
|
142
|
+
readonly parentId?: string | null | undefined
|
|
143
|
+
/** Null = the author record is gone (D5). Renders degraded, never crashes. */
|
|
144
|
+
readonly author?: ThreadAuthor | null | undefined
|
|
145
|
+
/** Markdown. Rendered through MarkdownRenderer's component map; NEVER rehype-raw (D7). */
|
|
146
|
+
readonly bodyMd: string
|
|
147
|
+
readonly createdAt: number
|
|
148
|
+
/** Set = "· edited" (D15). No edit history in v1. */
|
|
149
|
+
readonly editedAt?: number | null | undefined
|
|
150
|
+
/** Set = tombstone (D4). The row still renders and KEEPS its subtree. */
|
|
151
|
+
readonly deletedAt?: number | null | undefined
|
|
152
|
+
/**
|
|
153
|
+
* TRUE = the tombstone above was made by somebody OTHER than the author
|
|
154
|
+
* (FU-0031, FU-0032). Meaningless without `deletedAt`, and read nowhere else.
|
|
155
|
+
*
|
|
156
|
+
* One boolean rather than a `deletedBy` author, and that is a decision, not
|
|
157
|
+
* a shortcut: the wire deliberately never carries who removed a comment, so
|
|
158
|
+
* a field that could hold a name would be a field somebody eventually fills.
|
|
159
|
+
* A boolean cannot leak an identity it does not have.
|
|
160
|
+
*
|
|
161
|
+
* OPTIONAL, and absent means `[deleted]` — the render every host on 0.1.0
|
|
162
|
+
* already gets. A host with no moderation concept never sets it and never
|
|
163
|
+
* sees a word change.
|
|
164
|
+
*/
|
|
165
|
+
readonly moderated?: boolean | undefined
|
|
166
|
+
readonly reactions?: readonly ThreadReactionCount[] | undefined
|
|
167
|
+
readonly attachments?: readonly ThreadAttachment[] | undefined
|
|
168
|
+
/**
|
|
169
|
+
* At most three (ADR-150 D1), already ordered by the worker and already
|
|
170
|
+
* filtered to visible, successful rows. Absent is the normal state for a
|
|
171
|
+
* comment with no links — and also what a fail-soft preview read returns
|
|
172
|
+
* (D10), which is why an empty list can never be distinguished from an error
|
|
173
|
+
* and neither one is allowed to break the comment.
|
|
174
|
+
*/
|
|
175
|
+
readonly linkPreviews?: readonly ThreadLinkPreview[] | undefined
|
|
176
|
+
/** Vote variant only (D8). */
|
|
177
|
+
readonly score?: number | undefined
|
|
178
|
+
/** Vote variant only (D8). Lifted to props — never local state (correction 3). */
|
|
179
|
+
readonly viewerVote?: ThreadVote | undefined
|
|
180
|
+
/** Pinned / "top comment". Sorts first under `top`. */
|
|
181
|
+
readonly pinned?: boolean | undefined
|
|
182
|
+
/** Defaults to 'sent' when absent. */
|
|
183
|
+
readonly state?: CommentDeliveryState | undefined
|
|
184
|
+
/** Shown beside the retry on a failed send. */
|
|
185
|
+
readonly errorText?: string | undefined
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* A comment placed in the reply tree.
|
|
190
|
+
*
|
|
191
|
+
* `depth` is the TRUE stored depth (D3: "depth is stored true and rendered
|
|
192
|
+
* capped"). The render cap is a display concern and lives in the hook.
|
|
193
|
+
*
|
|
194
|
+
* `orphanedParentId` is the D4 distinction: non-null means this reply named a
|
|
195
|
+
* parent that is ABSENT from the list it was built from — "not fetched", not
|
|
196
|
+
* "deleted". A deleted parent is a tombstone row that is still present, so its
|
|
197
|
+
* children stay nested under it and this stays null.
|
|
198
|
+
*/
|
|
199
|
+
export interface CommentNode extends ThreadComment {
|
|
200
|
+
readonly replies: readonly CommentNode[]
|
|
201
|
+
readonly depth: number
|
|
202
|
+
readonly orphanedParentId: string | null
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export type ThreadSort = 'top' | 'new'
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Reactions and votes are never both on one comment (D8): reactions are
|
|
209
|
+
* acknowledgement, votes are ranking. The headless layer exposes both and the
|
|
210
|
+
* surface picks exactly one. Task threads take reactions; community and
|
|
211
|
+
* roadmap take votes.
|
|
212
|
+
*/
|
|
213
|
+
export type ThreadEngagement = 'reactions' | 'votes' | 'none'
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* One row the `@` picker may offer (ADR-149 D6).
|
|
217
|
+
*
|
|
218
|
+
* It lives here, beside the other data shapes, rather than in the rich
|
|
219
|
+
* composer, for one structural reason: the package barrel must stay free of
|
|
220
|
+
* TipTap so a thread LIST never pays for ProseMirror (ADR-148 D5), and a host
|
|
221
|
+
* needs this type to write its `mentionItems` callback.
|
|
222
|
+
*
|
|
223
|
+
* The picker is a CONVENIENCE, never a permission boundary. The host filters a
|
|
224
|
+
* list it already had; the server still parses `body_md` at write time and
|
|
225
|
+
* re-checks every mention it extracts, exactly as it does for one typed by
|
|
226
|
+
* hand or pasted (ADR-147 D11, restated in ADR-149 D6).
|
|
227
|
+
*/
|
|
228
|
+
export interface MentionSuggestionItem {
|
|
229
|
+
readonly id: string
|
|
230
|
+
readonly label: string
|
|
231
|
+
readonly type: MentionTargetType
|
|
232
|
+
}
|