@geomak/ui 1.7.5 → 1.9.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/dist/index.d.cts CHANGED
@@ -587,45 +587,82 @@ declare function useNotification(): {
587
587
  danger: (props: Omit<NotificationPayload, "type">) => void;
588
588
  };
589
589
 
590
+ type LoadingSpinnerSize = 'sm' | 'md' | 'lg';
590
591
  interface LoadingSpinnerProps {
591
- /** Text revealed letter-by-letter beneath the spinner. */
592
- prompt: string;
592
+ /**
593
+ * Text revealed letter-by-letter beneath the spinner. Optional — pass
594
+ * `undefined` for a pure spinner with no caption (e.g. inline mode).
595
+ */
596
+ prompt?: string;
597
+ /** Spinner size variant. Defaults to `'md'`. */
598
+ size?: LoadingSpinnerSize;
599
+ /**
600
+ * When `true`, renders inline at the call site (no portal, no fullscreen
601
+ * overlay, no backdrop). Useful inside cards, table rows, drawers, or
602
+ * empty-state slots. Defaults to `false` (fullscreen overlay).
603
+ */
604
+ inline?: boolean;
593
605
  /**
594
606
  * Optional override for the spinner ring colour. Accepts any CSS colour.
595
- * Defaults to the accent token so it picks up theme overrides.
607
+ * Defaults to the `--color-accent` token so it picks up theme overrides.
596
608
  */
597
609
  spinnerColor?: string;
598
610
  /**
599
611
  * Optional override for the prompt text colour.
600
- * Defaults to the foreground token (light/dark aware).
612
+ * Defaults to the `--color-foreground` token (light/dark aware).
601
613
  */
602
614
  textColor?: string;
603
615
  /**
604
- * Backdrop opacity (0 – 1). Defaults to 0.92 close enough to opaque to
605
- * block UI underneath while still hinting at the previous state.
616
+ * Backdrop opacity (0 – 1) for the fullscreen overlay. Defaults to 0.8
617
+ * close enough to opaque to block UI underneath while still hinting at the
618
+ * previous state. Ignored when `inline` is true.
606
619
  */
607
620
  backdropOpacity?: number;
608
621
  }
609
622
  /**
610
- * Full-screen loading overlay with a spinning ring and a staggered text
611
- * reveal. Portaled into `document.body` so it always covers the actual
612
- * viewport regardless of where it's rendered in the React tree.
613
- *
614
- * Honours `prefers-reduced-motion`: the spinner still rotates (continuous
615
- * spin is informative, not decorative) but the letter stagger collapses to
616
- * an instant reveal.
617
- *
618
- * @example
623
+ * Enterprise-grade loading indicator.
624
+ *
625
+ * Two concentric arcs counter-rotating around a breathing centre dot, with
626
+ * an optional staggered letter reveal beneath. Portaled to `document.body`
627
+ * for the fullscreen overlay so it always covers the actual viewport
628
+ * regardless of where it sits in the React tree.
629
+ *
630
+ * **Two modes:**
631
+ * - **Fullscreen overlay** (default): semi-opaque backdrop with
632
+ * `backdrop-blur-sm` for a modern depth effect. Use during page-level
633
+ * loading, route transitions, or critical mutations.
634
+ * - **Inline** (`inline`): just the spinner + optional caption rendered at
635
+ * the call site. Use inside cards, table rows, drawers, empty states,
636
+ * or anywhere a smaller loading affordance is appropriate.
637
+ *
638
+ * **Accessibility**: `role="status"` + `aria-label`/`aria-live` make the
639
+ * indicator announce-able to screen readers. `prefers-reduced-motion`
640
+ * collapses the letter stagger to instant reveal — the spinner rings keep
641
+ * rotating since a continuous spinner is informative, not decorative.
642
+ *
643
+ * @example Fullscreen overlay (page load)
644
+ * ```tsx
619
645
  * {isLoading && <LoadingSpinner prompt="Loading vessels…" />}
646
+ * ```
620
647
  *
621
- * @example
648
+ * @example Inline (inside a card)
649
+ * ```tsx
650
+ * <Card>
651
+ * {data ? <Chart data={data} /> : <LoadingSpinner inline size="sm" />}
652
+ * </Card>
653
+ * ```
654
+ *
655
+ * @example Themed overlay
656
+ * ```tsx
622
657
  * <LoadingSpinner
623
658
  * prompt="Saving"
659
+ * size="lg"
624
660
  * spinnerColor="#10b981"
625
661
  * backdropOpacity={0.7}
626
662
  * />
663
+ * ```
627
664
  */
