Overview
Akifilter component from the @akinon/akifilter package It is a customizable
filter component for React applications and provides advanced filtering
features.
Example Usage
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
const filterSchema: AkifilterSchema = [
{
key: 'username',
type: 'text',
label: 'Username',
placeholder: 'Enter username'
},
{
key: 'status',
type: 'select',
label: 'Status',
placeholder: 'Select status',
options: [
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' }
]
},
{
key: 'isActive',
type: 'checkbox',
label: 'Is Active'
}
];
const MyComponent = () => {
const handleValuesChange = (values: Record<string, unknown>) => {
console.log('Filter values changed:', values);
// Use values to fetch data, update URL params, etc.
};
return (
<Akifilter
filterSchema={filterSchema}
storageNamespace="users-filter"
onValuesChange={handleValuesChange}
defaultValues={{ status: 'active' }}
/>
);
};Akifilter Props
Akifilter props provide various options for configuring the filter component:
| Property | Description | Type | Default |
|---|---|---|---|
filterSchema | Declarative description of the filter fields | AkifilterSchema | [] |
storageNamespace | Optional namespace for local storage persistence. Filters are saved and restored automatically. | string | - |
defaultValues | Default values supplied by the host application | Partial<TFieldValues> | - |
onValuesChange | Callback fired on every filter value change with the normalised payload | (values: Partial<T>) => void | - |
onVisibleFieldsChange | Callback fired whenever visible field keys change | (keys: string[]) => void | - |
onSectionVisibleFieldsChange | Called with a section’s key and the keys of the fields it currently renders | (sectionKey: string, keys: string[]) => void | - |
onImportCsv | Called with the CSV file the user picked from the toolbar’s import button | (file: File) => void | - |
onImportXls | Called with the XLS/XLSX file the user picked from the toolbar’s import button | (file: File) => void | - |
onSectionImportCsv | Called with a section’s key and the CSV file picked from that section’s header | (sectionKey: string, file: File) => void | - |
onSectionImportXls | Called with a section’s key and the XLS/XLSX file picked from that section’s header | (sectionKey: string, file: File) => void | - |
onClearAll | Callback triggered when user requests clearing all filters | () => void | - |
enableImportCsv | Shows the CSV import button in the toolbar | boolean | false |
enableImportXls | Shows the XLS/XLSX import button in the toolbar | boolean | false |
filterActionsRef | Ref to access imperative filter actions like clearing specific filter values programmatically | React.Ref<AkifilterActionsRef> | - |
filterButtons | Quick-filter toggle buttons rendered to the left of the active filters bar | AkifilterButton[] | [] |
onFilterButtonsChange | Callback fired whenever the set of active filter buttons changes | (active: AkifilterButton[]) => void | - |
externalAppliedFilters | Extra active-filter chips whose source is not a form field (e.g. an imported file) | AppliedFilter[] | [] |
onRemoveExternalFilter | Called with the key of an external chip the user removed, and for each one on “Clear All” | (key: string) => void | - |
Storage Behavior
When storageNamespace is provided, Akifilter automatically persists filter
values and field visibility preferences to local storage:
- Filter values are saved on change (debounced)
- Visible field selections are saved when toggled
- Selectable sections persist their visible-field selection under a dedicated per-section storage key (see Section Fields)
- Values are restored on component mount
- A restored visible-field selection takes precedence over a boolean
config.visible - Nothing is written until the user actually changes the selection, so editing
config.visiblelater still reaches everyone who never opened the modal — the storage key is hashed from the field signature (key/type/mode) and does not change when aconfigdoes - Storage key is generated based on schema structure and namespace
This enables users to maintain their filter preferences across sessions.
Filter Buttons
The filterButtons prop renders a row of icon-only quick-filter toggle buttons
to the left of the active filters bar. They are useful for one-click filters
such as filtering products by type. Buttons live outside the filterSchema
form, so they can be used with or without form fields.
import {
Akifilter,
type AkifilterButton,
type AkifilterSchema
} from '@akinon/akifilter';
const filterSchema: AkifilterSchema = [
{
key: 'base_code',
type: 'text',
label: 'Base Code',
config: { visible: true }
},
{ key: 'sku', type: 'text', label: 'SKU', config: { visible: true } }
];
const filterButtons: AkifilterButton[] = [
{
key: 'product_type',
value: '01',
label: 'Simple',
icon: 'simple_product',
appliedPrefix: 'Product Type'
},
{
key: 'product_type',
value: '02',
label: 'Meta',
icon: 'variant_product',
appliedPrefix: 'Product Type'
}
];
const MyComponent = () => {
return (
<Akifilter
filterSchema={filterSchema}
filterButtons={filterButtons}
storageNamespace="product-pool"
onValuesChange={values => console.log('Values:', values)}
onFilterButtonsChange={active => console.log('Active buttons:', active)}
/>
);
};
// Activating Simple + Meta emits: { product_type: ['01', '02'] }AkifilterButton API
| Property | Description | Type | Required |
|---|---|---|---|
key | Filter key the button contributes to when active (e.g. 'product_type'). Must be distinct from your filterSchema field keys | string | Yes |
value | Value applied to key when active. Also the button’s identity, so it must be unique across the whole filterButtons array | string | number | Yes |
label | Human-readable name shown in the button tooltip and as the value part of the applied chip | string | Yes |
appliedPrefix | Prefix shown before label in the applied chip, rendered as "{appliedPrefix}: {label}" | string | Yes |
icon | Icon (from @akinon/icons) rendered inside the button | IconName | Yes |
singleChoice | When true, activating this button deactivates every other button | boolean | No |
Behavior
- Value merging: Active button values are merged into the
onValuesChangepayload under each button’skey. Buttons that share a key are combined into an array (e.g.{ product_type: ['simple', 'variant'] }); buttons with different keys emit under their own keys. - Key collisions: Button values are merged after the form values. So if a button key matches a filterSchema field key, the button value wins and overwrites whatever the form field put in the payload. Keep button keys distinct from schema field keys, otherwise a form selection can get dropped with no warning.
- Applied chips: Each active button appears as a removable chip in the
active filters bar (
{appliedPrefix}: {label}). Removing the chip deactivates the button, and Clear All deactivates all buttons. - Persistence: When
storageNamespaceis provided, active buttons are persisted to local storage alongside the other filters and restored on mount. - Single choice: Set
singleChoice: trueon buttons that should behave like a radio group — activating one deactivates the others.
Provide
filterButtonsas a referentially stable array (a module constant or memoized value) that is available on first render. Keep each buttonkeydistinct from yourfilterSchemafield keys, and eachvalueunique across all buttons.
Field Configuration
Each field in filterSchema supports an optional config property that enables
advanced control over field behavior:
const filterSchema: AkifilterSchema = [
{
key: 'status',
type: 'select',
label: 'Status',
placeholder: 'Select status',
options: [
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' }
],
config: {
disabled: true, // Field is disabled
visible: true // Field is visible on the first render
}
}
];config.disabled
Controls whether a filter field is disabled. Can be either:
-
Boolean: Static disable state
config: { disabled: true; } // Field always disabled -
Function: Dynamic disable state based on other field values
config: { disabled: formValues => !formValues.status; // Disable when status is empty }
config.visible
Controls whether a filter field is displayed. Can be either:
-
Boolean: the field’s visibility on the first render only. It decides whether the field is part of the default visible set; from then on the user’s selection in the “Select visible filters” modal wins and is restored on every later mount.
config: { visible: true; } // In the default set — until the user hides itconfig: { visible: false; } // Not in the default set — until the user shows itThe default set itself is all-or-nothing: if any field declares
visible: true, the default set is exactly those fields, and theDEFAULT_VISIBLE_COUNTfallback does not apply. Marking one field visible therefore hides every field the fallback would otherwise have shown — mark the whole set you want on screen, not just the extra one. -
Function: Dynamic visibility based on other field values
config: { visible: formValues => formValues.status === 'admin'; // Show only if admin status }The function is authoritative — the picker cannot override it — and it is resolved against the restored values from the first render on, so a field gated on a persisted value comes back visible, and with its own value intact. A field gated on a function takes no part in the default visible set: it neither counts as an explicit marking nor consumes one of the
DEFAULT_VISIBLE_COUNTslots. -
Function with
currentVisible: The callback receives an optional second parametercurrentVisible(boolean | undefined) indicating whether the field is currently visible (based on the user’s field visibility selection). This allows you to combine dynamic logic with the user’s visibility preference.config: { visible: (formValues, currentVisible) => { // Keep the field visible if the user has toggled it on, // otherwise show only when status is 'error' return currentVisible ?? formValues.status === 'error'; }; } -
Constant function: a function is always authoritative, so a constant one is how a field opts out of the picker entirely — the user’s selection cannot override it.
config: { visible: () => true; } // Always visibleconfig: { visible: () => false; } // Always hidden
A boolean never hides a field for good. Every field is listed in the “Select visible filters” modal whatever its
config.visible, so a boolean computed from a permission (visible: canSeeCost) can be switched back on by the user, and that choice is persisted. Gate on a function instead (visible: () => canSeeCost), or leave the field out of the schema altogether — the only way a filter is genuinely unavailable.
Field Labels
A field’s label drives its form input. Two optional props override it for one
surface each, so the active-filter chip and the “Select visible filters” modal can show
something different from the input:
| Property | Overrides | Type | Default |
|---|---|---|---|
appliedLabel | The active-filter chip, including its remove button’s label | string | label |
visibilityLabel | The field’s row in the “Select visible filters” modal, and what its search matches | string | label |
const filterSchema: AkifilterSchema = [
{
key: 'orderNo',
type: 'text',
label: 'Order No', // form input
appliedLabel: 'Order number', // chip: "Order number: 1001"
visibilityLabel: 'Order reference no' // row in the modal
},
{
key: 'excludeSection',
type: 'section',
label: 'Exclude Filters',
isExcludeSection: true,
fields: [
{
key: 'excludeStatus',
type: 'text',
label: 'Status',
// The chip is rendered outside the section, so it carries the context
// the section header would otherwise provide.
appliedLabel: 'Excluded status'
}
]
}
];Notes:
- Both are optional and fall back to
label→placeholder→key, so a field that sets neither behaves exactly as before. - Section fields support them too, exclude sections included.
- The modal’s search matches
visibilityLabel,label,placeholderandkey, so an overridden field stays findable under either name. - Neither prop affects the form input’s own label or accessible name — use
labelfor that. - Storage is keyed by field
key, never by a label, so overriding labels never invalidates persisted values or visibility selections. - They are Akifilter’s own props, not part of the shared form schema, so
AkiformBuilderignores them entirely — it renders the form label fromlabelalone.
Schemas built with the fluent builder pass them through its generic extend()
hatch:
import { field } from '@akinon/akiform-builder';
const filterSchema = [
field()
.key('orderNo')
.type('text')
.label('Order No')
.extend({
appliedLabel: 'Order number',
visibilityLabel: 'Order reference no'
})
.build()
];The call is type-checked even though @akinon/akiform-builder knows nothing
about filters: it exposes an empty FieldExtensions interface as an
augmentation point, and Akifilter declares its own props on it. extend()
accepts only declared keys, so a typo or a wrong type fails the build.
Application-specific metadata (a request key, a data type a custom field’s
render reads back) is declared the same way, from the application:
declare module '@akinon/akiform-builder' {
interface FieldExtensions {
requestKey?: string;
}
}extend() is the single channel for Akifilter-only field props — the label
overrides, the chip factories (appliedChips / onRemoveAppliedChip) and the
section import flags (enableImportCsv / enableImportXls) all take it.
isExcludeSection and selectableFields predate it and keep their own builder
setters.
Section Fields
Use type: 'section' to group related filters into a collapsible panel rendered
below the main filter grid. A section owns its own set of fields via the fields
property.
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
const filterSchema: AkifilterSchema = [
{ key: 'orderNo', type: 'text', label: 'Order No', config: { visible: true } },
{
key: 'advancedFilters',
type: 'section',
label: 'Advanced Filters',
defaultExpanded: false, // Start collapsed
fields: [
{ key: 'customerName', type: 'text', label: 'Customer Name' },
{ key: 'amount', type: 'number', label: 'Amount' }
]
}
];Section API
| Property | Description | Type | Default |
|---|---|---|---|
type | Must be 'section' | 'section' | - |
label | Section title rendered in the collapsible header | string | - |
fields | Fields belonging to the section (any regular field type) | AkifilterField[] | - |
defaultExpanded | Whether the section starts expanded | boolean | true |
isExcludeSection | Styles the section’s active chips as exclude filters (see below) | boolean | false |
selectableFields | Adds a funnel icon + visibility modal to the section header (see below) | boolean | false |
enableImportCsv | Adds a CSV import button to the section header. Requires onSectionImportCsv (see below) | boolean | false |
enableImportXls | Adds an XLS/XLSX import button to the section header. Requires onSectionImportXls (see below) | boolean | false |
Section field values are part of the same form payload as regular fields, so they appear in
onValuesChangeand as active-filter chips just like top-level fields. Section fields are not listed in the top-level “Select visible filters” modal — their visibility is managed per section viaselectableFields.
isExcludeSection
Set isExcludeSection: true to mark a section as exclude filters. The active
filter chips of that section’s fields are visually distinguished (red background,
red border, and an exclude icon) so users can tell exclusion rules apart from
regular filters.
const filterSchema: AkifilterSchema = [
{ key: 'sku', type: 'text', label: 'SKU', config: { visible: true } },
{
key: 'excludeSection',
type: 'section',
label: 'Exclude Filters',
isExcludeSection: true,
fields: [
{ key: 'excludeSku', type: 'text', label: 'Exclude SKU' },
{
key: 'excludeStatus',
type: 'select',
label: 'Exclude Status',
options: [
{ value: 'pending', label: 'Pending' },
{ value: 'completed', label: 'Completed' }
]
}
]
}
];selectableFields (funnel icon + visibility modal)
Set selectableFields: true to give a section its own funnel icon in the header.
Clicking it opens a visibility modal that lets users choose which of the
section’s fields are shown — the same field-selection experience as the
top-level filters. This is ideal for sections with many fields (for example,
attribute-type filters) where showing all of them at once would be overwhelming.
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
const attributeFields = Array.from({ length: 20 }, (_, index) => ({
key: `attr_${index + 1}`,
type: 'text' as const,
label: `Attribute ${index + 1}`
}));
const filterSchema: AkifilterSchema = [
{ key: 'sku', type: 'text', label: 'SKU', config: { visible: true } },
{
key: 'attributeSection',
type: 'section',
label: 'Attribute Filters',
selectableFields: true,
fields: attributeFields
},
{
// Combine with isExcludeSection for selectable exclude filters
key: 'excludeAttributeSection',
type: 'section',
label: 'Exclude Attribute Filters',
isExcludeSection: true,
selectableFields: true,
defaultExpanded: false,
fields: attributeFields
}
];Behavior of a selectable section:
- Header layout: the header renders as
Title → arrow → funnel button, with the funnel pinned to the far right. Clicking the funnel opens the modal without toggling the collapse; clicking anywhere else in the header expands/collapses the section. - Default visibility: fields with
config.visible: trueare shown; otherwise the first 8 fields are shown by default and the rest are opt-in through the funnel — mirroring the top-level filter behavior. - Search & pagination: the modal supports searching fields by name and paginates long lists.
- Value cleanup: hiding a field clears its value and removes its active chip, exactly like the top-level field selection.
- Dynamic visibility:
config.visibleboolean/function values (including thecurrentVisibleargument) are honored inside selectable sections too. - Per-section persistence: when
storageNamespaceis provided, each section’s visible-field selection is persisted under its own storage key and restored on mount, independently of the top-level filters and other sections. - Reporting: the section’s visible field keys are reported through
onSectionVisibleFieldsChange(see below), notonVisibleFieldsChange.
Set a unique
storageNamespaceper Akifilter instance. Section visibility storage keys are derived from the instance’s storage key + thesection.key. The instance storage key is built from the top-level (non-section) fields and the namespace only — section fields do not contribute to it. So two instances that both omitstorageNamespaceand have no top-level fields (section-only schemas) resolve to the same key, and sections that share akeywill leak their visibility selections into each other. Giving each instance a distinctstorageNamespacekeeps these selections isolated.
Section Import Buttons (CSV / XLS)
A section header can carry its own CSV and XLS/XLSX import buttons, next to the
funnel. Like the toolbar’s import buttons, they need both the schema flag and the
matching handler — a flag without a handler renders nothing instead of a dead
button. Akifilter never parses the file; it hands it to the host together with
the section’s key, so one handler can serve every section.
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
const filterSchema: AkifilterSchema = [
{ key: 'sku', type: 'text', label: 'SKU' },
{
key: 'excludeSection',
type: 'section',
label: 'Exclude Filters',
isExcludeSection: true,
enableImportCsv: true,
enableImportXls: true,
fields: [{ key: 'excludeSku', type: 'text', label: 'Exclude SKU' }]
}
];
const MyComponent = () => (
<Akifilter
filterSchema={filterSchema}
onSectionImportCsv={(sectionKey, file) => {
// e.g. POST the file to an exclude-by-file endpoint, then surface a chip
// through `externalAppliedFilters`
console.log(sectionKey, file.name);
}}
onSectionImportXls={(sectionKey, file) => console.log(sectionKey, file.name)}
onValuesChange={values => console.log('Values:', values)}
/>
);With the fluent builder the two flags travel through extend(), since they are
Akifilter’s props rather than the form builder’s:
field()
.key('excludeSection')
.type('section')
.label('Exclude Filters')
.isExcludeSection(true)
.extend({ enableImportCsv: true, enableImportXls: true })
.fields([field().key('excludeSku').type('text').label('Exclude SKU').build()])
.build();Behavior notes:
- Buttons are 36px squares matching the funnel, pinned to the far right of the header in the order CSV → XLS → funnel.
- Activating any header action — by click or Enter — never toggles the section’s collapse.
- Tooltips and accessible names are localized and interpolate the section label
(
section.actions.importCsvTooltip,section.actions.importXlsTooltip). - Picking the same file twice in a row still fires the handler.
Default Values Behavior
When using the filterSchema with defaultValue properties, note that:
- defaultValue in schema: Used as the schema default value
- defaultValues prop: Passed as initial values to the filter form
- Clear All action: Resets fields to schema
defaultValueonly (not externaldefaultValues)
This allows distinction between schema-defined defaults and external initial values. When clearing filters, only the schema-defined defaults are applied.
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
const filterSchema: AkifilterSchema = [
{
key: 'region',
type: 'select',
label: 'Region',
placeholder: 'Select region',
defaultValue: 'all', // Schema default - used on clear
options: [
{ value: 'all', label: 'All Regions' },
{ value: 'eu', label: 'Europe' },
{ value: 'us', label: 'United States' }
]
}
];
const MyComponent = () => {
return (
<Akifilter
filterSchema={filterSchema}
storageNamespace="products-filter"
defaultValues={{ region: 'eu' }} // Shows 'Europe' initially
onValuesChange={values => console.log('Values:', values)}
/>
);
};
// When user clicks "Clear All", region resets to 'all' (schema default)
// NOT to 'eu' (external defaultValues)Date Field with Time
Date fields support the showTime property to include time selection:
const filterSchema: AkifilterSchema = [
{
key: 'createdDate',
type: 'date',
label: 'Created Date',
showTime: true // Includes time picker
}
];When showTime is enabled, the applied filter displays both date and time in
localized format.
Custom Field Options
Custom fields (type: 'custom') can include an optional options array. This
array is not used for rendering — the render function handles the UI. Instead,
Akifilter uses it to resolve selected values into human-readable labels in the
applied filters bar. This works for both single and multi-select values.
const filterSchema: AkifilterSchema = [
{
key: 'category',
type: 'custom',
label: 'Category',
options: [
{ value: 'electronics', label: 'Electronics' },
{ value: 'clothing', label: 'Clothing' },
{ value: 'books', label: 'Books' }
],
render: ({ field, formValues, control }) => (
<MyCustomSelect field={field} formValues={formValues} control={control} />
)
}
];
// Single value: applied filter shows "Electronics" instead of "electronics"
// Multi-select value: applied filter shows "Electronics, Clothing" instead of "electronics,clothing"Without options, applied filter chips display raw values. With options,
labels are resolved automatically for both single values and arrays.
Multi-Value Fields and Field-Owned Chips
Tags input
A select field with mode: 'tags' and no options is a free-form multi-value
input: type a value, press Enter to turn it into a removable tag, repeat. The
active-filter chip joins the entries with , .
const filterSchema: AkifilterSchema = [
{
key: 'keywords',
type: 'select',
label: 'Keywords',
mode: 'tags',
options: []
}
];mode is part of the storage signature, so switching a field between single and
multi-value modes invalidates its persisted entry instead of restoring an
incompatible shape.
appliedChips / onRemoveAppliedChip
By default a field contributes one active-filter chip. A field that owns several filters at once — a bucket per filter type, a range that reads better as two chips — can produce its own instead:
| Property | Description | Type | Default |
|---|---|---|---|
appliedChips | Expands the field’s value into one chip per entry. Return [] for “no chips right now” | (ctx: { field, value, formValues }) => AkifilterFieldChip[] | - |
onRemoveAppliedChip | Maps one chip’s removal onto the field’s next value, which Akifilter commits, persists and emits. Return null to clear the field | (ctx: { field, value, id }) => unknown | - |
An AkifilterFieldChip is { id, value, label?, isExclude? }: label defaults
to the field’s applied label, isExclude to whether the field sits in an exclude
section. id must be stable for the same logical entry and unique within the
field — two chips sharing an id cannot be told apart on removal.
appliedChips is only called while the field’s own value is non-empty: the
emptiness rules that suppress a plain chip (null/undefined, a blank string,
an empty array) suppress these too. Use formValues to format the chips (a
unit or a mode held by a sibling field), not to derive them from a sibling while
this field holds nothing.
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
// One value holds every filter type's bucket plus the active type:
// { mode: 'exact', buckets: { contains: ['a', 'b'], exact: ['c'] } }
const filterSchema: AkifilterSchema = [
{
key: 'attributeName',
type: 'custom',
label: 'Attribute Name',
appliedChips: ({ value }) =>
Object.entries(value?.buckets ?? {})
.filter(([, values]) => values.length > 0)
.map(([mode, values]) => ({
id: mode,
label: `Attribute Name (${MODE_LABELS[mode]})`,
value: values.join(', ')
})),
onRemoveAppliedChip: ({ value, id }) => {
const buckets = { ...(value?.buckets ?? {}) };
delete buckets[id];
return Object.keys(buckets).length > 0
? { ...value, buckets }
: null;
},
render: ({ control }) => <MyTagsInput control={control} name="attributeName" />
}
];Why this matters: because every chip is derived from the field’s form value,
persistence comes for free — Akifilter stores and rehydrates the composite value
like any other one, Clear All clears the field like any other, and each chip
removes through its own route. Nothing has to be mirrored into host state or
written to storage by the application. State kept outside the component (an
active filter type in React state, for example) is not persisted, so a reload
would restore the values under the wrong type.
Declaring appliedChips also tells the storage layer that the field owns its
value shape, so a composite (object) value is rehydrated instead of being
rejected as schema drift — the same exemption type: 'custom' gets. Without the
prop, only custom fields may hold object values across a reload.
Mapping the buckets onto request keys stays host logic, since one logical filter can emit several keys at once.
Imperative Actions with filterActionsRef
The filterActionsRef prop provides imperative control over filter values,
allowing you to programmatically clear specific fields from outside the
component:
import { useRef } from 'react';
import {
Akifilter,
type AkifilterActionsRef,
type AkifilterSchema
} from '@akinon/akifilter';
import { Button } from '@akinon/ui-button';
const filterSchema: AkifilterSchema = [
{
key: 'orderNo',
type: 'text',
label: 'Order No',
placeholder: 'Enter order number'
},
{
key: 'customerName',
type: 'text',
label: 'Customer Name',
placeholder: 'Enter customer name'
},
{
key: 'status',
type: 'select',
label: 'Status',
options: [
{ value: 'pending', label: 'Pending' },
{ value: 'completed', label: 'Completed' }
]
}
];
const MyComponent = () => {
const filterActionsRef = useRef<AkifilterActionsRef>(null);
const handleClearOrderNo = () => {
// Clear a single field
filterActionsRef.current?.clearValue('orderNo');
};
const handleClearMultiple = () => {
// Clear multiple fields at once
filterActionsRef.current?.clearValue(['customerName', 'status']);
};
return (
<div>
<div style={{ marginBottom: 16, display: 'flex', gap: 8 }}>
<Button onClick={handleClearOrderNo}>Clear Order No</Button>
<Button onClick={handleClearMultiple}>
Clear Customer Name & Status
</Button>
</div>
<Akifilter
filterSchema={filterSchema}
filterActionsRef={filterActionsRef}
onValuesChange={values => console.log('Values changed:', values)}
/>
</div>
);
};AkifilterActionsRef API
| Method | Parameters | Description |
|---|---|---|
clearValue | keys: string | string[] | Clears the value of one or more filter fields by their key(s) |
Import Actions
Akifilter supports optional CSV and XLS/XLSX import buttons in the toolbar.
These are hidden by default and can be enabled via props. Clicking one opens the
native file picker, and the callback receives the selected File — Akifilter
never parses it, so the import workflow stays with the host application. Sections
can carry the same buttons in their header; see
Section Import Buttons.
import { Akifilter, type AkifilterSchema } from '@akinon/akifilter';
const filterSchema: AkifilterSchema = [
{
key: 'productId',
type: 'text',
label: 'Product ID'
}
];
const MyComponent = () => {
const handleImportCsv = (file: File) => {
// Implement CSV import logic
console.log('Import from CSV:', file.name);
};
const handleImportXls = (file: File) => {
// Implement XLS/XLSX import logic
console.log('Import from XLS/XLSX:', file.name);
};
return (
<Akifilter
filterSchema={filterSchema}
enableImportCsv={true}
enableImportXls={true}
onImportCsv={handleImportCsv}
onImportXls={handleImportXls}
onValuesChange={values => console.log('Values:', values)}
/>
);
};When enabled, import buttons appear in the filter toolbar, allowing users to trigger custom import workflows. Picking the same file twice in a row still fires the callback, so a retry after a failed import works.
Additional Callbacks
onClearAll
Triggered when the user clicks the “Clear All” button:
<Akifilter
filterSchema={filterSchema}
onClearAll={() => {
console.log('All filters cleared');
// Perform additional cleanup if needed
}}
onValuesChange={values => console.log('Values:', values)}
/>onVisibleFieldsChange
Triggered when the user changes which fields are visible in the filter:
<Akifilter
filterSchema={filterSchema}
onVisibleFieldsChange={visibleKeys => {
console.log('Visible fields:', visibleKeys);
// Track user preferences or analytics
}}
onValuesChange={values => console.log('Values:', values)}
/>onVisibleFieldsChange reports the top-level fields only; section fields are
reported separately through onSectionVisibleFieldsChange.
onSectionVisibleFieldsChange
Called with a section’s key and the keys of the fields it currently renders,
on mount and whenever that list changes, so one handler can serve every section:
const [sectionVisibleKeys, setSectionVisibleKeys] = useState<
Record<string, string[]>
>({});
const handleSectionVisibleFieldsChange = useCallback(
(sectionKey: string, keys: string[]) =>
setSectionVisibleKeys(current => ({ ...current, [sectionKey]: keys })),
[]
);
<Akifilter
filterSchema={filterSchema}
onSectionVisibleFieldsChange={handleSectionVisibleFieldsChange}
/>;- A
selectableFieldssection reports the selection made in its visibility modal, including the default set on first render and changes driven byconfig.visible. - Any other section renders all of its fields, so it reports every field key.
- Keep the handler’s identity stable (e.g. with
useCallback): a new function on every render re-notifies with an unchanged list.