@mohasinac/react 1.0.0 → 1.1.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
@@ -1,4 +1,5 @@
1
- import { RefObject } from 'react';
1
+ import { RefObject, Dispatch, SetStateAction } from 'react';
2
+ import * as next_navigation from 'next/navigation';
2
3
 
3
4
  /**
4
5
  * useMediaQuery Hook
@@ -363,4 +364,271 @@ interface UseCameraReturn {
363
364
  */
364
365
  declare function useCamera(): UseCameraReturn;
365
366
 
366
- export { type CountdownRemaining, type GestureType, type KeyModifiers, type SwipeDirection, type UseCameraOptions, type UseCameraReturn, type UseClickOutsideOptions, type UseGestureOptions, type UseKeyPressOptions, type UsePullToRefreshOptions, type UsePullToRefreshReturn, type UseSwipeOptions, useBreakpoint, useCamera, useClickOutside, useCountdown, useGesture, useKeyPress, useLongPress, useMediaQuery, usePullToRefresh, useSwipe };
367
+ interface UseBulkSelectionOptions<T> {
368
+ /** The current page / list of items being displayed. */
369
+ items: T[];
370
+ /** Extract a stable unique key from each item (e.g. `item => item.id`). */
371
+ keyExtractor: (item: T) => string;
372
+ /**
373
+ * Maximum number of IDs that can be selected at once.
374
+ * @default 100
375
+ */
376
+ maxSelection?: number;
377
+ }
378
+ interface UseBulkSelectionReturn {
379
+ selectedIds: string[];
380
+ selectedCount: number;
381
+ /** Returns `true` if the given ID is currently selected. */
382
+ isSelected: (id: string) => boolean;
383
+ /** `true` when every item on the current page is selected. */
384
+ isAllSelected: boolean;
385
+ /**
386
+ * `true` when some — but not all — items are selected.
387
+ * Use to set the indeterminate state on the header checkbox.
388
+ */
389
+ isIndeterminate: boolean;
390
+ /** Toggle one item in or out of the selection. Respects `maxSelection`. */
391
+ toggle: (id: string) => void;
392
+ /**
393
+ * Select all items on the current page (up to `maxSelection`),
394
+ * or deselect all if every item is already selected.
395
+ */
396
+ toggleAll: () => void;
397
+ /** Deselect everything. Call this after a bulk action completes. */
398
+ clearSelection: () => void;
399
+ /**
400
+ * Direct state setter — pass straight to a table's `onSelectionChange`
401
+ * or use when you need to replace the entire selection programmatically.
402
+ */
403
+ setSelectedIds: Dispatch<SetStateAction<string[]>>;
404
+ }
405
+ declare function useBulkSelection<T>({ items, keyExtractor, maxSelection, }: UseBulkSelectionOptions<T>): UseBulkSelectionReturn;
406
+
407
+ interface UseUrlTableOptions {
408
+ /** Default param values used when a param is absent from the URL */
409
+ defaults?: Record<string, string>;
410
+ }
411
+ declare function useUrlTable(options?: UseUrlTableOptions): {
412
+ params: next_navigation.ReadonlyURLSearchParams;
413
+ get: (key: string) => string;
414
+ getNumber: (key: string, fallback?: number) => number;
415
+ set: (key: string, value: string) => void;
416
+ setMany: (updates: Record<string, string>) => void;
417
+ clear: (keys?: string[]) => void;
418
+ setPage: (page: number) => void;
419
+ setPageSize: (size: number) => void;
420
+ setSort: (sort: string) => void;
421
+ buildSieveParams: (sieveFilters: string) => string;
422
+ buildSearchParams: () => string;
423
+ };
424
+
425
+ type UrlTable$1 = ReturnType<typeof useUrlTable>;
426
+ interface UsePendingFiltersOptions {
427
+ /** The page's useUrlTable instance */
428
+ table: UrlTable$1;
429
+ /** URL param keys to manage (e.g. ['status', 'category', 'role']) */
430
+ keys: string[];
431
+ }
432
+ interface UsePendingFiltersReturn {
433
+ /** Current uncommitted selections per key (string[] per key) */
434
+ pending: Record<string, string[]>;
435
+ /** Values currently in the URL (committed) per key */
436
+ applied: Record<string, string[]>;
437
+ /** true when pending differs from applied */
438
+ isDirty: boolean;
439
+ /** Total number of selected values across all pending keys */
440
+ pendingCount: number;
441
+ /** Total number of selected values in the URL (for badge display) */
442
+ appliedCount: number;
443
+ /** Update one key in pending state (does NOT write to URL) */
444
+ set: (key: string, values: string[]) => void;
445
+ /** Write all pending keys to the URL (resets page to 1) */
446
+ apply: () => void;
447
+ /** Discard pending, revert to applied (URL) state */
448
+ reset: () => void;
449
+ /** Clear all keys in both pending state and the URL */
450
+ clear: () => void;
451
+ }
452
+ /**
453
+ * usePendingFilters
454
+ *
455
+ * Manages local (uncommitted) filter state for a FilterDrawer or any deferred
456
+ * filter panel. Values are only written to the URL when `apply()` is called.
457
+ *
458
+ * Initialised from the current URL params on mount, so opening a drawer
459
+ * pre-fills any already-applied filters.
460
+ *
461
+ * @example
462
+ * ```ts
463
+ * const table = useUrlTable({ defaults: { pageSize: '25' } });
464
+ * const filters = usePendingFilters({ table, keys: ['status', 'category'] });
465
+ *
466
+ * <FilterDrawer
467
+ * onApply={() => { filters.apply(); setDrawerOpen(false); }}
468
+ * onReset={() => filters.clear()}
469
+ * activeCount={filters.appliedCount}
470
+ * >
471
+ * <FilterFacetSection
472
+ * title="Status"
473
+ * options={STATUS_OPTIONS}
474
+ * selected={filters.pending['status'] ?? []}
475
+ * onChange={(v) => filters.set('status', v)}
476
+ * />
477
+ * </FilterDrawer>
478
+ * ```
479
+ */
480
+ declare function usePendingFilters({ table, keys, }: UsePendingFiltersOptions): UsePendingFiltersReturn;
481
+
482
+ type UrlTable = ReturnType<typeof useUrlTable>;
483
+ /**
484
+ * Minimal UrlTable-compatible interface consumed by *Filters components.
485
+ */
486
+ interface PendingTable {
487
+ get: (key: string) => string;
488
+ set: (key: string, value: string) => void;
489
+ setMany: (updates: Record<string, string>) => void;
490
+ }
491
+ interface UsePendingTableReturn {
492
+ /** Drop-in UrlTable replacement that reads/writes to pending state */
493
+ pendingTable: PendingTable;
494
+ /** Count of applied (URL) filter values — use for the filter badge */
495
+ filterActiveCount: number;
496
+ /** Commit all pending changes to the URL (resets page to 1) */
497
+ onFilterApply: () => void;
498
+ /** Clear all filter keys from pending state and URL */
499
+ onFilterClear: () => void;
500
+ }
501
+ /**
502
+ * usePendingTable
503
+ *
504
+ * A thin wrapper around `usePendingFilters` that exposes a `pendingTable`
505
+ * object matching the `UrlTable` interface (`get`, `set`, `setMany`).
506
+ *
507
+ * Drop this into any listing view to replace the per-filter `useState` +
508
+ * `handleFilterApply` + `handleFilterClear` boilerplate.
509
+ *
510
+ * @example
511
+ * ```tsx
512
+ * const { pendingTable, filterActiveCount, onFilterApply, onFilterClear } =
513
+ * usePendingTable(table, ['status', 'category', 'minPrice', 'maxPrice']);
514
+ *
515
+ * // Pass pendingTable directly to any *Filters component:
516
+ * filterContent={<ProductFilters table={pendingTable} showStatus />}
517
+ * filterActiveCount={filterActiveCount}
518
+ * onFilterApply={onFilterApply}
519
+ * onFilterClear={onFilterClear}
520
+ * ```
521
+ */
522
+ declare function usePendingTable(table: UrlTable, keys: string[]): UsePendingTableReturn;
523
+
524
+ declare const UNSAVED_CHANGES_EVENT = "unsaved-changes:confirm";
525
+ interface UseUnsavedChangesOptions {
526
+ /** Current form values to compare against initial */
527
+ formValues: Record<string, string>;
528
+ /** Initial form values snapshot (taken when data loads) */
529
+ initialValues: Record<string, string> | null;
530
+ /** Additional dirty flag from outside the form (e.g. pending avatar upload) */
531
+ extraDirty?: boolean;
532
+ /**
533
+ * Called when the user attempts to navigate away with unsaved changes.
534
+ * Must return a Promise that resolves to `true` (continue) or `false` (stay).
535
+ * Defaults to `window.confirm` with a generic message.
536
+ */
537
+ confirmFn?: () => Promise<boolean>;
538
+ /**
539
+ * Message shown in the browser-native `beforeunload` dialog.
540
+ * Most modern browsers ignore custom messages and show their own text.
541
+ */
542
+ beforeUnloadWarning?: string;
543
+ }
544
+ interface UseUnsavedChangesReturn {
545
+ /** Whether there are any unsaved changes (form or extra) */
546
+ isDirty: boolean;
547
+ /** Whether the form fields specifically have changed */
548
+ isFormDirty: boolean;
549
+ /** Call after a successful save to reset initial values to current */
550
+ markClean: () => void;
551
+ /** Prompt the user and return true if they confirmed leaving, false otherwise */
552
+ confirmLeave: () => Promise<boolean>;
553
+ }
554
+ /**
555
+ * useUnsavedChanges
556
+ *
557
+ * Tracks unsaved form changes and warns users before navigating away.
558
+ *
559
+ * - Compares `formValues` to `initialValues` to detect form dirtiness
560
+ * - Accepts an optional `extraDirty` flag (e.g. pending file upload)
561
+ * - Registers a `beforeunload` handler while dirty
562
+ * - Exposes `confirmLeave()` which calls the provided `confirmFn` (or falls back
563
+ * to `window.confirm`) to handle navigation guards
564
+ * - `markClean()` resets the snapshot after a successful save
565
+ */
566
+ declare function useUnsavedChanges({ formValues, initialValues, extraDirty, confirmFn, beforeUnloadWarning, }: UseUnsavedChangesOptions): UseUnsavedChangesReturn;
567
+
568
+ interface BulkActionItemFailure {
569
+ id: string;
570
+ reason: string;
571
+ }
572
+ interface BulkActionSummary {
573
+ total: number;
574
+ succeeded: number;
575
+ skipped: number;
576
+ failed: number;
577
+ }
578
+ interface BulkActionResult<TData = Record<string, unknown>> {
579
+ action: string;
580
+ summary: BulkActionSummary;
581
+ succeeded: string[];
582
+ skipped: string[];
583
+ failed: BulkActionItemFailure[];
584
+ data?: TData;
585
+ }
586
+ interface BulkActionPayload {
587
+ action: string;
588
+ ids: string[];
589
+ }
590
+ interface UseBulkActionOptions<TPayload extends BulkActionPayload, TData = Record<string, unknown>> {
591
+ /**
592
+ * Async function that POSTs to the bulk endpoint.
593
+ * Must return `Promise<BulkActionResult<TData>>`.
594
+ */
595
+ mutationFn: (payload: TPayload) => Promise<BulkActionResult<TData>>;
596
+ /**
597
+ * Called when `mutationFn` resolves (even for partial failures).
598
+ * Inspect `result.summary` to build feedback.
599
+ */
600
+ onSuccess?: (result: BulkActionResult<TData>, payload: TPayload) => void | Promise<void>;
601
+ /**
602
+ * Called when `mutationFn` rejects.
603
+ */
604
+ onError?: (error: Error, payload: TPayload) => void;
605
+ /**
606
+ * When `true`, `execute()` parks the payload in `pendingPayload` instead of
607
+ * running immediately. The caller renders a confirm modal wired to
608
+ * `confirmAndExecute` / `cancelConfirm`.
609
+ */
610
+ requiresConfirm?: boolean;
611
+ }
612
+ interface UseBulkActionReturn<TPayload extends BulkActionPayload, TData = Record<string, unknown>> {
613
+ execute: (payload: TPayload) => Promise<void>;
614
+ isLoading: boolean;
615
+ result: BulkActionResult<TData> | null;
616
+ error: Error | null;
617
+ reset: () => void;
618
+ pendingPayload: TPayload | null;
619
+ confirmAndExecute: () => Promise<void>;
620
+ cancelConfirm: () => void;
621
+ }
622
+ /**
623
+ * useBulkAction
624
+ *
625
+ * Generic mutation hook for any `/bulk` endpoint.
626
+ * Handles loading state, partial-success result tracking, and an optional
627
+ * confirmation flow for destructive operations.
628
+ *
629
+ * Pair with `useBulkSelection` and a `BulkActionBar` component for the
630
+ * full admin list pattern.
631
+ */
632
+ declare function useBulkAction<TPayload extends BulkActionPayload = BulkActionPayload, TData = Record<string, unknown>>(options: UseBulkActionOptions<TPayload, TData>): UseBulkActionReturn<TPayload, TData>;
633
+
634
+ export { type BulkActionItemFailure, type BulkActionPayload, type BulkActionResult, type BulkActionSummary, type CountdownRemaining, type GestureType, type KeyModifiers, type PendingTable, type SwipeDirection, UNSAVED_CHANGES_EVENT, type UseBulkActionOptions, type UseBulkActionReturn, type UseBulkSelectionOptions, type UseBulkSelectionReturn, type UseCameraOptions, type UseCameraReturn, type UseClickOutsideOptions, type UseGestureOptions, type UseKeyPressOptions, type UsePendingFiltersOptions, type UsePendingFiltersReturn, type UsePendingTableReturn, type UsePullToRefreshOptions, type UsePullToRefreshReturn, type UseSwipeOptions, type UseUnsavedChangesOptions, type UseUnsavedChangesReturn, type UseUrlTableOptions, useBreakpoint, useBulkAction, useBulkSelection, useCamera, useClickOutside, useCountdown, useGesture, useKeyPress, useLongPress, useMediaQuery, usePendingFilters, usePendingTable, usePullToRefresh, useSwipe, useUnsavedChanges, useUrlTable };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { RefObject } from 'react';
1
+ import { RefObject, Dispatch, SetStateAction } from 'react';
2
+ import * as next_navigation from 'next/navigation';
2
3
 
