@starasia/admin 8.0.2 → 8.0.3

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.
@@ -0,0 +1,14 @@
1
+ import { default as React } from 'react';
2
+ import { FilterBarProps } from './types';
3
+
4
+ /**
5
+ * FilterBar — horizontal data-table toolbar.
6
+ *
7
+ * All-in-one: reads/writes filter + pagination state directly from the URL
8
+ * (via `useRouteInfo`), persists saved views & column prefs through a swappable
9
+ * storage adapter. Successor to `AdvanceFilterBar`.
10
+ *
11
+ * Layout — left: Add Filter + pills + Reset; right: result count + page size +
12
+ * pager + search + saved views + columns.
13
+ */
14
+ export declare const FilterBar: React.FC<FilterBarProps>;
@@ -0,0 +1,15 @@
1
+ import { default as React } from 'react';
2
+ import { FilterFieldConfig } from '../types';
3
+
4
+ interface AddFilterMenuProps {
5
+ configs: FilterFieldConfig[];
6
+ activeKeys: string[];
7
+ onToggle: (key: string) => void;
8
+ hasActiveFilter?: boolean;
9
+ }
10
+ /**
11
+ * The ⚙ Filter button → a menu listing the filter catalog. Selecting an item
12
+ * toggles its pill on the bar (the menu stays open for multi-add).
13
+ */
14
+ export declare const AddFilterMenu: React.FC<AddFilterMenuProps>;
15
+ export {};
@@ -0,0 +1,17 @@
1
+ import { default as React } from 'react';
2
+ import { OrderedColumn } from '../hooks/useColumnPrefs';
3
+
4
+ interface ColumnCustomizerProps {
5
+ columns: OrderedColumn[];
6
+ allVisible: boolean;
7
+ onToggle: (key: string) => void;
8
+ onReorder: (from: number, to: number) => void;
9
+ onSetAllHidden: (hidden: boolean) => void;
10
+ }
11
+ /**
12
+ * "Customize Column Table" panel — visibility toggles + hand-rolled HTML5
13
+ * drag-reorder (zero-dependency). Keyboard fallback: focus a row's drag handle
14
+ * and use ↑/↓ to move it.
15
+ */
16
+ export declare const ColumnCustomizer: React.FC<ColumnCustomizerProps>;
17
+ export {};
@@ -0,0 +1,14 @@
1
+ import { default as React } from 'react';
2
+ import { FilterFieldConfig } from '../types';
3
+
4
+ interface FilterPillsProps {
5
+ activeKeys: string[];
6
+ configByKey: Record<string, FilterFieldConfig>;
7
+ getValue: (field: FilterFieldConfig) => unknown;
8
+ setValue: (field: FilterFieldConfig, value: unknown) => void;
9
+ clearValue: (key: string) => void;
10
+ removeFilter: (key: string) => void;
11
+ }
12
+ /** Renders the active filter pills, dispatching each to its editor by type. */
13
+ export declare const FilterPills: React.FC<FilterPillsProps>;
14
+ export {};
@@ -0,0 +1,19 @@
1
+ import { default as React } from 'react';
2
+ import { Pagination as PaginationMeta } from '../../TableSection/types';
3
+
4
+ interface PaginationClusterProps {
5
+ pagination?: PaginationMeta;
6
+ pageKey?: string;
7
+ perPageKey?: string;
8
+ pageSizeOptions?: number[];
9
+ defaultPerPage?: number;
10
+ showResultCount?: boolean;
11
+ showPagination?: boolean;
12
+ }
13
+ /**
14
+ * Right-cluster pagination controls — mirrors `TableSection/Footer` semantics
15
+ * (URL `?page`/`?perPage`, same range math), laid out per the FilterBar Figma:
16
+ * result count → page-size (Menu) → pager.
17
+ */
18
+ export declare const PaginationCluster: React.FC<PaginationClusterProps>;
19
+ export {};
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ import { SavedView } from '../types';
3
+
4
+ interface SavedViewsProps {
5
+ views: SavedView[];
6
+ onApply: (view: SavedView) => void;
7
+ onDelete: (id: string) => void;
8
+ onSave: (name: string) => void;
9
+ }
10
+ /** Saved-views menu: apply / delete existing views, and save the current one. */
11
+ export declare const SavedViews: React.FC<SavedViewsProps>;
12
+ export {};
@@ -0,0 +1,21 @@
1
+ import { default as React } from 'react';
2
+
3
+ interface SearchBoxProps {
4
+ searchKey?: string;
5
+ filterKey?: string;
6
+ pageKey?: string;
7
+ placeholder?: string;
8
+ /** Single-key shortcut that focuses search. Default `/`. */
9
+ shortcut?: string;
10
+ }
11
+ /**
12
+ * Search control that animates open from an icon button. The input is always
13
+ * mounted so the wrapper can transition its width smoothly; the search icon
14
+ * lives in the input's `leftIcon` while expanded (no separate icon then).
15
+ *
16
+ * Behavior: clicking the icon (or the `/` shortcut) expands + focuses. On blur,
17
+ * it collapses **only when empty** — a non-empty search stays expanded. The
18
+ * term is written into the `?filter` blob (debounced, also resets the page).
19
+ */
20
+ export declare const SearchBox: React.FC<SearchBoxProps>;
21
+ export {};
@@ -0,0 +1,15 @@
1
+ import { default as React } from 'react';
2
+ import { DateFilterField, DateRangeFilterField } from '../../types';
3
+
4
+ interface DatePillProps {
5
+ field: DateFilterField | DateRangeFilterField;
6
+ value: Date | {
7
+ from: Date;
8
+ to: Date;
9
+ } | undefined;
10
+ onChange: (v: unknown) => void;
11
+ onClear: () => void;
12
+ }
13
+ /** Date / date-range pill backed by `StarasiaDate` (input-trigger mode). */
14
+ export declare const DatePill: React.FC<DatePillProps>;
15
+ export {};
@@ -0,0 +1,21 @@
1
+ import { default as React } from 'react';
2
+ import { SelectFilterField, PersonFilterField } from '../../types';
3
+
4
+ interface SelectPillProps {
5
+ field: SelectFilterField | PersonFilterField;
6
+ value: {
7
+ label: string;
8
+ value: string;
9
+ }[];
10
+ onChange: (v: {
11
+ label: string;
12
+ value: string;
13
+ }[]) => void;
14
+ }
15
+ /**
16
+ * Select pill — covers `dropdown` (single), `dropdown-multiple`, `radio`
17
+ * (single) and `person` (searchable multi). Backed by `@starasia/dropdown`,
18
+ * which renders the checkbox list / search seen in the Figma "Default Filter".
19
+ */
20
+ export declare const SelectPill: React.FC<SelectPillProps>;
21
+ export {};
@@ -0,0 +1,28 @@
1
+ import { ColumnConfig, ColumnPrefState, FilterStorageAdapter } from '../types';
2
+
3
+ interface UseColumnPrefsOptions {
4
+ columns: ColumnConfig[];
5
+ scopeId?: string;
6
+ storage: FilterStorageAdapter;
7
+ onColumnsChange?: (state: ColumnPrefState) => void;
8
+ }
9
+ export interface OrderedColumn extends ColumnConfig {
10
+ visible: boolean;
11
+ }
12
+ /**
13
+ * Column visibility + ordering state for the Customize Column panel.
14
+ * Persists to the storage adapter (namespaced by `scopeId`) and notifies the
15
+ * consumer via `onColumnsChange` so it can apply the result to its table.
16
+ * Without a `scopeId` the state is session-only (still fully functional).
17
+ */
18
+ export declare function useColumnPrefs({ columns, scopeId, storage, onColumnsChange }: UseColumnPrefsOptions): {
19
+ state: ColumnPrefState;
20
+ orderedColumns: OrderedColumn[];
21
+ allVisible: boolean;
22
+ toggleVisibility: (key: string) => void;
23
+ reorder: (from: number, to: number) => void;
24
+ setAllHidden: (hidden: boolean) => void;
25
+ reset: () => void;
26
+ applyState: (next: ColumnPrefState) => void;
27
+ };
28
+ export {};
@@ -0,0 +1,31 @@
1
+ import { FilterFieldConfig } from '../types';
2
+
3
+ interface UseFilterUrlOptions {
4
+ configs: FilterFieldConfig[];
5
+ filterKey?: string;
6
+ activeFilterKey?: string;
7
+ pageKey?: string;
8
+ searchKey?: string;
9
+ }
10
+ /**
11
+ * Single source of truth for filter state in the URL.
12
+ *
13
+ * - Active pills: `?filters=key1,key2` (comma list), unioned with any keys
14
+ * already present in the value blob (so a shared/saved URL restores pills).
15
+ * - Values: `?filter={...}` JSON-blob via the reused `AdvanceFilterBar/utils`.
16
+ */
17
+ export declare function useFilterUrl({ configs, filterKey, activeFilterKey, pageKey, searchKey }: UseFilterUrlOptions): {
18
+ activeKeys: string[];
19
+ configByKey: {
20
+ [k: string]: FilterFieldConfig;
21
+ };
22
+ hasActiveFilter: boolean;
23
+ addFilter: (key: string) => void;
24
+ removeFilter: (key: string) => void;
25
+ toggleFilter: (key: string) => void;
26
+ getValue: (field: FilterFieldConfig) => unknown;
27
+ setValue: (field: FilterFieldConfig, value: unknown) => void;
28
+ clearValue: (key: string) => void;
29
+ clearAll: () => void;
30
+ };
31
+ export {};
@@ -0,0 +1,26 @@
1
+ import { ColumnPrefState, FilterStorageAdapter, SavedView } from '../types';
2
+
3
+ interface UseSavedViewsOptions {
4
+ scopeId?: string;
5
+ storage: FilterStorageAdapter;
6
+ /** URL params captured into a view snapshot (filters, filter blob, perPage…). */
7
+ snapshotKeys: string[];
8
+ pageKey?: string;
9
+ /** Capture the current column prefs at save time. */
10
+ getColumns?: () => ColumnPrefState;
11
+ /** Apply column prefs when a view is restored. */
12
+ applyColumns?: (state: ColumnPrefState) => void;
13
+ }
14
+ /**
15
+ * Named saved views = persisted snapshots of the relevant URL params (+ column
16
+ * prefs). Applying a view hydrates the URL (and columns), so the rest of the
17
+ * bar reacts automatically.
18
+ */
19
+ export declare function useSavedViews({ scopeId, storage, snapshotKeys, pageKey, getColumns, applyColumns }: UseSavedViewsOptions): {
20
+ enabled: boolean;
21
+ views: SavedView[];
22
+ saveView: (name: string) => void;
23
+ deleteView: (id: string) => void;
24
+ applyView: (view: SavedView) => void;
25
+ };
26
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Focus/open search when a single-key shortcut (default `/`) is pressed —
3
+ * ignored while the user is typing in another field.
4
+ */
5
+ export declare function useSearchShortcut(key: string, onTrigger: () => void, enabled?: boolean): void;
@@ -0,0 +1,3 @@
1
+ export { FilterBar } from './FilterBar';
2
+ export { localStorageAdapter, storageKeys } from './utils/storage-adapter';
3
+ export type { FilterBarProps, FilterFieldConfig, FilterFieldType, SelectFilterField, PersonFilterField, DateFilterField, DateRangeFilterField, ColumnConfig, ColumnPrefState, SavedView, FilterStorageAdapter } from './types';
@@ -0,0 +1,124 @@
1
+ import { ReactNode } from 'react';
2
+ import { Pagination } from '../TableSection/types';
3
+
4
+ /**
5
+ * Filter field catalog.
6
+ *
7
+ * `type` mirrors the `InputType` union consumed by `AdvanceFilterBar/utils`
8
+ * (`handleChangeFilter`, `getValuesFilter`, …) so the JSON-blob URL encoding
9
+ * (`?filter={...}`) is reused as-is. `key` is the param name inside that blob.
10
+ */
11
+ export type FilterFieldType = 'date' | 'date-range' | 'dropdown' | 'dropdown-multiple' | 'radio' | 'person';
12
+ interface BaseFilterField {
13
+ /** Stable key — used as the field name inside the `?filter` blob. */
14
+ key: string;
15
+ /** Label shown in the Add Filter menu and on the active pill. */
16
+ label: string;
17
+ /** Optional leading icon (IconName via <Icon/> or any node). */
18
+ icon?: ReactNode;
19
+ /** When true the pill cannot be removed once added. */
20
+ locked?: boolean;
21
+ }
22
+ export interface SelectFilterField extends BaseFilterField {
23
+ type: 'dropdown' | 'dropdown-multiple' | 'radio';
24
+ options: {
25
+ label: string;
26
+ value: string;
27
+ }[];
28
+ searchable?: boolean;
29
+ isLoading?: boolean;
30
+ onSearch?: (q: string) => void;
31
+ }
32
+ export interface PersonFilterField extends BaseFilterField {
33
+ type: 'person';
34
+ options: {
35
+ label: string;
36
+ value: string;
37
+ description?: string;
38
+ avatar?: ReactNode;
39
+ }[];
40
+ searchable?: boolean;
41
+ isLoading?: boolean;
42
+ onSearch?: (q: string) => void;
43
+ }
44
+ export interface DateFilterField extends BaseFilterField {
45
+ type: 'date';
46
+ mode?: 'days' | 'months' | 'years';
47
+ }
48
+ export interface DateRangeFilterField extends BaseFilterField {
49
+ type: 'date-range';
50
+ }
51
+ export type FilterFieldConfig = SelectFilterField | PersonFilterField | DateFilterField | DateRangeFilterField;
52
+ /** A single column entry fed to the Customize Column panel. */
53
+ export interface ColumnConfig {
54
+ key: string;
55
+ title: string;
56
+ icon?: ReactNode;
57
+ /** Locked columns can't be hidden or reordered. */
58
+ locked?: boolean;
59
+ }
60
+ /** Visibility + ordering state emitted by the Customize Column panel. */
61
+ export interface ColumnPrefState {
62
+ /** Ordered column keys (full order, including hidden). */
63
+ order: string[];
64
+ /** Keys that are currently hidden. */
65
+ hidden: string[];
66
+ }
67
+ /** A named, persisted snapshot of filter + column state. */
68
+ export interface SavedView {
69
+ id: string;
70
+ name: string;
71
+ /** Snapshot of the relevant URL params (filters, filter, perPage, sort). */
72
+ query: Record<string, string>;
73
+ columns?: ColumnPrefState;
74
+ createdAt: number;
75
+ }
76
+ /**
77
+ * Pluggable persistence for saved views & column prefs.
78
+ * Default impl is localStorage-backed (`localStorageAdapter`); swap for a
79
+ * backend-backed adapter via the FilterBar `storage` prop.
80
+ */
81
+ export interface FilterStorageAdapter {
82
+ get<T>(key: string): T | null;
83
+ set<T>(key: string, value: T): void;
84
+ remove(key: string): void;
85
+ }
86
+ export interface FilterBarProps {
87
+ /** Catalog of filters offered in the Add Filter menu. */
88
+ filterConfigs?: FilterFieldConfig[];
89
+ /** URL param holding the JSON-blob of filter values. Default `'filter'`. */
90
+ filterKey?: string;
91
+ /** URL param holding the comma list of active pill keys. Default `'filters'`. */
92
+ activeFilterKey?: string;
93
+ /** URL param for the current page. Default `'page'`. */
94
+ pageKey?: string;
95
+ /** Show the expandable search control. */
96
+ searchable?: boolean;
97
+ /** Field name for search (inside the `?filter` blob). Default `'q'`. */
98
+ searchKey?: string;
99
+ searchPlaceholder?: string;
100
+ /** Single-key shortcut that focuses search. Default `'/'`. */
101
+ searchShortcut?: string;
102
+ /** Pagination meta from the API (drives result count + pager). */
103
+ pagination?: Pagination;
104
+ /** URL param for page size. Default `'perPage'`. */
105
+ perPageKey?: string;
106
+ /** Page-size choices. Default `[10, 25, 50, 100]`. */
107
+ pageSizeOptions?: number[];
108
+ showPagination?: boolean;
109
+ showResultCount?: boolean;
110
+ /** Columns shown in the Customize Column panel. */
111
+ columns?: ColumnConfig[];
112
+ /** Emitted when visibility/order changes — consumer applies to its table. */
113
+ onColumnsChange?: (state: ColumnPrefState) => void;
114
+ /** Enable named saved views. `scopeId` namespaces persisted data per table. */
115
+ savedViews?: {
116
+ enabled?: boolean;
117
+ scopeId: string;
118
+ };
119
+ /** Persistence adapter (default: localStorage via @starasia/storage). */
120
+ storage?: FilterStorageAdapter;
121
+ /** Called after a full reset (clears filters + search). */
122
+ onReset?: () => void;
123
+ }
124
+ export {};
@@ -0,0 +1,15 @@
1
+ import { FilterStorageAdapter } from '../types';
2
+
3
+ /**
4
+ * Default persistence adapter backed by `@starasia/storage` (SecureStorage →
5
+ * localStorage). Saved views and column prefs flow through this.
6
+ *
7
+ * Swap for a backend-backed adapter by passing a custom `FilterStorageAdapter`
8
+ * to the FilterBar `storage` prop — the rest of the component is agnostic.
9
+ */
10
+ export declare const localStorageAdapter: FilterStorageAdapter;
11
+ /** Namespaced storage keys so multiple tables don't collide. */
12
+ export declare const storageKeys: {
13
+ columns: (scopeId: string) => string;
14
+ savedViews: (scopeId: string) => string;
15
+ };
@@ -1,6 +1,6 @@
1
1
  import { BoxProps } from '@starasia/box';
