panelui-native 0.22.5 → 0.25.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/README.md +8 -3
- package/lib/module/components/area-chart/index.js +826 -0
- package/lib/module/components/area-chart/index.js.map +1 -0
- package/lib/module/components/avatar/index.js +1 -1
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +853 -0
- package/lib/module/components/bar-chart/index.js.map +1 -0
- package/lib/module/components/bottom-sheet/index.js +207 -23
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/breadcrumb/index.js +275 -0
- package/lib/module/components/breadcrumb/index.js.map +1 -0
- package/lib/module/components/chip/index.js +2 -2
- package/lib/module/components/direction/index.js +13 -19
- package/lib/module/components/direction/index.js.map +1 -1
- package/lib/module/components/frame/index.js +17 -0
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +28 -8
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/input-group/index.js +2 -2
- package/lib/module/components/input-group/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +4 -146
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/loader/index.js +758 -0
- package/lib/module/components/loader/index.js.map +1 -0
- package/lib/module/components/map/basemap.js +265 -0
- package/lib/module/components/map/basemap.js.map +1 -0
- package/lib/module/components/map/index.js +850 -0
- package/lib/module/components/map/index.js.map +1 -0
- package/lib/module/components/map/maplibre.js +87 -0
- package/lib/module/components/map/maplibre.js.map +1 -0
- package/lib/module/components/message/index.js +8 -7
- package/lib/module/components/message/index.js.map +1 -1
- package/lib/module/components/otp-input/index.js +2 -2
- package/lib/module/components/progress/index.js +8 -2
- package/lib/module/components/progress/index.js.map +1 -1
- package/lib/module/components/ring-chart/index.js +423 -0
- package/lib/module/components/ring-chart/index.js.map +1 -0
- package/lib/module/components/section-rail/index.js +2 -2
- package/lib/module/components/separator/index.js +76 -22
- package/lib/module/components/separator/index.js.map +1 -1
- package/lib/module/components/shimmer/index.js +10 -1
- package/lib/module/components/shimmer/index.js.map +1 -1
- package/lib/module/components/slider/index.js +19 -5
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/steps/index.js +1 -1
- package/lib/module/components/switch/index.js +6 -1
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/typography/index.js +5 -0
- package/lib/module/components/typography/index.js.map +1 -1
- package/lib/module/hooks/use-direction.js +43 -0
- package/lib/module/hooks/use-direction.js.map +1 -0
- package/lib/module/icons/index.js +117 -2
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +8 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/text.js +30 -10
- package/lib/module/primitives/text.js.map +1 -1
- package/lib/module/utils/chart.js +279 -0
- package/lib/module/utils/chart.js.map +1 -0
- package/lib/typescript/src/components/area-chart/index.d.ts +197 -0
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/bar-chart/index.d.ts +228 -0
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +75 -4
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/breadcrumb/index.d.ts +100 -0
- package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -0
- package/lib/typescript/src/components/direction/index.d.ts +11 -10
- package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
- package/lib/typescript/src/components/frame/index.d.ts +17 -0
- package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts +22 -3
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts +1 -0
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/loader/index.d.ts +38 -0
- package/lib/typescript/src/components/loader/index.d.ts.map +1 -0
- package/lib/typescript/src/components/map/basemap.d.ts +76 -0
- package/lib/typescript/src/components/map/basemap.d.ts.map +1 -0
- package/lib/typescript/src/components/map/index.d.ts +367 -0
- package/lib/typescript/src/components/map/index.d.ts.map +1 -0
- package/lib/typescript/src/components/map/maplibre.d.ts +193 -0
- package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -0
- package/lib/typescript/src/components/message/index.d.ts.map +1 -1
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
- package/lib/typescript/src/components/ring-chart/index.d.ts +156 -0
- package/lib/typescript/src/components/ring-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/separator/index.d.ts +36 -1
- package/lib/typescript/src/components/separator/index.d.ts.map +1 -1
- package/lib/typescript/src/components/shimmer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/typography/index.d.ts +6 -0
- package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-direction.d.ts +20 -0
- package/lib/typescript/src/hooks/use-direction.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +5 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +9 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/text.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +96 -0
- package/lib/typescript/src/utils/chart.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/components/area-chart/index.tsx +994 -0
- package/src/components/avatar/index.tsx +1 -1
- package/src/components/bar-chart/index.tsx +1048 -0
- package/src/components/bottom-sheet/index.tsx +280 -20
- package/src/components/breadcrumb/index.tsx +365 -0
- package/src/components/chip/index.tsx +2 -2
- package/src/components/direction/index.tsx +20 -21
- package/src/components/frame/index.tsx +17 -0
- package/src/components/heatmap-chart/index.tsx +58 -11
- package/src/components/input-group/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +13 -181
- package/src/components/loader/index.tsx +812 -0
- package/src/components/map/basemap.ts +304 -0
- package/src/components/map/index.tsx +1118 -0
- package/src/components/map/maplibre.ts +201 -0
- package/src/components/message/index.tsx +8 -7
- package/src/components/otp-input/index.tsx +2 -2
- package/src/components/progress/index.tsx +10 -2
- package/src/components/ring-chart/index.tsx +543 -0
- package/src/components/section-rail/index.tsx +2 -2
- package/src/components/separator/index.tsx +77 -19
- package/src/components/shimmer/index.tsx +10 -1
- package/src/components/slider/index.tsx +20 -5
- package/src/components/steps/index.tsx +1 -1
- package/src/components/switch/index.tsx +6 -1
- package/src/components/typography/index.tsx +5 -0
- package/src/hooks/use-direction.ts +45 -0
- package/src/icons/index.tsx +66 -0
- package/src/index.ts +84 -0
- package/src/primitives/text.tsx +25 -7
- package/src/utils/chart.ts +355 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Breadcrumb — the trail of where you are in a hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* A row of links back up the tree, ending in the page you are on. The last
|
|
5
|
+
* crumb is not a link: you cannot navigate to where you already are, so it is
|
|
6
|
+
* a plain, current-marked label rather than a dead tappable target.
|
|
7
|
+
*
|
|
8
|
+
* The separators are the component's job, not yours. `Breadcrumb.List` drops a
|
|
9
|
+
* chevron between each crumb it holds, so a trail is just its items — there is
|
|
10
|
+
* no separator to forget, mis-order, or leave dangling at the end. Change the
|
|
11
|
+
* glyph once with `separator` on the root and every gap follows.
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <Breadcrumb>
|
|
15
|
+
* <Breadcrumb.List>
|
|
16
|
+
* <Breadcrumb.Item>
|
|
17
|
+
* <Breadcrumb.Link onPress={goHome}>Home</Breadcrumb.Link>
|
|
18
|
+
* </Breadcrumb.Item>
|
|
19
|
+
* <Breadcrumb.Item>
|
|
20
|
+
* <Breadcrumb.Link onPress={goProjects}>Projects</Breadcrumb.Link>
|
|
21
|
+
* </Breadcrumb.Item>
|
|
22
|
+
* <Breadcrumb.Item>
|
|
23
|
+
* <Breadcrumb.Page>PanelUI</Breadcrumb.Page>
|
|
24
|
+
* </Breadcrumb.Item>
|
|
25
|
+
* </Breadcrumb.List>
|
|
26
|
+
* </Breadcrumb>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* A deep trail on a narrow phone does not wrap into a paragraph: give the list
|
|
30
|
+
* a `maxItems` and it keeps the first and last crumbs, folding the middle into
|
|
31
|
+
* a single ellipsis. Hand the ellipsis an `onEllipsisPress` and it becomes the
|
|
32
|
+
* handle for a menu of the hidden steps.
|
|
33
|
+
*/
|
|
34
|
+
import {
|
|
35
|
+
Children,
|
|
36
|
+
createContext,
|
|
37
|
+
forwardRef,
|
|
38
|
+
isValidElement,
|
|
39
|
+
useContext,
|
|
40
|
+
type ReactNode,
|
|
41
|
+
} from 'react';
|
|
42
|
+
import { View, type ViewProps } from 'react-native';
|
|
43
|
+
import { tv, type VariantProps } from 'tailwind-variants';
|
|
44
|
+
import { useCSSVariable } from 'uniwind';
|
|
45
|
+
import { ChevronRightIcon, EllipsisIcon } from '../../icons';
|
|
46
|
+
import {
|
|
47
|
+
AnimatedPressable,
|
|
48
|
+
type AnimatedPressableProps,
|
|
49
|
+
} from '../../primitives/animated-pressable';
|
|
50
|
+
import { Text, type TextProps } from '../../primitives/text';
|
|
51
|
+
import { cn } from '../../utils/cn';
|
|
52
|
+
|
|
53
|
+
type BreadcrumbSize = 'sm' | 'default';
|
|
54
|
+
|
|
55
|
+
const crumbText = tv({
|
|
56
|
+
base: '',
|
|
57
|
+
variants: {
|
|
58
|
+
size: {
|
|
59
|
+
sm: 'text-xs',
|
|
60
|
+
default: 'text-sm',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
defaultVariants: { size: 'default' },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sub-parts read the density and the shared separator glyph from here, so the
|
|
68
|
+
* trail is configured once on the root rather than repeated per crumb.
|
|
69
|
+
*/
|
|
70
|
+
const BreadcrumbContext = createContext<{
|
|
71
|
+
size: BreadcrumbSize;
|
|
72
|
+
separator: ReactNode;
|
|
73
|
+
}>({
|
|
74
|
+
size: 'default',
|
|
75
|
+
separator: null,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const useBreadcrumb = () => useContext(BreadcrumbContext);
|
|
79
|
+
|
|
80
|
+
export interface BreadcrumbProps extends ViewProps {
|
|
81
|
+
className?: string;
|
|
82
|
+
/** Text density for every crumb. `sm` for a dense header bar. */
|
|
83
|
+
size?: BreadcrumbSize;
|
|
84
|
+
/**
|
|
85
|
+
* The glyph `Breadcrumb.List` places between crumbs. Defaults to a chevron;
|
|
86
|
+
* pass a `<Text>/</Text>`, a slash, or any node to change every gap at once.
|
|
87
|
+
*/
|
|
88
|
+
separator?: ReactNode;
|
|
89
|
+
children?: ReactNode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The trail's landmark. Labelled for assistive tech so the row is announced as
|
|
94
|
+
* "Breadcrumb" rather than an anonymous run of links.
|
|
95
|
+
*/
|
|
96
|
+
const BreadcrumbRoot = forwardRef<View, BreadcrumbProps>(
|
|
97
|
+
({ className, size = 'default', separator, children, ...props }, ref) => (
|
|
98
|
+
<BreadcrumbContext.Provider value={{ size, separator: separator ?? null }}>
|
|
99
|
+
<View
|
|
100
|
+
ref={ref}
|
|
101
|
+
accessibilityLabel="Breadcrumb"
|
|
102
|
+
className={cn('w-full', className)}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
{children}
|
|
106
|
+
</View>
|
|
107
|
+
</BreadcrumbContext.Provider>
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
BreadcrumbRoot.displayName = 'Breadcrumb';
|
|
111
|
+
|
|
112
|
+
export interface BreadcrumbListProps extends ViewProps {
|
|
113
|
+
className?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Collapse the trail once it holds more than this many crumbs, so a deep
|
|
116
|
+
* path never wraps into a block of text on a narrow screen. The first
|
|
117
|
+
* `itemsBeforeCollapse` and last `itemsAfterCollapse` survive; the middle
|
|
118
|
+
* folds into a single ellipsis.
|
|
119
|
+
*/
|
|
120
|
+
maxItems?: number;
|
|
121
|
+
/** How many leading crumbs to keep when collapsing. Default 1. */
|
|
122
|
+
itemsBeforeCollapse?: number;
|
|
123
|
+
/** How many trailing crumbs to keep when collapsing. Default 1. */
|
|
124
|
+
itemsAfterCollapse?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Makes the collapsed ellipsis pressable — the handle for a menu listing the
|
|
127
|
+
* hidden steps. Without it the ellipsis is a static marker.
|
|
128
|
+
*/
|
|
129
|
+
onEllipsisPress?: () => void;
|
|
130
|
+
children?: ReactNode;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** A private marker: the slot the collapsed middle crumbs fold into. */
|
|
134
|
+
const COLLAPSE = Symbol('breadcrumb-collapse');
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The crumb row. It owns the separators — one between every pair of crumbs and
|
|
138
|
+
* none at the ends — and the collapsing, so a caller only ever lists items.
|
|
139
|
+
*/
|
|
140
|
+
const BreadcrumbList = forwardRef<View, BreadcrumbListProps>(
|
|
141
|
+
(
|
|
142
|
+
{
|
|
143
|
+
className,
|
|
144
|
+
maxItems,
|
|
145
|
+
itemsBeforeCollapse = 1,
|
|
146
|
+
itemsAfterCollapse = 1,
|
|
147
|
+
onEllipsisPress,
|
|
148
|
+
children,
|
|
149
|
+
...props
|
|
150
|
+
},
|
|
151
|
+
ref
|
|
152
|
+
) => {
|
|
153
|
+
const crumbs = Children.toArray(children).filter(isValidElement);
|
|
154
|
+
|
|
155
|
+
// Fold the middle only when hiding something actually shortens the row:
|
|
156
|
+
// keeping N-1 of N crumbs plus an ellipsis saves nothing.
|
|
157
|
+
let sequence: Array<ReactNode | typeof COLLAPSE> = crumbs;
|
|
158
|
+
if (
|
|
159
|
+
maxItems !== undefined &&
|
|
160
|
+
crumbs.length > maxItems &&
|
|
161
|
+
itemsBeforeCollapse + itemsAfterCollapse < crumbs.length
|
|
162
|
+
) {
|
|
163
|
+
const head = crumbs.slice(0, Math.max(0, itemsBeforeCollapse));
|
|
164
|
+
const tail =
|
|
165
|
+
itemsAfterCollapse > 0 ? crumbs.slice(crumbs.length - itemsAfterCollapse) : [];
|
|
166
|
+
sequence = [...head, COLLAPSE, ...tail];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<View
|
|
171
|
+
ref={ref}
|
|
172
|
+
accessibilityRole="list"
|
|
173
|
+
className={cn('flex-row flex-wrap items-center gap-1.5', className)}
|
|
174
|
+
{...props}
|
|
175
|
+
>
|
|
176
|
+
{sequence.map((node, i) => (
|
|
177
|
+
<View key={i} className="flex-row items-center gap-1.5">
|
|
178
|
+
{i > 0 ? <BreadcrumbSeparator /> : null}
|
|
179
|
+
{node === COLLAPSE ? (
|
|
180
|
+
<BreadcrumbEllipsis onPress={onEllipsisPress} />
|
|
181
|
+
) : (
|
|
182
|
+
node
|
|
183
|
+
)}
|
|
184
|
+
</View>
|
|
185
|
+
))}
|
|
186
|
+
</View>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
BreadcrumbList.displayName = 'Breadcrumb.List';
|
|
191
|
+
|
|
192
|
+
export interface BreadcrumbItemProps extends ViewProps {
|
|
193
|
+
className?: string;
|
|
194
|
+
children?: ReactNode;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** One crumb: wraps a link or the current page. */
|
|
198
|
+
const BreadcrumbItem = forwardRef<View, BreadcrumbItemProps>(
|
|
199
|
+
({ className, children, ...props }, ref) => (
|
|
200
|
+
<View
|
|
201
|
+
ref={ref}
|
|
202
|
+
className={cn('flex-row items-center', className)}
|
|
203
|
+
{...props}
|
|
204
|
+
>
|
|
205
|
+
{children}
|
|
206
|
+
</View>
|
|
207
|
+
)
|
|
208
|
+
);
|
|
209
|
+
BreadcrumbItem.displayName = 'Breadcrumb.Item';
|
|
210
|
+
|
|
211
|
+
export interface BreadcrumbLinkProps
|
|
212
|
+
extends Omit<AnimatedPressableProps, 'children'> {
|
|
213
|
+
className?: string;
|
|
214
|
+
/** Text style for the crumb's label. */
|
|
215
|
+
textClassName?: string;
|
|
216
|
+
children?: ReactNode;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* A navigable crumb — an ancestor you can jump back to. Muted until pressed,
|
|
221
|
+
* so the current page (which is not a link) reads as the emphasised end of the
|
|
222
|
+
* trail. A string child is wrapped in the crumb text style; anything else
|
|
223
|
+
* renders as given.
|
|
224
|
+
*/
|
|
225
|
+
const BreadcrumbLink = forwardRef<View, BreadcrumbLinkProps>(
|
|
226
|
+
({ className, textClassName, children, ...props }, ref) => {
|
|
227
|
+
const { size } = useBreadcrumb();
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<AnimatedPressable
|
|
231
|
+
ref={ref}
|
|
232
|
+
accessibilityRole="link"
|
|
233
|
+
pressScale={1}
|
|
234
|
+
pressOpacity={0.6}
|
|
235
|
+
className={cn('flex-row items-center', className)}
|
|
236
|
+
{...props}
|
|
237
|
+
>
|
|
238
|
+
{typeof children === 'string' ? (
|
|
239
|
+
<Text className={crumbText({ size, className: cn('text-muted-foreground', textClassName) })}>
|
|
240
|
+
{children}
|
|
241
|
+
</Text>
|
|
242
|
+
) : (
|
|
243
|
+
children
|
|
244
|
+
)}
|
|
245
|
+
</AnimatedPressable>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
BreadcrumbLink.displayName = 'Breadcrumb.Link';
|
|
250
|
+
|
|
251
|
+
export interface BreadcrumbPageProps extends TextProps {
|
|
252
|
+
className?: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The trailing crumb: where you are now. Not a link — `aria-current="page"`
|
|
257
|
+
* marks it as the destination, and it is painted in the full foreground so the
|
|
258
|
+
* trail resolves to it.
|
|
259
|
+
*/
|
|
260
|
+
const BreadcrumbPage = forwardRef<React.ElementRef<typeof Text>, BreadcrumbPageProps>(
|
|
261
|
+
({ className, ...props }, ref) => {
|
|
262
|
+
const { size } = useBreadcrumb();
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<Text
|
|
266
|
+
ref={ref}
|
|
267
|
+
aria-current="page"
|
|
268
|
+
accessibilityRole="text"
|
|
269
|
+
weight="medium"
|
|
270
|
+
className={crumbText({ size, className: cn('text-foreground', className) })}
|
|
271
|
+
{...props}
|
|
272
|
+
/>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
BreadcrumbPage.displayName = 'Breadcrumb.Page';
|
|
277
|
+
|
|
278
|
+
export interface BreadcrumbSeparatorProps extends ViewProps {
|
|
279
|
+
className?: string;
|
|
280
|
+
/** Override the glyph for this one gap. Falls back to the root's separator. */
|
|
281
|
+
children?: ReactNode;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The glyph between crumbs. `Breadcrumb.List` inserts it for you; it is public
|
|
286
|
+
* only for the rare hand-assembled trail. Hidden from screen readers — the
|
|
287
|
+
* order of the crumbs already conveys the hierarchy.
|
|
288
|
+
*/
|
|
289
|
+
const BreadcrumbSeparator = forwardRef<View, BreadcrumbSeparatorProps>(
|
|
290
|
+
({ className, children, ...props }, ref) => {
|
|
291
|
+
const { separator } = useBreadcrumb();
|
|
292
|
+
const rawTint = useCSSVariable('--color-muted-foreground');
|
|
293
|
+
const tint = typeof rawTint === 'string' ? rawTint : undefined;
|
|
294
|
+
const glyph = children ?? separator;
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<View
|
|
298
|
+
ref={ref}
|
|
299
|
+
aria-hidden
|
|
300
|
+
accessibilityElementsHidden
|
|
301
|
+
importantForAccessibility="no-hide-descendants"
|
|
302
|
+
className={cn('items-center justify-center', className)}
|
|
303
|
+
{...props}
|
|
304
|
+
>
|
|
305
|
+
{glyph ?? <ChevronRightIcon size={14} color={tint} />}
|
|
306
|
+
</View>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
BreadcrumbSeparator.displayName = 'Breadcrumb.Separator';
|
|
311
|
+
|
|
312
|
+
export interface BreadcrumbEllipsisProps
|
|
313
|
+
extends Omit<AnimatedPressableProps, 'children'> {
|
|
314
|
+
className?: string;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Stands in for the crumbs a collapsed trail hides. Static by default; give it
|
|
319
|
+
* an `onPress` (via the list's `onEllipsisPress`) and it becomes the trigger
|
|
320
|
+
* for a menu of the hidden steps. Labelled "Show more" so the collapse is not
|
|
321
|
+
* silent to assistive tech.
|
|
322
|
+
*/
|
|
323
|
+
const BreadcrumbEllipsis = forwardRef<View, BreadcrumbEllipsisProps>(
|
|
324
|
+
({ className, onPress, ...props }, ref) => {
|
|
325
|
+
const rawTint = useCSSVariable('--color-muted-foreground');
|
|
326
|
+
const tint = typeof rawTint === 'string' ? rawTint : undefined;
|
|
327
|
+
const glyph = <EllipsisIcon size={16} color={tint} />;
|
|
328
|
+
|
|
329
|
+
if (!onPress) {
|
|
330
|
+
return (
|
|
331
|
+
<View
|
|
332
|
+
ref={ref}
|
|
333
|
+
accessibilityLabel="More"
|
|
334
|
+
className={cn('h-5 items-center justify-center px-0.5', className)}
|
|
335
|
+
>
|
|
336
|
+
{glyph}
|
|
337
|
+
</View>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<AnimatedPressable
|
|
343
|
+
ref={ref}
|
|
344
|
+
accessibilityRole="button"
|
|
345
|
+
accessibilityLabel="Show more"
|
|
346
|
+
pressScale={0.92}
|
|
347
|
+
onPress={onPress}
|
|
348
|
+
className={cn('h-5 items-center justify-center px-0.5', className)}
|
|
349
|
+
{...props}
|
|
350
|
+
>
|
|
351
|
+
{glyph}
|
|
352
|
+
</AnimatedPressable>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
BreadcrumbEllipsis.displayName = 'Breadcrumb.Ellipsis';
|
|
357
|
+
|
|
358
|
+
export const Breadcrumb = Object.assign(BreadcrumbRoot, {
|
|
359
|
+
List: BreadcrumbList,
|
|
360
|
+
Item: BreadcrumbItem,
|
|
361
|
+
Link: BreadcrumbLink,
|
|
362
|
+
Page: BreadcrumbPage,
|
|
363
|
+
Separator: BreadcrumbSeparator,
|
|
364
|
+
Ellipsis: BreadcrumbEllipsis,
|
|
365
|
+
});
|
|
@@ -47,7 +47,7 @@ const chipVariants = tv({
|
|
|
47
47
|
root: 'flex-row items-center self-start rounded-full border border-transparent',
|
|
48
48
|
label: 'font-medium',
|
|
49
49
|
close:
|
|
50
|
-
'items-center justify-center rounded-full -
|
|
50
|
+
'items-center justify-center rounded-full -me-1 aspect-square opacity-70',
|
|
51
51
|
},
|
|
52
52
|
variants: {
|
|
53
53
|
variant: {
|
|
@@ -88,7 +88,7 @@ const chipVariants = tv({
|
|
|
88
88
|
},
|
|
89
89
|
/** Trims the trailing padding so the close target hugs the right edge. */
|
|
90
90
|
closable: {
|
|
91
|
-
true: { root: '
|
|
91
|
+
true: { root: 'pe-1' },
|
|
92
92
|
},
|
|
93
93
|
disabled: {
|
|
94
94
|
true: { root: 'opacity-[0.56]' },
|
|
@@ -33,16 +33,18 @@
|
|
|
33
33
|
* comes from the device — so an app that did force RTL the old way still reads
|
|
34
34
|
* back the truth.
|
|
35
35
|
*/
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
36
|
+
import { useContext, useMemo, type ReactNode } from 'react';
|
|
37
|
+
import { View, type ViewProps } from 'react-native';
|
|
38
|
+
import {
|
|
39
|
+
DirectionContext,
|
|
40
|
+
deviceDirection,
|
|
41
|
+
useDirection,
|
|
42
|
+
useDirectionSign,
|
|
43
|
+
type DirectionValue,
|
|
44
|
+
} from '../../hooks/use-direction';
|
|
39
45
|
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
/** What the device is set to, for anything outside a provider. */
|
|
43
|
-
const deviceDirection = (): DirectionValue => (I18nManager.isRTL ? 'rtl' : 'ltr');
|
|
44
|
-
|
|
45
|
-
const DirectionContext = createContext<DirectionValue | null>(null);
|
|
46
|
+
export { useDirection, useDirectionSign };
|
|
47
|
+
export type { DirectionValue };
|
|
46
48
|
|
|
47
49
|
export interface DirectionProps extends ViewProps {
|
|
48
50
|
className?: string;
|
|
@@ -56,9 +58,14 @@ export interface DirectionProps extends ViewProps {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
|
-
* Provider. Renders one `View` — it has to, since the flip is a style
|
|
60
|
-
*
|
|
61
|
-
*
|
|
61
|
+
* Provider. Renders one `View` — it has to, since the flip is a style.
|
|
62
|
+
*
|
|
63
|
+
* The view takes no layout of its own: it is as big as what is inside it, and
|
|
64
|
+
* `className` says otherwise. Wrapping a whole app therefore wants `flex-1`
|
|
65
|
+
* explicitly. It used to carry that by default, and every use inside a screen
|
|
66
|
+
* had to undo it with `flex-none` — a default that is wrong for one of its two
|
|
67
|
+
* uses is worse than no default, because the wrong one fails silently by
|
|
68
|
+
* swallowing the rest of the screen.
|
|
62
69
|
*/
|
|
63
70
|
export function Direction({ className, children, dir, style, ...props }: DirectionProps) {
|
|
64
71
|
const inherited = useContext(DirectionContext);
|
|
@@ -71,7 +78,7 @@ export function Direction({ className, children, dir, style, ...props }: Directi
|
|
|
71
78
|
|
|
72
79
|
return (
|
|
73
80
|
<DirectionContext.Provider value={value}>
|
|
74
|
-
<View className={
|
|
81
|
+
<View className={className} style={[directionStyle, style]} {...props}>
|
|
75
82
|
{children}
|
|
76
83
|
</View>
|
|
77
84
|
</DirectionContext.Provider>
|
|
@@ -80,11 +87,3 @@ export function Direction({ className, children, dir, style, ...props }: Directi
|
|
|
80
87
|
|
|
81
88
|
Direction.displayName = 'Direction';
|
|
82
89
|
|
|
83
|
-
/**
|
|
84
|
-
* The reading direction in force, for a component that has to flip maths Yoga
|
|
85
|
-
* cannot flip for it. Safe with no provider mounted: it falls back to the
|
|
86
|
-
* device's own setting.
|
|
87
|
-
*/
|
|
88
|
-
export function useDirection(): DirectionValue {
|
|
89
|
-
return useContext(DirectionContext) ?? deviceDirection();
|
|
90
|
-
}
|
|
@@ -14,6 +14,23 @@
|
|
|
14
14
|
* surfaces read as one misdrawn shape. The panel's bottom corners are not set
|
|
15
15
|
* at all — the shell clips them, so they take its radius exactly.
|
|
16
16
|
*
|
|
17
|
+
* That clip follows the shell's *border box*, not the box inside its border.
|
|
18
|
+
* Along the straight edges the panel is held off by the border width and the
|
|
19
|
+
* edge shows through, but at the corner arcs the panel's square corner is
|
|
20
|
+
* clipped to the outer radius and paints across the border. At the default
|
|
21
|
+
* hairline that is a sliver nobody sees. Give the shell a thicker border and
|
|
22
|
+
* the corners visibly eat it, so a Frame with `border-2` or more needs the
|
|
23
|
+
* panel told where to stop:
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Frame className="rounded-[28px] border-2 border-dashed">
|
|
27
|
+
* <Frame.Panel className="rounded-b-[26px]">…</Frame.Panel>
|
|
28
|
+
* </Frame>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* The radius to use is the shell's less its border width. It is on the caller
|
|
32
|
+
* because both arrive as `className` strings, which the component cannot read.
|
|
33
|
+
*
|
|
17
34
|
* ```tsx
|
|
18
35
|
* <Frame>
|
|
19
36
|
* <Frame.Header>
|
|
@@ -65,14 +65,23 @@ const AnimatedRect = Animated.createAnimatedComponent(Rect);
|
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Rows in a week — the default, and what the calendar helper and the weekday
|
|
68
|
-
* labels assume. The grid itself takes a `rows` prop, so
|
|
69
|
-
*
|
|
68
|
+
* labels assume. The grid itself takes a `rows` prop, so a grid whose bins are
|
|
69
|
+
* hours rather than days is the same component with a different number.
|
|
70
70
|
*/
|
|
71
71
|
const DAYS_IN_WEEK = 7;
|
|
72
72
|
|
|
73
73
|
/** Opacity of the base colour at each activity level. Index 0 is "nothing". */
|
|
74
74
|
const LEVEL_OPACITY = [1, 0.28, 0.5, 0.74, 1] as const;
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* A theme token rather than a colour. Tokens are named, not written, so the
|
|
78
|
+
* leading `--` tells the two apart without the caller having to say which
|
|
79
|
+
* kind they passed.
|
|
80
|
+
*/
|
|
81
|
+
function isToken(value: string | undefined): value is string {
|
|
82
|
+
return typeof value === 'string' && value.startsWith('--');
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
/** Room left for the weekday labels when the y-axis does not ask for its own. */
|
|
77
86
|
const DEFAULT_AXIS_WIDTH = 26;
|
|
78
87
|
|
|
@@ -317,8 +326,8 @@ export interface HeatmapChartProps extends ViewProps {
|
|
|
317
326
|
/** Which weekday is the top row. `0` is Sunday. Labels follow it. */
|
|
318
327
|
weekStartDay?: number;
|
|
319
328
|
/**
|
|
320
|
-
* Rows per column. Seven for a calendar; use another number
|
|
321
|
-
*
|
|
329
|
+
* Rows per column. Seven for a calendar; use another number when the bins
|
|
330
|
+
* are not weekdays — twenty-four for a grid of hours.
|
|
322
331
|
*/
|
|
323
332
|
rows?: number;
|
|
324
333
|
/**
|
|
@@ -333,8 +342,27 @@ export interface HeatmapChartProps extends ViewProps {
|
|
|
333
342
|
* opacities, which follows the theme.
|
|
334
343
|
*/
|
|
335
344
|
levelColors?: string[];
|
|
336
|
-
/**
|
|
345
|
+
/**
|
|
346
|
+
* Base colour for the derived ramp — the colour the busiest cells are drawn
|
|
347
|
+
* in, with the quieter levels the same colour at lower opacity.
|
|
348
|
+
*
|
|
349
|
+
* Takes a theme token by name as well as a literal, so `"--color-chart-3"`
|
|
350
|
+
* recolours the chart and keeps following the theme through light and dark.
|
|
351
|
+
* Defaults to `--color-chart-1`.
|
|
352
|
+
*/
|
|
337
353
|
color?: string;
|
|
354
|
+
/**
|
|
355
|
+
* Colour of a cell with nothing in it. Takes a token name too. Defaults to
|
|
356
|
+
* `--color-muted`, which is the right weight for "measured, and empty" —
|
|
357
|
+
* override it for a chart that should read as denser or fainter than that.
|
|
358
|
+
*/
|
|
359
|
+
emptyColor?: string;
|
|
360
|
+
/**
|
|
361
|
+
* Opacity of the base colour at each of the five levels, quietest first.
|
|
362
|
+
* The way to retune the ramp's contrast without having to name five colours.
|
|
363
|
+
* Ignored when `levelColors` is given, which sets the colours outright.
|
|
364
|
+
*/
|
|
365
|
+
levelOpacity?: number[];
|
|
338
366
|
/** Milliseconds for the reveal on mount. */
|
|
339
367
|
animationDuration?: number;
|
|
340
368
|
/** Opacity of every cell that is not the one under the finger. */
|
|
@@ -361,6 +389,8 @@ const HeatmapChartRoot = forwardRef<View, HeatmapChartProps>(function HeatmapCha
|
|
|
361
389
|
levels,
|
|
362
390
|
levelColors,
|
|
363
391
|
color,
|
|
392
|
+
emptyColor,
|
|
393
|
+
levelOpacity,
|
|
364
394
|
animationDuration = 900,
|
|
365
395
|
inactiveOpacity = 1,
|
|
366
396
|
onActiveCellChange,
|
|
@@ -378,10 +408,24 @@ const HeatmapChartRoot = forwardRef<View, HeatmapChartProps>(function HeatmapCha
|
|
|
378
408
|
const reveal = useSharedValue(0);
|
|
379
409
|
const reducedMotion = useReducedMotion();
|
|
380
410
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
411
|
+
/*
|
|
412
|
+
* `color` and `emptyColor` take a token name as readily as a literal. A
|
|
413
|
+
* literal is a colour frozen at the moment it was written — it cannot follow
|
|
414
|
+
* the theme into dark mode — so naming the token is almost always what the
|
|
415
|
+
* caller meant, and having to resolve it themselves is the reason they did
|
|
416
|
+
* not. The hooks run unconditionally; which of the two answers is used is
|
|
417
|
+
* decided after.
|
|
418
|
+
*/
|
|
419
|
+
const baseToken = useCSSVariable(isToken(color) ? color : '--color-chart-1');
|
|
420
|
+
const emptyToken = useCSSVariable(
|
|
421
|
+
isToken(emptyColor) ? emptyColor : '--color-muted'
|
|
422
|
+
);
|
|
423
|
+
const base =
|
|
424
|
+
(isToken(color) ? undefined : color) ??
|
|
425
|
+
(typeof baseToken === 'string' ? baseToken : '#262626');
|
|
426
|
+
const empty =
|
|
427
|
+
(isToken(emptyColor) ? undefined : emptyColor) ??
|
|
428
|
+
(typeof emptyToken === 'string' ? emptyToken : 'rgba(128,128,128,0.16)');
|
|
385
429
|
|
|
386
430
|
const parts = useMemo(() => splitParts(children), [children]);
|
|
387
431
|
// The y-axis is laid out beside the grid rather than over it, so its width
|
|
@@ -419,8 +463,11 @@ const HeatmapChartRoot = forwardRef<View, HeatmapChartProps>(function HeatmapCha
|
|
|
419
463
|
() =>
|
|
420
464
|
levelColors
|
|
421
465
|
? { ramp: levelColors, opacities: levelColors.map(() => 1) }
|
|
422
|
-
: {
|
|
423
|
-
|
|
466
|
+
: {
|
|
467
|
+
ramp: [empty, base, base, base, base],
|
|
468
|
+
opacities: levelOpacity ?? [...LEVEL_OPACITY],
|
|
469
|
+
},
|
|
470
|
+
[levelColors, levelOpacity, empty, base]
|
|
424
471
|
);
|
|
425
472
|
|
|
426
473
|
const levelOf = useMemo(
|
|
@@ -26,8 +26,8 @@ import { Input, type InputProps } from '../input';
|
|
|
26
26
|
const inputGroupVariants = tv({
|
|
27
27
|
slots: {
|
|
28
28
|
root: 'w-full',
|
|
29
|
-
prefix: 'absolute bottom-0
|
|
30
|
-
suffix: 'absolute bottom-0
|
|
29
|
+
prefix: 'absolute bottom-0 start-0 top-0 z-10 flex-row items-center justify-center gap-2 px-3',
|
|
30
|
+
suffix: 'absolute bottom-0 end-0 top-0 z-10 flex-row items-center justify-center gap-2 px-3',
|
|
31
31
|
},
|
|
32
32
|
variants: {
|
|
33
33
|
isDisabled: {
|