baaz-custom-components 5.1.2 → 5.2.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/dist/index.mjs CHANGED
@@ -1909,12 +1909,14 @@ var CustomBreadcrumb = ({
1909
1909
  var breadcrumb_default = CustomBreadcrumb;
1910
1910
 
1911
1911
  // src/components/custom/grid/index.tsx
1912
- import {
1912
+ import React6, {
1913
1913
  useCallback as useCallback2,
1914
- useRef as useRef3,
1914
+ useRef as useRef4,
1915
1915
  useImperativeHandle,
1916
1916
  forwardRef,
1917
- useEffect as useEffect7
1917
+ useEffect as useEffect9,
1918
+ useMemo as useMemo4,
1919
+ useState as useState9
1918
1920
  } from "react";
1919
1921
  import { Grid as SvarGrid, WillowDark } from "@svar-ui/react-grid";
1920
1922
 
@@ -1949,14 +1951,8 @@ function exportExcel(rows, name) {
1949
1951
  }
1950
1952
 
1951
1953
  // src/components/custom/grid/gridHeader/index.tsx
1952
- import { useEffect as useEffect6, useRef as useRef2, useState as useState7 } from "react";
1953
- import {
1954
- FileText,
1955
- FileSpreadsheet,
1956
- Search,
1957
- Ellipsis,
1958
- X as X3
1959
- } from "lucide-react";
1954
+ import React3, { useEffect as useEffect6, useRef as useRef2, useState as useState7 } from "react";
1955
+ import { FileText, FileSpreadsheet, Search, Ellipsis, X as X4 } from "lucide-react";
1960
1956
 
1961
1957
  // src/components/custom/grid/gridHeader/components/filters.tsx
1962
1958
  import { useEffect as useEffect5, useMemo as useMemo3, useRef, useState as useState6 } from "react";
@@ -2322,9 +2318,52 @@ var Filters = ({
2322
2318
  };
2323
2319
  var filters_default = Filters;
2324
2320
 
2325
- // src/components/custom/grid/gridHeader/index.tsx
2321
+ // src/components/custom/grid/gridHeader/components/selectionBar.tsx
2322
+ import { X as X3 } from "lucide-react";
2326
2323
  import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2327
- var GridHeader = ({
2324
+ function SelectionBar({
2325
+ selectedCount,
2326
+ selectedData,
2327
+ actions,
2328
+ onAction,
2329
+ onClear
2330
+ }) {
2331
+ return /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-150", children: [
2332
+ /* @__PURE__ */ jsx24(
2333
+ "button",
2334
+ {
2335
+ onClick: onClear,
2336
+ title: "Clear selection",
2337
+ className: "p-1.5 rounded-md hover:bg-input text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
2338
+ children: /* @__PURE__ */ jsx24(X3, { size: 15 })
2339
+ }
2340
+ ),
2341
+ /* @__PURE__ */ jsxs18("span", { className: "text-xs font-medium text-foreground whitespace-nowrap", children: [
2342
+ selectedCount,
2343
+ " ",
2344
+ selectedCount === 1 ? "row" : "rows",
2345
+ " selected"
2346
+ ] }),
2347
+ /* @__PURE__ */ jsx24("div", { className: "h-4 w-px bg-border mx-1" }),
2348
+ actions.map((action) => /* @__PURE__ */ jsxs18(
2349
+ "button",
2350
+ {
2351
+ onClick: () => onAction(action.id, selectedData),
2352
+ title: action.label,
2353
+ className: "flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium border border-border bg-primary/90 hover:bg-primary ease-in-out transition-colors cursor-pointer whitespace-nowrap",
2354
+ children: [
2355
+ action.icon && /* @__PURE__ */ jsx24("span", { className: "shrink-0 text-muted-foreground", children: action.icon }),
2356
+ action.label
2357
+ ]
2358
+ },
2359
+ action.id
2360
+ ))
2361
+ ] });
2362
+ }
2363
+
2364
+ // src/components/custom/grid/gridHeader/index.tsx
2365
+ import { Fragment as Fragment3, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
2366
+ function GridHeader({
2328
2367
  onPdf,
2329
2368
  onExcel,
2330
2369
  onSearch = () => {
@@ -2336,11 +2375,18 @@ var GridHeader = ({
2336
2375
  filterList,
2337
2376
  onFilterChange,
2338
2377
  initialFilters = [],
2339
- children
2340
- }) => {
2378
+ children,
2379
+ selectedCount = 0,
2380
+ selectedData = [],
2381
+ selectionActions = [],
2382
+ onSelectionAction,
2383
+ onClearSelection
2384
+ }) {
2385
+ console.log("GridHeader rendered");
2341
2386
  const [downloadMenu, setDownloadMenu] = useState7(false);
2342
2387
  const [searchOpen, setSearchOpen] = useState7(false);
2343
2388
  const downloadRef = useRef2(null);
2389
+ const isSelecting = selectedCount > 0;
2344
2390
  useEffect6(() => {
2345
2391
  const handleClickOutside = (e) => {
2346
2392
  if (downloadRef.current && !downloadRef.current.contains(e.target)) {
@@ -2350,9 +2396,18 @@ var GridHeader = ({
2350
2396
  document.addEventListener("mousedown", handleClickOutside);
2351
2397
  return () => document.removeEventListener("mousedown", handleClickOutside);
2352
2398
  }, []);
2353
- return /* @__PURE__ */ jsxs18("div", { className: "flex flex-col md:flex-row md:items-center justify-between bg-card py-2 px-2 md:px-4 rounded-tl-md rounded-tr-md border-l border-r border-t", children: [
2354
- /* @__PURE__ */ jsxs18("div", { className: "flex justify-between items-center gap-3", children: [
2355
- /* @__PURE__ */ jsx24(
2399
+ return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col md:flex-row md:items-center justify-between bg-card py-2 px-2 md:px-4 rounded-tl-md rounded-tr-md border-l border-r border-t min-h-[48px]", children: [
2400
+ /* @__PURE__ */ jsx25("div", { className: "flex items-center gap-3", children: isSelecting ? /* @__PURE__ */ jsx25(
2401
+ SelectionBar,
2402
+ {
2403
+ selectedCount,
2404
+ selectedData,
2405
+ actions: selectionActions,
2406
+ onAction: (actionId, data) => onSelectionAction == null ? void 0 : onSelectionAction(actionId, data),
2407
+ onClear: () => onClearSelection == null ? void 0 : onClearSelection()
2408
+ }
2409
+ ) : /* @__PURE__ */ jsxs19(Fragment3, { children: [
2410
+ /* @__PURE__ */ jsx25(
2356
2411
  filters_default,
2357
2412
  {
2358
2413
  onFilterChange,
@@ -2360,17 +2415,17 @@ var GridHeader = ({
2360
2415
  filterList
2361
2416
  }
2362
2417
  ),
2363
- download && /* @__PURE__ */ jsxs18("div", { className: "relative", ref: downloadRef, children: [
2364
- /* @__PURE__ */ jsx24(
2418
+ download && /* @__PURE__ */ jsxs19("div", { className: "relative", ref: downloadRef, children: [
2419
+ /* @__PURE__ */ jsx25(
2365
2420
  "button",
2366
2421
  {
2367
2422
  onClick: () => setDownloadMenu((p) => !p),
2368
2423
  className: "p-2 rounded-md hover:bg-input cursor-pointer",
2369
- children: /* @__PURE__ */ jsx24(Ellipsis, { size: 16 })
2424
+ children: /* @__PURE__ */ jsx25(Ellipsis, { size: 16 })
2370
2425
  }
2371
2426
  ),
2372
- downloadMenu && /* @__PURE__ */ jsxs18("div", { className: "absolute top-full right-0 mt-2 w-32 rounded-md border !bg-background shadow-lg z-50 overflow-hidden", children: [
2373
- /* @__PURE__ */ jsxs18(
2427
+ downloadMenu && /* @__PURE__ */ jsxs19("div", { className: "absolute top-full right-0 mt-2 w-32 rounded-md border !bg-background shadow-lg z-50 overflow-hidden", children: [
2428
+ /* @__PURE__ */ jsxs19(
2374
2429
  "button",
2375
2430
  {
2376
2431
  onClick: () => {
@@ -2379,12 +2434,12 @@ var GridHeader = ({
2379
2434
  },
2380
2435
  className: "flex items-center gap-2 w-full px-3 py-2 text-sm hover:bg-muted cursor-pointer",
2381
2436
  children: [
2382
- /* @__PURE__ */ jsx24(FileText, { size: 14 }),
2383
- "PDF"
2437
+ /* @__PURE__ */ jsx25(FileText, { size: 14 }),
2438
+ " PDF"
2384
2439
  ]
2385
2440
  }
2386
2441
  ),
2387
- /* @__PURE__ */ jsxs18(
2442
+ /* @__PURE__ */ jsxs19(
2388
2443
  "button",
2389
2444
  {
2390
2445
  onClick: () => {
@@ -2393,29 +2448,29 @@ var GridHeader = ({
2393
2448
  },
2394
2449
  className: "flex items-center gap-2 w-full px-3 py-2 text-sm hover:bg-muted cursor-pointer",
2395
2450
  children: [
2396
- /* @__PURE__ */ jsx24(FileSpreadsheet, { size: 14 }),
2397
- "Excel"
2451
+ /* @__PURE__ */ jsx25(FileSpreadsheet, { size: 14 }),
2452
+ " Excel"
2398
2453
  ]
2399
2454
  }
2400
2455
  )
2401
2456
  ] })
2402
2457
  ] })
2403
- ] }),
2458
+ ] }) }),
2404
2459
  children,
2405
- search && /* @__PURE__ */ jsx24("div", { className: "relative h-[40px] flex items-center", children: /* @__PURE__ */ jsx24(
2460
+ search && /* @__PURE__ */ jsx25("div", { className: "relative h-[40px] flex items-center", children: /* @__PURE__ */ jsx25(
2406
2461
  "div",
2407
2462
  {
2408
2463
  className: `absolute md:right-0 flex items-center transition-all duration-200 ${searchOpen ? "w-[250px]" : "w-[40px]"} h-[40px]`,
2409
- children: !searchOpen ? /* @__PURE__ */ jsx24(
2464
+ children: !searchOpen ? /* @__PURE__ */ jsx25(
2410
2465
  "button",
2411
2466
  {
2412
2467
  onClick: () => setSearchOpen(true),
2413
2468
  className: "w-[40px] h-[40px] flex items-center justify-center rounded-md hover:bg-input cursor-pointer",
2414
- children: /* @__PURE__ */ jsx24(Search, { size: 18 })
2469
+ children: /* @__PURE__ */ jsx25(Search, { size: 18 })
2415
2470
  }
2416
- ) : /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2 w-full h-full px-3 rounded-md border border-input bg-background", children: [
2417
- /* @__PURE__ */ jsx24(Search, { size: 16 }),
2418
- /* @__PURE__ */ jsx24(
2471
+ ) : /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 w-full h-full px-3 rounded-md border border-input bg-background", children: [
2472
+ /* @__PURE__ */ jsx25(Search, { size: 16 }),
2473
+ /* @__PURE__ */ jsx25(
2419
2474
  "input",
2420
2475
  {
2421
2476
  autoFocus: true,
@@ -2425,23 +2480,140 @@ var GridHeader = ({
2425
2480
  value: searchValue
2426
2481
  }
2427
2482
  ),
2428
- /* @__PURE__ */ jsx24(
2483
+ /* @__PURE__ */ jsx25(
2429
2484
  "button",
2430
2485
  {
2431
2486
  onClick: () => setSearchOpen(false),
2432
2487
  className: "cursor-pointer",
2433
- children: /* @__PURE__ */ jsx24(X3, { size: 16 })
2488
+ children: /* @__PURE__ */ jsx25(X4, { size: 16 })
2434
2489
  }
2435
2490
  )
2436
2491
  ] })
2437
2492
  }
2438
2493
  ) })
2439
2494
  ] });
2495
+ }
2496
+ var gridHeader_default = React3.memo(GridHeader);
2497
+
2498
+ // src/components/custom/grid/checkboxCell/checkBoxCell.tsx
2499
+ import React4, { useState as useState8, useEffect as useEffect7 } from "react";
2500
+ import { jsx as jsx26 } from "react/jsx-runtime";
2501
+ var CheckboxCell = ({ row, api, column }) => {
2502
+ const [isSelected, setIsSelected] = useState8(
2503
+ () => {
2504
+ var _a;
2505
+ return ((_a = api.getState().selectedRows) != null ? _a : []).includes(row.id);
2506
+ }
2507
+ );
2508
+ useEffect7(() => {
2509
+ const unsub = api.on("select-row", (ev) => {
2510
+ var _a;
2511
+ if ((ev == null ? void 0 : ev.id) === row.id) {
2512
+ setIsSelected(
2513
+ ((_a = api.getState().selectedRows) != null ? _a : []).includes(row.id)
2514
+ );
2515
+ }
2516
+ });
2517
+ return () => typeof unsub === "function" && unsub();
2518
+ }, [api, row.id]);
2519
+ const handleChange = (e) => {
2520
+ var _a, _b;
2521
+ e.stopPropagation();
2522
+ const checked = e.target.checked;
2523
+ api.exec("select-row", { id: row.id, mode: checked, toggle: true });
2524
+ (_b = (_a = column == null ? void 0 : column.config) == null ? void 0 : _a.onSelectionChange) == null ? void 0 : _b.call(_a, row.id, row, checked);
2525
+ };
2526
+ return /* @__PURE__ */ jsx26(
2527
+ "div",
2528
+ {
2529
+ className: "flex items-center justify-center w-full h-full",
2530
+ "data-action": "ignore-click",
2531
+ onClick: (e) => e.stopPropagation(),
2532
+ children: /* @__PURE__ */ jsx26(
2533
+ "input",
2534
+ {
2535
+ type: "checkbox",
2536
+ checked: isSelected,
2537
+ onChange: handleChange,
2538
+ className: "w-4 h-4 rounded border border-border bg-background accent-primary cursor-pointer focus:outline-none focus:ring-1 focus:ring-primary"
2539
+ }
2540
+ )
2541
+ }
2542
+ );
2440
2543
  };
2441
- var gridHeader_default = GridHeader;
2544
+ var checkBoxCell_default = React4.memo(CheckboxCell);
2545
+
2546
+ // src/components/custom/grid/checkboxCell/headerCheckBox.tsx
2547
+ import React5, { useEffect as useEffect8, useRef as useRef3, useReducer } from "react";
2548
+ import { jsx as jsx27 } from "react/jsx-runtime";
2549
+ var HeaderCheckbox = ({ api, column }) => {
2550
+ var _a, _b, _c;
2551
+ const checkboxRef = useRef3(null);
2552
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2553
+ useEffect8(() => {
2554
+ const unsub = api.on("select-row", () => forceUpdate());
2555
+ return () => typeof unsub === "function" && unsub();
2556
+ }, [api]);
2557
+ const pageData = (_b = (_a = column == null ? void 0 : column.config) == null ? void 0 : _a.getPageData()) != null ? _b : [];
2558
+ const selectedRows = (_c = api.getState().selectedRows) != null ? _c : [];
2559
+ const selectedOnPage = pageData.filter(
2560
+ (r) => selectedRows.includes(r.id)
2561
+ ).length;
2562
+ const allSelected = pageData.length > 0 && selectedOnPage === pageData.length;
2563
+ const someSelected = selectedOnPage > 0 && selectedOnPage < pageData.length;
2564
+ useEffect8(() => {
2565
+ if (checkboxRef.current) checkboxRef.current.indeterminate = someSelected;
2566
+ }, [someSelected]);
2567
+ const handleChange = (e) => {
2568
+ var _a2, _b2;
2569
+ e.stopPropagation();
2570
+ const checked = e.target.checked;
2571
+ pageData.forEach(
2572
+ (row) => api.exec("select-row", { id: row.id, mode: checked, toggle: true })
2573
+ );
2574
+ (_b2 = (_a2 = column == null ? void 0 : column.config) == null ? void 0 : _a2.onSelectAllPage) == null ? void 0 : _b2.call(_a2, pageData, checked);
2575
+ };
2576
+ return /* @__PURE__ */ jsx27(
2577
+ "div",
2578
+ {
2579
+ className: "flex items-center justify-center w-full h-full",
2580
+ "data-action": "ignore-click",
2581
+ onClick: (e) => e.stopPropagation(),
2582
+ children: /* @__PURE__ */ jsx27(
2583
+ "input",
2584
+ {
2585
+ ref: checkboxRef,
2586
+ type: "checkbox",
2587
+ checked: allSelected,
2588
+ onChange: handleChange,
2589
+ className: "w-4 h-4 rounded border border-border bg-background accent-primary cursor-pointer focus:outline-none focus:ring-1 focus:ring-primary"
2590
+ }
2591
+ )
2592
+ }
2593
+ );
2594
+ };
2595
+ var headerCheckBox_default = React5.memo(HeaderCheckbox);
2596
+
2597
+ // src/components/custom/grid/utils/buildCheckboxColumn.tsx
2598
+ function buildCheckboxColumn({
2599
+ getPageData,
2600
+ onSelectionChange,
2601
+ onSelectAllPage
2602
+ }) {
2603
+ const config = { getPageData, onSelectionChange, onSelectAllPage };
2604
+ return {
2605
+ id: "__selection__",
2606
+ width: 58,
2607
+ sortable: false,
2608
+ resize: false,
2609
+ config,
2610
+ header: { cell: headerCheckBox_default },
2611
+ cell: checkBoxCell_default
2612
+ };
2613
+ }
2442
2614
 
2443
2615
  // src/components/custom/grid/index.tsx
2444
- import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
2616
+ import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
2445
2617
  var Grid = forwardRef(
2446
2618
  (_a, ref) => {
2447
2619
  var _b = _a, {
@@ -2457,7 +2629,15 @@ var Grid = forwardRef(
2457
2629
  fonts,
2458
2630
  onFilterChange,
2459
2631
  initialFilters,
2460
- children
2632
+ children,
2633
+ onFocusCell = () => false,
2634
+ selectionEnabled = false,
2635
+ getSelectedIds,
2636
+ onRowSelectionChange,
2637
+ onSelectAllPage,
2638
+ selectionActions,
2639
+ onSelectionAction,
2640
+ onClearSelection
2461
2641
  } = _b, rest = __objRest(_b, [
2462
2642
  "data",
2463
2643
  "columns",
@@ -2471,95 +2651,144 @@ var Grid = forwardRef(
2471
2651
  "fonts",
2472
2652
  "onFilterChange",
2473
2653
  "initialFilters",
2474
- "children"
2654
+ "children",
2655
+ "onFocusCell",
2656
+ "selectionEnabled",
2657
+ "getSelectedIds",
2658
+ "onRowSelectionChange",
2659
+ "onSelectAllPage",
2660
+ "selectionActions",
2661
+ "onSelectionAction",
2662
+ "onClearSelection"
2475
2663
  ]);
2476
- const apiRef = useRef3(null);
2477
- const containerRef = useRef3(null);
2664
+ const apiRef = useRef4(null);
2665
+ const containerRef = useRef4(null);
2666
+ const [internalSelectedCount, setInternalSelectedCount] = useState9(0);
2667
+ const dataRef = useRef4(data);
2668
+ useEffect9(() => {
2669
+ dataRef.current = data;
2670
+ }, [data]);
2671
+ const onRowSelectionChangeRef = useRef4(onRowSelectionChange);
2672
+ useEffect9(() => {
2673
+ onRowSelectionChangeRef.current = onRowSelectionChange;
2674
+ }, [onRowSelectionChange]);
2675
+ const onSelectAllPageRef = useRef4(onSelectAllPage);
2676
+ useEffect9(() => {
2677
+ onSelectAllPageRef.current = onSelectAllPage;
2678
+ }, [onSelectAllPage]);
2478
2679
  useImperativeHandle(ref, () => apiRef.current, []);
2680
+ const getSelectedData = useCallback2(() => {
2681
+ var _a2, _b2, _c;
2682
+ const ids = (_b2 = (_a2 = apiRef.current) == null ? void 0 : _a2.getState().selectedRows) != null ? _b2 : [];
2683
+ return ((_c = dataRef.current) != null ? _c : []).filter((row) => ids.includes(row.id));
2684
+ }, []);
2685
+ const resolvedColumns = useMemo4(() => {
2686
+ if (!selectionEnabled || columns.length === 0) return columns;
2687
+ const checkboxCol = buildCheckboxColumn({
2688
+ getPageData: () => {
2689
+ var _a2;
2690
+ return (_a2 = dataRef.current) != null ? _a2 : [];
2691
+ },
2692
+ onSelectionChange: (id, rowData, checked) => {
2693
+ var _a2;
2694
+ (_a2 = onRowSelectionChangeRef.current) == null ? void 0 : _a2.call(onRowSelectionChangeRef, id, rowData, checked);
2695
+ },
2696
+ onSelectAllPage: (pageData, checked) => {
2697
+ var _a2;
2698
+ (_a2 = onSelectAllPageRef.current) == null ? void 0 : _a2.call(onSelectAllPageRef, pageData, checked);
2699
+ }
2700
+ });
2701
+ return [checkboxCol, ...columns];
2702
+ }, [selectionEnabled, columns]);
2479
2703
  const resizeColumns = useCallback2(() => {
2480
- var _a2;
2481
2704
  const api = apiRef.current;
2482
2705
  const container = containerRef.current;
2483
- if (!api) return;
2484
- const gridWidth = (_a2 = container == null ? void 0 : container.clientWidth) != null ? _a2 : 0;
2706
+ if (!api || !container) return;
2707
+ const gridWidth = container.clientWidth;
2708
+ const isFixed = (col) => col.id === "__selection__";
2709
+ const resizable = resolvedColumns.filter((col) => !isFixed(col));
2710
+ const fixed = resolvedColumns.filter((col) => isFixed(col));
2711
+ const fixedTotal = fixed.reduce((sum, col) => {
2712
+ var _a2, _b2, _c;
2713
+ const w = (_c = (_b2 = (_a2 = api.getColumn(col.id)) == null ? void 0 : _a2.width) != null ? _b2 : col.width) != null ? _c : 0;
2714
+ return sum + (isNaN(w) ? 0 : w);
2715
+ }, 0);
2716
+ const available = Math.max(gridWidth - fixedTotal, 0);
2485
2717
  if (data && data.length > 0) {
2486
- columns.forEach((col) => {
2487
- api.exec("resize-column", {
2488
- id: col.id,
2489
- auto: "data"
2490
- });
2491
- });
2492
- const totalColumnWidth = columns.reduce((sum, col) => {
2493
- var _a3;
2494
- const column = api.getColumn(col.id);
2495
- const width = (_a3 = column == null ? void 0 : column.width) != null ? _a3 : 0;
2496
- return sum + (isNaN(width) ? 0 : width);
2718
+ resizable.forEach(
2719
+ (col) => api.exec("resize-column", { id: col.id, auto: "data" })
2720
+ );
2721
+ const autoTotal = resizable.reduce((sum, col) => {
2722
+ var _a2, _b2;
2723
+ const w = (_b2 = (_a2 = api.getColumn(col.id)) == null ? void 0 : _a2.width) != null ? _b2 : 0;
2724
+ return sum + (isNaN(w) ? 0 : w);
2497
2725
  }, 0);
2498
- if (totalColumnWidth < gridWidth) {
2499
- const extra = (gridWidth - totalColumnWidth) / columns.length;
2500
- columns.forEach((col) => {
2501
- var _a3;
2502
- const column = api.getColumn(col.id);
2503
- const currentWidth = (_a3 = column == null ? void 0 : column.width) != null ? _a3 : 0;
2504
- if (!isNaN(currentWidth)) {
2505
- api.exec("resize-column", {
2506
- id: col.id,
2507
- width: currentWidth + extra
2508
- });
2509
- }
2726
+ if (autoTotal < available && resizable.length > 0) {
2727
+ const extra = (available - autoTotal) / resizable.length;
2728
+ resizable.forEach((col) => {
2729
+ var _a2, _b2;
2730
+ const w = (_b2 = (_a2 = api.getColumn(col.id)) == null ? void 0 : _a2.width) != null ? _b2 : 0;
2731
+ if (!isNaN(w))
2732
+ api.exec("resize-column", { id: col.id, width: w + extra });
2510
2733
  });
2511
2734
  }
2512
2735
  } else {
2513
- const columnWidth = gridWidth / columns.length;
2514
- columns.forEach((col) => {
2515
- api.exec("resize-column", {
2516
- id: col.id,
2517
- width: columnWidth
2518
- });
2519
- });
2736
+ const eq = resizable.length > 0 ? available / resizable.length : 0;
2737
+ resizable.forEach(
2738
+ (col) => api.exec("resize-column", { id: col.id, width: eq })
2739
+ );
2520
2740
  }
2521
- }, [columns, data]);
2522
- useEffect7(() => {
2523
- if (!containerRef.current) return;
2524
- const observer = new ResizeObserver(() => {
2525
- resizeColumns();
2526
- });
2527
- observer.observe(containerRef.current);
2528
- return () => observer.disconnect();
2529
- }, [resizeColumns]);
2741
+ }, [resolvedColumns, data]);
2530
2742
  const init = useCallback2(
2531
2743
  (api) => {
2532
2744
  apiRef.current = api;
2533
2745
  resizeColumns();
2746
+ api.intercept("focus-cell", onFocusCell);
2747
+ if (selectionEnabled) {
2748
+ api.on("select-row", () => {
2749
+ var _a2;
2750
+ setInternalSelectedCount(
2751
+ ((_a2 = api.getState().selectedRows) != null ? _a2 : []).length
2752
+ );
2753
+ });
2754
+ }
2534
2755
  },
2535
- [resizeColumns]
2756
+ [resizeColumns, onFocusCell, selectionEnabled]
2536
2757
  );
2537
- const getSelectedData = () => {
2538
- var _a2, _b2;
2539
- const selected = (_b2 = (_a2 = apiRef.current) == null ? void 0 : _a2.getState().selectedRows) != null ? _b2 : [];
2540
- let selectedData = data.filter((row) => {
2541
- return selected.includes(row.id);
2542
- });
2543
- return selectedData;
2544
- };
2758
+ useEffect9(() => {
2759
+ if (!containerRef.current) return;
2760
+ const observer = new ResizeObserver(() => resizeColumns());
2761
+ observer.observe(containerRef.current);
2762
+ return () => observer.disconnect();
2763
+ }, [resizeColumns]);
2764
+ useEffect9(() => {
2765
+ if (!selectionEnabled || !getSelectedIds || !apiRef.current) return;
2766
+ const pageIds = new Set((data != null ? data : []).map((r) => r.id));
2767
+ const toRestore = getSelectedIds().filter((id) => pageIds.has(id));
2768
+ toRestore.forEach(
2769
+ (id) => apiRef.current.exec("select-row", { id, mode: true })
2770
+ );
2771
+ }, [data, selectionEnabled, getSelectedIds]);
2772
+ const handleClearSelection = useCallback2(() => {
2773
+ var _a2;
2774
+ const api = apiRef.current;
2775
+ if (!api) return;
2776
+ const ids = [...(_a2 = api.getState().selectedRows) != null ? _a2 : []];
2777
+ ids.forEach((id) => api.exec("select-row", { id, mode: false }));
2778
+ onClearSelection == null ? void 0 : onClearSelection();
2779
+ }, [onClearSelection]);
2545
2780
  const handleExportPdf = useCallback2(() => {
2546
2781
  const selected = getSelectedData();
2547
- if (selected.length) {
2548
- exportPdf(selected, fileName);
2549
- } else {
2550
- onExportPdf == null ? void 0 : onExportPdf();
2551
- }
2552
- }, [getSelectedData, exportPdf, onExportPdf, fileName]);
2782
+ if (selected.length) exportPdf(selected, fileName);
2783
+ else onExportPdf == null ? void 0 : onExportPdf();
2784
+ }, [getSelectedData, fileName, onExportPdf]);
2553
2785
  const handleExportExcel = useCallback2(() => {
2554
2786
  const selected = getSelectedData();
2555
- if (selected.length) {
2556
- exportExcel(selected, fileName);
2557
- } else {
2558
- onExportExcel == null ? void 0 : onExportExcel();
2559
- }
2560
- }, [getSelectedData, exportExcel, onExportExcel, fileName]);
2561
- return /* @__PURE__ */ jsxs19("div", { className: "w-full h-full flex flex-col overflow-hidden", children: [
2562
- /* @__PURE__ */ jsx25(
2787
+ if (selected.length) exportExcel(selected, fileName);
2788
+ else onExportExcel == null ? void 0 : onExportExcel();
2789
+ }, [getSelectedData, fileName, onExportExcel]);
2790
+ return /* @__PURE__ */ jsxs20("div", { className: "w-full h-full flex flex-col overflow-hidden", children: [
2791
+ /* @__PURE__ */ jsx28(
2563
2792
  gridHeader_default,
2564
2793
  {
2565
2794
  onPdf: handleExportPdf,
@@ -2572,20 +2801,41 @@ var Grid = forwardRef(
2572
2801
  filterList: rest.filterList,
2573
2802
  onFilterChange,
2574
2803
  initialFilters,
2804
+ selectedCount: internalSelectedCount,
2805
+ selectedData: getSelectedData(),
2806
+ selectionActions,
2807
+ onSelectionAction,
2808
+ onClearSelection: handleClearSelection,
2575
2809
  children
2576
2810
  }
2577
2811
  ),
2578
- /* @__PURE__ */ jsx25("div", { className: "flex-1 w-full border border-border overflow-hidden", ref: containerRef, children: /* @__PURE__ */ jsx25(WillowDark, { fonts, children: /* @__PURE__ */ jsx25("div", { className: "w-full h-full relative", children: rest.loading ? /* @__PURE__ */ jsx25("div", { className: "absolute inset-0 bg-background/60 backdrop-blur-sm flex items-center justify-center z-10", children: /* @__PURE__ */ jsx25("div", { className: "animate-spin rounded-full h-8 w-8 border-2 border-primary border-t-transparent" }) }) : /* @__PURE__ */ jsx25(SvarGrid, __spreadProps(__spreadValues({}, rest), { data, columns, init })) }) }) })
2812
+ /* @__PURE__ */ jsx28(
2813
+ "div",
2814
+ {
2815
+ className: "flex-1 w-full border border-border overflow-hidden",
2816
+ ref: containerRef,
2817
+ children: /* @__PURE__ */ jsx28(WillowDark, { fonts, children: /* @__PURE__ */ jsx28("div", { className: "w-full h-full relative", children: rest.loading ? /* @__PURE__ */ jsx28("div", { className: "absolute inset-0 bg-background/60 backdrop-blur-sm flex items-center justify-center z-10", children: /* @__PURE__ */ jsx28("div", { className: "animate-spin rounded-full h-8 w-8 border-2 border-primary border-t-transparent" }) }) : /* @__PURE__ */ jsx28(
2818
+ SvarGrid,
2819
+ __spreadProps(__spreadValues({}, rest), {
2820
+ data,
2821
+ columns: resolvedColumns,
2822
+ init,
2823
+ select: selectionEnabled ? false : rest.select
2824
+ })
2825
+ ) }) })
2826
+ }
2827
+ )
2579
2828
  ] });
2580
2829
  }
2581
2830
  );
2582
- var grid_default = Grid;
2831
+ var grid_default = React6.memo(Grid);
2583
2832
 
2584
2833
  // src/components/custom/grid/sortableHeaderCell.tsx
2585
2834
  import { MoveDown, MoveUp, ArrowUpDown } from "lucide-react";
2586
- import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
2587
- function SortableHeaderCell({ cell, sortKey, sortOrder, onSortChange }) {
2588
- if (!(cell == null ? void 0 : cell.id)) return /* @__PURE__ */ jsx26("span", { children: cell == null ? void 0 : cell.text });
2835
+ import React7 from "react";
2836
+ import { jsx as jsx29, jsxs as jsxs21 } from "react/jsx-runtime";
2837
+ var SortableHeaderCell = ({ cell, sortKey, sortOrder, onSortChange }) => {
2838
+ if (!(cell == null ? void 0 : cell.id)) return /* @__PURE__ */ jsx29("span", { children: cell == null ? void 0 : cell.text });
2589
2839
  const active = sortKey === cell.id;
2590
2840
  const handleClick = () => {
2591
2841
  if (!onSortChange || !cell.id) return;
@@ -2599,20 +2849,27 @@ function SortableHeaderCell({ cell, sortKey, sortOrder, onSortChange }) {
2599
2849
  }
2600
2850
  onSortChange("", null);
2601
2851
  };
2602
- return /* @__PURE__ */ jsxs20(
2852
+ return /* @__PURE__ */ jsxs21(
2603
2853
  "div",
2604
2854
  {
2605
2855
  onClick: handleClick,
2606
2856
  className: "cursor-pointer flex items-center gap-2 justify-between select-none",
2607
2857
  children: [
2608
- /* @__PURE__ */ jsx26("span", { children: cell.text }),
2609
- !active && /* @__PURE__ */ jsx26(ArrowUpDown, { className: "w-4 h-4 opacity-50" }),
2610
- active && (sortOrder === "asc" ? /* @__PURE__ */ jsx26(MoveDown, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx26(MoveUp, { className: "w-4 h-4" }))
2858
+ /* @__PURE__ */ jsx29("span", { children: cell.text }),
2859
+ !active && /* @__PURE__ */ jsx29(ArrowUpDown, { className: "w-4 h-4 opacity-50" }),
2860
+ active && (sortOrder === "asc" ? /* @__PURE__ */ jsx29(MoveDown, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx29(MoveUp, { className: "w-4 h-4" }))
2611
2861
  ]
2612
2862
  }
2613
2863
  );
2614
- }
2615
- var sortableHeaderCell_default = SortableHeaderCell;
2864
+ };
2865
+ var sortableHeaderCell_default = React7.memo(SortableHeaderCell, (prev, next) => {
2866
+ var _a, _b, _c, _d;
2867
+ if (((_a = prev.cell) == null ? void 0 : _a.id) !== ((_b = next.cell) == null ? void 0 : _b.id)) return false;
2868
+ if (((_c = prev.cell) == null ? void 0 : _c.text) !== ((_d = next.cell) == null ? void 0 : _d.text)) return false;
2869
+ if (prev.sortKey !== next.sortKey) return false;
2870
+ if (prev.sortOrder !== next.sortOrder) return false;
2871
+ return true;
2872
+ });
2616
2873
 
2617
2874
  // src/utils/pagination.ts
2618
2875
  function getPaginationRange(currentPage, totalPages, siblingCount = 2) {
@@ -2646,7 +2903,7 @@ function getPaginationRange(currentPage, totalPages, siblingCount = 2) {
2646
2903
 
2647
2904
  // src/components/custom/pagination/index.tsx
2648
2905
  import { ChevronLeft, ChevronRight as ChevronRight3 } from "lucide-react";
2649
- import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
2906
+ import { jsx as jsx30, jsxs as jsxs22 } from "react/jsx-runtime";
2650
2907
  function Pagination({
2651
2908
  totalCount,
2652
2909
  count = null,
@@ -2666,47 +2923,47 @@ function Pagination({
2666
2923
  const handlePageSizeChange = (size) => {
2667
2924
  onPageSizeChange == null ? void 0 : onPageSizeChange(size);
2668
2925
  };
2669
- return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col-reverse gap-2 md:flex-row items-center justify-between bg-card px-3 py-2 rounded-br-md rounded-bl-md border-l border-r border-b", children: [
2670
- /* @__PURE__ */ jsxs21("div", { className: "flex justify-between w-full md:w-auto items-center gap-3 text-xs text-muted-foreground tracking-wider font-medium", children: [
2671
- /* @__PURE__ */ jsxs21("span", { children: [
2926
+ return /* @__PURE__ */ jsxs22("div", { className: "flex flex-col-reverse gap-2 md:flex-row items-center justify-between bg-card px-3 py-2 rounded-br-md rounded-bl-md border-l border-r border-b", children: [
2927
+ /* @__PURE__ */ jsxs22("div", { className: "flex justify-between w-full md:w-auto items-center gap-3 text-xs text-muted-foreground tracking-wider font-medium", children: [
2928
+ /* @__PURE__ */ jsxs22("span", { children: [
2672
2929
  "Showing ",
2673
- /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: count }),
2930
+ /* @__PURE__ */ jsx30("span", { className: "text-foreground", children: count }),
2674
2931
  " of",
2675
2932
  " ",
2676
- /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: totalCount })
2933
+ /* @__PURE__ */ jsx30("span", { className: "text-foreground", children: totalCount })
2677
2934
  ] }),
2678
- showSizeChanger && /* @__PURE__ */ jsx27(
2935
+ showSizeChanger && /* @__PURE__ */ jsx30(
2679
2936
  "select",
2680
2937
  {
2681
2938
  value: pageSize,
2682
2939
  onChange: (e) => handlePageSizeChange(Number(e.target.value)),
2683
2940
  className: "rounded-md border border-input bg-background px-3 py-2 text-xs text-foreground focus:outline-none",
2684
- children: sizeChangerOptions.map((size) => /* @__PURE__ */ jsxs21("option", { value: size, children: [
2941
+ children: sizeChangerOptions.map((size) => /* @__PURE__ */ jsxs22("option", { value: size, children: [
2685
2942
  size,
2686
2943
  " / page"
2687
2944
  ] }, size))
2688
2945
  }
2689
2946
  )
2690
2947
  ] }),
2691
- /* @__PURE__ */ jsxs21("div", { className: "flex w-full md:w-auto justify-between items-center gap-1", children: [
2692
- /* @__PURE__ */ jsx27(
2948
+ /* @__PURE__ */ jsxs22("div", { className: "flex w-full md:w-auto justify-between items-center gap-1", children: [
2949
+ /* @__PURE__ */ jsx30(
2693
2950
  "button",
2694
2951
  {
2695
2952
  disabled: currentPage === 1,
2696
2953
  onClick: () => handlePageChange(currentPage - 1),
2697
2954
  className: "border border-border w-8 h-8 rounded-md flex items-center justify-center disabled:opacity-40 hover:bg-card-foreground transition",
2698
- children: /* @__PURE__ */ jsx27(ChevronLeft, { className: "w-5 h-5 select-none" })
2955
+ children: /* @__PURE__ */ jsx30(ChevronLeft, { className: "w-5 h-5 select-none" })
2699
2956
  }
2700
2957
  ),
2701
2958
  pages.map(
2702
- (page, idx) => page === "dots" ? /* @__PURE__ */ jsx27(
2959
+ (page, idx) => page === "dots" ? /* @__PURE__ */ jsx30(
2703
2960
  "span",
2704
2961
  {
2705
2962
  className: "px-2 text-muted-foreground text-xs tracking-wider font-medium",
2706
2963
  children: "\u2026"
2707
2964
  },
2708
2965
  `dots-${idx}`
2709
- ) : /* @__PURE__ */ jsx27(
2966
+ ) : /* @__PURE__ */ jsx30(
2710
2967
  "button",
2711
2968
  {
2712
2969
  onClick: () => handlePageChange(page),
@@ -2716,13 +2973,13 @@ function Pagination({
2716
2973
  page
2717
2974
  )
2718
2975
  ),
2719
- /* @__PURE__ */ jsx27(
2976
+ /* @__PURE__ */ jsx30(
2720
2977
  "button",
2721
2978
  {
2722
2979
  disabled: currentPage === totalPages,
2723
2980
  onClick: () => handlePageChange(currentPage + 1),
2724
2981
  className: "border border-border w-8 h-8 rounded-md flex items-center justify-center disabled:opacity-40 hover:bg-card-foreground transition",
2725
- children: /* @__PURE__ */ jsx27(ChevronRight3, { className: "w-5 h-5 select-none" })
2982
+ children: /* @__PURE__ */ jsx30(ChevronRight3, { className: "w-5 h-5 select-none" })
2726
2983
  }
2727
2984
  )
2728
2985
  ] })
@@ -2731,9 +2988,9 @@ function Pagination({
2731
2988
  var pagination_default = Pagination;
2732
2989
 
2733
2990
  // src/components/custom/toolbar/index.tsx
2734
- import { ChevronRight as ChevronRight4, X as X4 } from "lucide-react";
2735
- import { useState as useState8, useRef as useRef4, useEffect as useEffect8 } from "react";
2736
- import { Fragment as Fragment3, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
2991
+ import { ChevronRight as ChevronRight4, X as X5 } from "lucide-react";
2992
+ import { useState as useState10, useRef as useRef5, useEffect as useEffect10 } from "react";
2993
+ import { Fragment as Fragment4, jsx as jsx31, jsxs as jsxs23 } from "react/jsx-runtime";
2737
2994
  var SlideToolbar = ({
2738
2995
  items,
2739
2996
  children,
@@ -2745,9 +3002,9 @@ var SlideToolbar = ({
2745
3002
  className = ""
2746
3003
  }) => {
2747
3004
  const resolvedItems = items != null ? items : children ? void 0 : [];
2748
- const [open, setOpen] = useState8(false);
2749
- const panelRef = useRef4(null);
2750
- useEffect8(() => {
3005
+ const [open, setOpen] = useState10(false);
3006
+ const panelRef = useRef5(null);
3007
+ useEffect10(() => {
2751
3008
  if (!open || !closeOnBackdrop) return;
2752
3009
  const handlePointerDown = (e) => {
2753
3010
  if (panelRef.current && !panelRef.current.contains(e.target)) {
@@ -2760,8 +3017,8 @@ var SlideToolbar = ({
2760
3017
  const handleItem = (item) => {
2761
3018
  onAction == null ? void 0 : onAction(item.id);
2762
3019
  };
2763
- return /* @__PURE__ */ jsxs22(Fragment3, { children: [
2764
- /* @__PURE__ */ jsx28("style", { children: `
3020
+ return /* @__PURE__ */ jsxs23(Fragment4, { children: [
3021
+ /* @__PURE__ */ jsx31("style", { children: `
2765
3022
  @keyframes bounceX {
2766
3023
  0%, 100% { transform: translateX(0px); }
2767
3024
  40% { transform: translateX(4px); }
@@ -2771,7 +3028,7 @@ var SlideToolbar = ({
2771
3028
  animation: bounceX 1.4s ease-in-out infinite;
2772
3029
  }
2773
3030
  ` }),
2774
- /* @__PURE__ */ jsx28(
3031
+ /* @__PURE__ */ jsx31(
2775
3032
  "div",
2776
3033
  {
2777
3034
  role: "button",
@@ -2785,7 +3042,7 @@ var SlideToolbar = ({
2785
3042
  "hover:brightness-110 transition-all duration-200",
2786
3043
  open ? "opacity-0 pointer-events-none !w-0 border-r-0" : "opacity-100 pointer-events-auto"
2787
3044
  ].join(" "),
2788
- children: /* @__PURE__ */ jsx28(
3045
+ children: /* @__PURE__ */ jsx31(
2789
3046
  ChevronRight4,
2790
3047
  {
2791
3048
  className: "text-primary-foreground chevron-bounce",
@@ -2794,7 +3051,7 @@ var SlideToolbar = ({
2794
3051
  )
2795
3052
  }
2796
3053
  ),
2797
- /* @__PURE__ */ jsxs22(
3054
+ /* @__PURE__ */ jsxs23(
2798
3055
  "div",
2799
3056
  {
2800
3057
  ref: panelRef,
@@ -2811,36 +3068,36 @@ var SlideToolbar = ({
2811
3068
  ].join(" "),
2812
3069
  style: { width: `${panelWidth}px` },
2813
3070
  children: [
2814
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border shrink-0", children: [
2815
- /* @__PURE__ */ jsx28("span", { className: "text-sm font-semibold text-foreground tracking-wide", children: title }),
2816
- /* @__PURE__ */ jsx28(
3071
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border shrink-0", children: [
3072
+ /* @__PURE__ */ jsx31("span", { className: "text-sm font-semibold text-foreground tracking-wide", children: title }),
3073
+ /* @__PURE__ */ jsx31(
2817
3074
  "button",
2818
3075
  {
2819
3076
  onClick: () => setOpen(false),
2820
3077
  "aria-label": "Close toolbar",
2821
3078
  className: "flex items-center justify-center w-6 h-6 rounded-md\n text-foreground/50 hover:text-foreground hover:bg-surface-alt\n transition-all duration-150 cursor-pointer",
2822
- children: /* @__PURE__ */ jsx28(X4, { size: 13 })
3079
+ children: /* @__PURE__ */ jsx31(X5, { size: 13 })
2823
3080
  }
2824
3081
  )
2825
3082
  ] }),
2826
- /* @__PURE__ */ jsx28("div", { className: "flex-1 overflow-y-auto overflow-x-hidden py-2 scrollbar-none", children: resolvedItems ? /* @__PURE__ */ jsxs22(Fragment3, { children: [
2827
- /* @__PURE__ */ jsx28("p", { className: "px-4 pt-1 pb-2 text-[10px] font-semibold tracking-[0.16em] uppercase text-foreground/35", children: "Actions" }),
2828
- /* @__PURE__ */ jsx28("nav", { className: "flex flex-col px-2", children: resolvedItems.map((item) => /* @__PURE__ */ jsxs22(
3083
+ /* @__PURE__ */ jsx31("div", { className: "flex-1 overflow-y-auto overflow-x-hidden py-2 scrollbar-none", children: resolvedItems ? /* @__PURE__ */ jsxs23(Fragment4, { children: [
3084
+ /* @__PURE__ */ jsx31("p", { className: "px-4 pt-1 pb-2 text-[10px] font-semibold tracking-[0.16em] uppercase text-foreground/35", children: "Actions" }),
3085
+ /* @__PURE__ */ jsx31("nav", { className: "flex flex-col px-2", children: resolvedItems.map((item) => /* @__PURE__ */ jsxs23(
2829
3086
  "button",
2830
3087
  {
2831
3088
  onClick: () => handleItem(item),
2832
3089
  className: "group flex items-center gap-3 px-3 py-2.5 rounded-lg w-full\n text-left text-[13px] font-medium cursor-pointer\n text-foreground/60 hover:text-foreground hover:bg-surface-alt\n transition-all duration-150 border-l-2 border-transparent\n hover:border-primary",
2833
3090
  children: [
2834
- /* @__PURE__ */ jsx28("span", { className: "flex shrink-0 text-foreground/40 group-hover:text-primary transition-colors duration-150", children: item.icon }),
2835
- /* @__PURE__ */ jsx28("span", { className: "flex-1 leading-none", children: item.label })
3091
+ /* @__PURE__ */ jsx31("span", { className: "flex shrink-0 text-foreground/40 group-hover:text-primary transition-colors duration-150", children: item.icon }),
3092
+ /* @__PURE__ */ jsx31("span", { className: "flex-1 leading-none", children: item.label })
2836
3093
  ]
2837
3094
  },
2838
3095
  item.id
2839
3096
  )) })
2840
3097
  ] }) : children }),
2841
- footerLabel && /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2 px-4 py-3 border-t border-border shrink-0", children: [
2842
- /* @__PURE__ */ jsx28("span", { className: "w-1.5 h-1.5 rounded-full bg-foreground/20 shrink-0" }),
2843
- /* @__PURE__ */ jsx28("span", { className: "text-[10px] tracking-[0.12em] uppercase text-foreground/30 font-medium", children: footerLabel })
3098
+ footerLabel && /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 px-4 py-3 border-t border-border shrink-0", children: [
3099
+ /* @__PURE__ */ jsx31("span", { className: "w-1.5 h-1.5 rounded-full bg-foreground/20 shrink-0" }),
3100
+ /* @__PURE__ */ jsx31("span", { className: "text-[10px] tracking-[0.12em] uppercase text-foreground/30 font-medium", children: footerLabel })
2844
3101
  ] })
2845
3102
  ]
2846
3103
  }