diff --git a/.cursor/rules/guidelines.mdc b/.cursor/rules/guidelines.mdc new file mode 100644 index 0000000..af714ed --- /dev/null +++ b/.cursor/rules/guidelines.mdc @@ -0,0 +1,76 @@ +--- +description: Guidelines for developing in the react-components submodule +globs: +--- + +# React Components Submodule + +This is a shared component library. Changes here affect all consuming applications. + +## Directory Structure + +``` +components/ # UI components +hooks/ # Custom React hooks +helpers/ # Component helper functions +types/ # TypeScript interfaces +assets/ # Static assets +``` + +## Component Guidelines + +### Export Pattern + +Use default exports for components: + +```typescript +export default function MyComponent(props: MyComponentProps) { ... } +``` + +### Props Interface + +Define interfaces in `types/` directory: + +```typescript +// types/my-component.ts +export interface MyComponentProps { + required: string; + optional?: number; +} +``` + +### Styling + +- Use Tailwind CSS classes +- Use `combineClassNames` from javascript-functions for conditional classes +- Avoid inline styles + +## Hook Guidelines + +- Hooks must start with `use` +- File name should match hook name +- Document return types explicitly + +## Dependencies + +- `@headlessui/react` - Accessible UI primitives +- `@nextui-org/react` - Tooltip component +- `@tabler/icons-react` - Icon library + +## Component-Specific Guidelines + +### KernTable + +See [kern-table.mdc](./kern-table.mdc) for comprehensive guidelines on using the KernTable component, including: +- Props structure and configuration +- Table data preparation patterns +- Sorting implementation +- State management with hooks +- Performance optimization + +## Testing + +When modifying components or hooks: +1. Test in consuming applications +2. Verify TypeScript types +3. Test accessibility diff --git a/.cursor/rules/kern-table.mdc b/.cursor/rules/kern-table.mdc new file mode 100644 index 0000000..f9302a0 --- /dev/null +++ b/.cursor/rules/kern-table.mdc @@ -0,0 +1,302 @@ +--- +description: Guidelines for using KernTable component +globs: + - **/components/**/*.tsx + - **/util/table-preparations/**/*.ts +--- + +# KernTable Component Guidelines + +The `KernTable` component is a reusable table component that provides sorting, cell rendering, and data display functionality. + +## Import + +```typescript +import KernTable from "@/submodules/react-components/components/kern-table/KernTable"; +``` + +## Props Structure + +### KernTableProps + +```typescript +{ + headers: Header[]; + values?: any[][]; + config?: { + sortKeyIdx?: SortKeyIdx; + onClickSortIdx?: (idx: number) => void; + addBorder?: boolean; + noEntriesText?: string; + } +} +``` + +### Header Structure + +Each header object should have: +- `column`: string - Display text for the column header +- `id`: string - Unique identifier for the column +- `hasSort?`: boolean - Whether the column is sortable +- `hasCheckboxes?`: boolean - Whether to show checkboxes in the header +- `checked?`: boolean - Checkbox checked state +- `onChange?`: function - Checkbox change handler +- `tooltip?`: string - Tooltip text for the header +- `wrapWhitespace?`: boolean - Whether to wrap whitespace in cells + +## Implementation Pattern + +### 1. Define Table Headers + +Create a constant array of header objects: + +```typescript +export const TABLE_HEADERS = [ + { column: "Column Name", id: "columnId", hasSort: true }, + { column: "Date Column", id: "dateColumn", hasSort: true }, + // ... more headers +]; +``` + +### 2. Prepare Table Body Data + +Create a preparation function that transforms raw data into table row format: + +```typescript +import { + toTableColumnText, + toTableColumnDate, + toTableColumnComponent +} from "@/submodules/react-components/helpers/kern-table-helper"; + +export function prepareTableBody(data: DataType[], callback: Function) { + if (!data || data.length === 0) return []; + return data.map((item) => [ + toTableColumnText(item.text), + toTableColumnDate(item.date), + toTableColumnComponent("ComponentName", item.value, { /* props */ }) + ]); +} +``` + +### 3. Define Default Sort Key + +Create a default sort key constant: + +```typescript +import { SortKeyIdx } from "@/submodules/react-components/types/sort"; +import { getDefaultSortKey } from "@/src/util/helper-functions"; + +export const DEFAULT_SORT_KEY: SortKeyIdx = getDefaultSortKey(); +``` + +### 4. Component Implementation + +Use the following pattern in your table component: + +```typescript +import { useCallback, useMemo, useState } from "react"; +import { useRefState } from "@/submodules/react-components/hooks/useRefState"; +import { useRefFor } from "@/submodules/react-components/hooks/useRefFor"; +import { + nextSortDirectionByIdx, + sortBySortKeyIdx, + sortPreppedArrayByIdx +} from "@/submodules/react-components/helpers/sort-functions"; +import { SortDirection, SortKeyIdx } from "@/submodules/react-components/types/sort"; + +export default function MyTable(props: MyTableProps) { + const { state: sortKey, setState: setSortKey, ref: sortKeyRef } = + useRefState(DEFAULT_SORT_KEY); + + const preparedValues = useMemo(() => { + const values = prepareTableBody(props.data, callback); + sortBySortKeyIdx(values, sortKeyRef.current); + return values; + }, [props.data]); + + const preparedValuesRef = useRefFor(preparedValues); + + const tableConfig = useMemo(() => { + function sortByPropertyIdx(idx: number) { + if (!preparedValuesRef.current || preparedValuesRef.current.length === 0) return; + + let newSortKey: SortKeyIdx; + if (nextSortDirectionByIdx(idx, sortKey) === SortDirection.NO_SORT) { + newSortKey = { ...DEFAULT_SORT_KEY }; + if (sortKey.idx === newSortKey.idx) newSortKey.direction = SortDirection.ASC; + sortBySortKeyIdx(preparedValuesRef.current, newSortKey); + } else { + newSortKey = sortPreppedArrayByIdx(preparedValuesRef.current, idx, sortKey); + } + setSortKey(newSortKey); + } + + return { + sortKeyIdx: sortKey, + onClickSortIdx: (idx: number) => sortByPropertyIdx(idx), + noEntriesText: "No entries available." + }; + }, [sortKey]); + + return ( +