panelui-native 0.51.1 → 0.54.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.
Files changed (46) hide show
  1. package/README.md +4 -1
  2. package/lib/module/components/candlestick-chart/index.js +1161 -0
  3. package/lib/module/components/candlestick-chart/index.js.map +1 -0
  4. package/lib/module/components/combobox/index.js +73 -7
  5. package/lib/module/components/combobox/index.js.map +1 -1
  6. package/lib/module/components/context-menu/index.js +529 -0
  7. package/lib/module/components/context-menu/index.js.map +1 -0
  8. package/lib/module/components/menu/index.js +18 -10
  9. package/lib/module/components/menu/index.js.map +1 -1
  10. package/lib/module/components/popover/index.js +71 -7
  11. package/lib/module/components/popover/index.js.map +1 -1
  12. package/lib/module/components/sortable/index.js +159 -23
  13. package/lib/module/components/sortable/index.js.map +1 -1
  14. package/lib/module/components/tabs/index.js +56 -15
  15. package/lib/module/components/tabs/index.js.map +1 -1
  16. package/lib/module/components/time-picker/index.js +295 -31
  17. package/lib/module/components/time-picker/index.js.map +1 -1
  18. package/lib/module/index.js +3 -1
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/typescript/src/components/candlestick-chart/index.d.ts +278 -0
  21. package/lib/typescript/src/components/candlestick-chart/index.d.ts.map +1 -0
  22. package/lib/typescript/src/components/combobox/index.d.ts.map +1 -1
  23. package/lib/typescript/src/components/context-menu/index.d.ts +270 -0
  24. package/lib/typescript/src/components/context-menu/index.d.ts.map +1 -0
  25. package/lib/typescript/src/components/menu/index.d.ts +20 -20
  26. package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
  27. package/lib/typescript/src/components/popover/index.d.ts +51 -1
  28. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  29. package/lib/typescript/src/components/sortable/index.d.ts +13 -3
  30. package/lib/typescript/src/components/sortable/index.d.ts.map +1 -1
  31. package/lib/typescript/src/components/tabs/index.d.ts +34 -2
  32. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  33. package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
  34. package/lib/typescript/src/index.d.ts +3 -1
  35. package/lib/typescript/src/index.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/components/candlestick-chart/index.tsx +1360 -0
  38. package/src/components/combobox/index.tsx +149 -18
  39. package/src/components/context-menu/index.tsx +658 -0
  40. package/src/components/menu/index.tsx +17 -10
  41. package/src/components/popover/index.tsx +94 -6
  42. package/src/components/sortable/index.tsx +278 -36
  43. package/src/components/tabs/index.tsx +82 -16
  44. package/src/components/tag-input/index.tsx +619 -0
  45. package/src/components/time-picker/index.tsx +330 -35
  46. package/src/index.ts +35 -0
