@reventlessdev/reventless-ui-slots 3.0.0-alpha.2 → 3.0.0-alpha.4

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/README.md CHANGED
@@ -145,6 +145,11 @@ the question.
145
145
  | `tracker.summary` | row | the line beside one row's name; its summary fields |
146
146
  | `detail.media` | row | the media column of a `detailLayout: "media"` page; the record's picture |
147
147
  | `detail.aside` | row | above the field set on a `detailLayout: "media"` page; nothing by default |
148
+ | `list.selection` | view | the bar over a list whose rows the caller can pick out: what is picked, a way to empty it, and the one command that takes the lot |
149
+
150
+ `list.selection` is the one id that is not `<mode>.<region>`: it belongs to the
151
+ list itself rather than to a mode, which is what lets a picking made in the table
152
+ survive a switch to cards.
148
153
 
149
154
  An id nothing offers is reported to the console at boot — a renderer nothing will
150
155
  ever call is a mistake the running app would otherwise never mention.
@@ -156,7 +161,7 @@ ever call is a mistake the running app would otherwise never mention.
156
161
  { row, label?, image?, open?, actions? }
157
162
 
158
163
  // view slot
159
- { rows, total?, selection?, run? }
164
+ { rows, total?, selection?, picked?, run? }
160
165
  ```
161
166
 
162
167
  `row` is the read model's own JSON. `image` is already resolved for this
@@ -164,10 +169,14 @@ deployment's asset origins. `actions` are already resolved against the
164
169
  deployment's declarations, the caller's permissions and the row's lifecycle
165
170
  state. `open` drills into the row.
166
171
 
167
- `selection` and `run` are declared and filled by nothing yet — cross-row
168
- selection is its own piece of work. They are in the contract from the start
169
- because discovering that a region needs a second arity after publishing would be
170
- a breaking change.
172
+ `selection` is the picked row **ids**; `picked` is the rows behind them, each as
173
+ the same row-slot payload above. Both, and not just the ids, because a list's
174
+ window is replaced page by page: a row picked on page 1 is gone from `rows` by
175
+ the time the caller is on page 3, and a bar whose job is to name what is in it
176
+ cannot look one up. `run` opens the command's form with the picked ids already in
177
+ place, and takes nothing: the region has exactly one command, settled before the
178
+ payload was built, so a renderer naming it would be repeating a decision it
179
+ cannot check.
171
180
 
172
181
  ## What a slot may not do
173
182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-ui-slots",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-alpha.4",
4
4
  "description": "Types and one runtime helper for writing custom renderers into the regions of Reventless AutoUI's standard view modes.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -54,5 +54,5 @@
54
54
  "react-dom": "^18.3.1",
55
55
  "rescript": "^12.3.0"
56
56
  },
57
- "gitHead": "6d74b362ba047678919b27c22f84e698dea48741"
57
+ "gitHead": "d65285daddaf66f75ff759d31596a0aee84cd3cb"
58
58
  }
@@ -20,7 +20,8 @@ type viewPayload = {
20
20
  rows: array<JSON.t>,
21
21
  total?: int,
22
22
  selection?: array<string>,
23
- run?: string => unit,
23
+ picked?: array<rowPayload>,
24
+ run?: unit => unit,
24
25
  }
25
26
 
26
27
  type hyperscript
@@ -144,6 +145,19 @@ module Format = {
144
145
  let at = Date.fromString(iso)
145
146
  Float.isNaN(Date.getTime(at)) ? "" : Date.toLocaleDateString(at)
146
147
  }
148
+
149
+ let pad = (n: int): string => n < 10 ? "0" ++ Int.toString(n) : Int.toString(n)
150
+
151
+ let isoDay = (iso: string): string => {
152
+ let at = Date.fromString(iso)
153
+ Float.isNaN(Date.getTime(at))
154
+ ? ""
155
+ : Int.toString(Date.getFullYear(at)) ++
156
+ "-" ++
157
+ pad(Date.getMonth(at) + 1) ++
158
+ "-" ++
159
+ pad(Date.getDate(at))
160
+ }
147
161
  }
148
162
 
149
163
  let titleOf = (payload: rowPayload): string =>
@@ -208,5 +222,6 @@ module RowSlot = {
208
222
  module ViewSlot = {
209
223
  let cardsEmpty = "cards.empty"
210
224
  let galleryEmpty = "gallery.empty"
211
- let all = [cardsEmpty, galleryEmpty]
225
+ let listSelection = "list.selection"
226
+ let all = [cardsEmpty, galleryEmpty, listSelection]
212
227
  }
@@ -121,10 +121,22 @@ function day(iso) {
121
121
  }
122
122
  }
123
123
 
124
- let Format = {
125
- money: money$1,
126
- day: day
127
- };
124
+ function pad(n) {
125
+ if (n < 10) {
126
+ return "0" + n.toString();
127
+ } else {
128
+ return n.toString();
129
+ }
130
+ }
131
+
132
+ function isoDay(iso) {
133
+ let at = new Date(iso);
134
+ if (Number.isNaN(at.getTime())) {
135
+ return "";
136
+ } else {
137
+ return at.getFullYear().toString() + "-" + pad(at.getMonth() + 1 | 0) + "-" + pad(at.getDate());
138
+ }
139
+ }
128
140
 
129
141
  function titleOf(payload) {
130
142
  let label = payload.label;
@@ -189,17 +201,27 @@ let cardsEmpty = "cards.empty";
189
201
 
190
202
  let galleryEmpty = "gallery.empty";
191
203
 
204
+ let listSelection = "list.selection";
205
+
192
206
  let all$1 = [
193
207
  cardsEmpty,
194
- galleryEmpty
208
+ galleryEmpty,
209
+ listSelection
195
210
  ];
196
211
 
197
212
  let ViewSlot = {
198
213
  cardsEmpty: cardsEmpty,
199
214
  galleryEmpty: galleryEmpty,
215
+ listSelection: listSelection,
200
216
  all: all$1
201
217
  };
202
218
 
219
+ let Format = {
220
+ money: money$1,
221
+ day: day,
222
+ isoDay: isoDay
223
+ };
224
+
203
225
  export {
204
226
  h,
205
227
  RowSlot,
@@ -42,19 +42,28 @@ type rowPayload = {
42
42
  actions?: array<action>,
43
43
  }
44
44
 
45
- /** What a per-view region is handed. `selection` and `run` are declared and
46
- filled by nothing yet — cross-row selection is its own piece of work. They
47
- are in the contract from the start because discovering that a region needs a
48
- second arity after publishing would be a breaking change. */
45
+ /** What a per-view region is handed. */
49
46
  type viewPayload = {
50
47
  /** The rows the mode is currently drawing — its page window, not the table. */
51
48
  rows: array<JSON.t>,
52
49
  /** How many rows that window holds. */
53
50
  total?: int,
54
- /** Row ids the caller has picked out, where the mode offers picking. */
51
+ /** Row ids the caller has picked out, where the view offers picking. */
55
52
  selection?: array<string>,
56
- /** Start the region's one command, by name. */
57
- run?: string => unit,
53
+ /** The rows behind `selection`, as they were when picked.
54
+
55
+ A list's window is replaced page by page, so a row picked on page 1 is
56
+ gone from `rows` by the time the caller is on page 3 — and a bar whose job
57
+ is to name what is in it cannot look one up. So the rows travel, each as
58
+ the same `rowPayload` a per-row region is handed. */
59
+ picked?: array<rowPayload>,
60
+ /** Start the region's one command, with the picked rows already in it.
61
+
62
+ Takes nothing. A region has exactly one command by construction, and which
63
+ one it is was settled before this payload was built — naming it here would
64
+ be a second copy of a decision made once, and a renderer has no honest way
65
+ to know the name it would be passing. */
66
+ run?: unit => unit,
58
67
  }
59
68
 
60
69
  /** React's `createElement`, as the shell handed it over. */
@@ -187,8 +196,21 @@ module Format: {
187
196
  most currencies and quietly wrong for JPY and TND. */
188
197
  let money: ((float, string)) => string
189
198
 
190
- /** An ISO instant as a short local date. `""` when it does not parse. */
199
+ /** An ISO instant as a short date **in the viewer's own locale**.
200
+
201
+ Which format that is comes from the runtime, not from you or from the
202
+ deployment: the same order reads `9/4/2026` to one customer and `04/09/2026`
203
+ to another, and a screenshot never matches CI. That is right for a surface
204
+ whose readers are the public, and wrong wherever a date has to be
205
+ unambiguous or stable — take `isoDay` there. */
191
206
  let day: string => string
207
+
208
+ /** An ISO instant as `YYYY-MM-DD`, the same everywhere.
209
+
210
+ Built from the **local** calendar parts rather than by slicing the string,
211
+ which would silently publish the UTC date and land a day out either side of
212
+ midnight for most of the world. */
213
+ let isoDay: string => string
192
214
  }
193
215
 
194
216
  /** What the mode would have titled this row, else the row's own `name`.
@@ -216,5 +238,13 @@ module ViewSlot: {
216
238
  let cardsEmpty: string
217
239
  /** The tile grid when the view has no rows. */
218
240
  let galleryEmpty: string
241
+ /** The bar over a list whose rows the caller can pick out: what is picked, a
242
+ way to empty it, and the one command that takes the lot.
243
+
244
+ Offered by the list producer rather than by a view mode — the first id
245
+ that is not `<mode>.<region>`. The list renders the mode inside its own
246
+ chrome and the bar is that chrome, so a picking made in the table survives
247
+ a switch to cards and the bar has to outlive both. */
248
+ let listSelection: string
219
249
  let all: array<string>
220
250
  }
package/src/index.d.ts CHANGED
@@ -48,21 +48,28 @@ export interface RowPayload<Row = Record<string, unknown>> {
48
48
  actions?: SlotAction[]
49
49
  }
50
50
 
51
- /** What a per-view region is handed.
52
- *
53
- * `selection` and `run` are declared and filled by nothing yet: cross-row
54
- * selection is its own piece of work. They are here from the start because
55
- * discovering that a region needs a second arity after the contract is
56
- * published would be a breaking change. */
51
+ /** What a per-view region is handed. */
57
52
  export interface ViewPayload<Row = Record<string, unknown>> {
58
53
  /** The rows the mode is currently drawing — its page window, not the table. */
59
54
  rows: Row[]
60
55
  /** How many rows that window holds. */
61
56
  total?: number
62
- /** Row ids the caller has picked out, where the mode offers picking. */
57
+ /** Row ids the caller has picked out, where the view offers picking. */
63
58
  selection?: string[]
64
- /** Start the region's one command, by name. */
65
- run?: (command: string) => void
59
+ /** The rows behind `selection`, as they were when picked.
60
+ *
61
+ * A list's window is replaced page by page, so a row picked on page 1 is gone
62
+ * from `rows` by the time the caller is on page 3 — and a bar whose job is to
63
+ * name what is in it cannot look one up. So the rows travel, each as the same
64
+ * `RowPayload` a per-row region is handed. */
65
+ picked?: RowPayload<Row>[]
66
+ /** Start the region's one command, with the picked rows already in it.
67
+ *
68
+ * Takes nothing. A region has exactly one command by construction, and which
69
+ * one it is was settled before this payload was built — naming it here would
70
+ * be a second copy of a decision made once, and a renderer has no honest way
71
+ * to know the name it would be passing. */
72
+ run?: () => void
66
73
  }
67
74
 
68
75
  /** Anything React will render. Deliberately structural — the package declares no
@@ -126,8 +133,14 @@ export type RowSlotId =
126
133
  * | --- | --- |
127
134
  * | `cards.empty` | the card grid when the view has no rows |
128
135
  * | `gallery.empty` | the tile grid when the view has no rows |
136
+ * | `list.selection` | the bar over a list whose rows the caller can pick out |
137
+ *
138
+ * `list.selection` is the one id that is not `<mode>.<region>`: it belongs to
139
+ * the list producer, not to a mode. The list renders the mode inside its own
140
+ * chrome and the bar is that chrome, so a picking made in the table survives a
141
+ * switch to cards and the bar has to outlive both.
129
142
  */
130
- export type ViewSlotId = 'cards.empty' | 'gallery.empty'
143
+ export type ViewSlotId = 'cards.empty' | 'gallery.empty' | 'list.selection'
131
144
 
132
145
  /**
133
146
  * Identity helper: type-checks a `register` function without changing it.