@lotics/ui 46.8.1 → 46.9.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/docs/catalog.md +24 -4
- package/examples/tpl_money.tsx +12 -12
- package/package.json +1 -1
- package/src/charge_lines.tsx +15 -11
- package/src/inline_select.tsx +12 -1
package/docs/catalog.md
CHANGED
|
@@ -1032,7 +1032,14 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
1032
1032
|
differs from the visible text is what breaks label-in-name.
|
|
1033
1033
|
- **`select`** — `Select`: rich/custom-rendered, single/multi, select-all, chips via
|
|
1034
1034
|
`renderSelected(item, { remove })` + `searchable` + `allowCustom` — the tag field is just
|
|
1035
|
-
a multi Select; opens `OptionList`.
|
|
1035
|
+
a multi Select; opens `OptionList`. **`emptyText` replaces the list's "No results" when the
|
|
1036
|
+
caller knows what the reader should DO about it** — where the missing thing gets created, why the
|
|
1037
|
+
set is narrowed. A select cannot know that; the reader is stuck at exactly that moment, and it is
|
|
1038
|
+
the one place a list may speak without becoming permanent furniture, since it shows only when the
|
|
1039
|
+
search finds nothing. Reach for it instead of a create-in-place row wherever the missing record
|
|
1040
|
+
cannot be validly made from this screen: a vehicle needs its documents, a party needs its tax id,
|
|
1041
|
+
and a stub minted here is a record that fails the next gate it meets.
|
|
1042
|
+
**Set `searchPlaceholder` whenever `allowCustom` is on**:
|
|
1036
1043
|
the create row only exists once there is a query, so on an untyped menu the placeholder is the
|
|
1037
1044
|
only always-visible place that can say a new value is allowed. `customOptionLabel` names what
|
|
1038
1045
|
the create row will
|
|
@@ -1391,9 +1398,22 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
1391
1398
|
and not interchangeable with it: a ledger has nothing to type into and this has no notion of
|
|
1392
1399
|
adjustments. A charge is ONE line — `quantity`/`unitPrice` derive the amount and the amount is
|
|
1393
1400
|
never typed; leave both out and the `amount` itself becomes the editor (`amountActions` puts a
|
|
1394
|
-
list price one tap away, `unitPriceActions` a rate).
|
|
1395
|
-
it
|
|
1396
|
-
|
|
1401
|
+
list price one tap away, `unitPriceActions` a rate). **A verb is named for the ACT, never for the
|
|
1402
|
+
value it would set** — label one with the target figure and every row already at that figure
|
|
1403
|
+
prints the same number twice in a row (three times, once the derived amount agrees), which is the
|
|
1404
|
+
normal state wherever the figures are pre-filled from the same source the verb applies. A fixed
|
|
1405
|
+
label also keeps its width equal across rows, which is what the pass-unconditionally rule exists
|
|
1406
|
+
to protect; put the figure in the accessible name if it is worth announcing. **A money verb renders
|
|
1407
|
+
INSIDE the field it acts on**, on that control's own surface — an `InlineButton` is an inner
|
|
1408
|
+
button and is never a thing standing on its own beside the control it belongs to. As a sibling it
|
|
1409
|
+
also takes its width out of the NAME's column, which is how a charge called "Vệ sinh bến bãi"
|
|
1410
|
+
renders as "Vệ sinh b…". The row's own verb (`action`, usually removal) is the exception that
|
|
1411
|
+
proves it: it belongs to the ROW rather than to any one figure, so it sits in a slot of its own,
|
|
1412
|
+
held open on every row so the money column cannot move between them.
|
|
1413
|
+
**And before reaching for a verb at all, ask whether the value is simply the field's DEFAULT** —
|
|
1414
|
+
a tariff, a list price, the figure a line is created with. Then there is no verb: the field
|
|
1415
|
+
already holds it, and clearing the field restores it. A button that re-applies what the field
|
|
1416
|
+
would hold anyway is a control for a state the reader cannot reach. `extra` carries a second control belonging
|
|
1397
1417
|
to the charge (how it was paid); `warning` names a problem on the line that has it; `locked`
|
|
1398
1418
|
turns the editors into values for a settled band and draws no verb of any kind; `empty` speaks for a record nobody has priced
|
|
1399
1419
|
yet. `formatMoney` is the BAND's, so two lines cannot disagree about a currency, and the `total`
|
package/examples/tpl_money.tsx
CHANGED
|
@@ -411,14 +411,19 @@ export function TplMoney() {
|
|
|
411
411
|
unitPrice={l.unitPrice}
|
|
412
412
|
onQuantityChange={(v) => patchLine(l.key, { quantity: v })}
|
|
413
413
|
onUnitPriceChange={(v) => patchLine(l.key, { unitPrice: v })}
|
|
414
|
-
/*
|
|
415
|
-
|
|
414
|
+
/* The rate card's ceiling, one tap, ON the field it fills — an
|
|
415
|
+
InlineButton is an inner button and never stands on its own beside
|
|
416
|
+
the control it acts on.
|
|
417
|
+
Named for the ACT, with the figure in the accessible name: a verb
|
|
418
|
+
labelled with the value it would set prints that value twice on
|
|
419
|
+
every row already holding it.
|
|
420
|
+
Unconditional + `disabled`, never conditional: a verb passed only when it
|
|
416
421
|
has work makes the field narrower on the rows that carry it, so those rows
|
|
417
422
|
stop sharing this money column with the rest. */
|
|
418
423
|
unitPriceActions={
|
|
419
424
|
<InlineButton
|
|
420
|
-
title=
|
|
421
|
-
accessibilityLabel=
|
|
425
|
+
title="Ceiling"
|
|
426
|
+
accessibilityLabel={`Apply the ceiling rate of ${money(CEILING_RATE)}`}
|
|
422
427
|
disabled={l.key !== "runs" || l.unitPrice === CEILING_RATE}
|
|
423
428
|
onPress={() => patchLine(l.key, { unitPrice: CEILING_RATE })}
|
|
424
429
|
/>
|
|
@@ -714,14 +719,9 @@ export function TplMoney() {
|
|
|
714
719
|
placeholder="—"
|
|
715
720
|
accessibilityLabel={`Amount for ${c.label}`}
|
|
716
721
|
/* The list price, one tap, ON the field it fills —
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
is a form row whose value column grows, so a verb
|
|
721
|
-
on the field's own surface moves nothing. In a
|
|
722
|
-
right-aligned money COLUMN it would eat the
|
|
723
|
-
field's width and slide the figure off the
|
|
724
|
-
column its neighbours are on. */
|
|
722
|
+
the same placement `ChargeLines` uses, because a
|
|
723
|
+
verb belongs to the control it acts on wherever
|
|
724
|
+
that control sits. */
|
|
725
725
|
actions={
|
|
726
726
|
<InlineButton
|
|
727
727
|
title={money(c.standard)}
|
package/package.json
CHANGED
package/src/charge_lines.tsx
CHANGED
|
@@ -246,16 +246,16 @@ export function ChargeLine(props: ChargeLineProps) {
|
|
|
246
246
|
placeholder="—"
|
|
247
247
|
align="right"
|
|
248
248
|
accessibilityLabel={words.unitPrice(label)}
|
|
249
|
+
/* INSIDE the field, on its own surface — an InlineButton is an inner
|
|
250
|
+
button, never a thing standing on its own beside the control it
|
|
251
|
+
acts on. Rendered as a sibling it also takes its width out of the
|
|
252
|
+
NAME's column, which is how a charge called "Vệ sinh bến bãi" came
|
|
253
|
+
to render as "Vệ sinh b...". */
|
|
254
|
+
actions={unitPriceActions}
|
|
249
255
|
/>
|
|
250
256
|
)}
|
|
251
257
|
</View>
|
|
252
258
|
)}
|
|
253
|
-
{/* BESIDE the price field, never inside it: a verb inside eats the field's
|
|
254
|
-
width, so the figure slides left and a row carrying a one-tap rate stops
|
|
255
|
-
sharing this column with a row that has none. Width is the verb's own —
|
|
256
|
-
the contract is that a caller passes it on every row and DISABLES it
|
|
257
|
-
where it has nothing to do, which is what keeps the rows equal. */}
|
|
258
|
-
{!priced || locked ? null : unitPriceActions}
|
|
259
259
|
{/* The amount ENDS the expression it derives from. Put it on a second
|
|
260
260
|
line and the reader parses left to right, then jumps back to find the
|
|
261
261
|
answer — and it lands in no column, so the total below closes nothing. */}
|
|
@@ -273,13 +273,11 @@ export function ChargeLine(props: ChargeLineProps) {
|
|
|
273
273
|
placeholder="—"
|
|
274
274
|
align="right"
|
|
275
275
|
accessibilityLabel={words.amount(label)}
|
|
276
|
+
/* The flat line's twin, and INSIDE its field for the same reason. */
|
|
277
|
+
actions={amountActions}
|
|
276
278
|
/>
|
|
277
279
|
)}
|
|
278
280
|
</View>
|
|
279
|
-
{/* The flat line's twin of `unitPriceActions`, on the figure a flat line
|
|
280
|
-
actually edits — its amount. Same contract: pass it on every row and
|
|
281
|
-
disable it where it has nothing to do. */}
|
|
282
|
-
{priced || locked ? null : amountActions}
|
|
283
281
|
{/* Held open whether or not this row has a verb, so the money column does
|
|
284
282
|
not shift between a row that can be removed and one that cannot. */}
|
|
285
283
|
<View style={styles.actionSlot}>{locked ? null : action}</View>
|
|
@@ -319,7 +317,13 @@ export function ChargeLine(props: ChargeLineProps) {
|
|
|
319
317
|
<View>
|
|
320
318
|
<View style={styles.row}>
|
|
321
319
|
<View style={styles.grow}>
|
|
322
|
-
|
|
320
|
+
{/* The name may take a SECOND line rather than lose a word. The fork
|
|
321
|
+
threshold is measured on the band, but what actually runs out is the
|
|
322
|
+
room LEFT for the name — and a money verb takes another slice of it,
|
|
323
|
+
so a band can sit comfortably above the fork width and still have
|
|
324
|
+
under 100px for a charge called "Vệ sinh bến bãi". Capped at two so a
|
|
325
|
+
pathological name cannot push the arithmetic down the page. */}
|
|
326
|
+
<Text size="sm" numberOfLines={2}>
|
|
323
327
|
{label}
|
|
324
328
|
</Text>
|
|
325
329
|
{props.extra ? <View style={styles.extra}>{props.extra}</View> : null}
|
package/src/inline_select.tsx
CHANGED
|
@@ -45,6 +45,13 @@ interface InlineSelectBaseProps<T extends string, D = unknown> {
|
|
|
45
45
|
accessibilityLabel?: string;
|
|
46
46
|
/** Show an in-menu search box; default false. */
|
|
47
47
|
searchable?: boolean;
|
|
48
|
+
/** What the list says when it has nothing to offer — no options at all, or a
|
|
49
|
+
* search that matched none. The default states the fact ("No results"); pass
|
|
50
|
+
* this when the caller knows what the reader should DO about it, which the
|
|
51
|
+
* list cannot know: where the missing thing is created, why the set is
|
|
52
|
+
* narrowed. It is the one moment a select may speak without becoming
|
|
53
|
+
* permanent furniture, because it appears only when the reader is stuck. */
|
|
54
|
+
emptyText?: string;
|
|
48
55
|
/** Offer a "create" row when the query matches no option — picking it commits the
|
|
49
56
|
* typed value (single) / adds it to the set (multi). Implies `searchable`. */
|
|
50
57
|
allowCustom?: boolean;
|
|
@@ -150,7 +157,7 @@ function InlineSelectShell(props: {
|
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
export function InlineSelect<T extends string, D = unknown>(props: InlineSelectProps<T, D>) {
|
|
153
|
-
const { options, renderOptionContent, getOptionDescription, placeholder, disabled, accessibilityLabel, searchable = false, allowCustom = false, customOptionLabel, customOptionPlacement, variant, actions, autoFocus = false } = props;
|
|
160
|
+
const { options, renderOptionContent, getOptionDescription, placeholder, disabled, accessibilityLabel, searchable = false, emptyText, allowCustom = false, customOptionLabel, customOptionPlacement, variant, actions, autoFocus = false } = props;
|
|
154
161
|
const labels = useLoticsLocale().inline;
|
|
155
162
|
const [open, setOpen] = useState(autoFocus);
|
|
156
163
|
const [saving, setSaving] = useState(false);
|
|
@@ -239,6 +246,8 @@ export function InlineSelect<T extends string, D = unknown>(props: InlineSelectP
|
|
|
239
246
|
<OptionList<T, true, D>
|
|
240
247
|
multi
|
|
241
248
|
getOptionDescription={getOptionDescription}
|
|
249
|
+
emptyText={emptyText}
|
|
250
|
+
|
|
242
251
|
search={{ mode: searchMode }}
|
|
243
252
|
options={options}
|
|
244
253
|
value={draft}
|
|
@@ -286,6 +295,8 @@ export function InlineSelect<T extends string, D = unknown>(props: InlineSelectP
|
|
|
286
295
|
>
|
|
287
296
|
<OptionList<T, false, D>
|
|
288
297
|
getOptionDescription={getOptionDescription}
|
|
298
|
+
emptyText={emptyText}
|
|
299
|
+
|
|
289
300
|
search={{ mode: searchMode }}
|
|
290
301
|
options={options}
|
|
291
302
|
value={value}
|