3
4
  /**
4
5
  * useMediaQuery Hook
@@ -363,4 +364,271 @@ interface UseCameraReturn {
363
364
  */
364
365
  declare function useCamera(): UseCameraReturn;
365
366
 
366
- export { type CountdownRemaining, type GestureType, type KeyModifiers, type SwipeDirection, type UseCameraOptions, type UseCameraReturn, type UseClickOutsideOptions, type UseGestureOptions, type UseKeyPressOptions, type UsePullToRefreshOptions, type UsePullToRefreshReturn, type UseSwipeOptions, useBreakpoint, useCamera, useClickOutside, useCountdown, useGesture, useKeyPress, useLongPress, useMediaQuery, usePullToRefresh, useSwipe };
367
+ interface UseBulkSelectionOptions<T> {
368
+ /** The current page / list of items being displayed. */
369
+ items: T[];
370
+ /** Extract a stable unique key from each item (e.g. `item => item.id`). */
371
+ keyExtractor: (item: T) => string;
372
+ /**
373
+ * Maximum number of IDs that can be selected at once.
374
+ * @default 100
375
+ */
376
+ maxSelection?: number;
377
+ }
378
+ interface UseBulkSelectionReturn {
379
+ selectedIds: string[];
380
+ selectedCount: number;
381
+ /** Returns `true` if the given ID is currently selected. */
382
+ isSelected: (id: string) => boolean;
383
+ /** `true` when every item on the current page is selected. */
384
+ isAllSelected: boolean;
385
+ /**
386
+ * `true` when some — but not all — items are selected.
387
+ * Use to set the indeterminate state on the header checkbox.
388
+ */
389
+ isIndeterminate: boolean;
390
+ /** Toggle one item in or out of the selection. Respects `maxSelection`. */
391
+ toggle: (id: string) => void;
392
+ /**
393
+ * Select all items on the current page (up to `maxSelection`),
394
+ * or deselect all if every item is already selected.
395
+ */
396
+ toggleAll: () => void;
397
+ /** Deselect everything. Call this after a bulk action completes. */
398
+ clearSelection: () => void;
399
+ /**
400
+ * Direct state setter — pass straight to a table's `onSelectionChange`
401
+ * or use when you need to replace the entire selection programmatically.
402
+ */
403
+ setSelectedIds: Dispatch<SetStateAction<string[]>>;
404
+ }
405
+ declare function useBulkSelection<T>({ items, keyExtractor, maxSelection, }: UseBulkSelectionOptions<T>): UseBulkSelectionReturn;
406
+
407
+ interface UseUrlTableOptions {
408
+ /** Default param values used when a param is absent from the URL */
409
+ defaults?: Record<string, string>;
410
+ }
411
+ declare function useUrlTable(options?: UseUrlTableOptions): {
412
+ params: next_navigation.ReadonlyURLSearchParams;
413
+ get: (key: string) => string;
414
+ getNumber: (key: string, fallback?: number) => number;
415
+ set: (key: string, value: string) => void;
416
+ setMany: (updates: Record<string, string>) => void;
417
+ clear: (keys?: string[]) => void;
418
+ setPage: (page: number) => void;
419
+ setPageSize: (size: number) => void;
420
+ setSort: (sort: string) => void;
421
+ buildSieveParams: (sieveFilters: string) => string;
422
+ buildSearchParams: () => string;
423
+ };
424
+
425
+ type UrlTable$1 = ReturnType<typeof useUrlTable>;
426
+ interface UsePendingFiltersOptions {
427
+ /** The page's useUrlTable instance */
428
+ table: UrlTable$1;
429
+ /** URL param keys to manage (e.g. ['status', 'category', 'role']) */
430
+ keys: string[];
431
+ }
432
+ interface UsePendingFiltersReturn {
433
+ /** Current uncommitted selections per key (string[] per key) */
434
+ pending: Record<string, string[]>;
435
+ /** Values currently in the URL (committed) per key */
436
+ applied: Record<string, string[]>;
437
+ /** true when pending differs from applied */
438
+ isDirty: boolean;
439
+ /** Total number of selected values across all pending keys */
440
+ pendingCount: number;
441
+ /** Total number of selected values in the URL (for badge display) */
442
+ appliedCount: number;
443
+ /** Update one key in pending state (does NOT write to URL) */
444
+ set: (key: string, values: string[]) => void;
445
+ /** Write all pending keys to the URL (resets page to 1) */
446
+ apply: () => void;
447
+ /** Discard pending, revert to applied (URL) state */
448
+ reset: () => void;
449
+ /** Clear all keys in both pending state and the URL */
450
+ clear: () => void;
451
+ }
452
+ /**
453
+ * usePendingFilters
454
+ *
455
+ * Manages local (uncommitted) filter state for a FilterDrawer or any deferred
456
+ * filter panel. Values are only written to the URL when `apply()` is called.
457
+ *
458
+ * Initialised from the current URL params on mount, so opening a drawer
459
+ * pre-fills any already-applied filters.
460
+ *
461
+ * @example
462
+ * ```ts
463
+ * const table = useUrlTable({ defaults: { pageSize: '25' } });
464
+ * const filters = usePendingFilters({ table, keys: ['status', 'category'] });
465
+ *
466
+ * <FilterDrawer
467
+ * onApply={() => { filters.apply(); setDrawerOpen(false); }}
468
+ * onReset={() => filters.clear()}
469
+ * activeCount={filters.appliedCount}
470
+ * >
471
+ * <FilterFacetSection
472
+ * title="Status"
473
+ * options={STATUS_OPTIONS}
474
+ * selected={filters.pending['status'] ?? []}
475
+ * onChange={(v) => filters.set('status', v)}
476
+ * />
477
+ * </FilterDrawer>
478
+ * ```
479
+ */
480
+ declare function usePendingFilters({ table, keys, }: UsePendingFiltersOptions): UsePendingFiltersReturn;
481
+
482
+ type UrlTable = ReturnType<typeof useUrlTable>;
483
+ /**
484
+ * Minimal UrlTable-compatible interface consumed by *Filters components.
485
+ */
486
+ interface PendingTable {
487
+ get: (key: string) => string;
488
+ set: (key: string, value: string) => void;
489
+ setMany: (updates: Record<string, string>) => void;
490
+ }
491
+ interface UsePendingTableReturn {
492
+ /** Drop-in UrlTable replacement that reads/writes to pending state */
493
+ pendingTable: PendingTable;
494
+ /** Count of applied (URL) filter values — use for the filter badge */
495
+ filterActiveCount: number;
496
+ /** Commit all pending changes to the URL (resets page to 1) */
497
+ onFilterApply: () => void;
498
+ /** Clear all filter keys from pending state and URL */
499
+ onFilterClear: () => void;
500
+ }
501
+ /**
502
+ * usePendingTable
503
+ *
504
+ * A thin wrapper around `usePendingFilters` that exposes a `pendingTable`
505
+ * object matching the `UrlTable` interface (`get`, `set`, `setMany`).
506
+ *
507
+ * Drop this into any listing view to replace the per-filter `useState` +
508
+ * `handleFilterApply` + `handleFilterClear` boilerplate.
509
+ *
510
+ * @example
511
+ * ```tsx
512
+ * const { pendingTable, filterActiveCount, onFilterApply, onFilterClear } =
513
+ * usePendingTable(table, ['status', 'category', 'minPrice', 'maxPrice']);
514
+ *
515
+ * // Pass pendingTable directly to any *Filters component:
516
+ * filterContent={<ProductFilters table={pendingTable} showStatus />}
517
+ * filterActiveCount={filterActiveCount}
518
+ * onFilterApply={onFilterApply}
519
+ * onFilterClear={onFilterClear}
520
+ * ```
521
+ */
522
+ declare function usePendingTable(table: UrlTable, keys: string[]): UsePendingTableReturn;
523
+
524
+ declare const UNSAVED_CHANGES_EVENT = "unsaved-changes:confirm";
525
+ interface UseUnsavedChangesOptions {
526
+ /** Current form values to compare against initial */
527
+ formValues: Record<string, string>;
528
+ /** Initial form values snapshot (taken when data loads) */
529
+ initialValues: Record<string, string> | null;
530
+ /** Additional dirty flag from outside the form (e.g. pending avatar upload) */
531
+ extraDirty?: boolean;
532
+ /**
533
+ * Called when the user attempts to navigate away with unsaved changes.
534
+ * Must return a Promise that resolves to `true` (continue) or `false` (stay).
535
+ * Defaults to `window.confirm` with a generic message.
536
+ */
537
+ confirmFn?: () => Promise<boolean>;
538
+ /**
539
+ * Message shown in the browser-native `beforeunload` dialog.
540
+ * Most modern browsers ignore custom messages and show their own text.
541
+ */
542
+ beforeUnloadWarning?: string;
543
+ }
544
+ interface UseUnsavedChangesReturn {
545
+ /** Whether there are any unsaved changes (form or extra) */
546
+ isDirty: boolean;
547
+ /** Whether the form fields specifically have changed */
548
+ isFormDirty: boolean;
549
+ /** Call after a successful save to reset initial values to current */
550
+ markClean: () => void;
551
+ /** Prompt the user and return true if they confirmed leaving, false otherwise */
552
+ confirmLeave: () => Promise<boolean>;
553
+ }
554
+ /**
555
+ * useUnsavedChanges
556
+ *
557
+ * Tracks unsaved form changes and warns users before navigating away.
558
+ *
559
+ * - Compares `formValues` to `initialValues` to detect form dirtiness
560
+ * - Accepts an optional `extraDirty` flag (e.g. pending file upload)
561
+ * - Registers a `beforeunload` handler while dirty
562
+ * - Exposes `confirmLeave()` which calls the provided `confirmFn` (or falls back
563
+ * to `window.confirm`) to handle navigation guards
564
+ * - `markClean()` resets the snapshot after a successful save
565
+ */
566
+ declare function useUnsavedChanges({ formValues, initialValues, extraDirty, confirmFn, beforeUnloadWarning, }: UseUnsavedChangesOptions): UseUnsavedChangesReturn;
567
+
568
+ interface BulkActionItemFailure {
569
+ id: string;
570
+ reason: string;
571
+ }
572
+ interface BulkActionSummary {
573
+ total: number;
574
+ succeeded: number;
575
+ skipped: number;
576
+ failed: number;
577
+ }
578
+ interface BulkActionResult<TData = Record<string, unknown>> {
579
+ action: string;
580
+ summary: BulkActionSummary;
581
+ succeeded: string[];
582
+ skipped: string[];
583
+ failed: BulkActionItemFailure[];
584
+ data?: TData;
585
+ }
586
+ interface BulkActionPayload {
587
+ action: string;
588
+ ids: string[];
589
+ }
590
+ interface UseBulkActionOptions<TPayload extends BulkActionPayload, TData = Record<string, unknown>> {
591
+ /**
592
+ * Async function that POSTs to the bulk endpoint.
593
+ * Must return `Promise<BulkActionResult<TData>>`.
594
+ */
595
+ mutationFn: (payload: TPayload) => Promise<BulkActionResult<TData>>;
596
+ /**
597
+ * Called when `mutationFn` resolves (even for partial failures).
598
+ * Inspect `result.summary` to build feedback.
599
+ */
600
+ onSuccess?: (result: BulkActionResult<TData>, payload: TPayload) => void | Promise<void>;
601
+ /**
602
+ * Called when `mutationFn` rejects.
603
+ */
604
+ onError?: (error: Error, payload: TPayload) => void;
605
+ /**
606
+ * When `true`, `execute()` parks the payload in `pendingPayload` instead of
607
+ * running immediately. The caller renders a confirm modal wired to
608
+ * `confirmAndExecute` / `cancelConfirm`.
609
+ */
610
+ requiresConfirm?: boolean;
611
+ }
612
+ interface UseBulkActionReturn<TPayload extends BulkActionPayload, TData = Record<string, unknown>> {
613
+ execute: (payload: TPayload) => Promise<void>;
614
+ isLoading: boolean;
615
+ result: BulkActionResult<TData> | null;
616
+ error: Error | null;
617
+ reset: () => void;
618
+ pendingPayload: TPayload | null;
619
+ confirmAndExecute: () => Promise<void>;
620
+ cancelConfirm: () => void;
621
+ }
622
+ /**
623
+ * useBulkAction
624
+ *
625
+ * Generic mutation hook for any `/bulk` endpoint.
626
+ * Handles loading state, partial-success result tracking, and an optional
627
+ * confirmation flow for destructive operations.
628
+ *
629
+ * Pair with `useBulkSelection` and a `BulkActionBar` component for the
630
+ * full admin list pattern.
631
+ */
632
+ declare function useBulkAction<TPayload extends BulkActionPayload = BulkActionPayload, TData = Record<string, unknown>>(options: UseBulkActionOptions<TPayload, TData>): UseBulkActionReturn<TPayload, TData>;
633
+
634
+ export { type BulkActionItemFailure, type BulkActionPayload, type BulkActionResult, type BulkActionSummary, type CountdownRemaining, type GestureType, type KeyModifiers, type PendingTable, type SwipeDirection, UNSAVED_CHANGES_EVENT, type UseBulkActionOptions, type UseBulkActionReturn, type UseBulkSelectionOptions, type UseBulkSelectionReturn, type UseCameraOptions, type UseCameraReturn, type UseClickOutsideOptions, type UseGestureOptions, type UseKeyPressOptions, type UsePendingFiltersOptions, type UsePendingFiltersReturn, type UsePendingTableReturn, type UsePullToRefreshOptions, type UsePullToRefreshReturn, type UseSwipeOptions, type UseUnsavedChangesOptions, type UseUnsavedChangesReturn, type UseUrlTableOptions, useBreakpoint, useBulkAction, useBulkSelection, useCamera, useClickOutside, useCountdown, useGesture, useKeyPress, useLongPress, useMediaQuery, usePendingFilters, usePendingTable, usePullToRefresh, useSwipe, useUnsavedChanges, useUrlTable };