@starasia/admin 8.2.7 → 8.2.9
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/components/FilterBar/components/MobileFilterBar.d.ts +51 -0
- package/dist/components/FilterBar/components/MobileFilterDrawer.d.ts +25 -0
- package/dist/components/FilterBar/components/MobileMoreMenu.d.ts +16 -0
- package/dist/components/FilterBar/hooks/useColumnPrefs.d.ts +3 -1
- package/dist/components/FilterBar/hooks/useFilterBarApiParams.d.ts +52 -0
- package/dist/components/FilterBar/index.d.ts +2 -0
- package/dist/components/FilterBar/types.d.ts +6 -0
- package/dist/components/FilterBar/utils/expand-date-compact.d.ts +33 -0
- package/dist/components/FilterBar/utils/relative-date.d.ts +17 -0
- package/dist/components/FilterBar/utils/storage-adapter.d.ts +1 -0
- package/dist/components/TableLayout/ColumnMenu.d.ts +15 -0
- package/dist/components/TableLayout/TableLayout.d.ts +4 -0
- package/dist/components/TableLayout/hooks/useTableSortApiParams.d.ts +64 -0
- package/dist/components/TableLayout/index.d.ts +4 -0
- package/dist/components/TableLayout/types.d.ts +149 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/index.cjs.js +20 -1
- package/dist/index.es.js +3840 -2635
- package/dist/index.umd.js +20 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/dist/components/DataGrid/DataGrid.d.ts +0 -4
- package/dist/components/DataGrid/index.d.ts +0 -2
- package/dist/components/DataGrid/types.d.ts +0 -29
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { FilterFieldConfig, SavedView } from '../types';
|
|
3
|
+
import { OrderedColumn } from '../hooks/useColumnPrefs';
|
|
4
|
+
import { Pagination as PaginationMeta } from '../../TableSection/types';
|
|
5
|
+
|
|
6
|
+
interface MobileFilterBarProps {
|
|
7
|
+
withPadding: boolean;
|
|
8
|
+
configs: FilterFieldConfig[];
|
|
9
|
+
activeKeys: string[];
|
|
10
|
+
configByKey: Record<string, FilterFieldConfig>;
|
|
11
|
+
hasActiveFilter: boolean;
|
|
12
|
+
toggleFilter: (key: string) => void;
|
|
13
|
+
removeFilter: (key: string) => void;
|
|
14
|
+
getValue: (field: FilterFieldConfig) => unknown;
|
|
15
|
+
setValue: (field: FilterFieldConfig, value: unknown) => void;
|
|
16
|
+
clearValue: (key: string) => void;
|
|
17
|
+
onReset: () => void;
|
|
18
|
+
searchable: boolean;
|
|
19
|
+
searchKey: string;
|
|
20
|
+
filterKey: string;
|
|
21
|
+
pageKey: string;
|
|
22
|
+
searchPlaceholder: string;
|
|
23
|
+
searchShortcut: string;
|
|
24
|
+
pagination?: PaginationMeta;
|
|
25
|
+
perPageKey: string;
|
|
26
|
+
pageSizeOptions: number[];
|
|
27
|
+
showPagination: boolean;
|
|
28
|
+
showResultCount: boolean;
|
|
29
|
+
showPageSize: boolean;
|
|
30
|
+
hasColumns: boolean;
|
|
31
|
+
orderedColumns: OrderedColumn[];
|
|
32
|
+
allVisible: boolean;
|
|
33
|
+
onToggleVisibility: (key: string) => void;
|
|
34
|
+
onReorder: (from: number, to: number) => void;
|
|
35
|
+
onSetAllHidden: (hidden: boolean) => void;
|
|
36
|
+
showSavedViews: boolean;
|
|
37
|
+
savedViews: SavedView[];
|
|
38
|
+
onApplyView: (view: SavedView) => void;
|
|
39
|
+
onDeleteView: (id: string) => void;
|
|
40
|
+
onSaveView: (name: string) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Mobile (<768px) variant of FilterBar. Replaces the horizontal toolbar with a
|
|
44
|
+
* two-row layout: a tight toolbar (search icon-toggle + "Filter (N)" trigger +
|
|
45
|
+
* saved views + columns + more) and a compact pagination row below.
|
|
46
|
+
*
|
|
47
|
+
* The filter pills + add-filter catalog move into a bottom `Drawer` opened by
|
|
48
|
+
* the Filter button. Page-size selection moves into the "More" overflow menu.
|
|
49
|
+
*/
|
|
50
|
+
export declare const MobileFilterBar: React.FC<MobileFilterBarProps>;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { FilterFieldConfig } from '../types';
|
|
3
|
+
|
|
4
|
+
interface MobileFilterDrawerProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
configs: FilterFieldConfig[];
|
|
8
|
+
activeKeys: string[];
|
|
9
|
+
configByKey: Record<string, FilterFieldConfig>;
|
|
10
|
+
hasActiveFilter: boolean;
|
|
11
|
+
onToggle: (key: string) => void;
|
|
12
|
+
onReset: () => void;
|
|
13
|
+
getValue: (field: FilterFieldConfig) => unknown;
|
|
14
|
+
setValue: (field: FilterFieldConfig, value: unknown) => void;
|
|
15
|
+
clearValue: (key: string) => void;
|
|
16
|
+
removeFilter: (key: string) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Bottom drawer that consolidates the filter UX for mobile: active pills
|
|
20
|
+
* (stacked vertically) at the top, followed by the full filter catalog as a
|
|
21
|
+
* toggleable list. Reuses `FilterPills` so each pill keeps its native editor
|
|
22
|
+
* popover — the popover floats over the drawer when tapped.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MobileFilterDrawer: React.FC<MobileFilterDrawerProps>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
interface MobileMoreMenuProps {
|
|
4
|
+
perPageKey: string;
|
|
5
|
+
pageKey: string;
|
|
6
|
+
pageSizeOptions: number[];
|
|
7
|
+
defaultPerPage?: number;
|
|
8
|
+
showPageSize: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Mobile-only "More" menu — overflow surface for the secondary toolbar
|
|
12
|
+
* affordances that don't fit on a narrow screen. For now this is just the
|
|
13
|
+
* page-size selector; saved-views / columns stay as their own icon buttons.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MobileMoreMenu: React.FC<MobileMoreMenuProps>;
|
|
16
|
+
export {};
|
|
@@ -5,6 +5,8 @@ interface UseColumnPrefsOptions {
|
|
|
5
5
|
scopeId?: string;
|
|
6
6
|
storage: FilterStorageAdapter;
|
|
7
7
|
onColumnsChange?: (state: ColumnPrefState) => void;
|
|
8
|
+
/** Controlled external state (mis. hide/freeze dari menu kolom tabel). */
|
|
9
|
+
value?: Partial<ColumnPrefState>;
|
|
8
10
|
}
|
|
9
11
|
export interface OrderedColumn extends ColumnConfig {
|
|
10
12
|
visible: boolean;
|
|
@@ -15,7 +17,7 @@ export interface OrderedColumn extends ColumnConfig {
|
|
|
15
17
|
* consumer via `onColumnsChange` so it can apply the result to its table.
|
|
16
18
|
* Without a `scopeId` the state is session-only (still fully functional).
|
|
17
19
|
*/
|
|
18
|
-
export declare function useColumnPrefs({ columns, scopeId, storage, onColumnsChange }: UseColumnPrefsOptions): {
|
|
20
|
+
export declare function useColumnPrefs({ columns, scopeId, storage, onColumnsChange, value }: UseColumnPrefsOptions): {
|
|
19
21
|
state: ColumnPrefState;
|
|
20
22
|
orderedColumns: OrderedColumn[];
|
|
21
23
|
allVisible: boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { FilterFieldConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface UseFilterBarApiParamsOptions {
|
|
4
|
+
/** Same catalog passed to `<FilterBar>`. */
|
|
5
|
+
filterConfigs: FilterFieldConfig[];
|
|
6
|
+
/** Mirror FilterBar URL keys. */
|
|
7
|
+
filterKey?: string;
|
|
8
|
+
activeFilterKey?: string;
|
|
9
|
+
pageKey?: string;
|
|
10
|
+
perPageKey?: string;
|
|
11
|
+
searchKey?: string;
|
|
12
|
+
/** Fallback when `?page` is absent. Default 1. */
|
|
13
|
+
defaultPage?: number;
|
|
14
|
+
/** Fallback when `?perPage` is absent. Default 10. */
|
|
15
|
+
defaultPerPage?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Rename output keys. Covers built-ins (`page`, `perPage`, `search`) and
|
|
18
|
+
* individual filter field keys. Example:
|
|
19
|
+
* `{ perPage: 'limit', status: 'statusId' }`
|
|
20
|
+
*/
|
|
21
|
+
paramKeys?: Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Per-field custom serializer. Receives the **decoded** value
|
|
24
|
+
* (`string | string[] | Date | DateCompactValue | undefined`) and returns
|
|
25
|
+
* whatever shape the API expects. Return `undefined` to omit the key.
|
|
26
|
+
* Keyed by the filter field key (before any `paramKeys` remap).
|
|
27
|
+
*/
|
|
28
|
+
serialize?: Record<string, (value: unknown) => unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface FilterBarApiParams {
|
|
31
|
+
/** Memoized, flat object ready to spread into a service call. */
|
|
32
|
+
params: Record<string, unknown>;
|
|
33
|
+
/** True when any filter (incl. search) is set. */
|
|
34
|
+
hasActiveFilter: boolean;
|
|
35
|
+
/** Decoded but pre-flattening values, keyed by field key — useful if a
|
|
36
|
+
* consumer wants the `{label, value}` pairs back for display. */
|
|
37
|
+
raw: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read FilterBar's URL state and produce a flat, API-ready params object that
|
|
41
|
+
* consumer service functions can accept directly. Drop-in for the
|
|
42
|
+
* `useQuery({ queryKey: [scope, params], queryFn: () => service.list(params) })`
|
|
43
|
+
* pattern documented in the `starasia-tanstack-query` skill.
|
|
44
|
+
*
|
|
45
|
+
* - `page` / `perPage` always present (coerced, with defaults)
|
|
46
|
+
* - `search` only when non-empty
|
|
47
|
+
* - Filter values flattened per type (single → value, multi → value[],
|
|
48
|
+
* date → ISO string, date-compact stays raw — pair with `expandDateCompact`)
|
|
49
|
+
* - Cleared filters are omitted (no `null`, no empty array)
|
|
50
|
+
* - `paramKeys` renames output keys; `serialize` overrides default flattening
|
|
51
|
+
*/
|
|
52
|
+
export declare const useFilterBarApiParams: (options: UseFilterBarApiParamsOptions) => FilterBarApiParams;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { FilterBar } from './FilterBar';
|
|
2
|
+
export { useFilterBarApiParams, type UseFilterBarApiParamsOptions, type FilterBarApiParams } from './hooks/useFilterBarApiParams';
|
|
3
|
+
export { expandDateCompact, type ExpandDateCompactOptions } from './utils/expand-date-compact';
|
|
2
4
|
export { localStorageAdapter, storageKeys } from './utils/storage-adapter';
|
|
3
5
|
export type { FilterBarProps, FilterFieldConfig, FilterFieldType, SelectFilterField, CheckboxMultipleFilterField, DateFilterField, DateCompactFilterField, DateCompactOp, DateCompactValue, RelativeDirection, RelativePeriod, ColumnConfig, ColumnPrefState, SavedView, FilterStorageAdapter } from './types';
|
|
@@ -140,6 +140,12 @@ export interface FilterBarProps {
|
|
|
140
140
|
columns?: ColumnConfig[];
|
|
141
141
|
/** Emitted when visibility/order changes — consumer applies to its table. */
|
|
142
142
|
onColumnsChange?: (state: ColumnPrefState) => void;
|
|
143
|
+
/**
|
|
144
|
+
* Controlled column state. Saat diisi, panel Customize Column ikut state ini
|
|
145
|
+
* (mis. hide/freeze dari menu kolom TableLayout) — sinkron dua arah dgn
|
|
146
|
+
* `onColumnsChange`. `frozenKey` & field lain di luar order/hidden diabaikan.
|
|
147
|
+
*/
|
|
148
|
+
columnsState?: Partial<ColumnPrefState>;
|
|
143
149
|
/** Enable named saved views. `scopeId` namespaces persisted data per table. */
|
|
144
150
|
savedViews?: {
|
|
145
151
|
enabled?: boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DateCompactValue } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface ExpandDateCompactOptions {
|
|
4
|
+
/** Param key for the start / single date. */
|
|
5
|
+
fromKey: string;
|
|
6
|
+
/** Param key for the end of a range (only used by `is-between` and
|
|
7
|
+
* `is-relative-to-today`). */
|
|
8
|
+
toKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional separate key carrying the operator. When provided:
|
|
11
|
+
* - is-empty / is-not-empty → `{ [opKey]: 'empty' | 'not-empty' }`
|
|
12
|
+
* - single-date ops (is / is-before / …) → also emits `{ [opKey]: op }`
|
|
13
|
+
* so the API can branch on direction.
|
|
14
|
+
* When omitted, those ops produce `{}` (single-date ops still emit
|
|
15
|
+
* `{ [fromKey]: ISO }`).
|
|
16
|
+
*/
|
|
17
|
+
opKey?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Expand a `DateCompactValue` (operator + payload, as stored by the
|
|
21
|
+
* `date-compact` filter type) into a flat `Record<string, string>` ready to
|
|
22
|
+
* spread into an API params object.
|
|
23
|
+
*
|
|
24
|
+
* - `is` / `is-before` / `is-after` / `is-on-or-before` / `is-on-or-after`
|
|
25
|
+
* → `{ [fromKey]: ISO }` (+ `{ [opKey]: op }` when `opKey` is set)
|
|
26
|
+
* - `is-between` → `{ [fromKey]: ISO, [toKey]: ISO }`
|
|
27
|
+
* - `is-relative-to-today` → resolved to absolute ISO via `computeRelativeRange`
|
|
28
|
+
* → `{ [fromKey]: ISO, [toKey]: ISO }`
|
|
29
|
+
* - `is-empty` / `is-not-empty` → `{ [opKey]: 'empty' | 'not-empty' }` when
|
|
30
|
+
* `opKey` is set, otherwise `{}` (caller's choice how to encode)
|
|
31
|
+
* - `undefined` value → `{}`
|
|
32
|
+
*/
|
|
33
|
+
export declare const expandDateCompact: (value: DateCompactValue | undefined, options: ExpandDateCompactOptions) => Record<string, string>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RelativeDirection, RelativePeriod } from '../types';
|
|
2
|
+
|
|
3
|
+
export declare const startOfDay: (d: Date) => Date;
|
|
4
|
+
export declare const shiftPeriod: (d: Date, n: number, p: RelativePeriod) => Date;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a relative spec (Past 2 weeks / Next 1 month / This year …) to a
|
|
7
|
+
* concrete date range anchored at today. Shared between DatePill (preview
|
|
8
|
+
* calendar) and expandDateCompact (API param expansion).
|
|
9
|
+
*/
|
|
10
|
+
export declare const computeRelativeRange: (rel: {
|
|
11
|
+
direction: RelativeDirection;
|
|
12
|
+
count?: number;
|
|
13
|
+
period: RelativePeriod;
|
|
14
|
+
}) => {
|
|
15
|
+
from: Date;
|
|
16
|
+
to: Date;
|
|
17
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ColumnMenuProps {
|
|
4
|
+
/** trigger tetap terlihat (mis. sedang di-sort / di-freeze) walau tidak hover */
|
|
5
|
+
active: boolean;
|
|
6
|
+
canSort: boolean;
|
|
7
|
+
canHide: boolean;
|
|
8
|
+
sortDir: 'asc' | 'desc' | null;
|
|
9
|
+
frozen: boolean;
|
|
10
|
+
onSort: (dir: 'asc' | 'desc') => void;
|
|
11
|
+
onClearSort: () => void;
|
|
12
|
+
onToggleFreeze: () => void;
|
|
13
|
+
onHide: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const ColumnMenu: ({ active, canSort, canHide, sortDir, frozen, onSort, onClearSort, onToggleFreeze, onHide }: ColumnMenuProps) => React.JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { TableLayoutProps } from './types';
|
|
3
|
+
|
|
4
|
+
export declare const TableLayout: <T>({ columns, data, getRowKey, size, resizable, widthStorageKey, columnState, onColumnStateChange, columnMenu, sort, defaultSort, onSortChange, height, mobileLayout, mobileBreakpoint, groupSeparator, summary, emptyState }: TableLayoutProps<T>) => React.JSX.Element;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { FilterStorageAdapter } from '../../FilterBar/types';
|
|
2
|
+
import { TableLayoutSort } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface UseTableSortApiParamsOptions {
|
|
5
|
+
/** URL param holding the sort key. Default `'sortBy'`. */
|
|
6
|
+
sortKeyParam?: string;
|
|
7
|
+
/** URL param holding the sort direction. Default `'sortOrder'`. */
|
|
8
|
+
sortOrderParam?: string;
|
|
9
|
+
/** URL param for pagination — auto-reset to 1 on sort change. Default `'page'`. */
|
|
10
|
+
pageKey?: string;
|
|
11
|
+
/** Sort used when URL has no sort (and no persisted snapshot). Default `null`. */
|
|
12
|
+
defaultSort?: TableLayoutSort | null;
|
|
13
|
+
/**
|
|
14
|
+
* Rename output param keys (in `params`) without changing URL keys.
|
|
15
|
+
* Example: `{ sortBy: 'orderBy', sortOrder: 'orderDir' }` for a backend
|
|
16
|
+
* with different names. Defaults to the same names as the URL.
|
|
17
|
+
*/
|
|
18
|
+
paramKeys?: {
|
|
19
|
+
sortBy?: string;
|
|
20
|
+
sortOrder?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Namespace for persistence. When provided, sort is saved to storage and
|
|
24
|
+
* auto-restored when the user navigates away and comes back to a URL with
|
|
25
|
+
* no sort params (URL always wins on direct visits). Conventionally matches
|
|
26
|
+
* `FilterBar.savedViews.scopeId` so list state stays consistent.
|
|
27
|
+
*/
|
|
28
|
+
scopeId?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Pluggable persistence (default `localStorageAdapter`). Only used when
|
|
31
|
+
* `scopeId` is set. Same shape as `FilterBar`'s `storage` prop.
|
|
32
|
+
*/
|
|
33
|
+
storage?: FilterStorageAdapter;
|
|
34
|
+
}
|
|
35
|
+
export interface TableSortApiParams {
|
|
36
|
+
/** Current sort, decoded from URL (or `defaultSort` when URL is empty). */
|
|
37
|
+
sort: TableLayoutSort | null;
|
|
38
|
+
/** Update sort + write to URL. Pass `null` to clear. Resets `?page` to 1
|
|
39
|
+
* so the user lands on the first page of the newly-sorted result. */
|
|
40
|
+
setSort: (next: TableLayoutSort | null) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Flat API params, ready to spread into a service call.
|
|
43
|
+
* `{ sortBy: 'createdAt', sortOrder: 'desc' }` when sort active, `{}`
|
|
44
|
+
* when not. Memoized — stable reference for tanstack `queryKey`.
|
|
45
|
+
*/
|
|
46
|
+
params: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Persist `<TableLayout>` sort state in the URL (`?sortBy=…&sortOrder=…`) and
|
|
50
|
+
* surface a flat `{ sortBy, sortOrder }` object ready to spread into a service
|
|
51
|
+
* call — companion to `useFilterBarApiParams`.
|
|
52
|
+
*
|
|
53
|
+
* Passing the returned `onSortChange` to `<TableLayout>` flips it into
|
|
54
|
+
* **server mode** (it won't sort the data itself, it just emits — perfect
|
|
55
|
+
* when the API does the sorting). The hook resets `?page` to 1 on every
|
|
56
|
+
* sort change so the user always lands on the first page of the new order.
|
|
57
|
+
*
|
|
58
|
+
* **Persistence** (opt-in via `scopeId`): mirrors `FilterBar.useLastFilterState`.
|
|
59
|
+
* Sort is autosaved to storage on every change, and auto-restored on mount
|
|
60
|
+
* when the URL has no sort params. URL always wins on direct visits.
|
|
61
|
+
* `setSort(null)` clears both URL and storage so explicit resets aren't
|
|
62
|
+
* undone by the next back-navigation.
|
|
63
|
+
*/
|
|
64
|
+
export declare const useTableSortApiParams: (options?: UseTableSortApiParamsOptions) => TableSortApiParams;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TableLayout } from './TableLayout';
|
|
2
|
+
export { ColumnMenu } from './ColumnMenu';
|
|
3
|
+
export { useTableSortApiParams, type UseTableSortApiParamsOptions, type TableSortApiParams } from './hooks/useTableSortApiParams';
|
|
4
|
+
export type { TableLayoutProps, TableLayoutColumn, TableLayoutAlign, TableLayoutColumnState, TableLayoutCellProps, TableLayoutSort, TableLayoutSortDirection } from './types';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { IconName } from '@starasia/icon';
|
|
3
|
+
|
|
4
|
+
export type TableLayoutAlign = 'left' | 'center' | 'right';
|
|
5
|
+
export type TableLayoutSortDirection = 'asc' | 'desc';
|
|
6
|
+
export interface TableLayoutSort {
|
|
7
|
+
/** key kolom yang di-sort */
|
|
8
|
+
key: string;
|
|
9
|
+
direction: TableLayoutSortDirection;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Atribut span/skip untuk satu cell body (kasus merge / rowspan-colspan).
|
|
13
|
+
* Consumer yang menghitung merge-nya; TableLayout hanya menerapkan.
|
|
14
|
+
*/
|
|
15
|
+
export interface TableLayoutCellProps {
|
|
16
|
+
colSpan?: number;
|
|
17
|
+
rowSpan?: number;
|
|
18
|
+
/** kalau true, `<td>` tidak dirender (tertutup span dari sel lain) */
|
|
19
|
+
hidden?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface TableLayoutColumn<T> {
|
|
22
|
+
/**
|
|
23
|
+
* Identitas stabil kolom — dipakai untuk hide/show & reorder.
|
|
24
|
+
* Kalau kosong, fallback ke `accessor` (string), lalu ke index.
|
|
25
|
+
*/
|
|
26
|
+
key?: string;
|
|
27
|
+
/** key pada row untuk ambil nilai otomatis; abaikan kalau pakai `render` */
|
|
28
|
+
accessor?: keyof T | (string & {});
|
|
29
|
+
/** judul kolom di header */
|
|
30
|
+
title: ReactNode;
|
|
31
|
+
/** icon di sebelah kiri judul header (otomatis 16×16) */
|
|
32
|
+
icon?: IconName;
|
|
33
|
+
/** custom render isi cell; menang atas `accessor` */
|
|
34
|
+
render?: (row: T, rowIndex: number, data: T[]) => ReactNode;
|
|
35
|
+
/** perataan header + cell (default 'left') */
|
|
36
|
+
align?: TableLayoutAlign;
|
|
37
|
+
/** lebar awal kolom, mis. '200px' | 160 */
|
|
38
|
+
width?: string | number;
|
|
39
|
+
/** lebar minimum saat di-resize (px) */
|
|
40
|
+
minWidth?: number;
|
|
41
|
+
/** override resize per kolom; default ikut prop `resizable` di TableLayout */
|
|
42
|
+
resizable?: boolean;
|
|
43
|
+
/** boleh di-sort lewat menu kolom (default true bila ada accessor/sortKey) */
|
|
44
|
+
sortable?: boolean;
|
|
45
|
+
/** sumber nilai untuk sort kalau beda dari `accessor` (mis. kolom `render`) */
|
|
46
|
+
sortKey?: keyof T;
|
|
47
|
+
/** sembunyikan menu kolom (Ascending/Descending/Freeze/Hide) di kolom ini */
|
|
48
|
+
disableMenu?: boolean;
|
|
49
|
+
/** kolom terkunci — tidak bisa di-hide (item "Hide column" disembunyikan) */
|
|
50
|
+
locked?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Jadi headline di layout mobile (judul kartu / teks utama baris collapsed).
|
|
53
|
+
* Kalau tidak ada kolom yang ditandai, fallback ke kolom pertama.
|
|
54
|
+
*/
|
|
55
|
+
primary?: boolean;
|
|
56
|
+
/** sembunyikan kolom ini sepenuhnya saat layout mobile */
|
|
57
|
+
hideOnMobile?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Grouped header: kalau diisi, kolom ini jadi GRUP (accessor/render diabaikan)
|
|
60
|
+
* dan `children` adalah leaf-column yang menyetir body. Mendukung 1 tingkat grup.
|
|
61
|
+
* Catatan: saat ada grup, resize & persist-width otomatis non-aktif.
|
|
62
|
+
*/
|
|
63
|
+
children?: TableLayoutColumn<T>[];
|
|
64
|
+
/**
|
|
65
|
+
* Merge sel body (rowspan/colspan). Dipanggil per baris; kembalikan
|
|
66
|
+
* `{ colSpan, rowSpan, hidden }`. Tandai `hidden: true` pada sel yang
|
|
67
|
+
* tertutup oleh span sel lain. Diabaikan di layout mobile.
|
|
68
|
+
*/
|
|
69
|
+
cellProps?: (row: T, rowIndex: number, data: T[]) => TableLayoutCellProps;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* State visibility + urutan kolom. Bentuknya kompatibel dengan `ColumnPrefState`
|
|
73
|
+
* yang di-emit `FilterBar.onColumnsChange`, jadi bisa langsung disambungkan.
|
|
74
|
+
*/
|
|
75
|
+
export interface TableLayoutColumnState {
|
|
76
|
+
/** urutan key kolom (boleh subset / lengkap) */
|
|
77
|
+
order?: string[];
|
|
78
|
+
/** key kolom yang disembunyikan */
|
|
79
|
+
hidden?: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Freeze "sampai kolom ini": semua leaf dari kiri s/d key ini dipin (sticky kiri).
|
|
82
|
+
* Kosong = tidak ada yang dibekukan.
|
|
83
|
+
*/
|
|
84
|
+
frozenKey?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface TableLayoutProps<T> {
|
|
87
|
+
/** definisi kolom (wajib) */
|
|
88
|
+
columns: TableLayoutColumn<T>[];
|
|
89
|
+
/** baris data (wajib) */
|
|
90
|
+
data: T[];
|
|
91
|
+
/** key unik per row; default pakai index */
|
|
92
|
+
getRowKey?: (row: T, index: number) => string | number;
|
|
93
|
+
/** padding scale, diteruskan ke primitif Table (default 'md') */
|
|
94
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
95
|
+
/** aktifkan drag-resize lebar kolom (default false) */
|
|
96
|
+
resizable?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Kalau diisi, lebar hasil resize dipersist ke localStorage dengan key ini
|
|
99
|
+
* (lewat `useColumnWidths`) dan dipulihkan saat reload.
|
|
100
|
+
*/
|
|
101
|
+
widthStorageKey?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Visibility, urutan, & freeze kolom. Controlled bila diisi (sambungkan dari
|
|
104
|
+
* `FilterBar.onColumnsChange` + `onColumnStateChange`); kalau tidak, TableLayout
|
|
105
|
+
* kelola sendiri (uncontrolled).
|
|
106
|
+
*/
|
|
107
|
+
columnState?: TableLayoutColumnState;
|
|
108
|
+
/** dipanggil saat hide/freeze dari menu kolom mengubah state */
|
|
109
|
+
onColumnStateChange?: (state: TableLayoutColumnState) => void;
|
|
110
|
+
/**
|
|
111
|
+
* Tampilkan menu kolom di header (Ascending, Descending, Freeze, Hide column).
|
|
112
|
+
* Muncul saat hover / saat salah satu item aktif.
|
|
113
|
+
*/
|
|
114
|
+
columnMenu?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Sort aktif (controlled). Kalau `onSortChange` diisi → mode server (TableLayout
|
|
117
|
+
* tidak mengurutkan data sendiri, hanya emit). Tanpa `onSortChange` → client-side.
|
|
118
|
+
*/
|
|
119
|
+
sort?: TableLayoutSort | null;
|
|
120
|
+
/** sort awal (uncontrolled) */
|
|
121
|
+
defaultSort?: TableLayoutSort | null;
|
|
122
|
+
/** dipanggil saat sort berubah; keberadaannya mengaktifkan mode server */
|
|
123
|
+
onSortChange?: (sort: TableLayoutSort | null) => void;
|
|
124
|
+
/**
|
|
125
|
+
* Tinggi area tabel. Default: mengisi sampai bawah layar
|
|
126
|
+
* (`calc(100vh - posisiAtas)`, ikut menyesuaikan saat window di-resize).
|
|
127
|
+
* Override bebas oleh consumer, mis. '60vh' | 480 | '100%'.
|
|
128
|
+
* Area selalu scrollable (vertikal & horizontal) dan header sticky.
|
|
129
|
+
*/
|
|
130
|
+
height?: string | number;
|
|
131
|
+
/**
|
|
132
|
+
* Layout saat layar < `mobileBreakpoint`:
|
|
133
|
+
* - 'cards' : tiap row jadi kartu (label : value bertumpuk) — default
|
|
134
|
+
* - 'expand' : list ringkas (kolom `primary`) + tap untuk memuai detail
|
|
135
|
+
* - 'scroll' : tetap tabel dengan horizontal scroll
|
|
136
|
+
*/
|
|
137
|
+
mobileLayout?: 'cards' | 'expand' | 'scroll';
|
|
138
|
+
/** titik switch ke layout mobile (default 'md' = < 768px) */
|
|
139
|
+
mobileBreakpoint?: 'sm' | 'md';
|
|
140
|
+
/**
|
|
141
|
+
* Baris pemisah full-width sebelum `row` (mis. grup separator "— Jakarta —").
|
|
142
|
+
* Kembalikan `null` kalau tidak ada separator untuk baris itu.
|
|
143
|
+
*/
|
|
144
|
+
groupSeparator?: (row: T, index: number, prevRow: T | undefined) => ReactNode | null;
|
|
145
|
+
/** baris summary/total full-width di paling bawah body */
|
|
146
|
+
summary?: ReactNode;
|
|
147
|
+
/** ditampilkan saat `data` kosong */
|
|
148
|
+
emptyState?: ReactNode;
|
|
149
|
+
}
|
|
@@ -13,7 +13,7 @@ export * from './Section';
|
|
|
13
13
|
export * from './AdvanceFilterBar';
|
|
14
14
|
export * from './FilterBar';
|
|
15
15
|
export * from './TableSection';
|
|
16
|
-
export * from './
|
|
16
|
+
export * from './TableLayout';
|
|
17
17
|
export * from './EachUtils';
|
|
18
18
|
export * from './DecisionWrapper';
|
|
19
19
|
export * from './Form';
|