Stack9 UI Package
The @april9au/stack9-ui package provides a comprehensive set of UI components, layouts, and utilities for building Stack9 applications. It includes form controls, data displays, navigation elements, and complete page templates built on top of Ant Design.
Installation
npm install @april9au/stack9-ui
Package Structure
@april9au/stack9-ui
├── components/
│ ├── common/ # Common reusable components
│ ├── layout/ # Layout components
│ └── native/ # Stack9-specific components
├── pages/ # Complete page templates
├── contexts/ # React contexts
├── hooks/ # UI-specific hooks
├── providers/ # UI providers
├── constants/ # Constants and configurations
└── utils/ # Utility functions
Core Setup
UIProvider
The root provider that registers all UI components and extensions.
import { UIProvider } from '@april9au/stack9-ui';
import { components } from './components';
import { extensions } from './extensions';
function App() {
return (
<UIProvider
components={components}
extensions={extensions}
>
{/* Your app */}
</UIProvider>
);
}
Props:
components: Component[]- Array of registered componentsextensions: object- Custom component extensions
App Component
The main application wrapper that handles routing and layout.
import { App } from '@april9au/stack9-ui';
function MyApp() {
return (
<App
customRoutes={[
{ route: '/custom', component: <CustomPage /> }
]}
onRouteChange={(route) => console.log('Route:', route)}
/>
);
}
Props:
customRoutes: RouteConfig[]- Custom route definitionsonRouteChange: (route: string) => void- Route change callbacktheme: ThemeConfig- Theme customization
Layout Components
S9Layout
Main layout wrapper with sidebar navigation.
import { S9Layout, LayoutHeader, LayoutSider, LayoutContent } from '@april9au/stack9-ui';
function AppLayout() {
return (
<S9Layout>
<LayoutHeader>
<Logo />
<UserMenu />
</LayoutHeader>
<S9Layout>
<LayoutSider width={240} collapsible>
<Navigation />
</LayoutSider>
<LayoutContent>
<Routes />
</LayoutContent>
</S9Layout>
</S9Layout>
);
}
S9Page
Standard page wrapper with title and breadcrumbs.
import { S9Page, S9PageTitle, S9PageHeader, S9PageContent } from '@april9au/stack9-ui';
function CustomerPage() {
return (
<S9Page>
<S9PageHeader
title="Customers"
breadcrumb={[
{ label: 'Home', path: '/' },
{ label: 'Customers' }
]}
actions={[
<S9Button type="primary">Add Customer</S9Button>
]}
/>
<S9PageContent>
<CustomerList />
</S9PageContent>
</S9Page>
);
}
S9PageHeader Props:
title: string- Page titlesubtitle?: string- Page subtitlebreadcrumb?: BreadcrumbItem[]- Breadcrumb navigationactions?: ReactNode[]- Action buttonsextra?: ReactNode- Additional content
S9Grid System
Responsive grid layout system based on 24 columns.
import { S9Row, S9Col } from '@april9au/stack9-ui';
function GridLayout() {
return (
<S9Row gutter={[16, 16]}>
<S9Col xs={24} sm={12} md={8} lg={6}>
<Card>Column 1</Card>
</S9Col>
<S9Col xs={24} sm={12} md={8} lg={6}>
<Card>Column 2</Card>
</S9Col>
<S9Col xs={24} sm={12} md={8} lg={12}>
<Card>Column 3</Card>
</S9Col>
</S9Row>
);
}
S9Row Props:
gutter?: [horizontal, vertical]- Spacing between columnsalign?: 'top' | 'middle' | 'bottom'- Vertical alignmentjustify?: 'start' | 'center' | 'end' | 'space-around' | 'space-between'
S9Col Props:
span?: number- Column width (1-24)offset?: number- Column offsetxs, sm, md, lg, xl, xxl?: number | ColConfig- Responsive breakpoints
Form Components
S9Form
Enhanced form component with validation and layout.
import { S9Form, S9TextField, S9NumericField, S9Button } from '@april9au/stack9-ui';
function CustomerForm() {
const [form] = S9Form.useForm();
const handleSubmit = async (values) => {
await saveCustomer(values);
};
return (
<S9Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<S9TextField
name="name"
label="Customer Name"
rules={[{ required: true, message: 'Name is required' }]}
/>
<S9NumericField
name="creditLimit"
label="Credit Limit"
min={0}
max={1000000}
prefix="$"
/>
<S9Button type="primary" htmlType="submit">
Save Customer
</S9Button>
</S9Form>
);
}
S9Form Props:
form?: FormInstance- Form instance from useForm()layout?: 'horizontal' | 'vertical' | 'inline'onFinish?: (values: any) => void- Submit handleronValuesChange?: (changed: any, all: any) => voidinitialValues?: object- Initial form values
S9TextField
Text input field with validation.
<S9TextField
name="email"
label="Email Address"
placeholder="Enter email"
rules={[
{ required: true, message: 'Email is required' },
{ type: 'email', message: 'Invalid email format' }
]}
prefix={<MailOutlined />}
maxLength={100}
/>
Props:
name: string- Field namelabel?: string- Field labelplaceholder?: string- Placeholder textrules?: Rule[]- Validation rulesdisabled?: boolean- Disable inputprefix?: ReactNode- Prefix icon/textsuffix?: ReactNode- Suffix icon/textmaxLength?: number- Maximum length
S9NumericField
Number input with formatting options.
<S9NumericField
name="price"
label="Price"
min={0}
max={9999.99}
precision={2}
prefix="$"
formatter={(value) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={(value) => value.replace(/\$\s?|(,*)/g, '')}
/>
Props:
min?: number- Minimum valuemax?: number- Maximum valuestep?: number- Step incrementprecision?: number- Decimal placesformatter?: (value: number) => stringparser?: (value: string) => number
S9DateField
Date/time picker with multiple formats.
<S9DateField
name="birthDate"
label="Birth Date"
format="YYYY-MM-DD"
showTime={false}
disabledDate={(date) => date.isAfter(dayjs())}
/>
Props:
format?: string- Date format stringshowTime?: boolean- Include time pickerpicker?: 'date' | 'week' | 'month' | 'year'disabledDate?: (date: Dayjs) => boolean
S9SingleDropDown / S9MultiDropDown
Dropdown select components.
<S9SingleDropDown
name="status"
label="Status"
options={[
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' }
]}
showSearch
filterOption={(input, option) =>
option.label.toLowerCase().includes(input.toLowerCase())
}
/>
<S9MultiDropDown
name="tags"
label="Tags"
options={tagOptions}
maxTagCount={3}
mode="multiple"
/>
SingleDropDown Props:
options: LabelValue[]- Dropdown optionsshowSearch?: boolean- Enable searchfilterOption?: (input: string, option: Option) => booleanallowClear?: boolean- Show clear button
MultiDropDown Props:
- All SingleDropDown props plus:
mode?: 'multiple' | 'tags'- Selection modemaxTagCount?: number- Maximum visible tagsmaxTagTextLength?: number- Maximum tag text length
S9Checkbox
Checkbox input component.
<S9Checkbox
name="terms"
value="accepted"
rules={[{ required: true, message: 'Please accept terms' }]}
>
I accept the terms and conditions
</S9Checkbox>
S9Radio
Radio button group.
<S9Radio
name="gender"
label="Gender"
options={[
{ label: 'Male', value: 'M' },
{ label: 'Female', value: 'F' },
{ label: 'Other', value: 'O' }
]}
optionType="button"
/>
S9FileField
File upload component.
<S9FileField
name="documents"
label="Documents"
multiple
maxCount={5}
accept=".pdf,.doc,.docx"
maxSize={10 * 1024 * 1024} // 10MB
onUpload={async (file) => {
const url = await uploadFile(file);
return { url, name: file.name };
}}
/>
Props:
multiple?: boolean- Allow multiple filesmaxCount?: number- Maximum file countaccept?: string- Accepted file typesmaxSize?: number- Maximum file size in bytesonUpload?: (file: File) => Promise<UploadResult>
S9RichTextEditor
Rich text editor with formatting tools.
<S9RichTextEditor
name="description"
label="Description"
toolbar={[
'bold', 'italic', 'underline',
'bulletList', 'orderedList',
'link', 'image'
]}
height={300}
/>
S9MonacoEditorField
Code editor with syntax highlighting.
<S9MonacoEditorField
name="query"
label="SQL Query"
language="sql"
theme="vs-dark"
height={400}
options={{
minimap: { enabled: false },
wordWrap: 'on'
}}
/>
Data Display Components
S9Table
Advanced data table with sorting, filtering, and pagination.
import { S9Table } from '@april9au/stack9-ui';
function CustomerTable() {
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
sorter: true,
filters: true
},
{
title: 'Email',
dataIndex: 'email',
key: 'email'
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
render: (status) => (
<S9Tag color={status === 'active' ? 'green' : 'red'}>
{status}
</S9Tag>
)
}
];
return (
<S9Table
columns={columns}
dataSource={customers}
rowKey="id"
pagination={{
pageSize: 20,
showSizeChanger: true
}}
rowSelection={{
type: 'checkbox',
onChange: (keys, rows) => console.log(keys, rows)
}}
/>
);
}
Props:
columns: ColumnConfig[]- Column definitionsdataSource: any[]- Data arrayrowKey: string | (record) => string- Unique row keypagination?: PaginationConfig- Pagination settingsrowSelection?: RowSelectionConfig- Row selectionexpandable?: ExpandableConfig- Expandable rowsscroll?: { x?: number, y?: number }- Scrolling
S9Tabs
Tab navigation component.
<S9Tabs
defaultActiveKey="1"
onChange={(key) => console.log('Tab:', key)}
>
<TabPane tab="Details" key="1">
<DetailsPanel />
</TabPane>
<TabPane tab="History" key="2">
<HistoryPanel />
</TabPane>
<TabPane tab="Notes" key="3" disabled>
<NotesPanel />
</TabPane>
</S9Tabs>
S9Fieldset
Display entity fields in a structured layout.
<S9Fieldset
context={{ id: entityId }}
schema={entitySchema}
formFields={[
{ field: 'name', colSize: 12 },
{ field: 'email', colSize: 12 },
{ field: 'phone', colSize: 8 },
{ field: 'status', colSize: 4 }
]}
mode="view"
/>
Props:
context: { id: number }- Entity contextschema: EntitySchema- Entity schemaformFields: FieldConfig[]- Field configurationmode?: 'view' | 'edit'- Display mode
Descriptions
Display multiple read-only fields.
<Descriptions bordered column={2}>
<DescriptionItem label="Customer Name">
John Doe
</DescriptionItem>
<DescriptionItem label="Email">
john@example.com
</DescriptionItem>
<DescriptionItem label="Address" span={2}>
123 Main Street, City, State 12345
</DescriptionItem>
</Descriptions>
Action Components
S9Button
Enhanced button component with loading states.
<S9Button
type="primary"
icon={<PlusOutlined />}
loading={isLoading}
onClick={handleClick}
>
Add Customer
</S9Button>
Props:
type?: 'primary' | 'default' | 'dashed' | 'text' | 'link'danger?: boolean- Red dangerous buttonloading?: boolean- Loading stateicon?: ReactNode- Button iconsize?: 'small' | 'middle' | 'large'block?: boolean- Full width button