628
- declare function LoadingSpinner({ prompt, spinnerColor, textColor, backdropOpacity, }: LoadingSpinnerProps): react_jsx_runtime.JSX.Element;
665
+ declare function LoadingSpinner({ prompt, size, inline, spinnerColor, textColor, backdropOpacity, }: LoadingSpinnerProps): react_jsx_runtime.JSX.Element;
629
666
 
630
667
  interface FadingBaseProps {
631
668
  className?: string;
@@ -1420,11 +1457,14 @@ interface TextInputProps {
1420
1457
  declare function TextInput({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout, onBlur, errorMessage, labelColor, }: TextInputProps): react_jsx_runtime.JSX.Element;
1421
1458
 
1422
1459
  interface NumberInputProps {
1460
+ /** Step size for the up/down buttons and native arrow-key handling. Default `1`. */
1423
1461
  step?: number;
1424
- value?: any;
1462
+ /** Current value. `undefined` renders an empty field; a number renders that value. */
1463
+ value?: number | '';
1464
+ /** Fires with the next number. Empty input resolves to `undefined`. */
1425
1465
  onChange?: (e: {
1426
1466
  target: {
1427
- value: number;
1467
+ value: number | undefined;
1428
1468
  id?: string;
1429
1469
  name?: string;
1430
1470
  };
@@ -1433,8 +1473,8 @@ interface NumberInputProps {
1433
1473
  htmlFor?: string;
1434
1474
  name?: string;
1435
1475
  disabled?: boolean;
1436
- /** 'horizontal' | 'vertical' */
1437
- layout?: string;
1476
+ /** Label/input orientation. Defaults to `'horizontal'`. */
1477
+ layout?: 'horizontal' | 'vertical';
1438
1478
  errorMessage?: React$1.ReactNode;
1439
1479
  inputStyle?: React$1.CSSProperties;
1440
1480
  labelStyle?: React$1.CSSProperties;
@@ -1443,12 +1483,42 @@ interface NumberInputProps {
1443
1483
  min?: number;
1444
1484
  max?: number;
1445
1485
  readOnly?: boolean;
1446
- [key: string]: any;
1486
+ /** Optional precision for floating-point steps (number of decimal places to round to). */
1487
+ precision?: number;
1447
1488
  }
1448
1489
  /**
1449
- * Number input with increment / decrement controls.
1490
+ * Numeric input with keyboard-accessible increment / decrement buttons.
1491
+ *
1492
+ * **What's improved over the previous version**
1493
+ * - Step buttons are real `<button>` elements with `aria-label`, focus rings,
1494
+ * and proper keyboard activation (Enter / Space). The previous version used
1495
+ * `<span onClick>` which keyboard-only users could not reach.
1496
+ * - Floating-point drift on decimal steps (`0.1 + 0.2 = 0.30000000000000004`)
1497
+ * is rounded out via a `precision` prop or auto-inferred from the step.
1498
+ * - Empty input resolves to `undefined` instead of `NaN` — works with form
1499
+ * libraries (RHF, Formik) that treat empty as "no value".
1500
+ * - The decrement chevron actually points down (the previous SVG was the up
1501
+ * chevron rotated, with the up chevron itself wrongly using the same path).
1502
+ * - Width is a prop, not hardcoded `w-60`. Default is `w-full` so the input
1503
+ * flows with its parent.
1504
+ *
1505
+ * @example
1506
+ * ```tsx
1507
+ * const [qty, setQty] = useState<number | undefined>(1)
1508
+ * <NumberInput
1509
+ * label="Quantity"
1510
+ * value={qty ?? ''}
1511
+ * onChange={({ target }) => setQty(target.value)}
1512
+ * min={0} max={99}
1513
+ * />
1514
+ * ```
1515
+ *
1516
+ * @example Decimal step
1517
+ * ```tsx
1518
+ * <NumberInput label="Tonnage" step={0.25} precision={2} />
1519
+ * ```
1450
1520
  */
1451
- declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, }: NumberInputProps): react_jsx_runtime.JSX.Element;
1521
+ declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, precision, }: NumberInputProps): react_jsx_runtime.JSX.Element;
1452
1522
 
1453
1523
  interface PasswordProps {
1454
1524
  value?: string;
package/dist/index.d.ts CHANGED
@@ -587,45 +587,82 @@ declare function useNotification(): {
587
587
  danger: (props: Omit<NotificationPayload, "type">) => void;
588
588
  };
589
589
 
590
+ type LoadingSpinnerSize = 'sm' | 'md' | 'lg';
590
591
  interface LoadingSpinnerProps {
591
- /** Text revealed letter-by-letter beneath the spinner. */
592
- prompt: string;
592
+ /**
593
+ * Text revealed letter-by-letter beneath the spinner. Optional — pass
594
+ * `undefined` for a pure spinner with no caption (e.g. inline mode).
595
+ */
596
+ prompt?: string;
597
+ /** Spinner size variant. Defaults to `'md'`. */
598
+ size?: LoadingSpinnerSize;
599
+ /**
600
+ * When `true`, renders inline at the call site (no portal, no fullscreen
601
+ * overlay, no backdrop). Useful inside cards, table rows, drawers, or
602
+ * empty-state slots. Defaults to `false` (fullscreen overlay).
603
+ */
604
+ inline?: boolean;
593
605
  /**
594
606
  * Optional override for the spinner ring colour. Accepts any CSS colour.
595
- * Defaults to the accent token so it picks up theme overrides.
607
+ * Defaults to the `--color-accent` token so it picks up theme overrides.
596
608
  */
597
609
  spinnerColor?: string;
598
610
  /**
599
611
  * Optional override for the prompt text colour.
600
- * Defaults to the foreground token (light/dark aware).
612
+ * Defaults to the `--color-foreground` token (light/dark aware).
601
613
  */
602
614
  textColor?: string;
603
615
  /**
604
- * Backdrop opacity (0 – 1). Defaults to 0.92 close enough to opaque to
605
- * block UI underneath while still hinting at the previous state.
616
+ * Backdrop opacity (0 – 1) for the fullscreen overlay. Defaults to 0.8
617
+ * close enough to opaque to block UI underneath while still hinting at the
618
+ * previous state. Ignored when `inline` is true.
606
619
  */
607
620
  backdropOpacity?: number;
608
621
  }
609
622
  /**
610
- * Full-screen loading overlay with a spinning ring and a staggered text
611
- * reveal. Portaled into `document.body` so it always covers the actual
612
- * viewport regardless of where it's rendered in the React tree.
613
- *
614
- * Honours `prefers-reduced-motion`: the spinner still rotates (continuous
615
- * spin is informative, not decorative) but the letter stagger collapses to
616
- * an instant reveal.
617
- *
618
- * @example
623
+ * Enterprise-grade loading indicator.
624
+ *
625
+ * Two concentric arcs counter-rotating around a breathing centre dot, with
626
+ * an optional staggered letter reveal beneath. Portaled to `document.body`
627
+ * for the fullscreen overlay so it always covers the actual viewport
628
+ * regardless of where it sits in the React tree.
629
+ *
630
+ * **Two modes:**
631
+ * - **Fullscreen overlay** (default): semi-opaque backdrop with
632
+ * `backdrop-blur-sm` for a modern depth effect. Use during page-level
633
+ * loading, route transitions, or critical mutations.
634
+ * - **Inline** (`inline`): just the spinner + optional caption rendered at
635
+ * the call site. Use inside cards, table rows, drawers, empty states,
636
+ * or anywhere a smaller loading affordance is appropriate.
637
+ *
638
+ * **Accessibility**: `role="status"` + `aria-label`/`aria-live` make the
639
+ * indicator announce-able to screen readers. `prefers-reduced-motion`
640
+ * collapses the letter stagger to instant reveal — the spinner rings keep
641
+ * rotating since a continuous spinner is informative, not decorative.
642
+ *
643
+ * @example Fullscreen overlay (page load)
644
+ * ```tsx
619
645
  * {isLoading && <LoadingSpinner prompt="Loading vessels…" />}
646
+ * ```
620
647
  *
621
- * @example
648
+ * @example Inline (inside a card)
649
+ * ```tsx
650
+ * <Card>
651
+ * {data ? <Chart data={data} /> : <LoadingSpinner inline size="sm" />}
652
+ * </Card>
653
+ * ```
654
+ *
655
+ * @example Themed overlay
656
+ * ```tsx
622
657
  * <LoadingSpinner
623
658
  * prompt="Saving"
659
+ * size="lg"
624
660
  * spinnerColor="#10b981"
625
661
  * backdropOpacity={0.7}
626
662
  * />
663
+ * ```
627
664
  */
628
- declare function LoadingSpinner({ prompt, spinnerColor, textColor, backdropOpacity, }: LoadingSpinnerProps): react_jsx_runtime.JSX.Element;
665
+ declare function LoadingSpinner({ prompt, size, inline, spinnerColor, textColor, backdropOpacity, }: LoadingSpinnerProps): react_jsx_runtime.JSX.Element;
629
666
 
630
667
  interface FadingBaseProps {
631
668
  className?: string;
@@ -1420,11 +1457,14 @@ interface TextInputProps {
1420
1457
  declare function TextInput({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout, onBlur, errorMessage, labelColor, }: TextInputProps): react_jsx_runtime.JSX.Element;
1421
1458
 
1422
1459
  interface NumberInputProps {
1460
+ /** Step size for the up/down buttons and native arrow-key handling. Default `1`. */
1423
1461
  step?: number;
1424
- value?: any;
1462
+ /** Current value. `undefined` renders an empty field; a number renders that value. */
1463
+ value?: number | '';
1464
+ /** Fires with the next number. Empty input resolves to `undefined`. */
1425
1465
  onChange?: (e: {
1426
1466
  target: {
1427
- value: number;
1467
+ value: number | undefined;
1428
1468
  id?: string;
1429
1469
  name?: string;
1430
1470
  };
@@ -1433,8 +1473,8 @@ interface NumberInputProps {
1433
1473
  htmlFor?: string;
1434
1474
  name?: string;
1435
1475
  disabled?: boolean;
1436
- /** 'horizontal' | 'vertical' */
1437
- layout?: string;
1476
+ /** Label/input orientation. Defaults to `'horizontal'`. */
1477
+ layout?: 'horizontal' | 'vertical';
1438
1478
  errorMessage?: React$1.ReactNode;
1439
1479
  inputStyle?: React$1.CSSProperties;
1440
1480
  labelStyle?: React$1.CSSProperties;
@@ -1443,12 +1483,42 @@ interface NumberInputProps {
1443
1483
  min?: number;
1444
1484
  max?: number;
1445
1485
  readOnly?: boolean;
1446
- [key: string]: any;
1486
+ /** Optional precision for floating-point steps (number of decimal places to round to). */
1487
+ precision?: number;
1447
1488
  }
1448
1489
  /**
1449
- * Number input with increment / decrement controls.
1490
+ * Numeric input with keyboard-accessible increment / decrement buttons.
1491
+ *
1492
+ * **What's improved over the previous version**
1493
+ * - Step buttons are real `<button>` elements with `aria-label`, focus rings,
1494
+ * and proper keyboard activation (Enter / Space). The previous version used
1495
+ * `<span onClick>` which keyboard-only users could not reach.
1496
+ * - Floating-point drift on decimal steps (`0.1 + 0.2 = 0.30000000000000004`)
1497
+ * is rounded out via a `precision` prop or auto-inferred from the step.
1498
+ * - Empty input resolves to `undefined` instead of `NaN` — works with form
1499
+ * libraries (RHF, Formik) that treat empty as "no value".
1500
+ * - The decrement chevron actually points down (the previous SVG was the up
1501
+ * chevron rotated, with the up chevron itself wrongly using the same path).
1502
+ * - Width is a prop, not hardcoded `w-60`. Default is `w-full` so the input
1503
+ * flows with its parent.
1504
+ *
1505
+ * @example
1506
+ * ```tsx
1507
+ * const [qty, setQty] = useState<number | undefined>(1)
1508
+ * <NumberInput
1509
+ * label="Quantity"
1510
+ * value={qty ?? ''}
1511
+ * onChange={({ target }) => setQty(target.value)}
1512
+ * min={0} max={99}
1513
+ * />
1514
+ * ```
1515
+ *
1516
+ * @example Decimal step
1517
+ * ```tsx
1518
+ * <NumberInput label="Tonnage" step={0.25} precision={2} />
1519
+ * ```
1450
1520
  */
1451
- declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, }: NumberInputProps): react_jsx_runtime.JSX.Element;
1521
+ declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, precision, }: NumberInputProps): react_jsx_runtime.JSX.Element;
1452
1522
 
1453
1523
  interface PasswordProps {
1454
1524
  value?: string;