panelui-native 0.59.0 → 0.61.1
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 +3 -1
- package/lib/module/components/area-chart/index.js +86 -1
- package/lib/module/components/area-chart/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +112 -2
- package/lib/module/components/bar-chart/index.js.map +1 -1
- package/lib/module/components/fab/index.js +63 -30
- package/lib/module/components/fab/index.js.map +1 -1
- package/lib/module/components/selection-mode/index.js +805 -0
- package/lib/module/components/selection-mode/index.js.map +1 -0
- package/lib/module/components/tabs/index.js +384 -341
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/treemap-chart/index.js +957 -0
- package/lib/module/components/treemap-chart/index.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/chart.js +131 -4
- package/lib/module/utils/chart.js.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts +28 -1
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts +33 -3
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/fab/index.d.ts +11 -5
- package/lib/typescript/src/components/fab/index.d.ts.map +1 -1
- package/lib/typescript/src/components/selection-mode/index.d.ts +498 -0
- package/lib/typescript/src/components/selection-mode/index.d.ts.map +1 -0
- package/lib/typescript/src/components/tabs/index.d.ts +64 -46
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/treemap-chart/index.d.ts +317 -0
- package/lib/typescript/src/components/treemap-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +26 -1
- package/lib/typescript/src/utils/chart.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/area-chart/index.tsx +86 -1
- package/src/components/bar-chart/index.tsx +115 -4
- package/src/components/fab/index.tsx +66 -31
- package/src/components/selection-mode/index.tsx +1027 -0
- package/src/components/tabs/index.tsx +452 -413
- package/src/components/treemap-chart/index.tsx +1175 -0
- package/src/index.ts +30 -0
- package/src/utils/chart.ts +158 -4
package/src/index.ts
CHANGED
|
@@ -332,6 +332,16 @@ export {
|
|
|
332
332
|
type SelectGroupProps,
|
|
333
333
|
type SelectPresentation,
|
|
334
334
|
} from './components/select';
|
|
335
|
+
export {
|
|
336
|
+
SelectionMode,
|
|
337
|
+
useSelectionMode,
|
|
338
|
+
type SelectionModeProps,
|
|
339
|
+
type SelectionModeItemProps,
|
|
340
|
+
type SelectionModeIndicatorProps,
|
|
341
|
+
type SelectionModeHeaderProps,
|
|
342
|
+
type SelectionModeBarProps,
|
|
343
|
+
type SelectionModeActionProps,
|
|
344
|
+
} from './components/selection-mode';
|
|
335
345
|
export {
|
|
336
346
|
SectionRail,
|
|
337
347
|
type SectionRailProps,
|
|
@@ -348,6 +358,9 @@ export {
|
|
|
348
358
|
type TabsListProps,
|
|
349
359
|
type TabsTriggerProps,
|
|
350
360
|
type TabsContentProps,
|
|
361
|
+
type TabsVariant,
|
|
362
|
+
type TabsKeepMounted,
|
|
363
|
+
type TabsAnimation,
|
|
351
364
|
} from './components/tabs';
|
|
352
365
|
export {
|
|
353
366
|
Table,
|
|
@@ -943,6 +956,23 @@ export {
|
|
|
943
956
|
type ToastIndicatorProps,
|
|
944
957
|
type ToastCloseProps,
|
|
945
958
|
} from './components/toast';
|
|
959
|
+
export {
|
|
960
|
+
TreemapChart,
|
|
961
|
+
useTreemapChart,
|
|
962
|
+
squarifyLayout,
|
|
963
|
+
type TreemapChartProps,
|
|
964
|
+
type TreemapChartHandle,
|
|
965
|
+
type TreemapChartStatus,
|
|
966
|
+
type TreemapDatum,
|
|
967
|
+
type TreemapTile,
|
|
968
|
+
type TreemapRect,
|
|
969
|
+
type TreemapChartTilesProps,
|
|
970
|
+
type TreemapChartLabelsProps,
|
|
971
|
+
type TreemapChartTooltipProps,
|
|
972
|
+
type TreemapChartLegendProps,
|
|
973
|
+
type TreemapChartHeaderProps,
|
|
974
|
+
type TreemapChartSkeletonProps,
|
|
975
|
+
} from './components/treemap-chart';
|
|
946
976
|
export {
|
|
947
977
|
Tree,
|
|
948
978
|
type TreeProps,
|
package/src/utils/chart.ts
CHANGED
|
@@ -36,12 +36,166 @@ export function useSeriesColor(
|
|
|
36
36
|
return explicit ?? (typeof token === 'string' ? token : FALLBACK_SERIES[index - 1]!);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* The `r, g, b` of a colour string, or `null` for one that cannot be read.
|
|
41
|
+
*
|
|
42
|
+
* Hex and `rgb()` / `rgba()` only, which is every form a theme token and a
|
|
43
|
+
* series colour take. A `null` is the caller's signal to keep whatever it
|
|
44
|
+
* would have used anyway, rather than a guess that could be the wrong end of
|
|
45
|
+
* the scale.
|
|
46
|
+
*/
|
|
47
|
+
function channelsOf(color: string): [number, number, number] | null {
|
|
48
|
+
const value = color.trim().toLowerCase();
|
|
49
|
+
|
|
50
|
+
if (value.startsWith('#')) {
|
|
51
|
+
const body =
|
|
52
|
+
value.length === 4 || value.length === 5
|
|
53
|
+
? value
|
|
54
|
+
.slice(1, 4)
|
|
55
|
+
.split('')
|
|
56
|
+
.map((digit) => digit + digit)
|
|
57
|
+
.join('')
|
|
58
|
+
: value.slice(1, 7);
|
|
59
|
+
if (body.length !== 6 || /[^0-9a-f]/.test(body)) return null;
|
|
60
|
+
return [
|
|
61
|
+
parseInt(body.slice(0, 2), 16),
|
|
62
|
+
parseInt(body.slice(2, 4), 16),
|
|
63
|
+
parseInt(body.slice(4, 6), 16),
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const fn = /^rgba?\(([^)]+)\)$/.exec(value);
|
|
68
|
+
if (!fn) return null;
|
|
69
|
+
|
|
70
|
+
const parts = fn[1]!.split(/[\s,/]+/).filter(Boolean);
|
|
71
|
+
if (parts.length < 3) return null;
|
|
72
|
+
|
|
73
|
+
const channels = parts.slice(0, 3).map((raw) => {
|
|
74
|
+
const n = parseFloat(raw);
|
|
75
|
+
if (Number.isNaN(n)) return null;
|
|
76
|
+
return raw.endsWith('%') ? (n / 100) * 255 : n;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return channels.some((c) => c === null)
|
|
80
|
+
? null
|
|
81
|
+
: (channels as [number, number, number]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** One channel, undone from the sRGB transfer curve. */
|
|
85
|
+
function linear(value: number): number {
|
|
86
|
+
const c = Math.min(1, Math.max(0, value / 255));
|
|
87
|
+
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Relative luminance, 0 for black and 1 for white. */
|
|
91
|
+
function luminanceOf(rgb: [number, number, number]): number {
|
|
92
|
+
return 0.2126 * linear(rgb[0]) + 0.7152 * linear(rgb[1]) + 0.0722 * linear(rgb[2]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Text laid over a fill: the solid colour and the softer one under it. */
|
|
96
|
+
export interface ChartInk {
|
|
97
|
+
color: string;
|
|
98
|
+
muted: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const LIGHT_INK: ChartInk = { color: '#ffffff', muted: 'rgba(255,255,255,0.82)' };
|
|
102
|
+
const DARK_INK: ChartInk = { color: '#0a0a0a', muted: 'rgba(10,10,10,0.82)' };
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The contrast ratio white keeps on a fill before the label is turned over.
|
|
106
|
+
*
|
|
107
|
+
* 3:1, which is the bar for bold text at this size. Higher and the mid-tone
|
|
108
|
+
* hues every theme uses for its second series onwards flip to dark text as
|
|
109
|
+
* well, which is a different chart rather than a legible one; lower and the
|
|
110
|
+
* pale fills this exists for stay unreadable.
|
|
111
|
+
*/
|
|
112
|
+
const INK_FLOOR = 3;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The text colour to lay over `fill`: white while white can be read on it,
|
|
116
|
+
* near-black once it cannot.
|
|
117
|
+
*
|
|
118
|
+
* Neither the fill nor the theme can decide this on its own. A series colour is
|
|
119
|
+
* a colour, not a mode — a theme is free to make it near-white, and a fixed
|
|
120
|
+
* white label on it disappears while the foreground token would be just as
|
|
121
|
+
* wrong on the next tile along.
|
|
122
|
+
*
|
|
123
|
+
* White is kept wherever it holds up, rather than always taking whichever ink
|
|
124
|
+
* scores higher. Charts are drawn white-on-hue, and turning over every tile
|
|
125
|
+
* that a darker label would technically suit changes what the chart looks like
|
|
126
|
+
* to fix the two that were unreadable.
|
|
127
|
+
*
|
|
128
|
+
* `behind` and `opacity` are for a fill drawn part-transparent: what the reader
|
|
129
|
+
* sees is the blend, and a half-strength tile on a pale ground is lighter than
|
|
130
|
+
* the colour it was given. Compositing happens in gamma space, the way the
|
|
131
|
+
* renderer does it, before the luminance is taken.
|
|
132
|
+
*/
|
|
133
|
+
export function inkOn(fill: string, behind?: string, opacity = 1): ChartInk {
|
|
134
|
+
const front = channelsOf(fill);
|
|
135
|
+
if (!front) return LIGHT_INK;
|
|
136
|
+
|
|
137
|
+
const alpha = Math.min(1, Math.max(0, opacity));
|
|
138
|
+
const ground = alpha < 1 && behind ? channelsOf(behind) : null;
|
|
139
|
+
const seen: [number, number, number] = ground
|
|
140
|
+
? [
|
|
141
|
+
alpha * front[0] + (1 - alpha) * ground[0],
|
|
142
|
+
alpha * front[1] + (1 - alpha) * ground[1],
|
|
143
|
+
alpha * front[2] + (1 - alpha) * ground[2],
|
|
144
|
+
]
|
|
145
|
+
: front;
|
|
146
|
+
|
|
147
|
+
return 1.05 / (luminanceOf(seen) + 0.05) >= INK_FLOOR ? LIGHT_INK : DARK_INK;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The suffixes, smallest first. Each one is a thousand of the one before it. */
|
|
151
|
+
const MAGNITUDES = ['k', 'M', 'B', 'T'] as const;
|
|
152
|
+
|
|
153
|
+
/** Drop the zeros a fixed decimal count left behind: `12.0` is `12`. */
|
|
154
|
+
function trimZeros(text: string): string {
|
|
155
|
+
return text.includes('.') ? text.replace(/\.?0+$/, '') : text;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* One decimal while the mantissa is a single digit, none after that.
|
|
160
|
+
*
|
|
161
|
+
* `1.2k` and `12k` rather than `1.2k` and `12.4k`: the second decimal place is
|
|
162
|
+
* spurious precision on a tick, and the label has one line to fit in.
|
|
163
|
+
*/
|
|
164
|
+
function mantissa(value: number): string {
|
|
165
|
+
return trimZeros(value.toFixed(Math.abs(value) < 10 ? 1 : 0));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** `12k` rather than `12400` — a readout has one line to say it in. */
|
|
40
169
|
export function compactNumber(value: number): string {
|
|
170
|
+
if (!Number.isFinite(value)) return String(value);
|
|
171
|
+
|
|
41
172
|
const abs = Math.abs(value);
|
|
42
|
-
if (abs
|
|
43
|
-
|
|
44
|
-
|
|
173
|
+
if (abs < 1_000) {
|
|
174
|
+
if (Number.isInteger(value)) return String(value);
|
|
175
|
+
// Below one, a single decimal rounds most values to `0`, which reads as
|
|
176
|
+
// nothing rather than as a small number.
|
|
177
|
+
return trimZeros(value.toFixed(abs < 1 ? 2 : 1));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let index = 0;
|
|
181
|
+
let scaled = value / 1_000;
|
|
182
|
+
while (Math.abs(scaled) >= 1_000 && index < MAGNITUDES.length - 1) {
|
|
183
|
+
scaled /= 1_000;
|
|
184
|
+
index += 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/*
|
|
188
|
+
* Rounding can push a mantissa over a thousand — 999,999 is `1000.0k` on the
|
|
189
|
+
* way in, which is a thousand thousands written the long way. Formatting
|
|
190
|
+
* first and promoting after is what makes it `1M`.
|
|
191
|
+
*/
|
|
192
|
+
let text = mantissa(scaled);
|
|
193
|
+
if (Math.abs(Number(text)) >= 1_000 && index < MAGNITUDES.length - 1) {
|
|
194
|
+
index += 1;
|
|
195
|
+
text = mantissa(scaled / 1_000);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return `${text}${MAGNITUDES[index]}`;
|
|
45
199
|
}
|
|
46
200
|
|
|
47
201
|
/** The drawable box inside a chart, after its padding is taken off. */
|