@payglocal_ui/flux-ui 0.3.0 → 0.3.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/src/index.ts CHANGED
@@ -278,7 +278,7 @@ export type { AvatarTagProps, AvatarTagSize } from "./avatar-tag";
278
278
  export { Calendar, CalendarDayButton } from "./calendar";
279
279
  export type { CalendarProps } from "./calendar";
280
280
  export type { DateRange } from "react-day-picker";
281
- export { DatePicker } from "./date-picker";
281
+ export { DatePicker, type DatePickerTimeOptions } from "./date-picker";
282
282
 
283
283
  // Charts
284
284
  export {
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as React from "react";
4
4
  import { createPortal } from "react-dom";
5
+ import { RemoveScroll } from "react-remove-scroll";
5
6
  import { Clock, ChevronDown, X } from "lucide-react";
6
7
  import { cn } from "./utils";
7
8
 
@@ -309,78 +310,106 @@ export const TimePicker = React.forwardRef<HTMLButtonElement, TimePickerProps>(
309
310
  }
310
311
 
311
312
  /* ── Panel ── */
313
+ /**
314
+ * Wrapped in `RemoveScroll` so the columns can be scrolled inside a modal
315
+ * Dialog or Drawer.
316
+ *
317
+ * Radix's Dialog renders its overlay inside `RemoveScroll` with only
318
+ * `Dialog.Content` as a shard. This panel is portaled to `document.body`, so
319
+ * it is neither the lock container nor a shard, and react-remove-scroll's
320
+ * `shouldPrevent` calls `preventDefault()` on every wheel and touch event it
321
+ * receives — leaving only the rows already on screen reachable, and every
322
+ * other time selectable solely by clicking two rows at a time.
323
+ *
324
+ * Mounting our own lock pushes it to the top of react-remove-scroll's
325
+ * `lockStack`, and the sidecar bails out for any lock that is not the last
326
+ * one, so this panel's scrolling is honoured while the dialog's own lock
327
+ * still holds for the page behind it. It is the same thing Radix `Select`
328
+ * does with its content, and the reason a Select works inside a dialog where
329
+ * this panel did not.
330
+ *
331
+ * Enabled unconditionally rather than only inside a dialog: the panel's
332
+ * position is `fixed` and computed once on open, so locking the page also
333
+ * stops it drifting away from its trigger on a scrollable page.
334
+ */
312
335
  const panel = open ? (
313
- <div
314
- ref={panelRef}
315
- className={cn(
316
- "isolate rounded-xl border border-border bg-popover text-popover-foreground shadow-lg",
317
- "flex flex-col gap-0"
318
- )}
319
- style={{
320
- position: "fixed",
321
- top: panelPos.top,
322
- left: panelPos.left,
323
- width: PANEL_W,
324
- zIndex: 20000,
325
- }}
326
- >
327
- {/* Header */}
328
- <div className="flex items-center gap-1.5 border-b border-border px-4 py-2.5">
329
- <Clock className="size-3.5 text-muted-foreground" />
330
- <span className="text-[13px] font-medium text-muted-foreground">
331
- {value ? displayTime(value, use24Hour) : "—"}
332
- </span>
333
- </div>
334
-
335
- {/* Column labels */}
336
+ <RemoveScroll allowPinchZoom>
336
337
  <div
337
- className="flex items-center justify-around px-2 pt-2 pb-0.5"
338
- style={{ paddingLeft: 8, paddingRight: use24Hour ? 8 : 8 }}
338
+ ref={panelRef}
339
+ className={cn(
340
+ "isolate rounded-xl border border-border bg-popover text-popover-foreground shadow-lg",
341
+ "flex flex-col gap-0"
342
+ )}
343
+ style={{
344
+ position: "fixed",
345
+ top: panelPos.top,
346
+ left: panelPos.left,
347
+ width: PANEL_W,
348
+ zIndex: 20000,
349
+ // See DatePicker: a modal Radix Dialog puts `pointer-events: none`
350
+ // on <body>, which this portaled panel would otherwise inherit —
351
+ // the columns rendered but no row could be clicked.
352
+ pointerEvents: "auto",
353
+ }}
339
354
  >
340
- <span className="w-16 text-center text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70">
341
- Hr
342
- </span>
343
- <span className="w-16 text-center text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70">
344
- Min
345
- </span>
346
- {!use24Hour && (
347
- <span className="w-16 text-center text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70">
348
- AM/PM
355
+ {/* Header */}
356
+ <div className="flex items-center gap-1.5 border-b border-border px-4 py-2.5">
357
+ <Clock className="size-3.5 text-muted-foreground" />
358
+ <span className="text-[13px] font-medium text-muted-foreground">
359
+ {value ? displayTime(value, use24Hour) : "—"}
349
360
  </span>
350
- )}
351
- </div>
361
+ </div>
352
362
 
353
- {/* Columns */}
354
- <div className="flex items-center justify-around px-2 pb-3">
355
- <ScrollColumn
356
- items={hours}
357
- selected={hour}
358
- onSelect={handleHourChange}
359
- renderItem={(h) => pad(h)}
360
- getKey={(h) => h}
361
- />
362
- <div className="text-[18px] font-light text-muted-foreground/50 pb-0.5">:</div>
363
- <ScrollColumn
364
- items={minutes}
365
- selected={minute}
366
- onSelect={handleMinuteChange}
367
- renderItem={(m) => pad(m)}
368
- getKey={(m) => m}
369
- />
370
- {!use24Hour && (
371
- <>
372
- <div className="w-px self-stretch bg-border mx-1" />
373
- <ScrollColumn
374
- items={["AM", "PM"] as const}
375
- selected={period}
376
- onSelect={handlePeriodChange}
377
- renderItem={(p) => p}
378
- getKey={(p) => p}
379
- />
380
- </>
381
- )}
363
+ {/* Column labels */}
364
+ <div
365
+ className="flex items-center justify-around px-2 pt-2 pb-0.5"
366
+ style={{ paddingLeft: 8, paddingRight: use24Hour ? 8 : 8 }}
367
+ >
368
+ <span className="w-16 text-center text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70">
369
+ Hr
370
+ </span>
371
+ <span className="w-16 text-center text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70">
372
+ Min
373
+ </span>
374
+ {!use24Hour && (
375
+ <span className="w-16 text-center text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/70">
376
+ AM/PM
377
+ </span>
378
+ )}
379
+ </div>
380
+
381
+ {/* Columns */}
382
+ <div className="flex items-center justify-around px-2 pb-3">
383
+ <ScrollColumn
384
+ items={hours}
385
+ selected={hour}
386
+ onSelect={handleHourChange}
387
+ renderItem={(h) => pad(h)}
388
+ getKey={(h) => h}
389
+ />
390
+ <div className="text-[18px] font-light text-muted-foreground/50 pb-0.5">:</div>
391
+ <ScrollColumn
392
+ items={minutes}
393
+ selected={minute}
394
+ onSelect={handleMinuteChange}
395
+ renderItem={(m) => pad(m)}
396
+ getKey={(m) => m}
397
+ />
398
+ {!use24Hour && (
399
+ <>
400
+ <div className="w-px self-stretch bg-border mx-1" />
401
+ <ScrollColumn
402
+ items={["AM", "PM"] as const}
403
+ selected={period}
404
+ onSelect={handlePeriodChange}
405
+ renderItem={(p) => p}
406
+ getKey={(p) => p}
407
+ />
408
+ </>
409
+ )}
410
+ </div>
382
411
  </div>
383
- </div>
412
+ </RemoveScroll>
384
413
  ) : null;
385
414
 
386
415
  /* ── Merge refs ── */