@lotics/ui 47.5.2 → 47.6.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 +6 -3
- package/docs/reviewing.md +5 -0
- package/package.json +1 -1
- package/src/count.tsx +31 -15
- package/src/progress_bar.tsx +20 -11
package/docs/catalog.md
CHANGED
|
@@ -389,7 +389,10 @@ Worked example: [`tpl_board`](../examples/tpl_board.tsx).
|
|
|
389
389
|
`TrendChip` (delta), `Sparkline`, `BarChart` / `LineChart` / `PieChart` (the canonical SVG
|
|
390
390
|
set — no recharts), `ProgressRing`, `ProgressBar` (its `compact` prop = ONE row, track + a
|
|
391
391
|
plain sm tabular count beside it — the cell/heading/peek-trigger meter. **The track clamps at
|
|
392
|
-
100%, the caption does not** — over its max it reads `2,100 / 2,000 (105%)`.
|
|
392
|
+
100%, the caption does not** — over its max it reads `2,100 / 2,000 (105%)`. **`max: null` is an
|
|
393
|
+
unbounded meter** — the caption states the value and the caller's `unboundedLabel`
|
|
394
|
+
(`1,250 (no limit)`) and the track stays empty, so a capped and an uncapped allowance side by
|
|
395
|
+
side stay one widget rather than a bar beside a hand-rolled lookalike. Numbers format in the
|
|
393
396
|
reader's locale, so
|
|
394
397
|
never hand-format the value you pass in — when display precision differs from the true value
|
|
395
398
|
(whole credits off a fractional balance), `formatValue` reshapes the caption text and leaves
|
|
@@ -1028,7 +1031,7 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
1028
1031
|
- **`switcher`** — `Switcher`: current-item trigger opening a popover of `MenuButton` items
|
|
1029
1032
|
(`SwitcherItem { id, label }`, `currentId`, `onSelect`) — the compact entity/workspace
|
|
1030
1033
|
switcher.
|
|
1031
|
-
- **`count`** — `Count`: a
|
|
1034
|
+
- **`count`** — `Count`: a 20px count disc that grows into a pill past one digit (`color` highlight|muted|red).
|
|
1032
1035
|
- **`shortcut_badge`** — `ShortcutBadge`: the keycap hint pill — a zinc-50 badge rendering a
|
|
1033
1036
|
shortcut from a raw string or `ShortcutDescriptor` (⌘B on Mac, Ctrl+B elsewhere); null on
|
|
1034
1037
|
small screens. `TextInputField shortcut` renders it built-in, while the field is EMPTY —
|
|
@@ -2011,7 +2014,7 @@ component rather than showing it at zero.
|
|
|
2011
2014
|
sizes the fixed label column (default 80, which fits a date and not a company name), and each
|
|
2012
2015
|
item's **`leading`** slot carries that entity's own mark ahead of its name.
|
|
2013
2016
|
- **`progress_bar`** — `ProgressBar`: the determinate meter; `compact` = ONE row, track + a
|
|
2014
|
-
plain sm tabular count beside it.
|
|
2017
|
+
plain sm tabular count beside it; `max: null` + `unboundedLabel` = the same meter with no bound.
|
|
2015
2018
|
- **`progress_ring`** — `ProgressRing`: the same meter in a circle, and the ONLY circular one. Same API shape as the bar — a real `value`/`max` rather than a
|
|
2016
2019
|
pre-computed percentage, optional `label`/`caption`, and a `format` that turns the centre
|
|
2017
2020
|
figure off. `format="none"` with no label leaves the arc bare at any size: that is the
|
package/docs/reviewing.md
CHANGED
|
@@ -137,6 +137,11 @@ rule behind it lives in the area doc named beside it; this file never restates o
|
|
|
137
137
|
- **Same shape → same treatment.** Group every element by its visual SHAPE (label-over-value,
|
|
138
138
|
figure+caption, icon+row, chip) and check each group has ONE treatment. A reader parses shape
|
|
139
139
|
before meaning, so an unexplained difference reads as an accident however principled the reason.
|
|
140
|
+
**The source-side signature is a branch that renders a kit component on one arm and a
|
|
141
|
+
hand-built lookalike on the other** — a meter with no bound, a row with no value — written to
|
|
142
|
+
match the primitive on the day it was built. The primitive moves and the copy does not, and the
|
|
143
|
+
two arms never render side by side on the fixture that was screenshotted, so measure a screen
|
|
144
|
+
where BOTH arms show. The fix is the missing state on the primitive, never a closer copy.
|
|
140
145
|
- **Singletons.** A rung/weight/ink combination appearing ONCE either wants siblings or wants to
|
|
141
146
|
join an existing class. Pick the sibling set by STRUCTURAL ROLE, not visual resemblance.
|
|
142
147
|
- **Count DISTINCT rungs on the surface, then name the role each one carries.** More rungs than
|
package/package.json
CHANGED
package/src/count.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Text } from "./text";
|
|
2
|
-
import { View } from "react-native";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
3
|
import { colors } from "./colors";
|
|
4
4
|
|
|
5
5
|
interface CountProps {
|
|
@@ -7,28 +7,33 @@ interface CountProps {
|
|
|
7
7
|
color?: "highlight" | "muted" | "red";
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* A count in a 20px disc that grows into a pill past one digit. `xs` because its line box (18)
|
|
12
|
+
* is the only body rung that fits inside the disc — `sm` is 24 tall, taller than the disc, so
|
|
13
|
+
* the digit was clipped and sat off-centre.
|
|
14
|
+
*/
|
|
10
15
|
export function Count(props: CountProps) {
|
|
11
16
|
const { count, color = "muted" } = props;
|
|
12
17
|
|
|
13
18
|
return (
|
|
14
19
|
<View
|
|
15
|
-
style={
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
: undefined,
|
|
27
|
-
}}
|
|
20
|
+
style={[
|
|
21
|
+
styles.pill,
|
|
22
|
+
{
|
|
23
|
+
backgroundColor:
|
|
24
|
+
color === "highlight"
|
|
25
|
+
? colors.zinc["700"]
|
|
26
|
+
: color === "red"
|
|
27
|
+
? colors.red["700"]
|
|
28
|
+
: undefined,
|
|
29
|
+
},
|
|
30
|
+
]}
|
|
28
31
|
>
|
|
29
32
|
<Text
|
|
30
33
|
numberOfLines={1}
|
|
31
|
-
size="
|
|
34
|
+
size="xs"
|
|
35
|
+
weight="medium"
|
|
36
|
+
tabular
|
|
32
37
|
color={color === "muted" ? "zinc-500" : "inverted"}
|
|
33
38
|
userSelect="none"
|
|
34
39
|
>
|
|
@@ -37,3 +42,14 @@ export function Count(props: CountProps) {
|
|
|
37
42
|
</View>
|
|
38
43
|
);
|
|
39
44
|
}
|
|
45
|
+
|
|
46
|
+
const styles = StyleSheet.create({
|
|
47
|
+
pill: {
|
|
48
|
+
minWidth: 20,
|
|
49
|
+
height: 20,
|
|
50
|
+
paddingHorizontal: 6,
|
|
51
|
+
justifyContent: "center",
|
|
52
|
+
alignItems: "center",
|
|
53
|
+
borderRadius: 999,
|
|
54
|
+
},
|
|
55
|
+
});
|
package/src/progress_bar.tsx
CHANGED
|
@@ -8,15 +8,20 @@ export type ProgressBarFormat = "percentage" | "fraction" | "none";
|
|
|
8
8
|
|
|
9
9
|
export interface ProgressBarProps {
|
|
10
10
|
value: number;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
|
|
11
|
+
/** `null` = no bound. The caption states the value and `unboundedLabel` in place of a
|
|
12
|
+
* fraction and the track stays empty, so a capped and an uncapped allowance side by side
|
|
13
|
+
* read as one widget. */
|
|
14
|
+
max: number | null;
|
|
15
|
+
/** What this bar measures ("Đã nhận", "Tiến độ giao") — the `Eyebrow` above the
|
|
16
|
+
* bar. Omit inside a band that already names it. */
|
|
15
17
|
title?: string;
|
|
16
18
|
/** Caption above-right of the bar: `percentage` → "50%", `fraction` →
|
|
17
19
|
* "1,250 / 2,500 (50%)" (separators follow the reader's locale). Reports the
|
|
18
20
|
* TRUE ratio — over `max` it reads "105%" while the track stays clamped. */
|
|
19
21
|
format?: ProgressBarFormat;
|
|
22
|
+
/** The qualifier beside the value when `max` is null — "1,250 (no limit)". The kit carries
|
|
23
|
+
* no i18n, so the caller supplies the words; without them the caption is the bare value. */
|
|
24
|
+
unboundedLabel?: string;
|
|
20
25
|
/**
|
|
21
26
|
* How `value` and `max` render inside the caption. Defaults to the reader's locale grouping.
|
|
22
27
|
*
|
|
@@ -61,6 +66,7 @@ export function ProgressBar(props: ProgressBarProps) {
|
|
|
61
66
|
format = "percentage",
|
|
62
67
|
formatValue,
|
|
63
68
|
formatPercent,
|
|
69
|
+
unboundedLabel,
|
|
64
70
|
color = colors.blue["500"],
|
|
65
71
|
completeColor = colors.green["500"],
|
|
66
72
|
compact = false,
|
|
@@ -76,17 +82,18 @@ export function ProgressBar(props: ProgressBarProps) {
|
|
|
76
82
|
* never exceeds its max, so this only diverges for the over-allowance case, where the true
|
|
77
83
|
* figure is the whole point.
|
|
78
84
|
*/
|
|
79
|
-
const ratio = max > 0 ? Math.max(0, (value / max) * 100) : 0;
|
|
85
|
+
const ratio = max !== null && max > 0 ? Math.max(0, (value / max) * 100) : 0;
|
|
80
86
|
const percentage = Math.min(100, ratio);
|
|
81
87
|
const isComplete = ratio >= 100;
|
|
82
88
|
const num = formatValue ?? ((n: number) => n.toLocaleString(localeTag));
|
|
83
89
|
const pct = formatPercent
|
|
84
90
|
? (r: number) => formatPercent(r / 100)
|
|
85
91
|
: (r: number) => `${Math.round(r)}%`;
|
|
92
|
+
const unbounded = unboundedLabel ? `${num(value)} (${unboundedLabel})` : num(value);
|
|
86
93
|
|
|
87
94
|
if (compact) {
|
|
88
95
|
const label =
|
|
89
|
-
format === "percentage" ? pct(ratio) : `${num(value)}/${num(max)}`;
|
|
96
|
+
max === null ? unbounded : format === "percentage" ? pct(ratio) : `${num(value)}/${num(max)}`;
|
|
90
97
|
return (
|
|
91
98
|
<View style={styles.compactRow}>
|
|
92
99
|
<View style={[styles.track, styles.compactTrack]}>
|
|
@@ -109,11 +116,13 @@ export function ProgressBar(props: ProgressBarProps) {
|
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
const caption =
|
|
112
|
-
format === "
|
|
113
|
-
?
|
|
114
|
-
:
|
|
115
|
-
?
|
|
116
|
-
:
|
|
119
|
+
format === "none"
|
|
120
|
+
? null
|
|
121
|
+
: max === null
|
|
122
|
+
? unbounded
|
|
123
|
+
: format === "fraction"
|
|
124
|
+
? `${num(value)} / ${num(max)} (${pct(ratio)})`
|
|
125
|
+
: pct(ratio);
|
|
117
126
|
|
|
118
127
|
return (
|
|
119
128
|
<View style={styles.container}>
|