@sinups/ui-kit
v0.1.20
Published
The DocSpace UI Library (`@sinups/ui-kit`) provides a set of reusable components, utilities, and integrations for your application. This guide outlines the steps to integrate and use the library effectively in your project.
Readme
DocSpace UI Library Documentation
The DocSpace UI Library (@sinups/ui-kit) provides a set of reusable components, utilities, and integrations for your application. This guide outlines the steps to integrate and use the library effectively in your project.
Table of Contents
Requirements
- react-redux 9+
- @reduxjs/toolkit 2+
- @mantine/core >=8.2.7
- @mantine/dates >=8.2.7 (for the Advanced Search date-range filter)
How to start
- Add env variable:
VITE_API_BASE_URL=/api- Add the package link to your
package.jsonto include the UI library locally:
npm install @sinups/ui-kit- Extend mantine theme
import { theme as docspaceTheme } from '@sinups/ui-kit';
export const themeConfig = createTheme({
...docspaceTheme
// project custom settings...
});- Add styles file in root file like
App.tsxright after mantine styles import.
import '@sinups/ui-kit/dist/ui-kit.css';- If component or widget uses translations add them to your i18n instance
import { translations } from '@sinups/ui-kit';
// Under init
Object.entries(translations).forEach(([language, resources]) => {
i18n.addResourceBundle(language, 'ds', resources);
});- If widget uses redux slice add it to your store declaration. For example:
import { notificationApi as notificationApiKit } from '@sinups/ui-kit';
// Add to reducers
{
//...
[notificationApiKit.reducerPath]: notificationApiKit.reducer,
/...
}
// Add to middlewares
[
//...
notificationApiKit.middleware
//...
]Advanced Search
A headless, generic search surface: the kit owns the chrome (input bar, debounce, dropdown/modal, loading / empty / error states), the consumer supplies the domain pieces through slots.
Install
npm install @mantine/core @mantine/dates @mantine/hooks @sinups/ui-kitImport the Mantine dates styles once, right after the core styles (the date-range filter needs them):
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css';Usage
import { useCallback, useState } from 'react';
import { Stack, Text, TextInput } from '@mantine/core';
import {
AdvancedSearchWidget,
SearchEmptyState,
SearchErrorState,
SearchFilterChipsSelect,
SearchFilterDateRange,
SearchResultRow,
type SearchFilterDateRangeValue
} from '@sinups/ui-kit';
interface MyItem {
id: string;
title: string;
subtitle: string;
}
interface MyFilters {
range: SearchFilterDateRangeValue;
calendars: string[];
place: string;
}
const DEFAULT_FILTERS: MyFilters = {
range: { from: null, to: null },
calendars: [],
place: ''
};
const CALENDAR_OPTIONS = [
{ value: 'personal', label: 'Личный', color: '#339AF0' },
{ value: 'work', label: 'Работа', color: '#51CF66' }
];
export const EventSearch = () => {
const [query, setQuery] = useState('');
const [filters, setFilters] = useState<MyFilters>(DEFAULT_FILTERS);
// Memoize onSearch — a new identity on every render resets the pending debounce.
const onSearch = useCallback(
(q: string, f: MyFilters): Promise<MyItem[]> => api.searchEvents(q, f),
[]
);
return (
<AdvancedSearchWidget<MyItem, MyFilters>
query={query}
onQueryChange={setQuery}
filters={filters}
onFiltersChange={setFilters}
defaultFilters={DEFAULT_FILTERS}
onSearch={onSearch}
placeholder="Поиск событий"
renderFilters={({ filters: current, setField }) => (
<Stack gap="xs">
<SearchFilterDateRange
value={current.range}
onChange={(next) => setField('range', next)}
fromLabel="с"
toLabel="по"
/>
<SearchFilterChipsSelect
options={CALENDAR_OPTIONS}
value={current.calendars}
onChange={(next) => setField('calendars', next)}
placeholder="Область поиска"
/>
<TextInput
size="sm"
placeholder="Место"
value={current.place}
onChange={(event) => setField('place', event.currentTarget.value)}
/>
</Stack>
)}
renderResultItem={(item) => (
<SearchResultRow
middle={
<Stack gap={2}>
<Text size="sm" fw={500}>
{item.title}
</Text>
<Text size="xs" c="dimmed">
{item.subtitle}
</Text>
</Stack>
}
/>
)}
renderEmpty={
<SearchEmptyState title="Ничего не найдено" description="Попробуйте изменить запрос" />
}
renderError={(_error, retry) => (
<SearchErrorState
title="Не удалось выполнить поиск"
retryLabel="Повторить"
onRetry={retry}
/>
)}
onSelect={(item) => openEventDetails(item.id)}
onViewAll={(q, f) => navigateToResultsPage(q, f)}
maxVisibleResults={5}
/>
);
};Search lifecycle: typing fires onSearch live (debounced), while filter edits commit only on «Найти» (or with liveFilters enabled). onViewAll renders the «show all» footer link (only when maxVisibleResults hides matches) and is also bound to Enter in the search field.
filtersApi contract
renderFilters receives an API object instead of owning the lifecycle:
filters— current filter values;setField(key, value)— single-field update; multiple calls within one tick compose (none are lost);setFilters(next)— bulk replace of the whole filter object;reset()— restoredefaultFilters, clear query + results, remount the form;submit()— run the search now (the «Найти» trigger).
Customization
| Prop | Purpose |
| ----------------------- | ---------------------------------------------------------------------------------------- |
| inputProps | Passthrough for the search TextInput (styles are slot-merged over the kit defaults) |
| actionIconProps | Passthrough for the clear/filter icons (variant is controlled while the panel is open) |
| popoverProps | Desktop Popover overrides (position, width, shadow, radius…) |
| modalProps | Mobile fullscreen Modal overrides (used when isMobile) |
| classNames / styles | Per-slot overrides: dropdown, filters, results (see the AdvancedSearchSlot type) |
| maxDropdownHeight | Scroll cap for the results area, default 360 |
| debounceMs | Query debounce, default 300 (filterDebounceMs for liveFilters) |
| getItemKey | Stable row key, defaults to the array index |
i18n: the widget chrome strings (reset / find / clear / errors…) live in the kit's ds:search.* namespace — consumers who register the kit translations (via uiKitPlugin or the resource-bundle snippet in step 4 above) get them automatically in both ru/en. Set isMobile to render the fullscreen modal layout instead of the desktop popover.
Developing Mode
If you want to develop new component and widget you can link this library to you parent project and have it working in developing mode.
- Add package link. Change package version to specific path to file. You can use
pwdcommand inside project directory to get it.
"@sinups/ui-kit": "file:/home/quest76/ui-kit",- Add ts config alias (parent project) in
tsconfig.json
{
compilerOptions: {
paths: {
"@sinups/ui-kit": ["/home/quest76/code/ui-kit/src"],
"@ds/*": ["/home/quest76/code/ui-kit/src/*"],
}
}
}- Add Vite aliases (parent project) in
vite.config.ts
{
resolve: {
alias: {
'@sinups/ui-kit': '/home/quest76/code/ui-kit/src',
'@ds': '/home/quest76/code/ui-kit/src',
}
}
}Commit Helper
The repository includes an interactive commit message generator based on scripts/commit.sh.
Available commands:
yarn commit:claude
yarn commit:codex
yarn commit:aiWhat each command does:
yarn commit:clauderuns the helper with Claude prefillyarn commit:codexruns the helper with Codex prefillyarn commit:airuns the helper in AI mode and uses Claude by default
Requirements:
claudeCLI must be installed forcommit:claudecodexCLI must be installed forcommit:codex- if the selected AI CLI is unavailable, the script falls back to manual mode
Typical flow:
- Stage your changes with
git add ... - Run one of the commands above
- Optionally paste BFT/specification text into the terminal
- Review or edit the suggested fields step by step
- Confirm the preview and create the commit
What the script asks for:
- commit type emoji
- board/task id
- short title
- what changed
- why changed
- what was tested
- RC / REQ / OWNER / AC
- public description
- TEST / DOC / optional CR, DCR, ADR trailers
Behavior details:
- the script reads the current branch name and tries to detect the board id automatically
- it reads staged diff first; if nothing is staged, it uses the working tree diff for AI analysis
- on commit, if nothing is staged, it runs
git add -ubeforegit commit - the last entered metadata is stored in
.commit-prefill.jsonand reused for the next commit on the same branch
Useful notes:
- press
Enterto accept the suggested or default value - for multiline sections like
What changed, enter one item per line and finish with an empty line - when BFT text is provided, the AI tries to extract
RC,REQ,OWNER, andACautomatically
