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
S9ActionsDropdown
Dropdown menu for multiple actions.
<S9ActionsDropdown
items={[
{
key: 'edit',
label: 'Edit',
icon: <EditOutlined />,
onClick: () => handleEdit(record)
},
{
key: 'delete',
label: 'Delete',
icon: <DeleteOutlined />,
danger: true,
onClick: () => handleDelete(record)
}
]}
/>
S9EntityActions
Standard entity action buttons (Edit, Delete, etc.).
<S9EntityActions
entity={customer}
entityKey="customer"
onEdit={() => navigateToEdit(customer.id)}
onDelete={async () => {
await deleteCustomer(customer.id);
refresh();
}}
permissions={{
canEdit: true,
canDelete: userRole === 'admin'
}}
/>
S9EntityWorkflowActions
Workflow transition buttons for entities.
<S9EntityWorkflowActions
entity={order}
entityKey="order"
currentState="pending"
availableTransitions={[
{ key: 'approve', label: 'Approve', nextState: 'approved' },
{ key: 'reject', label: 'Reject', nextState: 'rejected' }
]}
onTransition={async (transition) => {
await executeTransition(order.id, transition);
}}
/>
Feedback Components
Alert
Display alert messages.
<Alert
message="Success"
description="Customer has been created successfully."
type="success"
showIcon
closable
onClose={() => console.log('Alert closed')}
/>
Modal
Modal dialog component.
<Modal
title="Confirm Delete"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
confirmLoading={isDeleting}
>
<p>Are you sure you want to delete this customer?</p>
</Modal>
S9Drawer
Slide-out panel component.
<S9Drawer
title="Customer Details"
placement="right"
width={720}
open={drawerOpen}
onClose={() => setDrawerOpen(false)}
extra={
<S9Button onClick={handleSave}>Save</S9Button>
}
>
<CustomerForm />
</S9Drawer>
S9Tooltip
Tooltip component for additional information.
<S9Tooltip title="This field is required">
<InfoCircleOutlined />
</S9Tooltip>
S9Popconfirm
Confirmation popover.
<S9Popconfirm
title="Delete this item?"
description="This action cannot be undone."
onConfirm={handleDelete}
okText="Yes"
cancelText="No"
>
<S9Button danger>Delete</S9Button>
</S9Popconfirm>
Navigation Components
Menu
Navigation menu component.
<Menu
mode="inline"
defaultSelectedKeys={['dashboard']}
items={[
{
key: 'dashboard',
icon: <DashboardOutlined />,
label: 'Dashboard'
},
{
key: 'customers',
icon: <UserOutlined />,
label: 'Customers',
children: [
{ key: 'list', label: 'List' },
{ key: 'add', label: 'Add New' }
]
}
]}
onClick={(item) => navigate(item.key)}
/>
Breadcrumb
Breadcrumb navigation.
<Breadcrumb
items={[
{ title: 'Home', href: '/' },
{ title: 'Customers', href: '/customers' },
{ title: 'John Doe' }
]}
/>
Steps
Progress steps indicator.
<Steps
current={currentStep}
items={[
{ title: 'Basic Info', description: 'Customer details' },
{ title: 'Address', description: 'Location information' },
{ title: 'Review', description: 'Confirm details' }
]}
/>
Pagination
Pagination component.
<Pagination
current={currentPage}
total={500}
pageSize={20}
showSizeChanger
showQuickJumper
onChange={(page, pageSize) => {
setCurrentPage(page);
setPageSize(pageSize);
}}
/>
Data Entry Helpers
S9ScreenQueryDropdown
Dropdown populated from screen query.
<S9ScreenQueryDropdown
screenName="customer-list"
queryName="active-customers"
valueField="id"
labelField="name"
placeholder="Select customer"
onChange={(value, option) => {
setSelectedCustomer(value);
}}
/>
S9DropdownTypeahead
Typeahead search dropdown.
<S9DropdownTypeahead
entityName="products"
searchField="name"
valueField="id"
labelField="name"
placeholder="Search products..."
minSearchLength={2}
debounceMs={300}
onSelect={(value, record) => {
setSelectedProduct(record);
}}
/>
S9ColorPickerField
Color picker component.
<S9ColorPickerField
name="brandColor"
label="Brand Color"
format="hex"
showText
presets={[
{ label: 'Primary', colors: ['#1890ff', '#40a9ff'] },
{ label: 'Success', colors: ['#52c41a', '#73d13d'] }
]}
/>
S9OptionSet
Option set selector (radio/checkbox group).
<S9OptionSet
name="preferences"
label="Preferences"
options={[
{ label: 'Email Notifications', value: 'email' },
{ label: 'SMS Alerts', value: 'sms' },
{ label: 'Push Notifications', value: 'push' }
]}
type="checkbox"
layout="vertical"
/>
Display Components
S9Tag
Tag/label component.
<S9Tag color="green" icon={<CheckCircleOutlined />}>
Active
</S9Tag>
Badge
Badge for status indicators.
<Badge count={5} overflowCount={99}>
<BellOutlined />
</Badge>
<Badge status="success" text="Online" />
Avatar
User avatar component.
<Avatar size={64} src={userImage}>
{userName[0]}
</Avatar>
Card
Content container card.
<Card
title="Customer Details"
extra={<a href="#">More</a>}
actions={[
<EditOutlined key="edit" />,
<DeleteOutlined key="delete" />
]}
>
<p>Card content</p>
</Card>
Collapse
Collapsible panels.
<Collapse defaultActiveKey={['1']}>
<CollapsePanel header="General Information" key="1">
<GeneralInfo />
</CollapsePanel>
<CollapsePanel header="Contact Details" key="2">
<ContactDetails />
</CollapsePanel>
</Collapse>
Timeline
Timeline display component.
<Timeline>
<TimelineItem color="green">
Create account - 2024-01-01
</TimelineItem>
<TimelineItem color="blue">
First purchase - 2024-01-15
</TimelineItem>
<TimelineItem dot={<ClockCircleOutlined />}>
Pending approval
</TimelineItem>
</Timeline>
Statistic
Statistic display component.
<Statistic
title="Total Revenue"
value={112893}
precision={2}
prefix="$"
suffix="USD"
valueStyle={{ color: '#3f8600' }}
/>
Progress
Progress indicator.
<Progress
percent={75}
status="active"
strokeColor={{
'0%': '#108ee9',
'100%': '#87d068'
}}
/>
Empty
Empty state component.
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description="No data found"
>
<S9Button type="primary">Create Now</S9Button>
</Empty>