@remit/ui 0.0.28 → 0.0.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/ui",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -107,7 +107,15 @@ export const MessageBodyView = ({
107
107
 
108
108
  return (
109
109
  <div className={cn("message-body", className)}>
110
- {blockedImageCount > 0 && renderBlockedNotice?.(blockedImageCount)}
110
+ {/* `message-body-notice` is a styling hook, not a style: a container
111
+ that drops the body's horizontal inset (the mobile reading pane)
112
+ uses it to give the gutter back to this notice, which is app
113
+ chrome rather than part of the email. */}
114
+ {blockedImageCount > 0 && (
115
+ <div className="message-body-notice">
116
+ {renderBlockedNotice?.(blockedImageCount)}
117
+ </div>
118
+ )}
111
119
 
112
120
  {sanitizedHtml ? (
113
121
  // Email HTML renders inside a sandboxed iframe so its own CSS and any
@@ -232,14 +232,21 @@ export function ExpandedMessage({
232
232
  exactly what the app renders, not a divergent inline-HTML mock
233
233
  (#940). `framed` fixtures map to the newsletter treatment (author
234
234
  colors preserved); the rest render plain. */}
235
- {body ?? (
236
- <MessageBodyView
237
- className="mt-3"
238
- html={message.bodyHtml}
239
- category={message.framed ? "newsletter" : "personal"}
240
- allowImages
241
- />
242
- )}
235
+ {/* On a phone the email runs edge to edge: the negative margin cancels
236
+ this block's `px-2` so no app canvas shows beside the body and the
237
+ email doesn't read as sitting in a tinted frame (#763). The header
238
+ keeps its inset. The blocked-images notice is app chrome, not part
239
+ of the email, so it takes the gutter back. Desktop is unchanged. */}
240
+ <div className="-mx-2 [&_.message-body-notice]:px-2 lg:mx-0 lg:[&_.message-body-notice]:px-0">
241
+ {body ?? (
242
+ <MessageBodyView
243
+ className="mt-3"
244
+ html={message.bodyHtml}
245
+ category={message.framed ? "newsletter" : "personal"}
246
+ allowImages
247
+ />
248
+ )}
249
+ </div>
243
250
  </div>
244
251
  );
245
252
  }
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
+ import { FolderInput } from "lucide-react";
2
3
  import { SelectionTopBar } from "./selection-top-bar.js";
3
4
 
4
5
  const meta: Meta<typeof SelectionTopBar> = {
@@ -22,6 +23,18 @@ export default meta;
22
23
 
23
24
  type Story = StoryObj<typeof SelectionTopBar>;
24
25
 
26
+ /** Stand-in for the caller's move-to-folder trigger, which owns its own
27
+ * folder-picker data. The bar only reserves the slot. */
28
+ const MoveSlot = () => (
29
+ <button
30
+ type="button"
31
+ aria-label="Move selected messages"
32
+ className="inline-flex size-11 shrink-0 items-center justify-center rounded text-fg-muted hover:bg-surface-raised"
33
+ >
34
+ <FolderInput className="size-4" />
35
+ </button>
36
+ );
37
+
25
38
  export const One: Story = { args: { count: 1 } };
26
39
 
27
40
  export const Many: Story = { args: { count: 3 } };
@@ -75,7 +88,7 @@ export const AllSelected: Story = {
75
88
  /**
76
89
  * The search has more matches than are loaded: an escalation notice offers a
77
90
  * real button (not prose) naming the scope. Tapping it is what flips the
78
- * selection's identity from an id set to the search query (`useEscalatedDelete`
91
+ * selection's identity from an id set to the search query (`useEscalatedActions`
79
92
  * in web-client). No count in the label yet — the real client's own read path
80
93
  * (`ThreadOperations.searchThreads`) only counts within a capped recency
81
94
  * window short of paging the whole result set, and paging it just to seed a
@@ -107,11 +120,19 @@ export const EscalationAvailable: Story = {
107
120
  * Selection has been escalated to the search query: the count names the
108
121
  * query's total, not a materialized id count, and the notice offers a way
109
122
  * back to the bounded selection.
123
+ *
124
+ * Every verb the bar carries stays available here (#114). An escalated
125
+ * selection is a predicate rather than an id list, so the web-client runs
126
+ * move and mark-read by paging that predicate the same way delete does —
127
+ * from the bar's side nothing changes, which is the point: an escalated
128
+ * selection that could only be deleted forced anyone wanting to file those
129
+ * messages back to the loaded page.
110
130
  */
111
131
  export const Escalated: Story = {
112
132
  args: {
113
133
  count: 3412,
114
134
  statusLabel: 'All 3,412 matching "npm" selected',
135
+ moveSlot: <MoveSlot />,
115
136
  notice: {
116
137
  tone: "info",
117
138
  text: "",
@@ -199,3 +220,44 @@ export const PartialFailure: Story = {
199
220
  export const ZeroSelected: Story = {
200
221
  args: { count: 0 },
201
222
  };
223
+
224
+ /**
225
+ * A move over an escalated selection: same chunked run as a delete, worded for
226
+ * the action that is running and toned as ordinary progress rather than
227
+ * destructive. Mark-read and the move slot are hidden while it runs — nothing
228
+ * here can act mid-run.
229
+ */
230
+ export const MovingWithProgress: Story = {
231
+ args: {
232
+ count: 3412,
233
+ statusLabel: "Moving 1,200 of 3,412…",
234
+ isBusy: true,
235
+ progress: { value: 1200, max: 3412, tone: "info" },
236
+ },
237
+ };
238
+
239
+ /** Mark-read over the same escalated selection. */
240
+ export const MarkingReadWithProgress: Story = {
241
+ args: {
242
+ count: 3412,
243
+ statusLabel: "Marking 1,200 of 3,412 as read…",
244
+ isBusy: true,
245
+ progress: { value: 1200, max: 3412, tone: "info" },
246
+ },
247
+ };
248
+
249
+ /**
250
+ * Partial failure of a move rather than a delete: the notice names the action
251
+ * that ran, and Retry resends that same action against what is still selected.
252
+ */
253
+ export const PartialFailureMove: Story = {
254
+ args: {
255
+ count: 340,
256
+ moveSlot: <MoveSlot />,
257
+ notice: {
258
+ tone: "danger",
259
+ text: "3,072 moved. 340 couldn't be moved.",
260
+ action: { label: "Retry 340", onClick: () => undefined },
261
+ },
262
+ },
263
+ };