2
2
  import { PropsWithChildren, ReactNode, default as React } from 'react';
3
- import { IBadges } from '@starasia/badges';
3
+ import { IBadge } from '@starasia/badges';
4
4
  import { IconName } from '@starasia/icon/dist/types';
5
5
  import { ButtonProps } from '@starasia/button/dist/types';
6
6
 
@@ -24,9 +24,9 @@ interface ActionProps {
24
24
  interface SubHeaderProps {
25
25
  /** Content of this header row. */
26
26
  content: ReactNode;
27
- /** CSS `order` for positioning this row within the page column (header / subHeaders / body). Negative values move it above the main header. */
27
+ /** CSS `order` within the header region. Negative values render this row above the main header; positive values below it (but always above the page body). */
28
28
  order?: number;
29
- /** Show the subtle bottom border (same style as the main header). Defaults to `true`. */
29
+ /** Show the subtle bottom border (same style as the main header). Defaults to `true`. When `false`, the row's bottom padding is dropped too so the stack stays tight. */
30
30
  borderBottom?: boolean;
31
31
  }
32
32
  export interface PageLayoutProps extends PropsWithChildren {
@@ -44,7 +44,7 @@ export interface PageLayoutProps extends PropsWithChildren {
44
44
  subHeaders?: SubHeaderProps[];
45
45
  backButton?: BackButtonProps;
46
46
  action?: ActionProps[] | React.ReactNode;
47
- titleTagProps?: Omit<IBadges, "text" | "border" | "size">;
47
+ titleTagProps?: Omit<IBadge, "text" | "border" | "size">;
48
48
  containerProps?: Omit<BoxProps, "variant" | "gap">;
49
49
  headerProps?: Omit<BoxProps, "gap">;
50
50
  }
@@ -11,6 +11,7 @@ export * from './Pressable';
11
11
  export * from './RightDrawer';
12
12
  export * from './Section';
13
13
  export * from './AdvanceFilterBar';
14
+ export * from './FilterBar';
14
15
  export * from './TableSection';
15
16
  export * from './EachUtils';
16
17
  export * from './DecisionWrapper';