@@ -341,7 +341,7 @@ export interface MenuBackgroundProps extends ViewProps {
341
341
  * `overflow-hidden` on the panel is what keeps whatever that is inside the
342
342
  * corner radius.
343
343
  */
344
- function MenuBackground({ className, children, ...props }: MenuBackgroundProps) {
344
+ export function MenuBackground({ className, children, ...props }: MenuBackgroundProps) {
345
345
  const { background } = menuVariants();
346
346
 
347
347
  return (
@@ -365,7 +365,7 @@ export interface MenuLabelProps extends ViewProps {
365
365
  }
366
366
 
367
367
  /** Non-interactive heading over a run of rows. */
368
- function MenuLabel({ className, inset, children, ...props }: MenuLabelProps) {
368
+ export function MenuLabel({ className, inset, children, ...props }: MenuLabelProps) {
369
369
  const { label } = menuVariants({ inset });
370
370
 
371
371
  return (
@@ -384,7 +384,7 @@ export interface MenuSeparatorProps extends ViewProps {
384
384
  }
385
385
 
386
386
  /** Hairline between two runs of rows. */
387
- function MenuSeparator({ className, ...props }: MenuSeparatorProps) {
387
+ export function MenuSeparator({ className, ...props }: MenuSeparatorProps) {
388
388
  const { separator } = menuVariants();
389
389
 
390
390
  return (
@@ -435,7 +435,7 @@ export interface MenuItemProps extends Omit<PressableProps, 'children' | 'style'
435
435
  * that a row which is disabled or which opts out via `closeOnSelect` behaves
436
436
  * the same either way.
437
437
  */
438
- function MenuItem({
438
+ export function MenuItem({
439
439
  className,
440
440
  children,
441
441
  icon,
@@ -540,7 +540,7 @@ export interface MenuCheckboxItemProps extends Omit<MenuItemProps, 'icon' | 'ins
540
540
  * deliberately so: a set of toggles is nearly always set more than one at a
541
541
  * time, and closing after each one turns three taps into six.
542
542
  */
543
- function MenuCheckboxItem({
543
+ export function MenuCheckboxItem({
544
544
  checked = false,
545
545
  onCheckedChange,
546
546
  onSelect,
@@ -592,7 +592,7 @@ export interface MenuRadioGroupProps extends ViewProps {
592
592
  }
593
593
 
594
594
  /** A run of rows of which exactly one is chosen. */
595
- function MenuRadioGroup({ value, onValueChange, className, children, ...props }: MenuRadioGroupProps) {
595
+ export function MenuRadioGroup({ value, onValueChange, className, children, ...props }: MenuRadioGroupProps) {
596
596
  const context = useMemo(
597
597
  () => ({ value, select: (next: string) => onValueChange?.(next) }),
598
598
  [value, onValueChange]
@@ -616,7 +616,7 @@ export interface MenuRadioItemProps extends Omit<MenuItemProps, 'icon' | 'inset'
616
616
  }
617
617
 
618
618
  /** One option inside a `Menu.RadioGroup`. */
619
- function MenuRadioItem({
619
+ export function MenuRadioItem({
620
620
  value,
621
621
  indicator = 'check',
622
622
  onSelect,
@@ -694,7 +694,7 @@ export interface MenuSubProps {
694
694
  }
695
695
 
696
696
  /** Groups a `Menu.SubTrigger` with the rows it reveals. */
697
- function MenuSub({ children, defaultOpen = false, open, onOpenChange }: MenuSubProps) {
697
+ export function MenuSub({ children, defaultOpen = false, open, onOpenChange }: MenuSubProps) {
698
698
  const [internalOpen, setInternalOpen] = useState(defaultOpen);
699
699
  const isControlled = open !== undefined;
700
700
  const resolved = isControlled ? open : internalOpen;
@@ -723,7 +723,7 @@ export interface MenuSubTriggerProps
723
723
  * point down once open, so the row states which way its rows will appear
724
724
  * rather than only that it has some.
725
725
  */
726
- function MenuSubTrigger({ className, children, icon, onSelect, ...props }: MenuSubTriggerProps) {
726
+ export function MenuSubTrigger({ className, children, icon, onSelect, ...props }: MenuSubTriggerProps) {
727
727
  const { open, toggle } = useMenuSub('Menu.SubTrigger');
728
728
  const sign = useDirectionSign();
729
729
  const direction = useDirection();
@@ -790,7 +790,7 @@ export interface MenuSubContentProps extends ViewProps {
790
790
  * would otherwise report the animated height back, and the panel would settle
791
791
  * at whatever it happened to measure on the first frame.
792
792
  */
793
- function MenuSubContent({ className, children, ...props }: MenuSubContentProps) {
793
+ export function MenuSubContent({ className, children, ...props }: MenuSubContentProps) {
794
794
  const { open } = useMenuSub('Menu.SubContent');
795
795
  const reducedMotion = useReducedMotion();
796
796
  const [height, setHeight] = useState(0);
@@ -841,6 +841,13 @@ MenuSub.displayName = 'Menu.Sub';
841
841
  MenuSubTrigger.displayName = 'Menu.SubTrigger';
842
842
  MenuSubContent.displayName = 'Menu.SubContent';
843
843
 
844
+ /*
845
+ * The parts above are exported individually as well as hung off `Menu`, and the
846
+ * reason is `ContextMenu`: it presents these same components as its own rows, so
847
+ * the emitted type declarations for it have to be able to name them. `Menu` is
848
+ * still the only thing the package exports, so nothing about how these are
849
+ * reached from outside has changed.
850
+ */
844
851
  export const Menu = Object.assign(MenuRoot, {
845
852
  Trigger: MenuTrigger,
846
853
  Content: MenuContent,
@@ -72,17 +72,37 @@ const DEFAULT_OFFSET = 8;
72
72
  const SCREEN_MARGIN = 12;
73
73
  /** Side of the arrow square before it is rotated 45°. */
74
74
  const ARROW_SIZE = 12;
75
+ /**
76
+ * Headroom the sheet presentation leaves for the close button.
77
+ *
78
+ * That button floats over the sheet's top-end corner rather than sitting in the
79
+ * flow, so nothing below it is pushed out of its way. The sheet's own padding
80
+ * and grabber put the first child 24 points down; the button's lower edge is at
81
+ * 44. This is the difference, plus a little air — enough that a panel's first
82
+ * row clears the button instead of being drawn under it.
83
+ */
84
+ const SHEET_CLOSE_CLEARANCE = 24;
75
85
 
76
86
  export type PopoverPlacement = 'top' | 'bottom' | 'left' | 'right';
77
87
  export type PopoverAlign = 'start' | 'center' | 'end';
78
88
 
79
- interface TriggerRect {
89
+ /**
90
+ * The rectangle the panel is placed against, in window coordinates.
91
+ *
92
+ * Normally the trigger's own bounds. A zero-sized rect is meaningful too: it
93
+ * anchors the panel to a single point, which is what a menu opened by a long
94
+ * press on arbitrary content needs — there the interesting position is where
95
+ * the finger landed, not the bounds of whatever it landed on.
96
+ */
97
+ export interface PopoverAnchorRect {
80
98
  x: number;
81
99
  y: number;
82
100
  width: number;
83
101
  height: number;
84
102
  }
85
103
 
104
+ type TriggerRect = PopoverAnchorRect;
105
+
86
106
  interface PopoverContextValue {
87
107
  open: boolean;
88
108
  setOpen: (open: boolean) => void;
@@ -110,6 +130,38 @@ function usePopover(component: string): PopoverContextValue {
110
130
  return context;
111
131
  }
112
132
 
133
+ export interface PopoverAnchorControls {
134
+ open: boolean;
135
+ setOpen: (open: boolean) => void;
136
+ /**
137
+ * Place the panel against an explicit rect rather than against a measured
138
+ * trigger. Pass a zero-sized rect to anchor it to a point.
139
+ */
140
+ anchorTo: (rect: PopoverAnchorRect) => void;
141
+ }
142
+
143
+ /**
144
+ * For a trigger that opens the panel on something other than a plain press, or
145
+ * anchors it to something other than its own bounds.
146
+ *
147
+ * `Popover.Trigger` covers the ordinary case — press the thing, measure the
148
+ * thing, open next to it. A component built on this one may need neither half
149
+ * of that: a context menu opens on a long press and belongs at the point the
150
+ * finger landed. Rather than have it own a second copy of the placing,
151
+ * flipping and edge-clamping this file already does, it borrows them by
152
+ * setting the anchor itself.
153
+ *
154
+ * Only useful inside a `Popover`, which is the same rule every other part here
155
+ * follows.
156
+ */
157
+ export function usePopoverAnchor(component: string): PopoverAnchorControls {
158
+ const { open, setOpen, setTrigger } = usePopover(component);
159
+ return useMemo(
160
+ () => ({ open, setOpen, anchorTo: setTrigger }),
161
+ [open, setOpen, setTrigger]
162
+ );
163
+ }
164
+
113
165
  export interface PopoverProps {
114
166
  children: ReactNode;
115
167
  /** Controlled open state. */
@@ -271,6 +323,18 @@ export interface PopoverContentProps extends ViewProps {
271
323
  * not, so it is safe to pass either way.
272
324
  */
273
325
  blur?: boolean;
326
+ /**
327
+ * Dim the screen behind the panel.
328
+ *
329
+ * Off by default: a popover is a panel *beside* something, and dimming the
330
+ * page says the thing behind it has stopped being available — which is a
331
+ * dialog's claim, not a popover's. Worth turning on when the panel is the
332
+ * only thing that matters while it is up, which is what a menu opened on the
333
+ * content itself is. Ignored under `blur`, which draws its own dim.
334
+ */
335
+ scrim?: boolean;
336
+ /** The dim's classes, when `scrim` is set. */
337
+ scrimClassName?: string;
274
338
  children?: ReactNode;
275
339
  }
276
340
 
@@ -288,6 +352,8 @@ function PopoverContent({
288
352
  background,
289
353
  dismissible = true,
290
354
  blur = false,
355
+ scrim = false,
356
+ scrimClassName = 'bg-black/30',
291
357
  children,
292
358
  onLayout: onLayoutProp,
293
359
  style,
@@ -342,6 +408,23 @@ function PopoverContent({
342
408
  ? Math.min(Math.max(requestedWidth, minWidth), available)
343
409
  : requestedWidth;
344
410
 
411
+ /*
412
+ * The floor also has to reach a panel sized to its own contents, which is the
413
+ * case it matters most in — a panel with a width already knows how wide it is.
414
+ *
415
+ * Folding it into `width` would be wrong: that pins the panel open at exactly
416
+ * the floor and stops it growing for content that needs more. It is a real
417
+ * minimum instead, left off when there is nothing to enforce so a content-fit
418
+ * panel keeps shrinking to fit.
419
+ *
420
+ * Without this a `content-fit` panel takes its width from whatever inside it
421
+ * is *not* flexible. A row of a flexible label and a fixed glyph collapses to
422
+ * the glyph, and the panel comes up as a strip of icons with the words
423
+ * squeezed out of it.
424
+ */
425
+ const resolvedMinWidth =
426
+ minWidth === undefined ? undefined : Math.min(minWidth, available);
427
+
345
428
  /*
346
429
  * The cap is what keeps a tall panel reachable. `place` clamps the panel
347
430
  * inside the bounds, but a panel taller than the bounds cannot be clamped
@@ -434,9 +517,7 @@ function PopoverContent({
434
517
  width === 'full' || width === 'trigger' ? { width: '100%' } : null,
435
518
  typeof width === 'number' ? { width } : null,
436
519
  minWidth === undefined ? null : { minWidth },
437
- // Clear of the close button, which floats over the top-end
438
- // corner of the sheet rather than sitting in the flow.
439
- { paddingTop: 8 },
520
+ { paddingTop: SHEET_CLOSE_CLEARANCE },
440
521
  style,
441
522
  ]}
442
523
  >
@@ -458,8 +539,14 @@ function PopoverContent({
458
539
  <PopoverContext.Provider value={context}>
459
540
  <View className="absolute inset-0">
460
541
  {/* A popover does not dim the screen by default — the backdrop is
461
- there only to catch the outside tap. `blur` opts into a frost. */}
462
- {blur ? <Scrim blur intensity={20} dimClassName="bg-black/30" /> : null}
542
+ there only to catch the outside tap. `blur` opts into a frost,
543
+ `scrim` into a plain dim; the frost already draws one of its own,
544
+ so asking for both is not two dims. */}
545
+ {blur ? (
546
+ <Scrim blur intensity={20} dimClassName={scrimClassName} />
547
+ ) : scrim ? (
548
+ <Scrim dimClassName={scrimClassName} />
549
+ ) : null}
463
550
  <Pressable
464
551
  accessibilityLabel="Close"
465
552
  className="absolute inset-0"
@@ -486,6 +573,7 @@ function PopoverContent({
486
573
  maxWidth: bounds.right - bounds.left,
487
574
  maxHeight: resolvedMaxHeight,
488
575
  width: resolvedWidth,
576
+ minWidth: resolvedMinWidth,
489
577
  }}
490
578
  >
491
579
  <Animated.View