@starasia/admin 8.2.8 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/FilterBar/components/MobileFilterBar.d.ts +52 -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/hooks/useFilterBarControls.d.ts +39 -0
- package/dist/components/FilterBar/hooks/useLastFilterState.d.ts +11 -8
- package/dist/components/FilterBar/hooks/usePerPagePref.d.ts +23 -0
- package/dist/components/FilterBar/hooks/useUrlHydration.d.ts +23 -0
- package/dist/components/FilterBar/index.d.ts +3 -0
- package/dist/components/FilterBar/types.d.ts +25 -4
- 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 +2 -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 +182 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/index.cjs.js +43 -1
- package/dist/index.es.js +4098 -2675
- package/dist/index.umd.js +43 -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 -66
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
defaultPerPage: number;
|
|
28
|
+
showPagination: boolean;
|
|
29
|
+
showResultCount: boolean;
|
|
30
|
+
showPageSize: boolean;
|
|
31
|
+
hasColumns: boolean;
|
|
32
|
+
orderedColumns: OrderedColumn[];
|
|
33
|
+
allVisible: boolean;
|
|
34
|
+
onToggleVisibility: (key: string) => void;
|
|
35
|
+
onReorder: (from: number, to: number) => void;
|
|
36
|
+
onSetAllHidden: (hidden: boolean) => void;
|
|
37
|
+
showSavedViews: boolean;
|
|
38
|
+
savedViews: SavedView[];
|
|
39
|
+
onApplyView: (view: SavedView) => void;
|
|
40
|
+
onDeleteView: (id: string) => void;
|
|
41
|
+
onSaveView: (name: string) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mobile (<768px) variant of FilterBar. Replaces the horizontal toolbar with a
|
|
45
|
+
* two-row layout: a tight toolbar (search icon-toggle + "Filter (N)" trigger +
|
|
46
|
+
* saved views + columns + more) and a compact pagination row below.
|
|
47
|
+
*
|
|
48
|
+
* The filter pills + add-filter catalog move into a bottom `Drawer` opened by
|
|
49
|
+
* the Filter button. Page-size selection moves into the "More" overflow menu.
|
|
50
|
+
*/
|
|
51
|
+
export declare const MobileFilterBar: React.FC<MobileFilterBarProps>;
|
|
52
|
+
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;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FilterFieldConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface UseFilterBarControlsOptions {
|
|
4
|
+
/** Katalog filter yang SAMA dengan yang dipakai `<FilterBar>`. */
|
|
5
|
+
filterConfigs: FilterFieldConfig[];
|
|
6
|
+
/** Mirror key URL FilterBar (default sama dgn FilterBar). */
|
|
7
|
+
filterKey?: string;
|
|
8
|
+
activeFilterKey?: string;
|
|
9
|
+
pageKey?: string;
|
|
10
|
+
searchKey?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface FilterBarControls {
|
|
13
|
+
/** Set nilai filter (replace). Array kosong / `[]` → hapus filter. */
|
|
14
|
+
set: (key: string, value: string | string[]) => void;
|
|
15
|
+
/** Tambah/lepas satu nilai (cocok untuk klik kartu shortcut). */
|
|
16
|
+
toggle: (key: string, value: string) => void;
|
|
17
|
+
/** Hapus filter ini sepenuhnya (nilai + pill). */
|
|
18
|
+
clear: (key: string) => void;
|
|
19
|
+
/** Reset semua filter + search. */
|
|
20
|
+
clearAll: () => void;
|
|
21
|
+
/** Apakah `value` sedang aktif pada filter `key` (untuk styling kartu aktif). */
|
|
22
|
+
isActive: (key: string, value: string) => boolean;
|
|
23
|
+
/** Nilai-nilai aktif sekarang pada filter `key`. */
|
|
24
|
+
values: (key: string) => string[];
|
|
25
|
+
/** Ada filter aktif (apa pun). */
|
|
26
|
+
hasActiveFilter: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Kontrol filter dari LUAR `<FilterBar>` — untuk shortcut/quick-filter
|
|
30
|
+
* (mis. summary card status yang saat diklik memfilter tabel).
|
|
31
|
+
*
|
|
32
|
+
* Sumber kebenaran sama dengan FilterBar: URL. Jadi begitu helper ini menulis,
|
|
33
|
+
* pill FilterBar, tabel, dan `useFilterBarApiParams` ikut otomatis — tanpa
|
|
34
|
+
* lifting state. Companion arah-tulis dari `useFilterBarApiParams` (arah-baca).
|
|
35
|
+
*
|
|
36
|
+
* Khusus untuk filter select-type (`dropdown` / `dropdown-multiple` /
|
|
37
|
+
* `checkbox-multiple` / `radio`). Field lain (date) diabaikan.
|
|
38
|
+
*/
|
|
39
|
+
export declare const useFilterBarControls: ({ filterConfigs, filterKey, activeFilterKey, pageKey, searchKey }: UseFilterBarControlsOptions) => FilterBarControls;
|
|
@@ -3,7 +3,7 @@ import { FilterStorageAdapter } from '../types';
|
|
|
3
3
|
interface UseLastFilterStateOptions {
|
|
4
4
|
scopeId?: string;
|
|
5
5
|
storage: FilterStorageAdapter;
|
|
6
|
-
/** URL params to snapshot (e.g. [activeFilterKey, filterKey,
|
|
6
|
+
/** URL params to snapshot (e.g. [activeFilterKey, filterKey, pageKey]). */
|
|
7
7
|
snapshotKeys: string[];
|
|
8
8
|
/** Opt-out toggle (default true when scopeId is provided). */
|
|
9
9
|
enabled?: boolean;
|
|
@@ -11,17 +11,20 @@ interface UseLastFilterStateOptions {
|
|
|
11
11
|
/**
|
|
12
12
|
* Persists the latest filter URL params per scope, so link-based back
|
|
13
13
|
* navigation (which drops the query string) still restores the previous
|
|
14
|
-
* filters
|
|
15
|
-
* URL has none of the snapshot keys.
|
|
14
|
+
* filters. URL always wins.
|
|
16
15
|
*
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* NOTE: restore-ke-URL TIDAK dilakukan di sini — dikoordinasikan oleh
|
|
17
|
+
* `useUrlHydration` (satu `setSearchParams` gabungan) supaya tidak bentrok
|
|
18
|
+
* dengan restore perPage. Hook ini hanya autosave + expose `read()` + `clear()`.
|
|
19
|
+
*
|
|
20
|
+
* - **Autosave**: writes to `filterbar:last:{scopeId}` on every URL change.
|
|
21
|
+
* Snapshot KOSONG tidak ditulis (agar tidak menghapus snapshot saat URL
|
|
22
|
+
* kosong transient / pindah route).
|
|
23
|
+
* - **`clear()`**: removes the snapshot (dipanggil dari Reset).
|
|
22
24
|
*/
|
|
23
25
|
export declare function useLastFilterState({ scopeId, storage, snapshotKeys, enabled }: UseLastFilterStateOptions): {
|
|
24
26
|
enabled: boolean;
|
|
27
|
+
read: () => Record<string, string> | null;
|
|
25
28
|
clear: () => void;
|
|
26
29
|
};
|
|
27
30
|
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FilterStorageAdapter } from '../types';
|
|
2
|
+
|
|
3
|
+
interface UsePerPagePrefOptions {
|
|
4
|
+
scopeId?: string;
|
|
5
|
+
storage: FilterStorageAdapter;
|
|
6
|
+
perPageKey: string;
|
|
7
|
+
/** When provided, the restored value is validated against this list. */
|
|
8
|
+
pageSizeOptions?: number[];
|
|
9
|
+
/** Opt-out toggle (default true when scopeId is provided). */
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Persists the rows-per-page choice independently of the filter snapshot.
|
|
14
|
+
*
|
|
15
|
+
* UI preference — NOT wiped by Reset. Restore-ke-URL dikoordinasikan oleh
|
|
16
|
+
* `useUrlHydration`; hook ini hanya autosave + expose `read()` + `clear()`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function usePerPagePref({ scopeId, storage, perPageKey, pageSizeOptions, enabled }: UsePerPagePrefOptions): {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
read: () => string | null;
|
|
21
|
+
clear: () => void;
|
|
22
|
+
};
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface UseUrlHydrationOptions {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
/** Keys yang dianggap "filter snapshot" (mis. [filters, filter, page]). */
|
|
4
|
+
snapshotKeys: string[];
|
|
5
|
+
perPageKey: string;
|
|
6
|
+
/** Snapshot filter tersimpan (atau null). */
|
|
7
|
+
readSnapshot: () => Record<string, string> | null;
|
|
8
|
+
/** Page size tersimpan (string) atau null. */
|
|
9
|
+
readPerPage: () => string | null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Hidrasi URL dari preferensi tersimpan. Restore filter-snapshot & perPage
|
|
13
|
+
* digabung lewat micro-batcher → tidak saling menimpa, dan tahan banting untuk
|
|
14
|
+
* >1 FilterBar dalam satu halaman.
|
|
15
|
+
*
|
|
16
|
+
* Jalan saat "tiba" di halaman: mount ATAU `location.pathname` berubah (navigasi
|
|
17
|
+
* balik), jadi halaman yang di-keep-alive router tetap ter-restore tanpa hard
|
|
18
|
+
* refresh. Di-gate ke perubahan pathname agar TIDAK loop saat user mengubah
|
|
19
|
+
* filter di halaman yang sama. URL selalu menang (key yang sudah ada di URL
|
|
20
|
+
* tidak ditimpa).
|
|
21
|
+
*/
|
|
22
|
+
export declare function useUrlHydration({ enabled, snapshotKeys, perPageKey, readSnapshot, readPerPage }: UseUrlHydrationOptions): void;
|
|
23
|
+
export {};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { FilterBar } from './FilterBar';
|
|
2
|
+
export { useFilterBarApiParams, type UseFilterBarApiParamsOptions, type FilterBarApiParams } from './hooks/useFilterBarApiParams';
|
|
3
|
+
export { useFilterBarControls, type UseFilterBarControlsOptions, type FilterBarControls } from './hooks/useFilterBarControls';
|
|
4
|
+
export { expandDateCompact, type ExpandDateCompactOptions } from './utils/expand-date-compact';
|
|
2
5
|
export { localStorageAdapter, storageKeys } from './utils/storage-adapter';
|
|
3
6
|
export type { FilterBarProps, FilterFieldConfig, FilterFieldType, SelectFilterField, CheckboxMultipleFilterField, DateFilterField, DateCompactFilterField, DateCompactOp, DateCompactValue, RelativeDirection, RelativePeriod, ColumnConfig, ColumnPrefState, SavedView, FilterStorageAdapter } from './types';
|
|
@@ -132,6 +132,12 @@ export interface FilterBarProps {
|
|
|
132
132
|
perPageKey?: string;
|
|
133
133
|
/** Page-size choices. Default `[10, 25, 50, 100]`. */
|
|
134
134
|
pageSizeOptions?: number[];
|
|
135
|
+
/**
|
|
136
|
+
* Initial page size used when the URL (and the persisted preference under
|
|
137
|
+
* `savedViews.scopeId`) is empty. Defaults to `pageSizeOptions[0]`, so
|
|
138
|
+
* `pageSizeOptions={[25, 50, 100]}` naturally defaults to 25.
|
|
139
|
+
*/
|
|
140
|
+
defaultPerPage?: number;
|
|
135
141
|
showPagination?: boolean;
|
|
136
142
|
showResultCount?: boolean;
|
|
137
143
|
/** Show the rows-per-page selector. Default `true`. */
|
|
@@ -140,15 +146,30 @@ export interface FilterBarProps {
|
|
|
140
146
|
columns?: ColumnConfig[];
|
|
141
147
|
/** Emitted when visibility/order changes — consumer applies to its table. */
|
|
142
148
|
onColumnsChange?: (state: ColumnPrefState) => void;
|
|
143
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Controlled column state. Saat diisi, panel Customize Column ikut state ini
|
|
151
|
+
* (mis. hide/freeze dari menu kolom TableLayout) — sinkron dua arah dgn
|
|
152
|
+
* `onColumnsChange`. `frozenKey` & field lain di luar order/hidden diabaikan.
|
|
153
|
+
*/
|
|
154
|
+
columnsState?: Partial<ColumnPrefState>;
|
|
155
|
+
/**
|
|
156
|
+
* Namespace persistensi per tabel — dipakai untuk column prefs, last-filter
|
|
157
|
+
* snapshot, perPage, & saved views. WAJIB diisi agar fitur persist aktif.
|
|
158
|
+
* Boleh juga lewat `savedViews.scopeId` (kompatibilitas lama); kalau dua-duanya
|
|
159
|
+
* ada, prop top-level ini menang.
|
|
160
|
+
*/
|
|
161
|
+
scopeId?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Saved views. `enabled` mengontrol tampil/sembunyinya tombol Saved Views.
|
|
164
|
+
* `scopeId` di sini opsional & deprecated — pakai prop `scopeId` top-level.
|
|
165
|
+
*/
|
|
144
166
|
savedViews?: {
|
|
145
167
|
enabled?: boolean;
|
|
146
|
-
scopeId
|
|
168
|
+
scopeId?: string;
|
|
147
169
|
};
|
|
148
170
|
/**
|
|
149
171
|
* Auto-restore the last applied filter state when the URL has no filter
|
|
150
|
-
* params (e.g. link-based back navigation). Requires `
|
|
151
|
-
* Default `true`.
|
|
172
|
+
* params (e.g. link-based back navigation). Requires `scopeId`. Default `true`.
|
|
152
173
|
*/
|
|
153
174
|
persistLastState?: boolean;
|
|
154
175
|
/** Persistence adapter (default: localStorage via @starasia/storage). */
|
|
@@ -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, rowActions, }: 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, TableLayoutRowAction } from './types';
|