@cloudwick/astral-ui-cli 0.2.1 → 0.2.2

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.js CHANGED
@@ -1443,7 +1443,7 @@ var REGISTRY = {
1443
1443
  "react"
1444
1444
  ],
1445
1445
  "internalDependencies": [
1446
- "adpicon",
1446
+ "adpIcon",
1447
1447
  "button"
1448
1448
  ],
1449
1449
  "files": [
@@ -1466,7 +1466,7 @@ var REGISTRY = {
1466
1466
  ]
1467
1467
  },
1468
1468
  "adpicon": {
1469
- "name": "adpicon",
1469
+ "name": "adpIcon",
1470
1470
  "description": "## Overview",
1471
1471
  "dependencies": [
1472
1472
  "react",
@@ -1524,7 +1524,7 @@ var REGISTRY = {
1524
1524
  ]
1525
1525
  },
1526
1526
  "avatargroup": {
1527
- "name": "avatargroup",
1527
+ "name": "avatarGroup",
1528
1528
  "description": "## Overview",
1529
1529
  "dependencies": [
1530
1530
  "class-variance-authority",
@@ -1544,7 +1544,7 @@ var REGISTRY = {
1544
1544
  },
1545
1545
  {
1546
1546
  "name": "avatarGroup.tsx",
1547
- "content": 'import * as React from "react";\n\nimport { cn } from "@utils";\nimport { avatarGroupVariants } from "./avatarGroupVariants";\nimport { Avatar, IAvatarProps } from "../avatar";\n\n/**\n * AvatarGroup component for displaying a group of avatars with overflow handling\n */\nexport interface IAvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Accept children\n */\n children: React.ReactElement<IAvatarProps> | Array<React.ReactElement<IAvatarProps>>;\n /**\n * Callback to be called when the AvatarGroup is clicked\n */\n onClick?: () => void;\n /**\n * Maximum items to display in the group before the custom avatar is shown\n * @default 4\n */\n maxItems?: number;\n /**\n * Size of the Avatars\n * @default md\n */\n size?: IAvatarProps["size"];\n /**\n * Overwrite the styles for the AvatarGroup by passing space separated class names\n */\n className?: string;\n}\n\n/**\n * Filter children to only include valid Avatar components with a label\n */\nconst filterChildren = (\n children: React.ReactElement<IAvatarProps> | Array<React.ReactElement<IAvatarProps>>\n) => {\n const filteredArray: React.ReactElement<IAvatarProps>[] = [];\n\n if ( Array.isArray( children )) {\n children.forEach(( child: React.ReactElement<IAvatarProps> ) => {\n const { label } = child.props;\n if ( child.type === Avatar && label ) {\n filteredArray.push( child );\n }\n });\n } else {\n const { label } = children.props;\n if ( children.type === Avatar && label ) {\n filteredArray.push( children );\n }\n }\n\n return filteredArray;\n};\n\n/**\n * AvatarGroup component for displaying a group of avatars with overflow handling\n */\nexport const AvatarGroup = React.forwardRef<HTMLDivElement, IAvatarGroupProps>(\n ({ size = "md", maxItems = 4, className, children, onClick, ...props }, ref ) => {\n const setMaxItems = maxItems && maxItems > 1 ? maxItems : 4;\n const filteredChildren = React.useMemo(() => filterChildren( children ), [children]);\n\n const isClickable = Boolean( onClick );\n\n // Create the avatar elements\n const createAvatarElements = () => {\n return (\n <>\n {filteredChildren.slice( 0, setMaxItems ).map(( avatar, index ) => (\n <Avatar\n {...avatar.props}\n key={`avatar-group-avatar-${index}`}\n size={size}\n />\n ))}\n {filteredChildren.length > setMaxItems && (\n <Avatar\n label={`+${abbreviateNumber( filteredChildren.length - setMaxItems )}`}\n abbreviate={false}\n size={size}\n />\n )}\n </>\n );\n };\n\n if ( filteredChildren.length === 0 ) {\n return null;\n }\n\n const abbreviateNumber = ( num: number ): string => {\n if ( num < 1000 ) {\n return num.toString();\n }\n if ( num < 1000000 ) {\n return `${( num / 1000 ).toFixed( 1 )}k`;\n }\n return `${( num / 1000000 ).toFixed( 1 )}m`;\n };\n\n return (\n <div\n ref={ref}\n className={cn( avatarGroupVariants({ clickable: isClickable }), className )}\n onClick={onClick}\n role={onClick ? "button" : undefined}\n tabIndex={onClick ? 0 : undefined}\n {...props}\n >\n {createAvatarElements()}\n </div>\n );\n }\n);\n\nAvatarGroup.displayName = "AvatarGroup";'
1547
+ "content": 'import * as React from "react";\n\nimport { abbreviateNumber, cn } from "@utils";\nimport { avatarGroupVariants } from "./avatarGroupVariants";\nimport { Avatar, IAvatarProps } from "../avatar";\n\n/**\n * AvatarGroup component for displaying a group of avatars with overflow handling\n */\nexport interface IAvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Accept children\n */\n children: React.ReactElement<IAvatarProps> | Array<React.ReactElement<IAvatarProps>>;\n /**\n * Callback to be called when the AvatarGroup is clicked\n */\n onClick?: () => void;\n /**\n * Maximum items to display in the group before the custom avatar is shown\n * @default 4\n */\n maxItems?: number;\n /**\n * Size of the Avatars\n * @default md\n */\n size?: IAvatarProps["size"];\n /**\n * Overwrite the styles for the AvatarGroup by passing space separated class names\n */\n className?: string;\n}\n\n/**\n * Filter children to only include valid Avatar components with a label\n */\nconst filterChildren = (\n children: React.ReactElement<IAvatarProps> | Array<React.ReactElement<IAvatarProps>>\n) => {\n const filteredArray: React.ReactElement<IAvatarProps>[] = [];\n\n if ( Array.isArray( children )) {\n children.forEach(( child: React.ReactElement<IAvatarProps> ) => {\n const { label } = child.props;\n if ( child.type === Avatar && label ) {\n filteredArray.push( child );\n }\n });\n } else {\n const { label } = children.props;\n if ( children.type === Avatar && label ) {\n filteredArray.push( children );\n }\n }\n\n return filteredArray;\n};\n\n/**\n * AvatarGroup component for displaying a group of avatars with overflow handling\n */\nexport const AvatarGroup = React.forwardRef<HTMLDivElement, IAvatarGroupProps>(\n ({ size = "md", maxItems = 4, className, children, onClick, ...props }, ref ) => {\n const setMaxItems = maxItems && maxItems > 1 ? maxItems : 4;\n const filteredChildren = React.useMemo(() => filterChildren( children ), [children]);\n\n const isClickable = Boolean( onClick );\n\n // Create the avatar elements\n const createAvatarElements = () => {\n return (\n <>\n {filteredChildren.slice( 0, setMaxItems ).map(( avatar, index ) => (\n <Avatar\n {...avatar.props}\n key={`avatar-group-avatar-${index}`}\n size={size}\n />\n ))}\n {filteredChildren.length > setMaxItems && (\n <Avatar\n label={`+${abbreviateNumber( filteredChildren.length - setMaxItems, 1, false )}`}\n abbreviate={false}\n size={size}\n />\n )}\n </>\n );\n };\n\n if ( filteredChildren.length === 0 ) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn( avatarGroupVariants({ clickable: isClickable }), className )}\n onClick={onClick}\n role={onClick ? "button" : undefined}\n tabIndex={onClick ? 0 : undefined}\n {...props}\n >\n {createAvatarElements()}\n </div>\n );\n }\n);\n\nAvatarGroup.displayName = "AvatarGroup";'
1548
1548
  },
1549
1549
  {
1550
1550
  "name": "README.md",
@@ -1587,7 +1587,7 @@ var REGISTRY = {
1587
1587
  "react"
1588
1588
  ],
1589
1589
  "internalDependencies": [
1590
- "adpicon"
1590
+ "adpIcon"
1591
1591
  ],
1592
1592
  "files": [
1593
1593
  {
@@ -1622,7 +1622,7 @@ var REGISTRY = {
1622
1622
  ],
1623
1623
  "internalDependencies": [
1624
1624
  "tooltip",
1625
- "adpicon"
1625
+ "adpIcon"
1626
1626
  ],
1627
1627
  "files": [
1628
1628
  {
@@ -1651,9 +1651,9 @@ var REGISTRY = {
1651
1651
  "react"
1652
1652
  ],
1653
1653
  "internalDependencies": [
1654
- "ctagroup",
1654
+ "ctaGroup",
1655
1655
  "button",
1656
- "adpicon"
1656
+ "adpIcon"
1657
1657
  ],
1658
1658
  "files": [
1659
1659
  {
@@ -1805,7 +1805,7 @@ export const checkmarkClipPath = \`polygon(
1805
1805
  ]
1806
1806
  },
1807
1807
  "checkboxgroup": {
1808
- "name": "checkboxgroup",
1808
+ "name": "checkboxGroup",
1809
1809
  "description": "## Overview",
1810
1810
  "dependencies": [
1811
1811
  "class-variance-authority",
@@ -1834,7 +1834,7 @@ export const checkmarkClipPath = \`polygon(
1834
1834
  ]
1835
1835
  },
1836
1836
  "ctagroup": {
1837
- "name": "ctagroup",
1837
+ "name": "ctaGroup",
1838
1838
  "description": "## Overview",
1839
1839
  "dependencies": [
1840
1840
  "class-variance-authority",
@@ -1842,7 +1842,7 @@ export const checkmarkClipPath = \`polygon(
1842
1842
  "react-dom"
1843
1843
  ],
1844
1844
  "internalDependencies": [
1845
- "adpicon",
1845
+ "adpIcon",
1846
1846
  "dropdown",
1847
1847
  "tooltip"
1848
1848
  ],
@@ -1866,7 +1866,7 @@ export const checkmarkClipPath = \`polygon(
1866
1866
  ]
1867
1867
  },
1868
1868
  "datatable": {
1869
- "name": "datatable",
1869
+ "name": "dataTable",
1870
1870
  "description": "## Overview",
1871
1871
  "dependencies": [
1872
1872
  "react",
@@ -1878,13 +1878,13 @@ export const checkmarkClipPath = \`polygon(
1878
1878
  ],
1879
1879
  "internalDependencies": [
1880
1880
  "loader",
1881
- "adpicon",
1882
- "emptystate",
1881
+ "adpIcon",
1882
+ "emptyState",
1883
1883
  "badge",
1884
1884
  "button",
1885
1885
  "modal",
1886
1886
  "radio",
1887
- "textfield",
1887
+ "textField",
1888
1888
  "select",
1889
1889
  "pagination",
1890
1890
  "checkbox",
@@ -1919,7 +1919,7 @@ export const checkmarkClipPath = \`polygon(
1919
1919
  },
1920
1920
  {
1921
1921
  "name": "dataTable.tsx",
1922
- "content": 'import React, { type ReactNode, useCallback, useEffect, useRef, useState } from "react";\nimport {\n CellContext,\n createColumnHelper,\n getCoreRowModel,\n getFilteredRowModel,\n getSortedRowModel,\n OnChangeFn,\n RowSelectionState,\n useReactTable,\n type SortingState,\n type ColumnHelper,\n type ColumnPinningState,\n type Table as TReactTable,\n type SortingFnOption\n} from "@tanstack/react-table";\n\nimport { Button } from "../button/button";\nimport { TextField } from "../textField/textField";\nimport { Select, type IOption } from "../select/select";\nimport { ADPIcon, iconList } from "../adpIcon/adpIcon";\nimport { type TIconType } from "../adpIcon";\nimport { Pagination } from "../pagination/pagination";\nimport { Checkbox } from "../checkbox/checkbox";\nimport { cn } from "@utils";\nimport {\n dataTableContainerVariants,\n dataTableFiltersVariants,\n dataTableFilterSearchVariants,\n dataTableFilterSelectVariants,\n dataTableRecordsInfoVariants,\n dataTableContainerInnerVariants,\n dataTablePaginationVariants\n} from "./dataTableVariants";\nimport { PageSizeModal } from "./pageSizeModal";\nimport { DataTableColumnSettings } from "./DataTableColumnSettings";\nimport { AdvancedFilters } from "./advancedFilters";\nimport SelectedFilterList from "./selectedFilterList";\nimport { Table } from "./table";\n\n// Helper to find parent element with attribute\nconst findParentAttribute = ( element: HTMLElement, attr: string ): string | null => {\n let currentEl: HTMLElement | null = element;\n while ( currentEl ) {\n if ( currentEl.getAttribute( attr )) {\n return currentEl.getAttribute( attr );\n }\n currentEl = currentEl.parentElement;\n }\n return null;\n};\n\n// Helper to debounce function calls\nconst debounce = <T extends ( ...args: any[]) => any>(\n func: T,\n wait: number\n): (( ...args: Parameters<T> ) => void ) => {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n return ( ...args: Parameters<T> ) => {\n if ( timeout ) {\n clearTimeout( timeout );\n }\n timeout = setTimeout(() => func( ...args ), wait );\n };\n};\n\n// Utility function to derive configured columns\nconst deriveConfiguredColumns = (\n rawColumns: TIColumn[],\n selectable: boolean,\n columnHelper: ColumnHelper<any>\n) => {\n const derivedColumns = [\n ...( selectable\n ? [\n columnHelper.accessor( "select", {\n id: "select",\n header: ({ table }) => (\n <Checkbox\n checked={table.getIsAllRowsSelected()}\n // Use data-* attribute for indeterminate state\n data-indeterminate={table.getIsSomeRowsSelected().toString()}\n onChange={table.getToggleAllPageRowsSelectedHandler()}\n />\n ),\n cell: ({ row }) => (\n <Checkbox\n checked={row.getIsSelected()}\n disabled={!row.getCanSelect()}\n // Use data-* attribute for indeterminate state\n data-indeterminate={row.getIsSomeSelected().toString()}\n onChange={row.getToggleSelectedHandler()}\n />\n ),\n enableResizing: false,\n enableSorting: false,\n size: 56\n })\n ]\n : []),\n ...( rawColumns\n ?.sort(( a: any, b: any ) => Number( b?.fixed ?? false ) - Number( a?.fixed ?? false ))\n ?.filter( el => el?.selected === undefined || el?.selected !== false )?.map(( column: TIColumn ) => {\n const { cell, key, header, enableResizing, enableSorting, sortingFn, size } = column;\n return columnHelper.accessor( key, {\n header: header,\n cell: ( info: CellContext<any, any> ) => {\n return <>{typeof cell === "function" ? cell( info ) : info.getValue()}</>;\n },\n enableResizing: enableResizing ?? true,\n enableSorting: enableSorting ?? true,\n ...( sortingFn ? { sortingFn } : {}),\n size: size ? size : 400\n });\n }) ?? [])\n ];\n\n return derivedColumns;\n};\n\nconst DEBOUNCE_TIME = 500;\nconst HEIGHT = "500px";\n// Default page size options (maintained for backward compatibility but currently unused)\n\nconst PAGE_SIZE_OPTIONS = [ 25, 50, 100, 200, 500, 1000 ];\n\nexport type TIColumn = {\n id?: string;\n key: string;\n header?: any;\n cell?: ( row: CellContext<any, any> ) => ReactNode;\n /**\n * A fixed column -\n * * Cannot be re-ordered or hidden using the Column Preference panel\n * * The column will always be placed on the left side of the table irrespective of the index of the column configuration in IColumn[]\n *\n * @default false\n */\n fixed?: boolean;\n /**\n * Handle the visibility of the column\n * @default false\n */\n selected?: boolean;\n /**\n * To make the column sortable\n * @default true\n */\n enableSorting?: boolean;\n /**\n * To make the column resizable\n * @default true\n */\n enableResizing?: boolean;\n sortingFn?: SortingFnOption<any>;\n /**\n * To adjust the width of the column\n */\n size?: number;\n /**\n * Hides the column from the Column Preference panel\n * @default false\n */\n hideColPreference?: boolean;\n};\n\nexport type TFilterItem = {\n id?: string;\n selectedValue?: any;\n icon?: TIconType;\n title: string;\n placeholder?: string;\n onChange?: () => void;\n options: IOption[];\n [key: string]: any;\n};\n\nexport type TAdvancedFilterItem = {\n id: string;\n title: string;\n subTitle?: string;\n selectedValue?: any;\n setSelectedValue?: ( value: any ) => void;\n // Custom component for the filter\n filterComponent?: React.ReactNode;\n icon?: TIconType;\n isMulti?: boolean;\n options?: IOption[];\n render?: React.ReactNode | (() => React.ReactNode );\n onChange?: () => void;\n};\n\nexport type TFilters = Record<string, any>;\n\ninterface ISearchProps {\n placeholder?: string;\n hidden?: boolean;\n onChange?: ( e: React.ChangeEvent<HTMLInputElement> ) => void;\n value?: string;\n /**\n * Props to control the onSearch function debounceTime\n */\n debounceTime?: number;\n /**\n * Attach the debounced search function\n */\n onSearch?: ( value: string ) => void;\n}\n\ninterface IPaginationProps {\n onPageSizeChange?: ( args: number ) => void;\n onPageChange?: ( args: number ) => void;\n totalRecords?: number;\n offset?: number;\n pageSize?: number;\n}\n\nexport interface IDataTableProps<T> {\n /**\n * Defines the column structure for the Data Table, including column headers,\n * accessors, and any additional configurations.\n */\n columns: TIColumn[];\n /**\n * The array of data objects that will populate the rows of the Data Table.\n */\n data: T[];\n /**\n * Optional: Provide custom class names as a space-separated\n * string to override the default Data Table styling.\n */\n className?: string;\n /**\n * Configuration for applying advanced filters to the Data Table.\n * Allows complex filtering logic and customization.\n */\n advancedFilters?: TAdvancedFilterItem[];\n /**\n * Configuration for applying basic filters to the Data Table.\n * Enables simple filtering by key-value pairs.\n * Showing maximum 3 basic filters in the table.\n */\n basicFilters?: TFilterItem[];\n /**\n * Allows to set filters by key-value pairs.\n */\n filters?: TFilters;\n /**\n * A callback function used to update the list of applied filters.\n */\n setFilters?: ( filters: TFilters ) => void;\n /**\n * Boolean value to display a loading state in the Data Table,\n * typically while loading or processing data.\n * @default false\n */\n loading?: boolean;\n /**\n * A callback function triggered to refresh the Data Table,\n * typically re-fetching or updating the data.\n */\n onRefresh?: () => void;\n /**\n * To adjust the width of the columns\n * @default false\n */\n resizable?: boolean;\n /**\n * To adding virtualization for column in the data table\n * @default true\n */\n columnVirtualization?: boolean;\n /**\n * To adding virtualization for row in the data table\n * @default true\n */\n rowVirtualization?: boolean;\n /**\n * To pin the first columns of the data table\n * @default true\n */\n pinnedFirstColumn?: boolean;\n /**\n * To make the row selectable by showing checkboxes\n * @default false\n */\n selectable?: boolean;\n /**\n * Callback function triggered on row selection.\n * @param selectedRows - Index List of the selected rows\n */\n onRowSelection?: ( selectedRows: object ) => void;\n /**\n * Allows to set sort by using key-value pair.\n */\n sorting?: SortingState;\n /**\n * Callback function triggered on row sorting.\n * @param sortState - Index List of the selected rows\n */\n setSorting?: OnChangeFn<SortingState> | undefined;\n /**\n * Allows to set pagination props.\n */\n paginationProps?: IPaginationProps;\n /**\n * To Adjust the height of the table.\n * @default 100%\n */\n height?: string;\n /**\n * To set the page size option\n * @default PAGE_SIZE_OPTIONS\n */\n pageSizeOptions?: number[];\n /**\n * To add the custom CTAs\n */\n customCTAs?: ReactNode;\n /**\n * Callback function triggered on change of Column Preferences.\n * @param columns - columns array with updated order and selected key value\n */\n setColumns: ( columns: TIColumn[]) => void;\n /**\n * Allows to set search textfield props.\n */\n searchProps?: ISearchProps;\n}\n\n/**\n * DataTable is a powerful component that displays tabular data with advanced features\n * like filtering, sorting, pagination, and row selection.\n */\nexport const DataTable = <T extends object>({\n advancedFilters,\n basicFilters,\n className,\n columns,\n data = [],\n filters,\n loading = false,\n paginationProps,\n resizable = false,\n selectable = false,\n /** pin first column on left */\n pinnedFirstColumn = true,\n /** enable row virtualization */\n rowVirtualization = true,\n /** enable column virtualization */\n columnVirtualization = true,\n sorting,\n height = HEIGHT,\n customCTAs,\n searchProps,\n pageSizeOptions = PAGE_SIZE_OPTIONS,\n setFilters,\n setSorting,\n onRefresh,\n onRowSelection,\n setColumns\n}: IDataTableProps<T> ): ReactNode => {\n const columnHelper: ColumnHelper<T> = createColumnHelper<T>();\n const [ configuredColumns, setConfiguredColumns ] = useState(\n deriveConfiguredColumns(\n columns,\n selectable,\n columnHelper\n )\n );\n const [ globalFilter, setGlobalFilter ] = useState<string>( "" );\n const [ rowSelection, setRowSelection ] = useState<RowSelectionState>({});\n const [ selectedFilters, setSelectedFilters ] = useState( filters || {});\n const [ columnPinning, setColumnPinning ] = useState<ColumnPinningState>({\n left: pinnedFirstColumn ? [ "select", ( configuredColumns?.[1] as any )?.accessorKey ?? "" ] : []\n });\n\n const { totalRecords, offset, pageSize, onPageChange, onPageSizeChange } = paginationProps ?? {};\n const [ showRecordsChangeModal, setShowRecordsChangeModal ] = useState( false );\n const tableContainerRef = useRef<HTMLDivElement>( null );\n const [ isRtl, setIsRtl ] = useState( false );\n const [ showSettingsDropdown, setShowSettingsDropdown ] = useState( false );\n\n const totalPages = totalRecords && pageSize && Math.ceil( totalRecords / pageSize );\n\n const table: TReactTable<T> = useReactTable({\n data,\n columns: configuredColumns,\n state: {\n rowSelection,\n globalFilter,\n sorting,\n columnPinning\n },\n globalFilterFn: "includesString",\n columnResizeMode: "onChange",\n columnResizeDirection: "ltr",\n enableRowSelection: selectable,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel() ?? undefined,\n onRowSelectionChange: setRowSelection ?? undefined,\n onGlobalFilterChange: setGlobalFilter ?? undefined,\n onSortingChange: setSorting ?? undefined,\n getSortedRowModel: sorting ? getSortedRowModel() : undefined,\n onColumnPinningChange: setColumnPinning ?? undefined\n });\n\n // For handling basic filter\n const handleBasicFilterChange = ( e: any, id: string ) => {\n setSelectedFilters(( prev: any ) => ({\n ...prev,\n [id]: e\n }));\n basicFilters?.find(( el: any ) => el.id === id )?.onChange?.();\n };\n\n // Clear advanced filters -\n const handleClearFilter = () => {\n const newSelectedFilters = Object.keys( selectedFilters ).reduce(( acc: any, key ) => {\n if ( basicFilters?.some(( filter: any ) => filter.id === key )) {\n acc[key] = selectedFilters[key];\n }\n return acc;\n }, {});\n\n setSelectedFilters( newSelectedFilters );\n advancedFilters?.forEach(( filter ) => {\n if ( filter?.setSelectedValue ) {\n filter?.setSelectedValue( null );\n }\n });\n };\n\n // Remove filter from selected filter list -\n const handleRemoveFilter = ( key: string, index?: number ) => {\n setSelectedFilters(( prevFilters: any ) => {\n const updatedFilters = { ...prevFilters };\n const value = updatedFilters[key];\n\n if ( Array.isArray( value ) && typeof index === "number" ) {\n updatedFilters[key] = value.filter(( _, i ) => i !== index );\n if ( updatedFilters[key].length === 0 ) {\n delete updatedFilters[key];\n }\n } else {\n delete updatedFilters[key];\n }\n return updatedFilters;\n });\n\n advancedFilters?.forEach(( filter ) => {\n if ( filter?.id === key && filter?.setSelectedValue ) {\n if ( Array.isArray( selectedFilters[key]) && typeof index === "number" ) {\n const arr = [...( selectedFilters[key] as any[])]; arr.splice( index, 1 );\n filter.setSelectedValue( arr.length ? arr : null );\n } else {\n filter.setSelectedValue( null );\n }\n }\n });\n };\n\n const handleClearAllSelectedFilters = () => {\n setSelectedFilters({});\n advancedFilters?.forEach(( filter ) => {\n if ( filter?.setSelectedValue ) {\n filter?.setSelectedValue( null );\n }\n });\n };\n\n const toggleRecordsModal = () => {\n setShowRecordsChangeModal( !showRecordsChangeModal );\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const onSearchInput = useCallback( debounce(( value ) => {\n searchProps?.onSearch?.( value );\n }, searchProps?.debounceTime ?? DEBOUNCE_TIME ), []);\n\n const onSearchTextChange = ( e: React.ChangeEvent<HTMLInputElement> ) => {\n if ( searchProps?.onChange && typeof searchProps?.onChange === "function" ) {\n searchProps?.onChange( e );\n }\n\n if ( typeof searchProps?.onSearch === "undefined" ) {\n setGlobalFilter( e.target.value );\n } else {\n onSearchInput( e.target.value );\n }\n };\n\n // Filter Bar of the Data Table\n const FilterBar = () => {\n const { value = "", placeholder = "Search in this page", hidden } = searchProps || {};\n return (\n <div className={cn( dataTableFiltersVariants())}>\n {!hidden && (\n <TextField\n placeholder={placeholder}\n prefixIcon={iconList.search}\n className={cn( dataTableFilterSearchVariants())}\n onChange={onSearchTextChange}\n value={value ? value : globalFilter}\n suffixIcon={\n globalFilter || value ? (\n <ADPIcon\n className="cursor-pointer"\n icon={iconList.cross}\n onClick={() => {\n if ( searchProps?.onSearch ) {\n searchProps.onSearch( "" );\n } else {\n setGlobalFilter( "" );\n }\n }}\n />\n ) : undefined\n }\n />\n )}\n {basicFilters?.slice( 0, 3 )?.map(( props: TFilterItem ) => {\n return (\n <Select\n {...props}\n options={props?.options}\n data-testid={`table-filter-${props.id}`}\n key={props.id}\n className={cn( dataTableFilterSelectVariants())}\n onChange={( e ) => handleBasicFilterChange( e, props.id ?? "" )}\n value={selectedFilters[props.id ?? ""] || null}\n prefixIcon={props.icon ? <ADPIcon className="text-gray-800" icon={props.icon} /> : undefined}\n placeholder={\n <div className="flex gap-2 items-center">\n <span className="text-secondary-500 dark:text-secondary-400 font-medium text-xs">\n {props?.placeholder || props?.title}\n </span>\n </div>\n }\n />\n );\n })}\n {advancedFilters && advancedFilters.length > 0 && (\n <AdvancedFilters\n advancedFilters={advancedFilters}\n basicFilters={basicFilters}\n selectedFilters={selectedFilters}\n setSelectedFilters={setSelectedFilters}\n handleClearFilter={handleClearFilter}\n />\n )}\n <div className="flex items-center gap-4 ms-auto">\n {typeof onRefresh === "function" && (\n <Button\n prefixIcon={iconList.reload}\n size="xs"\n variant="outline"\n className="border-primary-500"\n onClick={onRefresh}\n />\n )}\n {columns && (\n <div className="relative">\n <Button\n prefixIcon={iconList.settings}\n size="xs"\n variant="outline"\n className="border-primary-500"\n onClick={() => setShowSettingsDropdown( !showSettingsDropdown )}\n />\n <DataTableColumnSettings\n columns={columns}\n setColumns={setColumns}\n isOpen={showSettingsDropdown}\n onClose={() => setShowSettingsDropdown( false )}\n />\n </div>\n )}\n </div>\n {customCTAs}\n </div>\n );\n };\n\n useEffect(() => {\n setConfiguredColumns( deriveConfiguredColumns( columns, selectable, columnHelper ));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [columns]);\n\n // Return the selected rows\n useEffect(() => {\n if ( rowSelection ) {\n onRowSelection?.( rowSelection );\n }\n }, [ onRowSelection, rowSelection ]);\n\n useEffect(() => {\n setFilters?.( selectedFilters );\n }, [ selectedFilters, setFilters ]);\n\n useEffect(() => {\n setColumnPinning({\n left: pinnedFirstColumn ? [ "select", ( configuredColumns?.[selectable ? 1 : 0] as any )?.accessorKey ?? "" ] : []\n });\n }, [ configuredColumns, selectable, pinnedFirstColumn ]);\n\n useEffect(() => {\n const tableContainer = tableContainerRef.current;\n if ( tableContainer ) {\n const dir = findParentAttribute( tableContainer, "dir" );\n setIsRtl( dir === "rtl" );\n }\n }, [tableContainerRef]);\n\n return (\n <div\n data-testid="table"\n className={cn( dataTableContainerVariants(), className )}\n ref={tableContainerRef}\n >\n {FilterBar()}\n <SelectedFilterList\n advancedFilters={advancedFilters ?? []}\n basicFilters={basicFilters ?? []}\n selectedFilters={selectedFilters}\n handleRemoveFilter={handleRemoveFilter}\n handleClearAllSelectedFilters={handleClearAllSelectedFilters}\n />\n <div className={cn( dataTableRecordsInfoVariants())}>\n {totalRecords && offset && pageSize && !loading ? (\n <>\n <span className="text-secondary-400 dark:text-secondary-200">\n Showing {offset} - {Math.min( offset - 1 + pageSize, totalRecords )} of {totalRecords} results.{" "}\n </span>\n <Button size="sm" variant="text" onClick={toggleRecordsModal} className="inline">\n See more\n </Button>\n </>\n ) : <></>}\n </div>\n <div\n className={cn( dataTableContainerInnerVariants())}\n style={{ height }}\n >\n <Table<T>\n table={table}\n columnVirtualization={columnVirtualization}\n rowVirtualization={rowVirtualization}\n selectable={selectable}\n loading={loading}\n resizable={resizable}\n height={height ?? HEIGHT}\n isRtl={isRtl}\n />\n </div>\n {Boolean( totalPages ) && <div className={cn( dataTablePaginationVariants())}>\n <Pagination\n layout="detailed"\n variant="outline"\n totalPages={totalPages}\n onPageChange={onPageChange}\n activePageNo={offset && Math.ceil( offset / ( pageSize ?? 25 ))}\n />\n </div>\n }\n\n {/* Page Size Modal */}\n <PageSizeModal\n show={showRecordsChangeModal}\n toggleVisibility={toggleRecordsModal}\n pageSize={pageSize || 25}\n pageSizeOptions={pageSizeOptions}\n onPageSizeChange={onPageSizeChange}\n onPageChange={onPageChange}\n />\n </div>\n );\n};'
1922
+ "content": 'import React, { type ReactNode, useCallback, useEffect, useRef, useState } from "react";\nimport {\n CellContext,\n createColumnHelper,\n getCoreRowModel,\n getFilteredRowModel,\n getSortedRowModel,\n OnChangeFn,\n RowSelectionState,\n useReactTable,\n type SortingState,\n type ColumnHelper,\n type ColumnPinningState,\n type Table as TReactTable,\n type SortingFnOption\n} from "@tanstack/react-table";\n\nimport { Button } from "../button/button";\nimport { TextField } from "../textField/textField";\nimport { Select, type IOption } from "../select/select";\nimport { ADPIcon, iconList } from "../adpIcon/adpIcon";\nimport { type TIconType } from "../adpIcon";\nimport { Pagination } from "../pagination/pagination";\nimport { Checkbox } from "../checkbox/checkbox";\nimport { cn, debounce, findParentAttribute } from "@utils";\nimport {\n dataTableContainerVariants,\n dataTableFiltersVariants,\n dataTableFilterSearchVariants,\n dataTableFilterSelectVariants,\n dataTableRecordsInfoVariants,\n dataTableContainerInnerVariants,\n dataTablePaginationVariants\n} from "./dataTableVariants";\nimport { PageSizeModal } from "./pageSizeModal";\nimport { DataTableColumnSettings } from "./DataTableColumnSettings";\nimport { AdvancedFilters } from "./advancedFilters";\nimport SelectedFilterList from "./selectedFilterList";\nimport { Table } from "./table";\n\n// Utility function to derive configured columns\nconst deriveConfiguredColumns = (\n rawColumns: TIColumn[],\n selectable: boolean,\n columnHelper: ColumnHelper<any>\n) => {\n const derivedColumns = [\n ...( selectable\n ? [\n columnHelper.accessor( "select", {\n id: "select",\n header: ({ table }) => (\n <Checkbox\n checked={table.getIsAllRowsSelected()}\n // Use data-* attribute for indeterminate state\n data-indeterminate={table.getIsSomeRowsSelected().toString()}\n onChange={table.getToggleAllPageRowsSelectedHandler()}\n />\n ),\n cell: ({ row }) => (\n <Checkbox\n checked={row.getIsSelected()}\n disabled={!row.getCanSelect()}\n // Use data-* attribute for indeterminate state\n data-indeterminate={row.getIsSomeSelected().toString()}\n onChange={row.getToggleSelectedHandler()}\n />\n ),\n enableResizing: false,\n enableSorting: false,\n size: 56\n })\n ]\n : []),\n ...( rawColumns\n ?.sort(( a: any, b: any ) => Number( b?.fixed ?? false ) - Number( a?.fixed ?? false ))\n ?.filter( el => el?.selected === undefined || el?.selected !== false )?.map(( column: TIColumn ) => {\n const { cell, key, header, enableResizing, enableSorting, sortingFn, size } = column;\n return columnHelper.accessor( key, {\n header: header,\n cell: ( info: CellContext<any, any> ) => {\n return <>{typeof cell === "function" ? cell( info ) : info.getValue()}</>;\n },\n enableResizing: enableResizing ?? true,\n enableSorting: enableSorting ?? true,\n ...( sortingFn ? { sortingFn } : {}),\n size: size ? size : 400\n });\n }) ?? [])\n ];\n\n return derivedColumns;\n};\n\nconst DEBOUNCE_TIME = 500;\nconst HEIGHT = "500px";\n// Default page size options (maintained for backward compatibility but currently unused)\n\nconst PAGE_SIZE_OPTIONS = [ 25, 50, 100, 200, 500, 1000 ];\n\nexport type TIColumn = {\n id?: string;\n key: string;\n header?: any;\n cell?: ( row: CellContext<any, any> ) => ReactNode;\n /**\n * A fixed column -\n * * Cannot be re-ordered or hidden using the Column Preference panel\n * * The column will always be placed on the left side of the table irrespective of the index of the column configuration in IColumn[]\n *\n * @default false\n */\n fixed?: boolean;\n /**\n * Handle the visibility of the column\n * @default false\n */\n selected?: boolean;\n /**\n * To make the column sortable\n * @default true\n */\n enableSorting?: boolean;\n /**\n * To make the column resizable\n * @default true\n */\n enableResizing?: boolean;\n sortingFn?: SortingFnOption<any>;\n /**\n * To adjust the width of the column\n */\n size?: number;\n /**\n * Hides the column from the Column Preference panel\n * @default false\n */\n hideColPreference?: boolean;\n};\n\nexport type TFilterItem = {\n id?: string;\n selectedValue?: any;\n icon?: TIconType;\n title: string;\n placeholder?: string;\n onChange?: () => void;\n options: IOption[];\n [key: string]: any;\n};\n\nexport type TAdvancedFilterItem = {\n id: string;\n title: string;\n subTitle?: string;\n selectedValue?: any;\n setSelectedValue?: ( value: any ) => void;\n // Custom component for the filter\n filterComponent?: React.ReactNode;\n icon?: TIconType;\n isMulti?: boolean;\n options?: IOption[];\n render?: React.ReactNode | (() => React.ReactNode );\n onChange?: () => void;\n};\n\nexport type TFilters = Record<string, any>;\n\ninterface ISearchProps {\n placeholder?: string;\n hidden?: boolean;\n onChange?: ( e: React.ChangeEvent<HTMLInputElement> ) => void;\n value?: string;\n /**\n * Props to control the onSearch function debounceTime\n */\n debounceTime?: number;\n /**\n * Attach the debounced search function\n */\n onSearch?: ( value: string ) => void;\n}\n\ninterface IPaginationProps {\n onPageSizeChange?: ( args: number ) => void;\n onPageChange?: ( args: number ) => void;\n totalRecords?: number;\n offset?: number;\n pageSize?: number;\n}\n\nexport interface IDataTableProps<T> {\n /**\n * Defines the column structure for the Data Table, including column headers,\n * accessors, and any additional configurations.\n */\n columns: TIColumn[];\n /**\n * The array of data objects that will populate the rows of the Data Table.\n */\n data: T[];\n /**\n * Optional: Provide custom class names as a space-separated\n * string to override the default Data Table styling.\n */\n className?: string;\n /**\n * Configuration for applying advanced filters to the Data Table.\n * Allows complex filtering logic and customization.\n */\n advancedFilters?: TAdvancedFilterItem[];\n /**\n * Configuration for applying basic filters to the Data Table.\n * Enables simple filtering by key-value pairs.\n * Showing maximum 3 basic filters in the table.\n */\n basicFilters?: TFilterItem[];\n /**\n * Allows to set filters by key-value pairs.\n */\n filters?: TFilters;\n /**\n * A callback function used to update the list of applied filters.\n */\n setFilters?: ( filters: TFilters ) => void;\n /**\n * Boolean value to display a loading state in the Data Table,\n * typically while loading or processing data.\n * @default false\n */\n loading?: boolean;\n /**\n * A callback function triggered to refresh the Data Table,\n * typically re-fetching or updating the data.\n */\n onRefresh?: () => void;\n /**\n * To adjust the width of the columns\n * @default false\n */\n resizable?: boolean;\n /**\n * To adding virtualization for column in the data table\n * @default true\n */\n columnVirtualization?: boolean;\n /**\n * To adding virtualization for row in the data table\n * @default true\n */\n rowVirtualization?: boolean;\n /**\n * To pin the first columns of the data table\n * @default true\n */\n pinnedFirstColumn?: boolean;\n /**\n * To make the row selectable by showing checkboxes\n * @default false\n */\n selectable?: boolean;\n /**\n * Callback function triggered on row selection.\n * @param selectedRows - Index List of the selected rows\n */\n onRowSelection?: ( selectedRows: object ) => void;\n /**\n * Allows to set sort by using key-value pair.\n */\n sorting?: SortingState;\n /**\n * Callback function triggered on row sorting.\n * @param sortState - Index List of the selected rows\n */\n setSorting?: OnChangeFn<SortingState> | undefined;\n /**\n * Allows to set pagination props.\n */\n paginationProps?: IPaginationProps;\n /**\n * To Adjust the height of the table.\n * @default 100%\n */\n height?: string;\n /**\n * To set the page size option\n * @default PAGE_SIZE_OPTIONS\n */\n pageSizeOptions?: number[];\n /**\n * To add the custom CTAs\n */\n customCTAs?: ReactNode;\n /**\n * Callback function triggered on change of Column Preferences.\n * @param columns - columns array with updated order and selected key value\n */\n setColumns: ( columns: TIColumn[]) => void;\n /**\n * Allows to set search textfield props.\n */\n searchProps?: ISearchProps;\n}\n\n/**\n * DataTable is a powerful component that displays tabular data with advanced features\n * like filtering, sorting, pagination, and row selection.\n */\nexport const DataTable = <T extends object>({\n advancedFilters,\n basicFilters,\n className,\n columns,\n data = [],\n filters,\n loading = false,\n paginationProps,\n resizable = false,\n selectable = false,\n /** pin first column on left */\n pinnedFirstColumn = true,\n /** enable row virtualization */\n rowVirtualization = true,\n /** enable column virtualization */\n columnVirtualization = true,\n sorting,\n height = HEIGHT,\n customCTAs,\n searchProps,\n pageSizeOptions = PAGE_SIZE_OPTIONS,\n setFilters,\n setSorting,\n onRefresh,\n onRowSelection,\n setColumns\n}: IDataTableProps<T> ): ReactNode => {\n const columnHelper: ColumnHelper<T> = createColumnHelper<T>();\n const [ configuredColumns, setConfiguredColumns ] = useState(\n deriveConfiguredColumns(\n columns,\n selectable,\n columnHelper\n )\n );\n const [ globalFilter, setGlobalFilter ] = useState<string>( "" );\n const [ rowSelection, setRowSelection ] = useState<RowSelectionState>({});\n const [ selectedFilters, setSelectedFilters ] = useState( filters || {});\n const [ columnPinning, setColumnPinning ] = useState<ColumnPinningState>({\n left: pinnedFirstColumn ? [ "select", ( configuredColumns?.[1] as any )?.accessorKey ?? "" ] : []\n });\n\n const { totalRecords, offset, pageSize, onPageChange, onPageSizeChange } = paginationProps ?? {};\n const [ showRecordsChangeModal, setShowRecordsChangeModal ] = useState( false );\n const tableContainerRef = useRef<HTMLDivElement>( null );\n const [ isRtl, setIsRtl ] = useState( false );\n const [ showSettingsDropdown, setShowSettingsDropdown ] = useState( false );\n\n const totalPages = totalRecords && pageSize && Math.ceil( totalRecords / pageSize );\n\n const table: TReactTable<T> = useReactTable({\n data,\n columns: configuredColumns,\n state: {\n rowSelection,\n globalFilter,\n sorting,\n columnPinning\n },\n globalFilterFn: "includesString",\n columnResizeMode: "onChange",\n columnResizeDirection: "ltr",\n enableRowSelection: selectable,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel() ?? undefined,\n onRowSelectionChange: setRowSelection ?? undefined,\n onGlobalFilterChange: setGlobalFilter ?? undefined,\n onSortingChange: setSorting ?? undefined,\n getSortedRowModel: sorting ? getSortedRowModel() : undefined,\n onColumnPinningChange: setColumnPinning ?? undefined\n });\n\n // For handling basic filter\n const handleBasicFilterChange = ( e: any, id: string ) => {\n setSelectedFilters(( prev: any ) => ({\n ...prev,\n [id]: e\n }));\n basicFilters?.find(( el: any ) => el.id === id )?.onChange?.();\n };\n\n // Clear advanced filters -\n const handleClearFilter = () => {\n const newSelectedFilters = Object.keys( selectedFilters ).reduce(( acc: any, key ) => {\n if ( basicFilters?.some(( filter: any ) => filter.id === key )) {\n acc[key] = selectedFilters[key];\n }\n return acc;\n }, {});\n\n setSelectedFilters( newSelectedFilters );\n advancedFilters?.forEach(( filter ) => {\n if ( filter?.setSelectedValue ) {\n filter?.setSelectedValue( null );\n }\n });\n };\n\n // Remove filter from selected filter list -\n const handleRemoveFilter = ( key: string, index?: number ) => {\n setSelectedFilters(( prevFilters: any ) => {\n const updatedFilters = { ...prevFilters };\n const value = updatedFilters[key];\n\n if ( Array.isArray( value ) && typeof index === "number" ) {\n updatedFilters[key] = value.filter(( _, i ) => i !== index );\n if ( updatedFilters[key].length === 0 ) {\n delete updatedFilters[key];\n }\n } else {\n delete updatedFilters[key];\n }\n return updatedFilters;\n });\n\n advancedFilters?.forEach(( filter ) => {\n if ( filter?.id === key && filter?.setSelectedValue ) {\n if ( Array.isArray( selectedFilters[key]) && typeof index === "number" ) {\n const arr = [...( selectedFilters[key] as any[])]; arr.splice( index, 1 );\n filter.setSelectedValue( arr.length ? arr : null );\n } else {\n filter.setSelectedValue( null );\n }\n }\n });\n };\n\n const handleClearAllSelectedFilters = () => {\n setSelectedFilters({});\n advancedFilters?.forEach(( filter ) => {\n if ( filter?.setSelectedValue ) {\n filter?.setSelectedValue( null );\n }\n });\n };\n\n const toggleRecordsModal = () => {\n setShowRecordsChangeModal( !showRecordsChangeModal );\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const onSearchInput = useCallback( debounce(( value ) => {\n searchProps?.onSearch?.( value );\n }, searchProps?.debounceTime ?? DEBOUNCE_TIME ), []);\n\n const onSearchTextChange = ( e: React.ChangeEvent<HTMLInputElement> ) => {\n if ( searchProps?.onChange && typeof searchProps?.onChange === "function" ) {\n searchProps?.onChange( e );\n }\n\n if ( typeof searchProps?.onSearch === "undefined" ) {\n setGlobalFilter( e.target.value );\n } else {\n onSearchInput( e.target.value );\n }\n };\n\n // Filter Bar of the Data Table\n const FilterBar = () => {\n const { value = "", placeholder = "Search in this page", hidden } = searchProps || {};\n return (\n <div className={cn( dataTableFiltersVariants())}>\n {!hidden && (\n <TextField\n placeholder={placeholder}\n prefixIcon={iconList.search}\n className={cn( dataTableFilterSearchVariants())}\n onChange={onSearchTextChange}\n value={value ? value : globalFilter}\n suffixIcon={\n globalFilter || value ? (\n <ADPIcon\n className="cursor-pointer"\n icon={iconList.cross}\n onClick={() => {\n if ( searchProps?.onSearch ) {\n searchProps.onSearch( "" );\n } else {\n setGlobalFilter( "" );\n }\n }}\n />\n ) : undefined\n }\n />\n )}\n {basicFilters?.slice( 0, 3 )?.map(( props: TFilterItem ) => {\n return (\n <Select\n {...props}\n options={props?.options}\n data-testid={`table-filter-${props.id}`}\n key={props.id}\n className={cn( dataTableFilterSelectVariants())}\n onChange={( e ) => handleBasicFilterChange( e, props.id ?? "" )}\n value={selectedFilters[props.id ?? ""] || null}\n prefixIcon={props.icon ? <ADPIcon className="text-gray-800" icon={props.icon} /> : undefined}\n placeholder={\n <div className="flex gap-2 items-center">\n <span className="text-secondary-500 dark:text-secondary-400 font-medium text-xs">\n {props?.placeholder || props?.title}\n </span>\n </div>\n }\n />\n );\n })}\n {advancedFilters && advancedFilters.length > 0 && (\n <AdvancedFilters\n advancedFilters={advancedFilters}\n basicFilters={basicFilters}\n selectedFilters={selectedFilters}\n setSelectedFilters={setSelectedFilters}\n handleClearFilter={handleClearFilter}\n />\n )}\n <div className="flex items-center gap-4 ms-auto">\n {typeof onRefresh === "function" && (\n <Button\n prefixIcon={iconList.reload}\n size="xs"\n variant="outline"\n className="border-primary-500"\n onClick={onRefresh}\n />\n )}\n {columns && (\n <div className="relative">\n <Button\n prefixIcon={iconList.settings}\n size="xs"\n variant="outline"\n className="border-primary-500"\n onClick={() => setShowSettingsDropdown( !showSettingsDropdown )}\n />\n <DataTableColumnSettings\n columns={columns}\n setColumns={setColumns}\n isOpen={showSettingsDropdown}\n onClose={() => setShowSettingsDropdown( false )}\n />\n </div>\n )}\n </div>\n {customCTAs}\n </div>\n );\n };\n\n useEffect(() => {\n setConfiguredColumns( deriveConfiguredColumns( columns, selectable, columnHelper ));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [columns]);\n\n // Return the selected rows\n useEffect(() => {\n if ( rowSelection ) {\n onRowSelection?.( rowSelection );\n }\n }, [ onRowSelection, rowSelection ]);\n\n useEffect(() => {\n setFilters?.( selectedFilters );\n }, [ selectedFilters, setFilters ]);\n\n useEffect(() => {\n setColumnPinning({\n left: pinnedFirstColumn ? [ "select", ( configuredColumns?.[selectable ? 1 : 0] as any )?.accessorKey ?? "" ] : []\n });\n }, [ configuredColumns, selectable, pinnedFirstColumn ]);\n\n useEffect(() => {\n const tableContainer = tableContainerRef.current;\n if ( tableContainer ) {\n const dir = findParentAttribute( tableContainer, "dir" );\n setIsRtl( dir === "rtl" );\n }\n }, [tableContainerRef]);\n\n return (\n <div\n data-testid="table"\n className={cn( dataTableContainerVariants(), className )}\n ref={tableContainerRef}\n >\n {FilterBar()}\n <SelectedFilterList\n advancedFilters={advancedFilters ?? []}\n basicFilters={basicFilters ?? []}\n selectedFilters={selectedFilters}\n handleRemoveFilter={handleRemoveFilter}\n handleClearAllSelectedFilters={handleClearAllSelectedFilters}\n />\n <div className={cn( dataTableRecordsInfoVariants())}>\n {totalRecords && offset && pageSize && !loading ? (\n <>\n <span className="text-secondary-400 dark:text-secondary-200">\n Showing {offset} - {Math.min( offset - 1 + pageSize, totalRecords )} of {totalRecords} results.{" "}\n </span>\n <Button size="sm" variant="text" onClick={toggleRecordsModal} className="inline">\n See more\n </Button>\n </>\n ) : <></>}\n </div>\n <div\n className={cn( dataTableContainerInnerVariants())}\n style={{ height }}\n >\n <Table<T>\n table={table}\n columnVirtualization={columnVirtualization}\n rowVirtualization={rowVirtualization}\n selectable={selectable}\n loading={loading}\n resizable={resizable}\n height={height ?? HEIGHT}\n isRtl={isRtl}\n />\n </div>\n {Boolean( totalPages ) && <div className={cn( dataTablePaginationVariants())}>\n <Pagination\n layout="detailed"\n variant="outline"\n totalPages={totalPages}\n onPageChange={onPageChange}\n activePageNo={offset && Math.ceil( offset / ( pageSize ?? 25 ))}\n />\n </div>\n }\n\n {/* Page Size Modal */}\n <PageSizeModal\n show={showRecordsChangeModal}\n toggleVisibility={toggleRecordsModal}\n pageSize={pageSize || 25}\n pageSizeOptions={pageSizeOptions}\n onPageSizeChange={onPageSizeChange}\n onPageChange={onPageChange}\n />\n </div>\n );\n};'
1923
1923
  },
1924
1924
  {
1925
1925
  "name": "advancedFilters.tsx",
@@ -1936,7 +1936,7 @@ export const checkmarkClipPath = \`polygon(
1936
1936
  ]
1937
1937
  },
1938
1938
  "datepicker": {
1939
- "name": "datepicker",
1939
+ "name": "datePicker",
1940
1940
  "description": "## Overview",
1941
1941
  "dependencies": [
1942
1942
  "class-variance-authority",
@@ -1944,7 +1944,7 @@ export const checkmarkClipPath = \`polygon(
1944
1944
  "react-datepicker"
1945
1945
  ],
1946
1946
  "internalDependencies": [
1947
- "textfield"
1947
+ "textField"
1948
1948
  ],
1949
1949
  "files": [
1950
1950
  {
@@ -2120,7 +2120,7 @@ DropdownItem.displayName = "DropdownItem";`
2120
2120
  ]
2121
2121
  },
2122
2122
  "emptystate": {
2123
- "name": "emptystate",
2123
+ "name": "emptyState",
2124
2124
  "description": "## Overview",
2125
2125
  "dependencies": [
2126
2126
  "class-variance-authority",
@@ -2157,14 +2157,14 @@ DropdownItem.displayName = "DropdownItem";`
2157
2157
  ]
2158
2158
  },
2159
2159
  "fileupload": {
2160
- "name": "fileupload",
2160
+ "name": "fileUpload",
2161
2161
  "description": "## Overview",
2162
2162
  "dependencies": [
2163
2163
  "react",
2164
2164
  "class-variance-authority"
2165
2165
  ],
2166
2166
  "internalDependencies": [
2167
- "adpicon",
2167
+ "adpIcon",
2168
2168
  "progress",
2169
2169
  "tooltip",
2170
2170
  "button"
@@ -2273,7 +2273,7 @@ DropdownItem.displayName = "DropdownItem";`
2273
2273
  },
2274
2274
  {
2275
2275
  "name": "otp.tsx",
2276
- "content": 'import * as React from "react";\nimport { useState, useCallback, useEffect, ReactNode } from "react";\nimport { cn } from "@utils";\nimport { Input } from "./input";\nimport { keyboardKeys } from "@constants";\nimport { otpTextVariants } from "./otpVariants";\n\n/**\n * OTP component for one-time password input with multiple fields\n */\nexport interface IOtpProps extends Omit<React.HTMLAttributes<HTMLFieldSetElement>, "onFocus" | "onBlur" | "onChange"> {\n /**\n * Value of the OTP input.\n */\n initialValue?: string;\n /**\n * Pass a regex to restrict what can be entered in the OTP input.\n */\n regexValidation?: string;\n /**\n * Callback triggered when the OTP input value changes.\n */\n onChange?: ( val: string ) => void;\n /**\n * Callback triggered when the OTP input loses focus.\n */\n onBlur?: React.FocusEventHandler<HTMLDivElement>;\n /**\n * Callback triggered when the OTP input gains focus.\n */\n onFocus?: React.FocusEventHandler<HTMLDivElement>;\n /**\n * Regular static label.\n */\n label?: ReactNode;\n /**\n * Pass OTP length.\n * @default 6\n */\n otpLength?: number;\n /**\n * Overwrite the style by passing space separated class names.\n */\n className?: string;\n /**\n * Additional information or guidance displayed below the OTP input.\n */\n helperText?: ReactNode;\n /**\n * Pass a boolean value to indicate if the OTP input is in an error state.\n * @default false\n */\n error?: boolean;\n /**\n * Whether the OTP input is disabled.\n * @default false\n */\n disabled?: boolean;\n}\n\n/**\n * OTP input component for one-time password entry\n */\nexport const OtpInput = React.forwardRef<HTMLFieldSetElement, IOtpProps>(\n ({\n initialValue,\n regexValidation,\n onChange,\n onBlur,\n onFocus,\n label,\n helperText,\n error = false,\n otpLength = 6,\n className,\n disabled = false,\n ...props\n }: IOtpProps, ref ): React.ReactElement | null => {\n const validOtpLength = Math.abs( otpLength );\n const [ otpValue, setOtpValue ] = useState<Array<string>>(\n Array( validOtpLength ).fill( "" )\n );\n const [ focusedInput, setFocusedInput ] = useState(\n initialValue ? initialValue.length - 1 : -1\n );\n const [ isFocused, setIsFocused ] = useState( false );\n\n // This useEffect is executed if the user has provided an initial value\n useEffect(() => {\n if ( initialValue ) {\n const temp = [];\n for ( let i = 0; i < validOtpLength; i++ ) {\n temp[i] = initialValue?.[i] || "";\n }\n setOtpValue( temp );\n }\n }, [ validOtpLength, initialValue ]);\n\n // isInputValid is used to check whether the input provided is valid or not\n const isInputValid = useCallback(\n ( val: string ) => {\n const regularExpression = new RegExp( regexValidation ?? "" );\n return regexValidation ? regularExpression.test( val ) : /[0-9]/.test( val );\n },\n [regexValidation]\n );\n\n const onInputChange =\n ( idx: number ) => ( event: React.ChangeEvent<HTMLInputElement> ) => {\n const values = [...otpValue];\n const { value } = event.target;\n values[idx] = value;\n if ( isInputValid( value ) || value === "" ) {\n setOtpValue([...values]);\n onChange?.( values.join( "" ));\n\n if ( idx < validOtpLength - 1 && value !== "" ) {\n setFocusedInput(( index ) => index + 1 );\n } else if ( idx !== 0 && value === "" ) {\n setFocusedInput(( index ) => index - 1 );\n }\n }\n };\n\n // handleKeyDown is being used to react to the keyboard inputs that the user provides while the component is focused\n const handleKeyDown = useCallback(\n ( event: React.KeyboardEvent<HTMLInputElement> ) => {\n let currentFocused = focusedInput;\n const { key } = event;\n if ( key === keyboardKeys.tab ) {\n return;\n }\n switch ( key ) {\n case keyboardKeys.arrowLeft:\n if ( currentFocused !== 0 ) {\n currentFocused--;\n }\n break;\n case keyboardKeys.backspace:\n if ( otpValue[currentFocused] === "" && currentFocused !== 0 ) {\n currentFocused--;\n }\n break;\n case keyboardKeys.arrowRight:\n if ( currentFocused < validOtpLength - 1 ) {\n currentFocused++;\n }\n break;\n default:\n if (\n otpValue[currentFocused] === key &&\n currentFocused < validOtpLength - 1\n ) {\n currentFocused++;\n }\n }\n setFocusedInput( currentFocused );\n },\n [ focusedInput, otpValue, validOtpLength ]\n );\n\n //handlePaste is used to enter text into the component that has been copied by the user\n const handlePaste = useCallback(\n ( event: React.ClipboardEvent<HTMLDivElement> ) => {\n const pastedValue = event.clipboardData\n .getData( "text" )\n ?.substring( 0, validOtpLength );\n const otp = [];\n\n for ( let i = 0; i < validOtpLength; i++ ) {\n otp.push( isInputValid( pastedValue[i]) ? pastedValue[i] : "" );\n }\n\n setOtpValue( otp );\n onChange?.( otp.join( "" ));\n setFocusedInput(\n pastedValue.length <= validOtpLength - 1\n ? pastedValue.length\n : validOtpLength - 1\n );\n },\n [ isInputValid, onChange, validOtpLength ]\n );\n\n const onFocusCallback = useCallback(\n ( event: React.FocusEvent<HTMLDivElement> ) => {\n if ( !isFocused ) {\n setIsFocused( true );\n onFocus?.( event );\n setFocusedInput( Number( event.target.id ));\n }\n },\n [ isFocused, onFocus ]\n );\n\n const onBlurCallback = useCallback(\n ( event: React.FocusEvent<HTMLDivElement> ) => {\n if ( isFocused ) {\n setIsFocused( false );\n onBlur?.( event );\n setFocusedInput( -1 );\n }\n },\n [ isFocused, onBlur ]\n );\n\n const handleEvents = useCallback(\n ( event: React.FocusEvent<HTMLDivElement> ) =>\n event.type === "focus" ? onFocusCallback( event ) : onBlurCallback( event ),\n [ onBlurCallback, onFocusCallback ]\n );\n\n /* Debounce has been used because of the shifting focus of inputs in the otpInput component and we want the whole component\n to act like one focusable component and therefore we have debounced the whole onFocus and onBlur events in order to avoid\n the continuous firing of these events while the focused inputs inside are changing */\n const debounce = <Params extends any[]>(\n func: ( ...args: Params ) => any,\n timeout: number\n ): (( ...args: Params ) => void ) => {\n let timer: NodeJS.Timeout;\n return ( ...args: Params ) => {\n if ( timer ) {\n clearTimeout( timer );\n }\n timer = setTimeout(() => {\n func( ...args );\n }, timeout );\n };\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const handleFocusAndBlur = useCallback(\n debounce(( event ) => handleEvents( event ), 50 ),\n [handleEvents]\n );\n\n return validOtpLength\n ? (\n <fieldset\n className={cn( "inline-flex flex-col gap-2 m-0 p-0 border-0 min-w-0", className )}\n disabled={disabled}\n ref={ref}\n {...props}\n >\n {label && (\n <legend className={cn( "font-medium text-base text-secondary-500 dark:text-secondary-300 mb-2 p-0" )}>\n {label}\n </legend>\n )}\n <div\n className={cn( "inline-flex gap-2" )}\n onPaste={handlePaste}\n onBlur={handleFocusAndBlur}\n onFocus={handleFocusAndBlur}\n >\n {\n Array( validOtpLength )\n .fill( "" )\n .map(( _, index ) => (\n <Input\n key={index}\n index={index}\n handleChange={onInputChange( index )}\n focused={focusedInput === index}\n handleKeyDown={handleKeyDown}\n value={otpValue?.[index]}\n id={`${index}`}\n error={error}\n />\n ))}\n </div>\n {helperText && (\n <div\n className={cn( otpTextVariants({ error }))}\n >\n {helperText}\n </div>\n )}\n </fieldset>\n )\n : null;\n }\n);\n\nOtpInput.displayName = "OtpInput";'
2276
+ "content": 'import * as React from "react";\nimport { useState, useCallback, useEffect, ReactNode } from "react";\nimport { cn, debounce } from "@utils";\nimport { Input } from "./input";\nimport { keyboardKeys } from "@constants";\nimport { otpTextVariants } from "./otpVariants";\n\n/**\n * OTP component for one-time password input with multiple fields\n */\nexport interface IOtpProps extends Omit<React.HTMLAttributes<HTMLFieldSetElement>, "onFocus" | "onBlur" | "onChange"> {\n /**\n * Value of the OTP input.\n */\n initialValue?: string;\n /**\n * Pass a regex to restrict what can be entered in the OTP input.\n */\n regexValidation?: string;\n /**\n * Callback triggered when the OTP input value changes.\n */\n onChange?: ( val: string ) => void;\n /**\n * Callback triggered when the OTP input loses focus.\n */\n onBlur?: React.FocusEventHandler<HTMLDivElement>;\n /**\n * Callback triggered when the OTP input gains focus.\n */\n onFocus?: React.FocusEventHandler<HTMLDivElement>;\n /**\n * Regular static label.\n */\n label?: ReactNode;\n /**\n * Pass OTP length.\n * @default 6\n */\n otpLength?: number;\n /**\n * Overwrite the style by passing space separated class names.\n */\n className?: string;\n /**\n * Additional information or guidance displayed below the OTP input.\n */\n helperText?: ReactNode;\n /**\n * Pass a boolean value to indicate if the OTP input is in an error state.\n * @default false\n */\n error?: boolean;\n /**\n * Whether the OTP input is disabled.\n * @default false\n */\n disabled?: boolean;\n}\n\n/**\n * OTP input component for one-time password entry\n */\nexport const OtpInput = React.forwardRef<HTMLFieldSetElement, IOtpProps>(\n ({\n initialValue,\n regexValidation,\n onChange,\n onBlur,\n onFocus,\n label,\n helperText,\n error = false,\n otpLength = 6,\n className,\n disabled = false,\n ...props\n }: IOtpProps, ref ): React.ReactElement | null => {\n const validOtpLength = Math.abs( otpLength );\n const [ otpValue, setOtpValue ] = useState<Array<string>>(\n Array( validOtpLength ).fill( "" )\n );\n const [ focusedInput, setFocusedInput ] = useState(\n initialValue ? initialValue.length - 1 : -1\n );\n const [ isFocused, setIsFocused ] = useState( false );\n\n // This useEffect is executed if the user has provided an initial value\n useEffect(() => {\n if ( initialValue ) {\n const temp = [];\n for ( let i = 0; i < validOtpLength; i++ ) {\n temp[i] = initialValue?.[i] || "";\n }\n setOtpValue( temp );\n }\n }, [ validOtpLength, initialValue ]);\n\n // isInputValid is used to check whether the input provided is valid or not\n const isInputValid = useCallback(\n ( val: string ) => {\n const regularExpression = new RegExp( regexValidation ?? "" );\n return regexValidation ? regularExpression.test( val ) : /[0-9]/.test( val );\n },\n [regexValidation]\n );\n\n const onInputChange =\n ( idx: number ) => ( event: React.ChangeEvent<HTMLInputElement> ) => {\n const values = [...otpValue];\n const { value } = event.target;\n values[idx] = value;\n if ( isInputValid( value ) || value === "" ) {\n setOtpValue([...values]);\n onChange?.( values.join( "" ));\n\n if ( idx < validOtpLength - 1 && value !== "" ) {\n setFocusedInput(( index ) => index + 1 );\n } else if ( idx !== 0 && value === "" ) {\n setFocusedInput(( index ) => index - 1 );\n }\n }\n };\n\n // handleKeyDown is being used to react to the keyboard inputs that the user provides while the component is focused\n const handleKeyDown = useCallback(\n ( event: React.KeyboardEvent<HTMLInputElement> ) => {\n let currentFocused = focusedInput;\n const { key } = event;\n if ( key === keyboardKeys.tab ) {\n return;\n }\n switch ( key ) {\n case keyboardKeys.arrowLeft:\n if ( currentFocused !== 0 ) {\n currentFocused--;\n }\n break;\n case keyboardKeys.backspace:\n if ( otpValue[currentFocused] === "" && currentFocused !== 0 ) {\n currentFocused--;\n }\n break;\n case keyboardKeys.arrowRight:\n if ( currentFocused < validOtpLength - 1 ) {\n currentFocused++;\n }\n break;\n default:\n if (\n otpValue[currentFocused] === key &&\n currentFocused < validOtpLength - 1\n ) {\n currentFocused++;\n }\n }\n setFocusedInput( currentFocused );\n },\n [ focusedInput, otpValue, validOtpLength ]\n );\n\n //handlePaste is used to enter text into the component that has been copied by the user\n const handlePaste = useCallback(\n ( event: React.ClipboardEvent<HTMLDivElement> ) => {\n const pastedValue = event.clipboardData\n .getData( "text" )\n ?.substring( 0, validOtpLength );\n const otp = [];\n\n for ( let i = 0; i < validOtpLength; i++ ) {\n otp.push( isInputValid( pastedValue[i]) ? pastedValue[i] : "" );\n }\n\n setOtpValue( otp );\n onChange?.( otp.join( "" ));\n setFocusedInput(\n pastedValue.length <= validOtpLength - 1\n ? pastedValue.length\n : validOtpLength - 1\n );\n },\n [ isInputValid, onChange, validOtpLength ]\n );\n\n const onFocusCallback = useCallback(\n ( event: React.FocusEvent<HTMLDivElement> ) => {\n if ( !isFocused ) {\n setIsFocused( true );\n onFocus?.( event );\n setFocusedInput( Number( event.target.id ));\n }\n },\n [ isFocused, onFocus ]\n );\n\n const onBlurCallback = useCallback(\n ( event: React.FocusEvent<HTMLDivElement> ) => {\n if ( isFocused ) {\n setIsFocused( false );\n onBlur?.( event );\n setFocusedInput( -1 );\n }\n },\n [ isFocused, onBlur ]\n );\n\n const handleEvents = useCallback(\n ( event: React.FocusEvent<HTMLDivElement> ) =>\n event.type === "focus" ? onFocusCallback( event ) : onBlurCallback( event ),\n [ onBlurCallback, onFocusCallback ]\n );\n\n /* Debounce has been used because of the shifting focus of inputs in the otpInput component and we want the whole component\n to act like one focusable component and therefore we have debounced the whole onFocus and onBlur events in order to avoid\n the continuous firing of these events while the focused inputs inside are changing */\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const handleFocusAndBlur = useCallback(\n debounce(( event ) => handleEvents( event ), 50 ),\n [handleEvents]\n );\n\n return validOtpLength\n ? (\n <fieldset\n className={cn( "inline-flex flex-col gap-2 m-0 p-0 border-0 min-w-0", className )}\n disabled={disabled}\n ref={ref}\n {...props}\n >\n {label && (\n <legend className={cn( "font-medium text-base text-secondary-500 dark:text-secondary-300 mb-2 p-0" )}>\n {label}\n </legend>\n )}\n <div\n className={cn( "inline-flex gap-2" )}\n onPaste={handlePaste}\n onBlur={handleFocusAndBlur}\n onFocus={handleFocusAndBlur}\n >\n {\n Array( validOtpLength )\n .fill( "" )\n .map(( _, index ) => (\n <Input\n key={index}\n index={index}\n handleChange={onInputChange( index )}\n focused={focusedInput === index}\n handleKeyDown={handleKeyDown}\n value={otpValue?.[index]}\n id={`${index}`}\n error={error}\n />\n ))}\n </div>\n {helperText && (\n <div\n className={cn( otpTextVariants({ error }))}\n >\n {helperText}\n </div>\n )}\n </fieldset>\n )\n : null;\n }\n);\n\nOtpInput.displayName = "OtpInput";'
2277
2277
  },
2278
2278
  {
2279
2279
  "name": "input.tsx",
@@ -2298,7 +2298,7 @@ DropdownItem.displayName = "DropdownItem";`
2298
2298
  ],
2299
2299
  "internalDependencies": [
2300
2300
  "button",
2301
- "adpicon"
2301
+ "adpIcon"
2302
2302
  ],
2303
2303
  "files": [
2304
2304
  {
@@ -2330,7 +2330,7 @@ DropdownItem.displayName = "DropdownItem";`
2330
2330
  ],
2331
2331
  "internalDependencies": [
2332
2332
  "button",
2333
- "adpicon"
2333
+ "adpIcon"
2334
2334
  ],
2335
2335
  "files": [
2336
2336
  {
@@ -2465,7 +2465,7 @@ PanelHeader.displayName = "PanelHeader";`
2465
2465
  ]
2466
2466
  },
2467
2467
  "panelgroup": {
2468
- "name": "panelgroup",
2468
+ "name": "panelGroup",
2469
2469
  "description": "## Overview",
2470
2470
  "dependencies": [
2471
2471
  "class-variance-authority",
@@ -2565,15 +2565,15 @@ PanelGroup.displayName = "PanelGroup";`
2565
2565
  ]
2566
2566
  },
2567
2567
  "passwordfield": {
2568
- "name": "passwordfield",
2568
+ "name": "passwordField",
2569
2569
  "description": "## Overview",
2570
2570
  "dependencies": [
2571
2571
  "react"
2572
2572
  ],
2573
2573
  "internalDependencies": [
2574
- "textfield",
2574
+ "textField",
2575
2575
  "button",
2576
- "adpicon"
2576
+ "adpIcon"
2577
2577
  ],
2578
2578
  "files": [
2579
2579
  {
@@ -2697,7 +2697,7 @@ export const radioLabelVariants = cva(
2697
2697
  ]
2698
2698
  },
2699
2699
  "radiogroup": {
2700
- "name": "radiogroup",
2700
+ "name": "radioGroup",
2701
2701
  "description": "## Overview",
2702
2702
  "dependencies": [
2703
2703
  "class-variance-authority",
@@ -2737,7 +2737,7 @@ export const radioLabelVariants = cva(
2737
2737
  "files": [
2738
2738
  {
2739
2739
  "name": "utils.ts",
2740
- "content": '/**\n * Finds the closest parent element of the given element that has the specified attribute set,\n * and returns the value of that attribute.\n *\n * @param {HTMLElement} element - The starting element to search from.\n * @param {string} attribute - The attribute to search for.\n * @returns {string | null} The value of the specified attribute on the closest parent element that has it set, or null if no such parent element is found.\n */\nexport const findParentAttribute = ( element: HTMLElement, attribute: string ): string | null => {\n if ( !element ) {\n return null;\n }\n let { parentElement } = element;\n while ( parentElement ) {\n const value = parentElement.getAttribute( attribute );\n if ( value ) {\n return value;\n }\n parentElement = parentElement?.parentElement;\n }\n\n return null;\n};\n\n/**\n * Updates the cursor style to grabbing and disables text selection\n */\nexport const updateCursor = () => {\n document.body.style.cursor = "grabbing";\n document.body.style.userSelect = "none";\n};\n\n/**\n * Resets the cursor style and text selection\n */\nexport const resetCursor = () => {\n document.body.style.cursor = "";\n document.body.style.userSelect = "";\n};\n\n/**\n * Scrolls an HTML element smoothly to a specified offset along the top or left axis.\n *\n * @param element - The HTML element to scroll.\n * @param scrollToOffset - The offset to scroll to along the specified axis.\n * @param scrollProp - The axis to scroll along, either "top" or "left".\n */\nexport const handleScrollToHelper = (\n element: HTMLDivElement,\n scrollToOffset: number,\n scrollProp: "top" | "left"\n) => {\n element.scroll({\n [scrollProp]: scrollToOffset,\n behavior: "smooth"\n });\n};'
2740
+ "content": '/**\n * Updates the cursor style to grabbing and disables text selection\n */\nexport const updateCursor = () => {\n document.body.style.cursor = "grabbing";\n document.body.style.userSelect = "none";\n};\n\n/**\n * Resets the cursor style and text selection\n */\nexport const resetCursor = () => {\n document.body.style.cursor = "";\n document.body.style.userSelect = "";\n};\n\n/**\n * Scrolls an HTML element smoothly to a specified offset along the top or left axis.\n *\n * @param element - The HTML element to scroll.\n * @param scrollToOffset - The offset to scroll to along the specified axis.\n * @param scrollProp - The axis to scroll along, either "top" or "left".\n */\nexport const handleScrollToHelper = (\n element: HTMLDivElement,\n scrollToOffset: number,\n scrollProp: "top" | "left"\n) => {\n element.scroll({\n [scrollProp]: scrollToOffset,\n behavior: "smooth"\n });\n};'
2741
2741
  },
2742
2742
  {
2743
2743
  "name": "scrollbarVariants.ts",
@@ -2745,7 +2745,7 @@ export const radioLabelVariants = cva(
2745
2745
  },
2746
2746
  {
2747
2747
  "name": "scrollbar.tsx",
2748
- "content": 'import React, { useRef, useEffect, useCallback, ElementType, useState, forwardRef } from "react";\nimport { cn, useWindowSize } from "@utils";\nimport mergeRefs from "react-merge-refs";\nimport { keyboardKeys, mobileBreakPoint } from "@constants";\nimport { findParentAttribute, updateCursor, resetCursor, handleScrollToHelper } from "./utils";\nimport {\n scrollbarVariants,\n scrollbarContentVariants,\n scrollbarYContainerVariants,\n scrollbarTrackYVariants,\n scrollbarThumbYVariants,\n scrollbarXContainerVariants,\n scrollbarTrackXVariants,\n scrollbarThumbXVariants\n} from "./scrollbarVariants";\n\n// Avoid void elements that can\'t have children\ntype TVoidElements = "area" | "base" | "br" | "col" | "embed" | "hr" | "img" | "input" | "link" |\n "meta" | "param" | "source" | "track" | "wbr" | "audio" | "video" | "canvas" | "iframe" |\n "embed" | "object" | "param" | "picture" | "portal" | "svg" | "math" | "svg";\n\n/**\n * Props for the Scrollbar component\n */\nexport interface IScrollbarProps {\n /**\n * children to render inside the scroll container\n */\n children: React.ReactNode;\n\n /**\n * The type of tag to render for the scroll container, the default value is "div".\n * @default div\n */\n as?: keyof Omit<JSX.IntrinsicElements, TVoidElements>;\n\n /**\n * Overwrite the scrollbar container styles by passing space separated class names.\n * @optional\n */\n className?: string;\n\n /**\n * Pass a boolean value to set the visibility of the scroller, by default the scrollbar will be visible on content hover.\n * @optional\n * @default false\n */\n alwaysVisible?: boolean;\n\n /**\n * Set it to true if you want to directly scroll to the click position,\n * by default the scroller will move in the direction of the click\n * @default false\n */\n scrollToClickPosition?: boolean;\n\n /**\n * Optional ID for the scrollbar container\n */\n id?: string;\n}\n\n/**\n * Scrollbar component provides a custom scrollbar with support for both vertical and horizontal scrolling.\n * It includes features like custom styling, touch support, RTL support, and keyboard navigation.\n */\nexport const Scrollbar = forwardRef<HTMLElement, IScrollbarProps>(\n ({\n children,\n as = "div",\n className,\n alwaysVisible = false,\n scrollToClickPosition = false,\n id\n }, ref ) => {\n // Set up the element tag\n const Tag = as as ElementType;\n\n // Refs for DOM elements\n const scrollbarContainerRef = useRef<HTMLDivElement>( null );\n const contentRef = useRef<HTMLDivElement>( null );\n\n const trackRefY = useRef<HTMLDivElement>( null );\n const thumbRefY = useRef<HTMLDivElement>( null );\n const scrollerContainerRefY = useRef<HTMLDivElement>( null );\n\n const trackRefX = useRef<HTMLDivElement>( null );\n const thumbRefX = useRef<HTMLDivElement>( null );\n const scrollerContainerRefX = useRef<HTMLDivElement>( null );\n\n // State for RTL and mobile detection\n const [ isRtl, setIsRtl ] = useState<boolean>( false );\n const { width } = useWindowSize();\n const isMobile = width <= mobileBreakPoint;\n const scrollThumbTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>( null );\n\n // Handle the scrolling event\n const handleScrollContent = useCallback(() => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const thumbX = thumbRefX.current;\n\n const trackY = trackRefY.current;\n const trackX = trackRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX || !trackY || !trackX ) {\n return;\n }\n\n // Calculate the scroll percentage\n const scrollPercentageY = ( contentElement.scrollTop * 100 ) / contentElement.scrollHeight;\n const scrollPercentageX = ( contentElement.scrollLeft * 100 ) / contentElement.scrollWidth;\n\n // Set the Y scrollbar thumb position\n thumbY.style.top = `${scrollPercentageY}%`;\n\n // if in case of rtl, behaviour of scrolling on X-Axis would be inversed\n if ( isRtl ) {\n const maxScrollX = contentElement.scrollWidth - contentElement.clientWidth;\n const thumbPositionX = ( maxScrollX * 100 ) / contentElement.scrollWidth;\n thumbX.style.left = `${thumbPositionX + scrollPercentageX}%`;\n } else {\n thumbX.style.left = `${scrollPercentageX}%`;\n }\n\n if ( isMobile ) {\n // For mobile browsers, show the scroll thumb immediately\n thumbY.style.opacity = "1";\n thumbX.style.opacity = "1";\n trackY.style.opacity = "1";\n trackX.style.opacity = "1";\n\n // Clear any existing timeout\n if ( scrollThumbTimeoutRef.current ) {\n clearTimeout( scrollThumbTimeoutRef.current );\n }\n\n // Set a new timeout to hide the scroll thumb after 1 second of inactivity\n scrollThumbTimeoutRef.current = setTimeout(() => {\n thumbY.style.opacity = "0";\n thumbX.style.opacity = "0";\n trackY.style.opacity = "0";\n trackX.style.opacity = "0";\n }, 1000 );\n }\n }, [ isMobile, isRtl ]);\n\n /**\n * Handle the scrollbar track click\n * When the scrollbar track is clicked, it captures the click\'s position and scrolls toward it.\n */\n const handleClickTrack = useCallback(\n ( event: React.MouseEvent<HTMLDivElement>, scrollDirection: "X" | "Y" ) => {\n const contentElement = contentRef.current;\n const trackY = trackRefY.current;\n const thumbY = thumbRefY.current;\n const trackX = trackRefX.current;\n const thumbX = thumbRefX.current;\n\n if ( !trackY || !thumbY || !contentElement || !trackX || !thumbX ) {\n return;\n }\n\n if ( scrollToClickPosition ) {\n if ( scrollDirection === "Y" ) {\n const boundY = trackY.getBoundingClientRect();\n const percentageY = ( event.clientY - boundY.top ) / boundY.height;\n const scrollHeight = contentElement.scrollHeight - contentElement.clientHeight;\n const scrollToOffsetY = percentageY * scrollHeight;\n\n handleScrollToHelper( contentElement, scrollToOffsetY, "top" );\n } else if ( scrollDirection === "X" ) {\n const boundX = trackX.getBoundingClientRect();\n let percentageX = ( event.clientX - boundX.left ) / boundX.width;\n\n if ( isRtl ) {\n percentageX = ( event.clientX - boundX.right ) / boundX.width;\n }\n\n const scrollWidth = contentElement.scrollWidth - contentElement.clientWidth;\n const scrollToOffsetX = percentageX * scrollWidth;\n\n handleScrollToHelper( contentElement, scrollToOffsetX, "left" );\n }\n } else {\n if ( scrollDirection === "Y" ) {\n const boundY = trackY.getBoundingClientRect();\n const thumbBoundY = thumbY.getBoundingClientRect();\n const clickPositionY = event.clientY - boundY.top;\n const thumbMidY = thumbBoundY.top + ( thumbBoundY.bottom - thumbBoundY.top ) / 2;\n const scrollAmount = contentElement.clientHeight * 0.5;\n const scrollDirectionUnits = clickPositionY > thumbMidY ? scrollAmount : -scrollAmount;\n\n handleScrollToHelper( contentElement, contentElement.scrollTop + scrollDirectionUnits, "top" );\n } else if ( scrollDirection === "X" ) {\n const boundX = trackX.getBoundingClientRect();\n const thumbBoundX = thumbX.getBoundingClientRect();\n const clickPositionX = event.clientX - boundX.left;\n const thumbMidX = thumbBoundX.left + ( thumbBoundX.right - thumbBoundX.left ) / 2;\n const scrollAmount = contentElement.clientWidth * 0.5;\n const scrollDirectionUnits = clickPositionX > thumbMidX ? scrollAmount : -scrollAmount;\n\n handleScrollToHelper( contentElement, contentElement.scrollLeft + scrollDirectionUnits, "left" );\n }\n }\n },\n [ isRtl, scrollToClickPosition ]\n );\n\n /**\n * Handles the mouse down event on the scrollbar thumb to enable scrolling by dragging the thumb.\n */\n const handleMouseDown = useCallback(\n ( parentEvent: React.MouseEvent<HTMLDivElement>, scrollDirection: "X" | "Y" ) => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const thumbX = thumbRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX ) {\n return;\n }\n\n const startPos = {\n top: contentElement.scrollTop,\n left: contentElement.scrollLeft,\n x: parentEvent.clientX,\n y: parentEvent.clientY\n };\n\n const handleMouseMove = ( childEvent: MouseEvent ) => {\n const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;\n const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;\n\n if ( scrollDirection === "Y" ) {\n const differenceY = childEvent.clientY - startPos.y;\n contentElement.scrollTop = startPos.top + differenceY / scrollRatioY;\n } else if ( scrollDirection === "X" ) {\n let differenceX = childEvent.clientX - startPos.x;\n if ( isRtl ) {\n differenceX = startPos.x - childEvent.clientX;\n }\n\n if ( isRtl ) {\n contentElement.scrollLeft = startPos.left - differenceX / scrollRatioX;\n } else {\n contentElement.scrollLeft = startPos.left + differenceX / scrollRatioX;\n }\n }\n updateCursor();\n };\n\n const preventSelection = ( event: Event ) => {\n event.preventDefault(); // Prevent text selection\n };\n\n // Helper function to remove and reset the events and cursor style\n const handleMouseUp = () => {\n document.body.removeEventListener( "mousemove", handleMouseMove );\n document.body.removeEventListener( "mouseup", handleMouseUp );\n document.body.removeEventListener( "selectstart", preventSelection );\n resetCursor();\n };\n\n // Attach the events to the body on mouseMove event and when mouse is released remove\n document.body.addEventListener( "mousemove", handleMouseMove );\n document.body.addEventListener( "mouseup", handleMouseUp );\n document.body.addEventListener( "selectstart", preventSelection );\n },\n [isRtl]\n );\n\n /**\n * To handle the thumb drag for screen touch devices\n */\n const handleTouchStart = useCallback(\n ( parentEvent: React.TouchEvent<HTMLDivElement>, scrollDirection: "X" | "Y" ) => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const thumbX = thumbRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX ) {\n return;\n }\n\n const parentTouchArray = Array.from( parentEvent.targetTouches );\n const [parentTouch] = parentTouchArray;\n\n const startPos = {\n top: contentElement.scrollTop,\n left: contentElement.scrollLeft,\n x: parentTouch.clientX,\n y: parentTouch.clientY\n };\n\n const handleTouchMove = ( childEvent: TouchEvent ) => {\n const childTouchArray = Array.from( childEvent.targetTouches );\n const [childTouch] = childTouchArray;\n\n const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;\n const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;\n\n if ( scrollDirection === "Y" ) {\n const differenceY = childTouch?.clientY - startPos.y;\n contentElement.scrollTop = startPos.top + differenceY / scrollRatioY;\n } else if ( scrollDirection === "X" ) {\n let differenceX = childTouch?.clientX - startPos.x;\n if ( isRtl ) {\n differenceX = startPos.x - childTouch?.clientX;\n }\n\n // The scroll behaviour is reversed if the rtl value is true/enabled\n if ( isRtl ) {\n contentElement.scrollLeft = startPos.left - differenceX / scrollRatioX;\n } else {\n contentElement.scrollLeft = startPos.left + differenceX / scrollRatioX;\n }\n }\n\n updateCursor();\n };\n\n // Helper function to remove and reset the events and cursor style\n const handleTouchEnd = () => {\n if ( scrollDirection === "Y" ) {\n thumbY.removeEventListener( "touchmove", handleTouchMove );\n thumbY.removeEventListener( "touchend", handleTouchEnd );\n } else {\n thumbX.removeEventListener( "touchmove", handleTouchMove );\n thumbX.removeEventListener( "touchend", handleTouchEnd );\n }\n resetCursor();\n };\n\n // Attach the events to the Thumb Element on touchMove event and when touch is released remove\n if ( scrollDirection === "Y" ) {\n thumbY.addEventListener( "touchmove", handleTouchMove );\n thumbY.addEventListener( "touchend", handleTouchEnd );\n } else {\n thumbX.addEventListener( "touchmove", handleTouchMove );\n thumbX.addEventListener( "touchend", handleTouchEnd );\n }\n },\n [isRtl]\n );\n\n /**\n * Calculates the dimensions of the scrollbar thumb elements based on the content size and visibility.\n */\n const calculateThumbDimensions = useCallback(() => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const scrollbarContainerY = scrollerContainerRefY.current;\n const thumbX = thumbRefX.current;\n const scrollbarContainerX = scrollerContainerRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX ) {\n return;\n }\n\n const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;\n if ( scrollRatioY >= 1 ) {\n scrollbarContainerY?.classList.add( "invisible" );\n } else {\n scrollbarContainerY?.classList.remove( "invisible" );\n thumbY.style.height = `${scrollRatioY * 100}%`;\n }\n\n const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;\n if ( scrollRatioX >= 1 ) {\n scrollbarContainerX?.classList.add( "invisible" );\n } else {\n scrollbarContainerX?.classList.remove( "invisible" );\n thumbX.style.width = `${scrollRatioX * 100}%`;\n }\n }, []);\n\n // Check for RTL direction\n useEffect(() => {\n const scrollbarContainerElement = scrollbarContainerRef.current;\n if ( scrollbarContainerElement ) {\n const dir = findParentAttribute( scrollbarContainerElement, "dir" );\n setIsRtl( dir === "rtl" );\n }\n }, [scrollbarContainerRef]);\n\n // Calculate initial dimensions and handle RTL positioning\n useEffect(() => {\n calculateThumbDimensions();\n\n const contentElement = contentRef.current;\n const thumbX = thumbRefX.current;\n\n if ( !contentElement || !thumbX ) {\n return;\n }\n\n if ( isRtl ) {\n const maxScrollX = contentElement.scrollWidth - contentElement.clientWidth;\n const thumbPositionX = ( maxScrollX * 100 ) / contentElement.scrollWidth;\n thumbX.style.left = `${thumbPositionX}%`;\n }\n }, [ calculateThumbDimensions, isRtl ]);\n\n // Set up observers and keyboard event handling\n useEffect(() => {\n const mutationObserver = new MutationObserver( calculateThumbDimensions );\n const resizeObserver = new ResizeObserver( calculateThumbDimensions );\n\n if ( contentRef.current ) {\n mutationObserver.observe( contentRef.current, { childList: true, subtree: true });\n resizeObserver.observe( contentRef.current );\n }\n\n const scrollbarContainerElement = scrollbarContainerRef.current;\n\n const handleKeyDown = ( event: KeyboardEvent ) => {\n const { key } = event;\n const contentElement = contentRef.current;\n\n if ( !contentElement ) {\n return;\n }\n\n const scrollStep = 50;\n\n switch ( key ) {\n case keyboardKeys.arrowUp: {\n handleScrollToHelper( contentElement, contentElement.scrollTop - scrollStep, "top" );\n break;\n }\n case keyboardKeys.arrowDown: {\n handleScrollToHelper( contentElement, contentElement.scrollTop + scrollStep, "top" );\n break;\n }\n case keyboardKeys.arrowLeft: {\n handleScrollToHelper( contentElement, contentElement.scrollLeft - scrollStep, "left" );\n break;\n }\n case keyboardKeys.arrowRight: {\n handleScrollToHelper( contentElement, contentElement.scrollLeft + scrollStep, "left" );\n break;\n }\n case keyboardKeys.pageUp: {\n handleScrollToHelper( contentElement, contentElement.scrollTop - contentElement.clientHeight, "top" );\n break;\n }\n case keyboardKeys.pageDown: {\n handleScrollToHelper( contentElement, contentElement.scrollTop + contentElement.clientHeight, "top" );\n break;\n }\n case keyboardKeys.home: {\n handleScrollToHelper( contentElement, 0, "top" );\n break;\n }\n case keyboardKeys.end: {\n handleScrollToHelper( contentElement, contentElement.scrollHeight, "top" );\n break;\n }\n default:\n break;\n }\n };\n\n if ( scrollbarContainerElement ) {\n scrollbarContainerElement.addEventListener( "keydown", handleKeyDown );\n }\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n\n if ( scrollbarContainerElement ) {\n scrollbarContainerElement.removeEventListener( "keydown", handleKeyDown );\n }\n };\n }, [calculateThumbDimensions]);\n\n const visibility = alwaysVisible ? "alwaysVisible" : "default";\n\n return (\n <Tag\n id={id}\n data-testid="scrollbar"\n className={cn( scrollbarVariants({ visibility }), className )}\n ref={mergeRefs([ ref, scrollbarContainerRef ])}\n >\n <div\n className={cn( scrollbarContentVariants())}\n ref={contentRef}\n onScroll={handleScrollContent}\n >\n {children}\n </div>\n\n {/* y-axis scrollbar */}\n <div\n ref={scrollerContainerRefY}\n data-testid="scrollbar-y"\n className={cn( scrollbarYContainerVariants({ visibility }))}\n >\n <div\n data-testid="scrollbar-track-y"\n className={cn( scrollbarTrackYVariants({ visibility }))}\n ref={trackRefY}\n onMouseDown={( event ) => handleClickTrack( event, "Y" )}\n />\n <div\n data-testid="scrollbar-thumb-y"\n className={cn( scrollbarThumbYVariants({ visibility }))}\n ref={thumbRefY}\n onMouseDown={( event ) => handleMouseDown( event, "Y" )}\n onTouchStart={( event ) => handleTouchStart( event, "Y" )}\n />\n </div>\n\n {/* x-axis scrollbar */}\n <div\n data-testid="scrollbar-x"\n ref={scrollerContainerRefX}\n className={cn( scrollbarXContainerVariants({ visibility }))}\n >\n <div\n className={cn( scrollbarTrackXVariants({ visibility }))}\n ref={trackRefX}\n onMouseDown={( event ) => handleClickTrack( event, "X" )}\n />\n <div\n data-testid="scrollbar-thumb-x"\n className={cn( scrollbarThumbXVariants({ visibility }))}\n ref={thumbRefX}\n onMouseDown={( event ) => handleMouseDown( event, "X" )}\n onTouchStart={( event ) => handleTouchStart( event, "X" )}\n />\n </div>\n </Tag>\n );\n }\n);\n\nScrollbar.displayName = "Scrollbar";'
2748
+ "content": 'import React, { useRef, useEffect, useCallback, ElementType, useState, forwardRef } from "react";\nimport { cn, useWindowSize, findParentAttribute } from "@utils";\nimport mergeRefs from "react-merge-refs";\nimport { keyboardKeys, mobileBreakPoint } from "@constants";\nimport { updateCursor, resetCursor, handleScrollToHelper } from "./utils";\nimport {\n scrollbarVariants,\n scrollbarContentVariants,\n scrollbarYContainerVariants,\n scrollbarTrackYVariants,\n scrollbarThumbYVariants,\n scrollbarXContainerVariants,\n scrollbarTrackXVariants,\n scrollbarThumbXVariants\n} from "./scrollbarVariants";\n\n// Avoid void elements that can\'t have children\ntype TVoidElements = "area" | "base" | "br" | "col" | "embed" | "hr" | "img" | "input" | "link" |\n "meta" | "param" | "source" | "track" | "wbr" | "audio" | "video" | "canvas" | "iframe" |\n "embed" | "object" | "param" | "picture" | "portal" | "svg" | "math" | "svg";\n\n/**\n * Props for the Scrollbar component\n */\nexport interface IScrollbarProps {\n /**\n * children to render inside the scroll container\n */\n children: React.ReactNode;\n\n /**\n * The type of tag to render for the scroll container, the default value is "div".\n * @default div\n */\n as?: keyof Omit<JSX.IntrinsicElements, TVoidElements>;\n\n /**\n * Overwrite the scrollbar container styles by passing space separated class names.\n * @optional\n */\n className?: string;\n\n /**\n * Pass a boolean value to set the visibility of the scroller, by default the scrollbar will be visible on content hover.\n * @optional\n * @default false\n */\n alwaysVisible?: boolean;\n\n /**\n * Set it to true if you want to directly scroll to the click position,\n * by default the scroller will move in the direction of the click\n * @default false\n */\n scrollToClickPosition?: boolean;\n\n /**\n * Optional ID for the scrollbar container\n */\n id?: string;\n}\n\n/**\n * Scrollbar component provides a custom scrollbar with support for both vertical and horizontal scrolling.\n * It includes features like custom styling, touch support, RTL support, and keyboard navigation.\n */\nexport const Scrollbar = forwardRef<HTMLElement, IScrollbarProps>(\n ({\n children,\n as = "div",\n className,\n alwaysVisible = false,\n scrollToClickPosition = false,\n id\n }, ref ) => {\n // Set up the element tag\n const Tag = as as ElementType;\n\n // Refs for DOM elements\n const scrollbarContainerRef = useRef<HTMLDivElement>( null );\n const contentRef = useRef<HTMLDivElement>( null );\n\n const trackRefY = useRef<HTMLDivElement>( null );\n const thumbRefY = useRef<HTMLDivElement>( null );\n const scrollerContainerRefY = useRef<HTMLDivElement>( null );\n\n const trackRefX = useRef<HTMLDivElement>( null );\n const thumbRefX = useRef<HTMLDivElement>( null );\n const scrollerContainerRefX = useRef<HTMLDivElement>( null );\n\n // State for RTL and mobile detection\n const [ isRtl, setIsRtl ] = useState<boolean>( false );\n const { width } = useWindowSize();\n const isMobile = width <= mobileBreakPoint;\n const scrollThumbTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>( null );\n\n // Handle the scrolling event\n const handleScrollContent = useCallback(() => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const thumbX = thumbRefX.current;\n\n const trackY = trackRefY.current;\n const trackX = trackRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX || !trackY || !trackX ) {\n return;\n }\n\n // Calculate the scroll percentage\n const scrollPercentageY = ( contentElement.scrollTop * 100 ) / contentElement.scrollHeight;\n const scrollPercentageX = ( contentElement.scrollLeft * 100 ) / contentElement.scrollWidth;\n\n // Set the Y scrollbar thumb position\n thumbY.style.top = `${scrollPercentageY}%`;\n\n // if in case of rtl, behaviour of scrolling on X-Axis would be inversed\n if ( isRtl ) {\n const maxScrollX = contentElement.scrollWidth - contentElement.clientWidth;\n const thumbPositionX = ( maxScrollX * 100 ) / contentElement.scrollWidth;\n thumbX.style.left = `${thumbPositionX + scrollPercentageX}%`;\n } else {\n thumbX.style.left = `${scrollPercentageX}%`;\n }\n\n if ( isMobile ) {\n // For mobile browsers, show the scroll thumb immediately\n thumbY.style.opacity = "1";\n thumbX.style.opacity = "1";\n trackY.style.opacity = "1";\n trackX.style.opacity = "1";\n\n // Clear any existing timeout\n if ( scrollThumbTimeoutRef.current ) {\n clearTimeout( scrollThumbTimeoutRef.current );\n }\n\n // Set a new timeout to hide the scroll thumb after 1 second of inactivity\n scrollThumbTimeoutRef.current = setTimeout(() => {\n thumbY.style.opacity = "0";\n thumbX.style.opacity = "0";\n trackY.style.opacity = "0";\n trackX.style.opacity = "0";\n }, 1000 );\n }\n }, [ isMobile, isRtl ]);\n\n /**\n * Handle the scrollbar track click\n * When the scrollbar track is clicked, it captures the click\'s position and scrolls toward it.\n */\n const handleClickTrack = useCallback(\n ( event: React.MouseEvent<HTMLDivElement>, scrollDirection: "X" | "Y" ) => {\n const contentElement = contentRef.current;\n const trackY = trackRefY.current;\n const thumbY = thumbRefY.current;\n const trackX = trackRefX.current;\n const thumbX = thumbRefX.current;\n\n if ( !trackY || !thumbY || !contentElement || !trackX || !thumbX ) {\n return;\n }\n\n if ( scrollToClickPosition ) {\n if ( scrollDirection === "Y" ) {\n const boundY = trackY.getBoundingClientRect();\n const percentageY = ( event.clientY - boundY.top ) / boundY.height;\n const scrollHeight = contentElement.scrollHeight - contentElement.clientHeight;\n const scrollToOffsetY = percentageY * scrollHeight;\n\n handleScrollToHelper( contentElement, scrollToOffsetY, "top" );\n } else if ( scrollDirection === "X" ) {\n const boundX = trackX.getBoundingClientRect();\n let percentageX = ( event.clientX - boundX.left ) / boundX.width;\n\n if ( isRtl ) {\n percentageX = ( event.clientX - boundX.right ) / boundX.width;\n }\n\n const scrollWidth = contentElement.scrollWidth - contentElement.clientWidth;\n const scrollToOffsetX = percentageX * scrollWidth;\n\n handleScrollToHelper( contentElement, scrollToOffsetX, "left" );\n }\n } else {\n if ( scrollDirection === "Y" ) {\n const boundY = trackY.getBoundingClientRect();\n const thumbBoundY = thumbY.getBoundingClientRect();\n const clickPositionY = event.clientY - boundY.top;\n const thumbMidY = thumbBoundY.top + ( thumbBoundY.bottom - thumbBoundY.top ) / 2;\n const scrollAmount = contentElement.clientHeight * 0.5;\n const scrollDirectionUnits = clickPositionY > thumbMidY ? scrollAmount : -scrollAmount;\n\n handleScrollToHelper( contentElement, contentElement.scrollTop + scrollDirectionUnits, "top" );\n } else if ( scrollDirection === "X" ) {\n const boundX = trackX.getBoundingClientRect();\n const thumbBoundX = thumbX.getBoundingClientRect();\n const clickPositionX = event.clientX - boundX.left;\n const thumbMidX = thumbBoundX.left + ( thumbBoundX.right - thumbBoundX.left ) / 2;\n const scrollAmount = contentElement.clientWidth * 0.5;\n const scrollDirectionUnits = clickPositionX > thumbMidX ? scrollAmount : -scrollAmount;\n\n handleScrollToHelper( contentElement, contentElement.scrollLeft + scrollDirectionUnits, "left" );\n }\n }\n },\n [ isRtl, scrollToClickPosition ]\n );\n\n /**\n * Handles the mouse down event on the scrollbar thumb to enable scrolling by dragging the thumb.\n */\n const handleMouseDown = useCallback(\n ( parentEvent: React.MouseEvent<HTMLDivElement>, scrollDirection: "X" | "Y" ) => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const thumbX = thumbRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX ) {\n return;\n }\n\n const startPos = {\n top: contentElement.scrollTop,\n left: contentElement.scrollLeft,\n x: parentEvent.clientX,\n y: parentEvent.clientY\n };\n\n const handleMouseMove = ( childEvent: MouseEvent ) => {\n const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;\n const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;\n\n if ( scrollDirection === "Y" ) {\n const differenceY = childEvent.clientY - startPos.y;\n contentElement.scrollTop = startPos.top + differenceY / scrollRatioY;\n } else if ( scrollDirection === "X" ) {\n let differenceX = childEvent.clientX - startPos.x;\n if ( isRtl ) {\n differenceX = startPos.x - childEvent.clientX;\n }\n\n if ( isRtl ) {\n contentElement.scrollLeft = startPos.left - differenceX / scrollRatioX;\n } else {\n contentElement.scrollLeft = startPos.left + differenceX / scrollRatioX;\n }\n }\n updateCursor();\n };\n\n const preventSelection = ( event: Event ) => {\n event.preventDefault(); // Prevent text selection\n };\n\n // Helper function to remove and reset the events and cursor style\n const handleMouseUp = () => {\n document.body.removeEventListener( "mousemove", handleMouseMove );\n document.body.removeEventListener( "mouseup", handleMouseUp );\n document.body.removeEventListener( "selectstart", preventSelection );\n resetCursor();\n };\n\n // Attach the events to the body on mouseMove event and when mouse is released remove\n document.body.addEventListener( "mousemove", handleMouseMove );\n document.body.addEventListener( "mouseup", handleMouseUp );\n document.body.addEventListener( "selectstart", preventSelection );\n },\n [isRtl]\n );\n\n /**\n * To handle the thumb drag for screen touch devices\n */\n const handleTouchStart = useCallback(\n ( parentEvent: React.TouchEvent<HTMLDivElement>, scrollDirection: "X" | "Y" ) => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const thumbX = thumbRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX ) {\n return;\n }\n\n const parentTouchArray = Array.from( parentEvent.targetTouches );\n const [parentTouch] = parentTouchArray;\n\n const startPos = {\n top: contentElement.scrollTop,\n left: contentElement.scrollLeft,\n x: parentTouch.clientX,\n y: parentTouch.clientY\n };\n\n const handleTouchMove = ( childEvent: TouchEvent ) => {\n const childTouchArray = Array.from( childEvent.targetTouches );\n const [childTouch] = childTouchArray;\n\n const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;\n const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;\n\n if ( scrollDirection === "Y" ) {\n const differenceY = childTouch?.clientY - startPos.y;\n contentElement.scrollTop = startPos.top + differenceY / scrollRatioY;\n } else if ( scrollDirection === "X" ) {\n let differenceX = childTouch?.clientX - startPos.x;\n if ( isRtl ) {\n differenceX = startPos.x - childTouch?.clientX;\n }\n\n // The scroll behaviour is reversed if the rtl value is true/enabled\n if ( isRtl ) {\n contentElement.scrollLeft = startPos.left - differenceX / scrollRatioX;\n } else {\n contentElement.scrollLeft = startPos.left + differenceX / scrollRatioX;\n }\n }\n\n updateCursor();\n };\n\n // Helper function to remove and reset the events and cursor style\n const handleTouchEnd = () => {\n if ( scrollDirection === "Y" ) {\n thumbY.removeEventListener( "touchmove", handleTouchMove );\n thumbY.removeEventListener( "touchend", handleTouchEnd );\n } else {\n thumbX.removeEventListener( "touchmove", handleTouchMove );\n thumbX.removeEventListener( "touchend", handleTouchEnd );\n }\n resetCursor();\n };\n\n // Attach the events to the Thumb Element on touchMove event and when touch is released remove\n if ( scrollDirection === "Y" ) {\n thumbY.addEventListener( "touchmove", handleTouchMove );\n thumbY.addEventListener( "touchend", handleTouchEnd );\n } else {\n thumbX.addEventListener( "touchmove", handleTouchMove );\n thumbX.addEventListener( "touchend", handleTouchEnd );\n }\n },\n [isRtl]\n );\n\n /**\n * Calculates the dimensions of the scrollbar thumb elements based on the content size and visibility.\n */\n const calculateThumbDimensions = useCallback(() => {\n const contentElement = contentRef.current;\n const thumbY = thumbRefY.current;\n const scrollbarContainerY = scrollerContainerRefY.current;\n const thumbX = thumbRefX.current;\n const scrollbarContainerX = scrollerContainerRefX.current;\n\n if ( !thumbY || !contentElement || !thumbX ) {\n return;\n }\n\n const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;\n if ( scrollRatioY >= 1 ) {\n scrollbarContainerY?.classList.add( "invisible" );\n } else {\n scrollbarContainerY?.classList.remove( "invisible" );\n thumbY.style.height = `${scrollRatioY * 100}%`;\n }\n\n const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;\n if ( scrollRatioX >= 1 ) {\n scrollbarContainerX?.classList.add( "invisible" );\n } else {\n scrollbarContainerX?.classList.remove( "invisible" );\n thumbX.style.width = `${scrollRatioX * 100}%`;\n }\n }, []);\n\n // Check for RTL direction\n useEffect(() => {\n const scrollbarContainerElement = scrollbarContainerRef.current;\n if ( scrollbarContainerElement ) {\n const dir = findParentAttribute( scrollbarContainerElement, "dir" );\n setIsRtl( dir === "rtl" );\n }\n }, [scrollbarContainerRef]);\n\n // Calculate initial dimensions and handle RTL positioning\n useEffect(() => {\n calculateThumbDimensions();\n\n const contentElement = contentRef.current;\n const thumbX = thumbRefX.current;\n\n if ( !contentElement || !thumbX ) {\n return;\n }\n\n if ( isRtl ) {\n const maxScrollX = contentElement.scrollWidth - contentElement.clientWidth;\n const thumbPositionX = ( maxScrollX * 100 ) / contentElement.scrollWidth;\n thumbX.style.left = `${thumbPositionX}%`;\n }\n }, [ calculateThumbDimensions, isRtl ]);\n\n // Set up observers and keyboard event handling\n useEffect(() => {\n const mutationObserver = new MutationObserver( calculateThumbDimensions );\n const resizeObserver = new ResizeObserver( calculateThumbDimensions );\n\n if ( contentRef.current ) {\n mutationObserver.observe( contentRef.current, { childList: true, subtree: true });\n resizeObserver.observe( contentRef.current );\n }\n\n const scrollbarContainerElement = scrollbarContainerRef.current;\n\n const handleKeyDown = ( event: KeyboardEvent ) => {\n const { key } = event;\n const contentElement = contentRef.current;\n\n if ( !contentElement ) {\n return;\n }\n\n const scrollStep = 50;\n\n switch ( key ) {\n case keyboardKeys.arrowUp: {\n handleScrollToHelper( contentElement, contentElement.scrollTop - scrollStep, "top" );\n break;\n }\n case keyboardKeys.arrowDown: {\n handleScrollToHelper( contentElement, contentElement.scrollTop + scrollStep, "top" );\n break;\n }\n case keyboardKeys.arrowLeft: {\n handleScrollToHelper( contentElement, contentElement.scrollLeft - scrollStep, "left" );\n break;\n }\n case keyboardKeys.arrowRight: {\n handleScrollToHelper( contentElement, contentElement.scrollLeft + scrollStep, "left" );\n break;\n }\n case keyboardKeys.pageUp: {\n handleScrollToHelper( contentElement, contentElement.scrollTop - contentElement.clientHeight, "top" );\n break;\n }\n case keyboardKeys.pageDown: {\n handleScrollToHelper( contentElement, contentElement.scrollTop + contentElement.clientHeight, "top" );\n break;\n }\n case keyboardKeys.home: {\n handleScrollToHelper( contentElement, 0, "top" );\n break;\n }\n case keyboardKeys.end: {\n handleScrollToHelper( contentElement, contentElement.scrollHeight, "top" );\n break;\n }\n default:\n break;\n }\n };\n\n if ( scrollbarContainerElement ) {\n scrollbarContainerElement.addEventListener( "keydown", handleKeyDown );\n }\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n\n if ( scrollbarContainerElement ) {\n scrollbarContainerElement.removeEventListener( "keydown", handleKeyDown );\n }\n };\n }, [calculateThumbDimensions]);\n\n const visibility = alwaysVisible ? "alwaysVisible" : "default";\n\n return (\n <Tag\n id={id}\n data-testid="scrollbar"\n className={cn( scrollbarVariants({ visibility }), className )}\n ref={mergeRefs([ ref, scrollbarContainerRef ])}\n >\n <div\n className={cn( scrollbarContentVariants())}\n ref={contentRef}\n onScroll={handleScrollContent}\n >\n {children}\n </div>\n\n {/* y-axis scrollbar */}\n <div\n ref={scrollerContainerRefY}\n data-testid="scrollbar-y"\n className={cn( scrollbarYContainerVariants({ visibility }))}\n >\n <div\n data-testid="scrollbar-track-y"\n className={cn( scrollbarTrackYVariants({ visibility }))}\n ref={trackRefY}\n onMouseDown={( event ) => handleClickTrack( event, "Y" )}\n />\n <div\n data-testid="scrollbar-thumb-y"\n className={cn( scrollbarThumbYVariants({ visibility }))}\n ref={thumbRefY}\n onMouseDown={( event ) => handleMouseDown( event, "Y" )}\n onTouchStart={( event ) => handleTouchStart( event, "Y" )}\n />\n </div>\n\n {/* x-axis scrollbar */}\n <div\n data-testid="scrollbar-x"\n ref={scrollerContainerRefX}\n className={cn( scrollbarXContainerVariants({ visibility }))}\n >\n <div\n className={cn( scrollbarTrackXVariants({ visibility }))}\n ref={trackRefX}\n onMouseDown={( event ) => handleClickTrack( event, "X" )}\n />\n <div\n data-testid="scrollbar-thumb-x"\n className={cn( scrollbarThumbXVariants({ visibility }))}\n ref={thumbRefX}\n onMouseDown={( event ) => handleMouseDown( event, "X" )}\n onTouchStart={( event ) => handleTouchStart( event, "X" )}\n />\n </div>\n </Tag>\n );\n }\n);\n\nScrollbar.displayName = "Scrollbar";'
2749
2749
  },
2750
2750
  {
2751
2751
  "name": "index.ts",
@@ -2768,7 +2768,7 @@ export const radioLabelVariants = cva(
2768
2768
  ],
2769
2769
  "internalDependencies": [
2770
2770
  "checkbox",
2771
- "adpicon",
2771
+ "adpIcon",
2772
2772
  "button"
2773
2773
  ],
2774
2774
  "files": [
@@ -2802,7 +2802,7 @@ export const radioLabelVariants = cva(
2802
2802
  "react"
2803
2803
  ],
2804
2804
  "internalDependencies": [
2805
- "adpicon"
2805
+ "adpIcon"
2806
2806
  ],
2807
2807
  "files": [
2808
2808
  {
@@ -2824,14 +2824,14 @@ export const radioLabelVariants = cva(
2824
2824
  ]
2825
2825
  },
2826
2826
  "textcopy": {
2827
- "name": "textcopy",
2827
+ "name": "textCopy",
2828
2828
  "description": "## Overview",
2829
2829
  "dependencies": [
2830
2830
  "class-variance-authority",
2831
2831
  "react"
2832
2832
  ],
2833
2833
  "internalDependencies": [
2834
- "adpicon",
2834
+ "adpIcon",
2835
2835
  "button"
2836
2836
  ],
2837
2837
  "files": [
@@ -2868,14 +2868,14 @@ export const textCopyVariants = cva(
2868
2868
  ]
2869
2869
  },
2870
2870
  "textfield": {
2871
- "name": "textfield",
2871
+ "name": "textField",
2872
2872
  "description": "## Overview",
2873
2873
  "dependencies": [
2874
2874
  "class-variance-authority",
2875
2875
  "react"
2876
2876
  ],
2877
2877
  "internalDependencies": [
2878
- "adpicon"
2878
+ "adpIcon"
2879
2879
  ],
2880
2880
  "files": [
2881
2881
  {
@@ -2904,7 +2904,7 @@ export const textCopyVariants = cva(
2904
2904
  "react"
2905
2905
  ],
2906
2906
  "internalDependencies": [
2907
- "adpicon"
2907
+ "adpIcon"
2908
2908
  ],
2909
2909
  "files": [
2910
2910
  {
@@ -2933,7 +2933,7 @@ export const textCopyVariants = cva(
2933
2933
  "react"
2934
2934
  ],
2935
2935
  "internalDependencies": [
2936
- "adpicon",
2936
+ "adpIcon",
2937
2937
  "button"
2938
2938
  ],
2939
2939
  "files": [
@@ -2963,7 +2963,7 @@ export const textCopyVariants = cva(
2963
2963
  "react"
2964
2964
  ],
2965
2965
  "internalDependencies": [
2966
- "adpicon"
2966
+ "adpIcon"
2967
2967
  ],
2968
2968
  "files": [
2969
2969
  {
@@ -2993,7 +2993,7 @@ export const textCopyVariants = cva(
2993
2993
  "@floating-ui/react"
2994
2994
  ],
2995
2995
  "internalDependencies": [
2996
- "adpicon"
2996
+ "adpIcon"
2997
2997
  ],
2998
2998
  "files": [
2999
2999
  {
@@ -3953,7 +3953,7 @@ import { cn } from '@utils';
3953
3953
  var import_gradient_string = __toESM(require("gradient-string"));
3954
3954
  var packageInfo = {
3955
3955
  name: "@cloudwick/astral-ui-cli",
3956
- version: "0.2.1"
3956
+ version: "0.2.2"
3957
3957
  };
3958
3958
  var program = new import_commander5.Command().name(packageInfo.name).description("CLI for installing Astral UI components in any codebase").version(
3959
3959
  packageInfo.version,