panelui-native 0.28.1 → 0.32.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 +22 -3
- package/lib/module/components/calendar/index.js +450 -132
- package/lib/module/components/calendar/index.js.map +1 -1
- package/lib/module/components/code-block/highlight.js +363 -0
- package/lib/module/components/code-block/highlight.js.map +1 -0
- package/lib/module/components/code-block/index.js +313 -0
- package/lib/module/components/code-block/index.js.map +1 -0
- package/lib/module/components/date-picker/index.js +8 -1
- package/lib/module/components/date-picker/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +9 -8
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/map/index.js +50 -4
- package/lib/module/components/map/index.js.map +1 -1
- package/lib/module/components/menu/index.js +714 -0
- package/lib/module/components/menu/index.js.map +1 -0
- package/lib/module/components/plan/index.js +582 -0
- package/lib/module/components/plan/index.js.map +1 -0
- package/lib/module/components/popover/index.js +35 -4
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/post/index.js +992 -0
- package/lib/module/components/post/index.js.map +1 -0
- package/lib/module/components/radio-group/index.js +13 -8
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/reasoning/index.js +244 -0
- package/lib/module/components/reasoning/index.js.map +1 -0
- package/lib/module/components/response/index.js +296 -0
- package/lib/module/components/response/index.js.map +1 -0
- package/lib/module/components/response/markdown.js +463 -0
- package/lib/module/components/response/markdown.js.map +1 -0
- package/lib/module/components/select/index.js +97 -15
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/sources/index.js +221 -0
- package/lib/module/components/sources/index.js.map +1 -0
- package/lib/module/components/table/index.js +632 -0
- package/lib/module/components/table/index.js.map +1 -0
- package/lib/module/components/task/index.js +300 -0
- package/lib/module/components/task/index.js.map +1 -0
- package/lib/module/components/textarea/index.js +295 -0
- package/lib/module/components/textarea/index.js.map +1 -0
- package/lib/module/components/tooltip/index.js +179 -23
- package/lib/module/components/tooltip/index.js.map +1 -1
- package/lib/module/icons/index.js +339 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +12 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/collapse.js +112 -0
- package/lib/module/primitives/collapse.js.map +1 -0
- package/lib/module/utils/date.js +129 -22
- package/lib/module/utils/date.js.map +1 -1
- package/lib/typescript/src/components/calendar/index.d.ts +79 -5
- package/lib/typescript/src/components/calendar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/code-block/highlight.d.ts +39 -0
- package/lib/typescript/src/components/code-block/highlight.d.ts.map +1 -0
- package/lib/typescript/src/components/code-block/index.d.ts +113 -0
- package/lib/typescript/src/components/code-block/index.d.ts.map +1 -0
- package/lib/typescript/src/components/date-picker/index.d.ts +7 -1
- package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/index.d.ts +8 -1
- package/lib/typescript/src/components/map/index.d.ts.map +1 -1
- package/lib/typescript/src/components/menu/index.d.ts +261 -0
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -0
- package/lib/typescript/src/components/plan/index.d.ts +238 -0
- package/lib/typescript/src/components/plan/index.d.ts.map +1 -0
- package/lib/typescript/src/components/popover/index.d.ts +35 -1
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/post/index.d.ts +358 -0
- package/lib/typescript/src/components/post/index.d.ts.map +1 -0
- package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/reasoning/index.d.ts +97 -0
- package/lib/typescript/src/components/reasoning/index.d.ts.map +1 -0
- package/lib/typescript/src/components/response/index.d.ts +91 -0
- package/lib/typescript/src/components/response/index.d.ts.map +1 -0
- package/lib/typescript/src/components/response/markdown.d.ts +88 -0
- package/lib/typescript/src/components/response/markdown.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts +27 -2
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sources/index.d.ts +85 -0
- package/lib/typescript/src/components/sources/index.d.ts.map +1 -0
- package/lib/typescript/src/components/table/index.d.ts +274 -0
- package/lib/typescript/src/components/table/index.d.ts.map +1 -0
- package/lib/typescript/src/components/task/index.d.ts +103 -0
- package/lib/typescript/src/components/task/index.d.ts.map +1 -0
- package/lib/typescript/src/components/textarea/index.d.ts +175 -0
- package/lib/typescript/src/components/textarea/index.d.ts.map +1 -0
- package/lib/typescript/src/components/tooltip/index.d.ts +63 -8
- package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +54 -1
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +14 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/collapse.d.ts +42 -0
- package/lib/typescript/src/primitives/collapse.d.ts.map +1 -0
- package/lib/typescript/src/utils/date.d.ts +49 -5
- package/lib/typescript/src/utils/date.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/calendar/index.tsx +519 -139
- package/src/components/code-block/highlight.ts +278 -0
- package/src/components/code-block/index.tsx +341 -0
- package/src/components/date-picker/index.tsx +19 -1
- package/src/components/heatmap-chart/index.tsx +9 -9
- package/src/components/map/index.tsx +53 -4
- package/src/components/menu/index.tsx +812 -0
- package/src/components/plan/index.tsx +648 -0
- package/src/components/popover/index.tsx +69 -2
- package/src/components/post/index.tsx +1116 -0
- package/src/components/radio-group/index.tsx +11 -2
- package/src/components/reasoning/index.tsx +297 -0
- package/src/components/response/index.tsx +390 -0
- package/src/components/response/markdown.ts +489 -0
- package/src/components/select/index.tsx +132 -12
- package/src/components/sources/index.tsx +278 -0
- package/src/components/table/index.tsx +747 -0
- package/src/components/task/index.tsx +329 -0
- package/src/components/textarea/index.tsx +381 -0
- package/src/components/tooltip/index.tsx +203 -20
- package/src/icons/index.tsx +264 -1
- package/src/index.ts +131 -0
- package/src/primitives/collapse.tsx +128 -0
- package/src/utils/date.ts +146 -22
- package/theme.css +112 -0
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A popover is a panel you open and deal with; a tooltip is a whisper. It
|
|
5
5
|
* carries a word or two about what a control does, appears without taking the
|
|
6
|
-
* screen, and goes away on its own. That is why it is inverted
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* screen, and goes away on its own. That is why it is inverted by default, why
|
|
7
|
+
* it is not dismissible with a scrim, and why it closes after a beat instead of
|
|
8
|
+
* waiting to be told.
|
|
9
|
+
*
|
|
10
|
+
* The inversion is a default rather than a rule, because the whisper has a
|
|
11
|
+
* larger sibling: a tooltip that carries a heading and a sentence stops reading
|
|
12
|
+
* as a different layer and starts reading as a panel with the wrong colours.
|
|
13
|
+
* `variant="surface"` makes it one, and the sizing props — `width`,
|
|
14
|
+
* `minWidth`, `maxHeight`, `scrollable` — are what let it hold that much.
|
|
9
15
|
*
|
|
10
16
|
* On touch there is no hover to open it, so the gesture is a long press by
|
|
11
17
|
* default — the platform's own "tell me more" gesture — with `openOn="press"`
|
|
@@ -44,6 +50,7 @@ import {
|
|
|
44
50
|
} from 'react';
|
|
45
51
|
import {
|
|
46
52
|
Pressable,
|
|
53
|
+
ScrollView,
|
|
47
54
|
useWindowDimensions,
|
|
48
55
|
View,
|
|
49
56
|
type LayoutChangeEvent,
|
|
@@ -58,6 +65,7 @@ import Animated, {
|
|
|
58
65
|
withTiming,
|
|
59
66
|
} from 'react-native-reanimated';
|
|
60
67
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
68
|
+
import { tv } from 'tailwind-variants';
|
|
61
69
|
import { Portal } from '../../primitives/portal';
|
|
62
70
|
import { Text, type TextProps } from '../../primitives/text';
|
|
63
71
|
import { cn } from '../../utils/cn';
|
|
@@ -73,6 +81,57 @@ const DEFAULT_DURATION = 1500;
|
|
|
73
81
|
|
|
74
82
|
export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
75
83
|
export type TooltipAlign = 'start' | 'center' | 'end';
|
|
84
|
+
export type TooltipVariant = 'inverted' | 'surface';
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Every colour the tooltip has, in one place.
|
|
88
|
+
*
|
|
89
|
+
* They were three literals at three call sites — the panel, the arrow and the
|
|
90
|
+
* default text — which meant retheming a tooltip took three `className`
|
|
91
|
+
* overrides that each had to be kept in step with the others. As a variant it
|
|
92
|
+
* is one prop, and the arrow and the text read it off the context rather than
|
|
93
|
+
* being told again.
|
|
94
|
+
*/
|
|
95
|
+
const tooltipVariants = tv({
|
|
96
|
+
slots: {
|
|
97
|
+
content: 'rounded-lg px-2.5 py-1.5 shadow-md',
|
|
98
|
+
text: 'text-sm font-medium',
|
|
99
|
+
title: 'text-sm font-semibold',
|
|
100
|
+
description: 'text-sm',
|
|
101
|
+
arrow: 'rounded-[1px]',
|
|
102
|
+
},
|
|
103
|
+
variants: {
|
|
104
|
+
variant: {
|
|
105
|
+
/*
|
|
106
|
+
* The default, and deliberately not a surface colour: a whisper over the
|
|
107
|
+
* page should read as a different layer rather than as another panel of
|
|
108
|
+
* it. It is the treatment a one-line label wants.
|
|
109
|
+
*/
|
|
110
|
+
inverted: {
|
|
111
|
+
content: 'bg-foreground',
|
|
112
|
+
text: 'text-background',
|
|
113
|
+
title: 'text-background',
|
|
114
|
+
description: 'text-background/70',
|
|
115
|
+
arrow: 'bg-foreground',
|
|
116
|
+
},
|
|
117
|
+
/*
|
|
118
|
+
* For a tooltip carrying more than a label — a heading, a sentence, a row
|
|
119
|
+
* of controls. At that size the inversion stops reading as a whisper and
|
|
120
|
+
* starts reading as a panel with the wrong colours, so it becomes one.
|
|
121
|
+
*/
|
|
122
|
+
surface: {
|
|
123
|
+
content: 'border border-border bg-popover',
|
|
124
|
+
text: 'text-popover-foreground',
|
|
125
|
+
title: 'text-popover-foreground',
|
|
126
|
+
description: 'text-muted-foreground',
|
|
127
|
+
arrow: 'border-border bg-popover',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
defaultVariants: {
|
|
132
|
+
variant: 'inverted',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
76
135
|
|
|
77
136
|
interface TriggerRect {
|
|
78
137
|
x: number;
|
|
@@ -97,6 +156,9 @@ interface TooltipContextValue {
|
|
|
97
156
|
openOn: TooltipOpenOn;
|
|
98
157
|
/** Accessibility label carried onto the trigger. */
|
|
99
158
|
label?: string;
|
|
159
|
+
/** Which set of colours the panel, arrow and text draw from. */
|
|
160
|
+
variant: TooltipVariant;
|
|
161
|
+
setVariant: (variant: TooltipVariant) => void;
|
|
100
162
|
}
|
|
101
163
|
|
|
102
164
|
export type TooltipOpenOn = 'longPress' | 'press';
|
|
@@ -149,6 +211,12 @@ function TooltipRoot({
|
|
|
149
211
|
const [trigger, setTrigger] = useState<TriggerRect | null>(null);
|
|
150
212
|
const [placement, setPlacement] = useState<TooltipPlacement>('top');
|
|
151
213
|
const [arrowOffset, setArrowOffset] = useState(0);
|
|
214
|
+
/*
|
|
215
|
+
* Published by Content rather than passed to it, because the arrow and the
|
|
216
|
+
* text sit beside the panel in the tree and would otherwise each have to be
|
|
217
|
+
* told which colours to use.
|
|
218
|
+
*/
|
|
219
|
+
const [variant, setVariant] = useState<TooltipVariant>('inverted');
|
|
152
220
|
|
|
153
221
|
const isControlled = open !== undefined;
|
|
154
222
|
const resolvedOpen = isControlled ? open : internalOpen;
|
|
@@ -186,8 +254,10 @@ function TooltipRoot({
|
|
|
186
254
|
setArrowOffset,
|
|
187
255
|
openOn,
|
|
188
256
|
label,
|
|
257
|
+
variant,
|
|
258
|
+
setVariant,
|
|
189
259
|
}),
|
|
190
|
-
[resolvedOpen, show, hide, trigger, placement, arrowOffset, openOn, label]
|
|
260
|
+
[resolvedOpen, show, hide, trigger, placement, arrowOffset, openOn, label, variant]
|
|
191
261
|
);
|
|
192
262
|
|
|
193
263
|
return <TooltipContext.Provider value={context}>{children}</TooltipContext.Provider>;
|
|
@@ -282,6 +352,34 @@ export interface TooltipContentProps extends ViewProps {
|
|
|
282
352
|
offset?: number;
|
|
283
353
|
/** Nudge along the alignment axis, in pixels. */
|
|
284
354
|
alignOffset?: number;
|
|
355
|
+
/**
|
|
356
|
+
* Which set of colours the panel, its arrow and its text draw from.
|
|
357
|
+
*
|
|
358
|
+
* `inverted` is the default and right for a label: a whisper over the page
|
|
359
|
+
* should read as a different layer rather than as another panel of it.
|
|
360
|
+
* `surface` matches the popover — reach for it once the tooltip carries a
|
|
361
|
+
* heading and a sentence, where the inversion stops reading as a whisper.
|
|
362
|
+
*/
|
|
363
|
+
variant?: TooltipVariant;
|
|
364
|
+
/**
|
|
365
|
+
* `content-fit` sizes to the content, `trigger` matches the trigger's width,
|
|
366
|
+
* `full` spans the safe area, and a number is that many pixels. Worth setting
|
|
367
|
+
* for anything longer than a label, which would otherwise run to whatever
|
|
368
|
+
* width the sentence happens to want.
|
|
369
|
+
*/
|
|
370
|
+
width?: number | 'trigger' | 'full' | 'content-fit';
|
|
371
|
+
/** Floor for the panel's width, in pixels. */
|
|
372
|
+
minWidth?: number;
|
|
373
|
+
/**
|
|
374
|
+
* Ceiling for the panel's height, in pixels. Always clamped to the room
|
|
375
|
+
* inside the safe area, which is also the default.
|
|
376
|
+
*/
|
|
377
|
+
maxHeight?: number;
|
|
378
|
+
/**
|
|
379
|
+
* Scroll the body when it is taller than `maxHeight`. Off by default — a
|
|
380
|
+
* label has nothing to scroll, and a scroller around one only adds a bounce.
|
|
381
|
+
*/
|
|
382
|
+
scrollable?: boolean;
|
|
285
383
|
children?: ReactNode;
|
|
286
384
|
}
|
|
287
385
|
|
|
@@ -291,13 +389,25 @@ function TooltipContent({
|
|
|
291
389
|
align = 'center',
|
|
292
390
|
offset = DEFAULT_OFFSET,
|
|
293
391
|
alignOffset = 0,
|
|
392
|
+
variant = 'inverted',
|
|
393
|
+
width = 'content-fit',
|
|
394
|
+
minWidth,
|
|
395
|
+
maxHeight,
|
|
396
|
+
scrollable = false,
|
|
294
397
|
children,
|
|
295
398
|
onLayout: onLayoutProp,
|
|
296
399
|
style,
|
|
297
400
|
...props
|
|
298
401
|
}: TooltipContentProps) {
|
|
299
402
|
const context = useTooltip('Tooltip.Content');
|
|
300
|
-
const { open, hide, trigger, setPlacement, setArrowOffset } = context;
|
|
403
|
+
const { open, hide, trigger, setPlacement, setArrowOffset, setVariant } = context;
|
|
404
|
+
const slots = tooltipVariants({ variant });
|
|
405
|
+
|
|
406
|
+
// The arrow and the text sit beside this panel in the tree, so the chosen
|
|
407
|
+
// colours are published rather than passed down.
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
setVariant(variant);
|
|
410
|
+
}, [variant, setVariant]);
|
|
301
411
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
302
412
|
const insets = useSafeAreaInsets();
|
|
303
413
|
const [size, setSize] = useState<{ width: number; height: number } | null>(null);
|
|
@@ -332,6 +442,28 @@ function TooltipContent({
|
|
|
332
442
|
bottom: screenHeight - insets.bottom - SCREEN_MARGIN,
|
|
333
443
|
};
|
|
334
444
|
|
|
445
|
+
const available = bounds.right - bounds.left;
|
|
446
|
+
const requestedWidth =
|
|
447
|
+
width === 'content-fit'
|
|
448
|
+
? undefined
|
|
449
|
+
: width === 'trigger'
|
|
450
|
+
? trigger?.width
|
|
451
|
+
: width === 'full'
|
|
452
|
+
? available
|
|
453
|
+
: width;
|
|
454
|
+
|
|
455
|
+
// The floor never wins past the space there actually is — a panel wider than
|
|
456
|
+
// the screen is worse than a cramped one.
|
|
457
|
+
const resolvedWidth =
|
|
458
|
+
requestedWidth === undefined
|
|
459
|
+
? minWidth === undefined
|
|
460
|
+
? undefined
|
|
461
|
+
: Math.min(minWidth, available)
|
|
462
|
+
: Math.min(Math.max(requestedWidth, minWidth ?? 0), available);
|
|
463
|
+
|
|
464
|
+
const room = bounds.bottom - bounds.top;
|
|
465
|
+
const resolvedMaxHeight = Math.min(maxHeight ?? room, room);
|
|
466
|
+
|
|
335
467
|
const position =
|
|
336
468
|
trigger && size
|
|
337
469
|
? place({ trigger, size, placement, align, offset, alignOffset, bounds })
|
|
@@ -418,14 +550,20 @@ function TooltipContent({
|
|
|
418
550
|
// so it is laid out off-screen rather than at the origin.
|
|
419
551
|
top: position?.top ?? -9999,
|
|
420
552
|
left: position?.left ?? -9999,
|
|
421
|
-
maxWidth:
|
|
553
|
+
maxWidth: available,
|
|
554
|
+
maxHeight: resolvedMaxHeight,
|
|
555
|
+
width: resolvedWidth,
|
|
422
556
|
}}
|
|
423
557
|
>
|
|
424
558
|
<Animated.View
|
|
425
|
-
|
|
559
|
+
// A panel with a heading and a paragraph is a group, not a run of
|
|
560
|
+
// text — the flat role would have a screen reader read it as one
|
|
561
|
+
// string with no structure.
|
|
562
|
+
accessibilityRole={scrollable || width !== 'content-fit' ? undefined : 'text'}
|
|
426
563
|
style={[labelStyle, style]}
|
|
427
564
|
className={cn(
|
|
428
|
-
|
|
565
|
+
slots.content(),
|
|
566
|
+
scrollable ? 'overflow-hidden' : undefined,
|
|
429
567
|
className
|
|
430
568
|
)}
|
|
431
569
|
{...props}
|
|
@@ -438,12 +576,12 @@ function TooltipContent({
|
|
|
438
576
|
view bare. Only the text nodes need the treatment; an element
|
|
439
577
|
is already responsible for itself.
|
|
440
578
|
*/}
|
|
441
|
-
{
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
)
|
|
579
|
+
{scrollable ? (
|
|
580
|
+
<ScrollView bounces={false} showsVerticalScrollIndicator={false}>
|
|
581
|
+
{wrapTooltipText(children)}
|
|
582
|
+
</ScrollView>
|
|
583
|
+
) : (
|
|
584
|
+
wrapTooltipText(children)
|
|
447
585
|
)}
|
|
448
586
|
</Animated.View>
|
|
449
587
|
</Animated.View>
|
|
@@ -453,11 +591,53 @@ function TooltipContent({
|
|
|
453
591
|
);
|
|
454
592
|
}
|
|
455
593
|
|
|
456
|
-
/**
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
594
|
+
/** Bare strings become the label's default text; elements speak for themselves. */
|
|
595
|
+
function wrapTooltipText(children: ReactNode) {
|
|
596
|
+
return Children.map(children, (child) =>
|
|
597
|
+
typeof child === 'string' || typeof child === 'number' ? (
|
|
598
|
+
<TooltipText>{child}</TooltipText>
|
|
599
|
+
) : (
|
|
600
|
+
child
|
|
601
|
+
)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** The label's default text, coloured to whatever the panel is made of. */
|
|
606
|
+
function TooltipText({ className, ...props }: TextProps) {
|
|
607
|
+
const { variant } = useTooltip('Tooltip.Text');
|
|
608
|
+
const { text } = tooltipVariants({ variant });
|
|
609
|
+
|
|
610
|
+
return <Text className={cn(text(), className)} {...props} />;
|
|
611
|
+
}
|
|
460
612
|
TooltipText.displayName = 'Tooltip.Text';
|
|
613
|
+
TooltipTitle.displayName = 'Tooltip.Title';
|
|
614
|
+
TooltipDescription.displayName = 'Tooltip.Description';
|
|
615
|
+
|
|
616
|
+
export interface TooltipTitleProps extends TextProps {
|
|
617
|
+
className?: string;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** A heading, for a tooltip carrying more than a label. */
|
|
621
|
+
function TooltipTitle({ className, ...props }: TooltipTitleProps) {
|
|
622
|
+
const { variant } = useTooltip('Tooltip.Title');
|
|
623
|
+
const { title } = tooltipVariants({ variant });
|
|
624
|
+
|
|
625
|
+
return (
|
|
626
|
+
<Text accessibilityRole="header" className={cn(title(), className)} {...props} />
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export interface TooltipDescriptionProps extends TextProps {
|
|
631
|
+
className?: string;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** The sentence under a `Tooltip.Title`, in the panel's secondary colour. */
|
|
635
|
+
function TooltipDescription({ className, ...props }: TooltipDescriptionProps) {
|
|
636
|
+
const { variant } = useTooltip('Tooltip.Description');
|
|
637
|
+
const { description } = tooltipVariants({ variant });
|
|
638
|
+
|
|
639
|
+
return <Text className={cn(description(), className)} {...props} />;
|
|
640
|
+
}
|
|
461
641
|
|
|
462
642
|
export interface TooltipArrowProps extends ViewProps {
|
|
463
643
|
className?: string;
|
|
@@ -471,7 +651,8 @@ export interface TooltipArrowProps extends ViewProps {
|
|
|
471
651
|
* the trigger rather than over the label's middle.
|
|
472
652
|
*/
|
|
473
653
|
function TooltipArrow({ className, style, ...props }: TooltipArrowProps) {
|
|
474
|
-
const { trigger, placement, arrowOffset } = useTooltip('Tooltip.Arrow');
|
|
654
|
+
const { trigger, placement, arrowOffset, variant } = useTooltip('Tooltip.Arrow');
|
|
655
|
+
const { arrow } = tooltipVariants({ variant });
|
|
475
656
|
if (!trigger) return null;
|
|
476
657
|
|
|
477
658
|
const vertical = placement === 'top' || placement === 'bottom';
|
|
@@ -499,7 +680,7 @@ function TooltipArrow({ className, style, ...props }: TooltipArrowProps) {
|
|
|
499
680
|
},
|
|
500
681
|
style,
|
|
501
682
|
]}
|
|
502
|
-
className={cn(
|
|
683
|
+
className={cn(arrow(), className)}
|
|
503
684
|
{...props}
|
|
504
685
|
/>
|
|
505
686
|
);
|
|
@@ -606,5 +787,7 @@ export const Tooltip = Object.assign(TooltipRoot, {
|
|
|
606
787
|
Trigger: TooltipTrigger,
|
|
607
788
|
Content: TooltipContent,
|
|
608
789
|
Arrow: TooltipArrow,
|
|
790
|
+
Title: TooltipTitle,
|
|
791
|
+
Description: TooltipDescription,
|
|
609
792
|
Text: TooltipText,
|
|
610
793
|
});
|
package/src/icons/index.tsx
CHANGED
|
@@ -133,12 +133,18 @@ export function CalendarIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
export function BookmarkIcon({
|
|
136
|
+
export function BookmarkIcon({
|
|
137
|
+
size = 16,
|
|
138
|
+
color,
|
|
139
|
+
filled,
|
|
140
|
+
...props
|
|
141
|
+
}: IconProps & { filled?: boolean }) {
|
|
137
142
|
const resolved = useResolvedColor(color, '#737373');
|
|
138
143
|
return (
|
|
139
144
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
140
145
|
<Path
|
|
141
146
|
d="m19 21-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z"
|
|
147
|
+
fill={filled ? resolved : 'none'}
|
|
142
148
|
stroke={resolved}
|
|
143
149
|
strokeWidth={2}
|
|
144
150
|
strokeLinecap="round"
|
|
@@ -872,3 +878,260 @@ export function UnlockIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
872
878
|
</Svg>
|
|
873
879
|
);
|
|
874
880
|
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Four-pointed sparkles. The mark for a model doing something on your behalf —
|
|
884
|
+
* a reasoning step, a generated plan — where a gear or a brain reads as
|
|
885
|
+
* machinery rather than as assistance.
|
|
886
|
+
*/
|
|
887
|
+
export function SparklesIcon({ size = 16, color, ...props }: IconProps) {
|
|
888
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
889
|
+
return (
|
|
890
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
891
|
+
<Path
|
|
892
|
+
d="M11 3 12.6 8.4 18 10l-5.4 1.6L11 17l-1.6-5.4L4 10l5.4-1.6z"
|
|
893
|
+
stroke={resolved}
|
|
894
|
+
strokeWidth={2}
|
|
895
|
+
strokeLinejoin="round"
|
|
896
|
+
/>
|
|
897
|
+
<Path
|
|
898
|
+
d="M18 15.5 18.7 18l2.3.7-2.3.7-.7 2.3-.7-2.3-2.3-.7 2.3-.7z"
|
|
899
|
+
stroke={resolved}
|
|
900
|
+
strokeWidth={1.6}
|
|
901
|
+
strokeLinejoin="round"
|
|
902
|
+
/>
|
|
903
|
+
</Svg>
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/** Two stacked sheets — the copy action, paired with CheckIcon once it has run. */
|
|
908
|
+
export function CopyIcon({ size = 16, color, ...props }: IconProps) {
|
|
909
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
910
|
+
return (
|
|
911
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
912
|
+
<Path
|
|
913
|
+
d="M9 9h10v12H9z"
|
|
914
|
+
stroke={resolved}
|
|
915
|
+
strokeWidth={2}
|
|
916
|
+
strokeLinejoin="round"
|
|
917
|
+
/>
|
|
918
|
+
<Path
|
|
919
|
+
d="M15 5H5v12h2"
|
|
920
|
+
stroke={resolved}
|
|
921
|
+
strokeWidth={2}
|
|
922
|
+
strokeLinecap="round"
|
|
923
|
+
strokeLinejoin="round"
|
|
924
|
+
/>
|
|
925
|
+
</Svg>
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/** Two links of a chain, for a row that opens somewhere outside the app. */
|
|
930
|
+
export function LinkIcon({ size = 16, color, ...props }: IconProps) {
|
|
931
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
932
|
+
return (
|
|
933
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
934
|
+
<Path
|
|
935
|
+
d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.7 1.7"
|
|
936
|
+
stroke={resolved}
|
|
937
|
+
strokeWidth={2}
|
|
938
|
+
strokeLinecap="round"
|
|
939
|
+
strokeLinejoin="round"
|
|
940
|
+
/>
|
|
941
|
+
<Path
|
|
942
|
+
d="M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.7-1.7"
|
|
943
|
+
stroke={resolved}
|
|
944
|
+
strokeWidth={2}
|
|
945
|
+
strokeLinecap="round"
|
|
946
|
+
strokeLinejoin="round"
|
|
947
|
+
/>
|
|
948
|
+
</Svg>
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/** A list with its items ticked off — a plan, or a run of steps. */
|
|
953
|
+
export function ListChecksIcon({ size = 16, color, ...props }: IconProps) {
|
|
954
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
955
|
+
return (
|
|
956
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
957
|
+
<Path
|
|
958
|
+
d="m3 6 1.8 1.8L8 4.5M3 16l1.8 1.8L8 14.5"
|
|
959
|
+
stroke={resolved}
|
|
960
|
+
strokeWidth={2}
|
|
961
|
+
strokeLinecap="round"
|
|
962
|
+
strokeLinejoin="round"
|
|
963
|
+
/>
|
|
964
|
+
<Path
|
|
965
|
+
d="M12 6.5h9M12 17.5h9"
|
|
966
|
+
stroke={resolved}
|
|
967
|
+
strokeWidth={2}
|
|
968
|
+
strokeLinecap="round"
|
|
969
|
+
/>
|
|
970
|
+
</Svg>
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/** A hollow ring, for a step that has not started. */
|
|
975
|
+
export function CircleIcon({ size = 16, color, ...props }: IconProps) {
|
|
976
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
977
|
+
return (
|
|
978
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
979
|
+
<Circle cx={12} cy={12} r={8} stroke={resolved} strokeWidth={2} />
|
|
980
|
+
</Svg>
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/* -------------------------------------------------------------------------- */
|
|
985
|
+
/* Social */
|
|
986
|
+
/* -------------------------------------------------------------------------- */
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* The icons a post's action row is made of.
|
|
990
|
+
*
|
|
991
|
+
* These are the only ones in the set that take `filled`, and it is not a
|
|
992
|
+
* stylistic choice: a like, a save and a vote are toggles, and the outline and
|
|
993
|
+
* the solid are the two states of one control. Drawn as two different icons
|
|
994
|
+
* they would swap shape under the finger; drawn as one that fills, the shape
|
|
995
|
+
* stays put and only the inside changes.
|
|
996
|
+
*/
|
|
997
|
+
export interface ToggleIconProps extends IconProps {
|
|
998
|
+
/** Solid rather than outlined — the on state of a like, a save, a vote. */
|
|
999
|
+
filled?: boolean;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/** Upvote. Filled once the vote is cast. */
|
|
1003
|
+
export function ArrowUpIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1004
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1005
|
+
return (
|
|
1006
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1007
|
+
<Path
|
|
1008
|
+
d="M9 18v-6H5l7-7 7 7h-4v6z"
|
|
1009
|
+
fill={filled ? resolved : 'none'}
|
|
1010
|
+
stroke={resolved}
|
|
1011
|
+
strokeWidth={2}
|
|
1012
|
+
strokeLinecap="round"
|
|
1013
|
+
strokeLinejoin="round"
|
|
1014
|
+
/>
|
|
1015
|
+
</Svg>
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/** Downvote. The same arrow, turned over. */
|
|
1020
|
+
export function ArrowDownIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1021
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1022
|
+
return (
|
|
1023
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1024
|
+
<Path
|
|
1025
|
+
d="M9 6v6H5l7 7 7-7h-4V6z"
|
|
1026
|
+
fill={filled ? resolved : 'none'}
|
|
1027
|
+
stroke={resolved}
|
|
1028
|
+
strokeWidth={2}
|
|
1029
|
+
strokeLinecap="round"
|
|
1030
|
+
strokeLinejoin="round"
|
|
1031
|
+
/>
|
|
1032
|
+
</Svg>
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/** A like. */
|
|
1037
|
+
export function HeartIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1038
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1039
|
+
return (
|
|
1040
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1041
|
+
<Path
|
|
1042
|
+
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
|
|
1043
|
+
fill={filled ? resolved : 'none'}
|
|
1044
|
+
stroke={resolved}
|
|
1045
|
+
strokeWidth={2}
|
|
1046
|
+
strokeLinecap="round"
|
|
1047
|
+
strokeLinejoin="round"
|
|
1048
|
+
/>
|
|
1049
|
+
</Svg>
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/** A reply, or the count of them. */
|
|
1054
|
+
export function MessageCircleIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1055
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1056
|
+
return (
|
|
1057
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1058
|
+
<Path
|
|
1059
|
+
d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"
|
|
1060
|
+
fill={filled ? resolved : 'none'}
|
|
1061
|
+
stroke={resolved}
|
|
1062
|
+
strokeWidth={2}
|
|
1063
|
+
strokeLinecap="round"
|
|
1064
|
+
strokeLinejoin="round"
|
|
1065
|
+
/>
|
|
1066
|
+
</Svg>
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/** Views — how many times a thing has been seen rather than acted on. */
|
|
1071
|
+
export function EyeIcon({ size = 16, color, ...props }: IconProps) {
|
|
1072
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1073
|
+
return (
|
|
1074
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1075
|
+
<Path
|
|
1076
|
+
d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0"
|
|
1077
|
+
stroke={resolved}
|
|
1078
|
+
strokeWidth={2}
|
|
1079
|
+
strokeLinecap="round"
|
|
1080
|
+
strokeLinejoin="round"
|
|
1081
|
+
/>
|
|
1082
|
+
<Circle cx={12} cy={12} r={3} stroke={resolved} strokeWidth={2} />
|
|
1083
|
+
</Svg>
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/** A repost — the same thing sent round again. */
|
|
1088
|
+
export function RepeatIcon({ size = 16, color, ...props }: IconProps) {
|
|
1089
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1090
|
+
return (
|
|
1091
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1092
|
+
<Path
|
|
1093
|
+
d="m2 9 3-3 3 3M5 6v10a2 2 0 0 0 2 2h6M22 15l-3 3-3-3M19 18V8a2 2 0 0 0-2-2h-6"
|
|
1094
|
+
stroke={resolved}
|
|
1095
|
+
strokeWidth={2}
|
|
1096
|
+
strokeLinecap="round"
|
|
1097
|
+
strokeLinejoin="round"
|
|
1098
|
+
/>
|
|
1099
|
+
</Svg>
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
export interface BadgeCheckIconProps extends IconProps {
|
|
1104
|
+
/** The tick's colour when the rosette is solid. Defaults to white. */
|
|
1105
|
+
checkColor?: string;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* The verified rosette.
|
|
1110
|
+
*
|
|
1111
|
+
* Solid by default, unlike everything else here. A verification mark is a
|
|
1112
|
+
* claim about the account rather than a control, and an outlined one next to
|
|
1113
|
+
* an outlined like button reads as another thing you could press.
|
|
1114
|
+
*/
|
|
1115
|
+
export function BadgeCheckIcon({
|
|
1116
|
+
size = 16,
|
|
1117
|
+
color,
|
|
1118
|
+
checkColor = '#ffffff',
|
|
1119
|
+
...props
|
|
1120
|
+
}: BadgeCheckIconProps) {
|
|
1121
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1122
|
+
return (
|
|
1123
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1124
|
+
<Path
|
|
1125
|
+
d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"
|
|
1126
|
+
fill={resolved}
|
|
1127
|
+
/>
|
|
1128
|
+
<Path
|
|
1129
|
+
d="m9 12 2 2 4-4"
|
|
1130
|
+
stroke={checkColor}
|
|
1131
|
+
strokeWidth={2.5}
|
|
1132
|
+
strokeLinecap="round"
|
|
1133
|
+
strokeLinejoin="round"
|
|
1134
|
+
/>
|
|
1135
|
+
</Svg>
|
|
1136
|
+
);
|
|
1137
|
+
}
|