feat(globalStateStore): implement global state management with persistence
- refactor state structure to include app, layout, navigation, owner, program, session, and user - add slices for managing program, session, owner, user, layout, navigation, and app states - create context provider for global state with automatic fetching and throttling - implement persistence using IndexedDB with localStorage fallback - add comprehensive README documentation for usage and API
This commit is contained in:
381
README.md
381
README.md
@@ -8,14 +8,38 @@ Oranguru is a comprehensive component library that extends Mantine's component e
|
||||
|
||||
Currently featuring advanced menu components, Oranguru is designed to grow into a full suite of enhanced Mantine components that offer more flexibility and power than their standard counterparts.
|
||||
|
||||
## Features
|
||||
## Components
|
||||
|
||||
### Current Components
|
||||
- **Enhanced Context Menus**: Better menu positioning and visibility control
|
||||
- **Custom Rendering**: Support for custom menu item renderers and complete menu rendering
|
||||
- **Async Actions**: Built-in support for async menu item actions with loading states
|
||||
### MantineBetterMenu
|
||||
|
||||
Enhanced context menus with better positioning and visibility control
|
||||
|
||||
### Gridler
|
||||
|
||||
Powerful data grid component with sorting, filtering, and pagination
|
||||
|
||||
### Former
|
||||
|
||||
Form component with React Hook Form integration and validation
|
||||
|
||||
### FormerControllers
|
||||
|
||||
Pre-built form input controls for use with Former
|
||||
|
||||
### Boxer
|
||||
|
||||
Advanced combobox/select with virtualization and server-side data support
|
||||
|
||||
### ErrorBoundary
|
||||
|
||||
React error boundary components for graceful error handling
|
||||
|
||||
### GlobalStateStore
|
||||
|
||||
Zustand-based global state management with automatic persistence
|
||||
|
||||
## Core Features
|
||||
|
||||
### Core Features
|
||||
- **State Management**: Zustand-based store for component state management
|
||||
- **TypeScript Support**: Full TypeScript definitions included
|
||||
- **Portal-based Rendering**: Proper z-index handling through React portals
|
||||
@@ -37,133 +61,266 @@ npm install react@">= 19.0.0" zustand@">= 5.0.0" @mantine/core@"^8.3.1" @mantine
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup
|
||||
### MantineBetterMenu
|
||||
|
||||
```tsx
|
||||
import { MantineBetterMenusProvider } from '@warkypublic/oranguru';
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
import { MantineBetterMenusProvider, useMantineBetterMenus } from '@warkypublic/oranguru';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<MantineProvider>
|
||||
<MantineBetterMenusProvider>
|
||||
{/* Your app content */}
|
||||
</MantineBetterMenusProvider>
|
||||
</MantineProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
// Wrap app with provider
|
||||
<MantineBetterMenusProvider>
|
||||
<App />
|
||||
</MantineBetterMenusProvider>
|
||||
|
||||
### Using the Menu Hook
|
||||
|
||||
```tsx
|
||||
import { useMantineBetterMenus } from '@warkypublic/oranguru';
|
||||
|
||||
function MyComponent() {
|
||||
const { show, hide } = useMantineBetterMenus();
|
||||
|
||||
const handleContextMenu = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
show('my-menu', {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
items: [
|
||||
{
|
||||
label: 'Edit',
|
||||
onClick: () => console.log('Edit clicked')
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
onClick: () => console.log('Delete clicked')
|
||||
},
|
||||
{
|
||||
isDivider: true
|
||||
},
|
||||
{
|
||||
label: 'Async Action',
|
||||
onClickAsync: async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
console.log('Async action completed');
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div onContextMenu={handleContextMenu}>
|
||||
Right-click me for a context menu
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Menu Items
|
||||
|
||||
```tsx
|
||||
const customMenuItem = {
|
||||
renderer: ({ loading }: any) => (
|
||||
<div style={{ padding: '8px 12px' }}>
|
||||
{loading ? 'Loading...' : 'Custom Item'}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
show('custom-menu', {
|
||||
// Use in components
|
||||
const { show, hide } = useMantineBetterMenus();
|
||||
show('menu-id', {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
items: [customMenuItem]
|
||||
items: [
|
||||
{ label: 'Edit', onClick: () => {} },
|
||||
{ isDivider: true },
|
||||
{ label: 'Async', onClickAsync: async () => {} }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Gridler
|
||||
|
||||
```tsx
|
||||
import { Gridler } from '@warkypublic/oranguru';
|
||||
|
||||
// Local data
|
||||
<Gridler columns={columns} uniqueid="my-grid">
|
||||
<Gridler.LocalDataAdaptor data={data} />
|
||||
</Gridler>
|
||||
|
||||
// API data
|
||||
<Gridler columns={columns} uniqueid="my-grid">
|
||||
<Gridler.APIAdaptorForGoLangv2 apiURL="/api/data" />
|
||||
</Gridler>
|
||||
|
||||
// With inline editing form
|
||||
<Gridler columns={columns} uniqueid="editable-grid" ref={gridRef}>
|
||||
<Gridler.APIAdaptorForGoLangv2 url="/api/data" />
|
||||
<Gridler.FormAdaptor
|
||||
changeOnActiveClick={true}
|
||||
descriptionField="name"
|
||||
onRequestForm={(request, data) => {
|
||||
setFormProps({ opened: true, request, values: data });
|
||||
}}
|
||||
/>
|
||||
</Gridler>
|
||||
|
||||
<FormerDialog
|
||||
former={{ request: formProps.request, values: formProps.values }}
|
||||
opened={formProps.opened}
|
||||
onClose={() => setFormProps({ opened: false })}
|
||||
>
|
||||
<TextInputCtrl label="Name" name="name" />
|
||||
<NativeSelectCtrl label="Type" name="type" data={["A", "B"]} />
|
||||
</FormerDialog>
|
||||
|
||||
// Columns definition
|
||||
const columns = [
|
||||
{ id: 'name', title: 'Name', width: 200 },
|
||||
{ id: 'email', title: 'Email', width: 250 }
|
||||
];
|
||||
```
|
||||
|
||||
### Former
|
||||
|
||||
```tsx
|
||||
import { Former, FormerDialog } from '@warkypublic/oranguru';
|
||||
|
||||
const formRef = useRef<FormerRef>(null);
|
||||
|
||||
<Former
|
||||
ref={formRef}
|
||||
onSave={async (data) => { /* save logic */ }}
|
||||
primeData={{ name: '', email: '' }}
|
||||
wrapper={FormerDialog}
|
||||
>
|
||||
{/* Form content */}
|
||||
</Former>
|
||||
|
||||
// Methods: formRef.current.show(), .save(), .reset()
|
||||
```
|
||||
|
||||
### FormerControllers
|
||||
|
||||
```tsx
|
||||
import {
|
||||
TextInputCtrl,
|
||||
PasswordInputCtrl,
|
||||
NativeSelectCtrl,
|
||||
TextAreaCtrl,
|
||||
SwitchCtrl,
|
||||
ButtonCtrl
|
||||
} from '@warkypublic/oranguru';
|
||||
|
||||
<Former>
|
||||
<TextInputCtrl name="username" label="Username" />
|
||||
<PasswordInputCtrl name="password" label="Password" />
|
||||
<NativeSelectCtrl name="role" data={['Admin', 'User']} />
|
||||
<SwitchCtrl name="active" label="Active" />
|
||||
<ButtonCtrl type="submit">Save</ButtonCtrl>
|
||||
</Former>
|
||||
```
|
||||
|
||||
### Boxer
|
||||
|
||||
```tsx
|
||||
import { Boxer } from '@warkypublic/oranguru';
|
||||
|
||||
// Local data
|
||||
<Boxer
|
||||
data={[{ label: 'Apple', value: 'apple' }]}
|
||||
dataSource="local"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
searchable
|
||||
clearable
|
||||
/>
|
||||
|
||||
// Server-side data
|
||||
<Boxer
|
||||
dataSource="server"
|
||||
onAPICall={async ({ page, pageSize, search }) => ({
|
||||
data: [...],
|
||||
total: 100
|
||||
})}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
/>
|
||||
|
||||
// Multi-select
|
||||
<Boxer multiSelect value={values} onChange={setValues} />
|
||||
```
|
||||
|
||||
### ErrorBoundary
|
||||
|
||||
```tsx
|
||||
import { ReactErrorBoundary, ReactBasicErrorBoundary } from '@warkypublic/oranguru';
|
||||
|
||||
// Full-featured error boundary
|
||||
<ReactErrorBoundary
|
||||
namespace="my-component"
|
||||
reportAPI="/api/errors"
|
||||
onResetClick={() => {}}
|
||||
>
|
||||
<App />
|
||||
</ReactErrorBoundary>
|
||||
|
||||
// Basic error boundary
|
||||
<ReactBasicErrorBoundary>
|
||||
<App />
|
||||
</ReactBasicErrorBoundary>
|
||||
```
|
||||
|
||||
### GlobalStateStore
|
||||
|
||||
```tsx
|
||||
import {
|
||||
GlobalStateStoreProvider,
|
||||
useGlobalStateStore,
|
||||
GlobalStateStore
|
||||
} from '@warkypublic/oranguru';
|
||||
|
||||
// Wrap app
|
||||
<GlobalStateStoreProvider
|
||||
apiURL="https://api.example.com"
|
||||
fetchOnMount={true}
|
||||
throttleMs={5000}
|
||||
>
|
||||
<App />
|
||||
</GlobalStateStoreProvider>
|
||||
|
||||
// Use in components
|
||||
const { program, session, user, layout } = useGlobalStateStore();
|
||||
const { refetch } = useGlobalStateStoreContext();
|
||||
|
||||
// Outside React
|
||||
GlobalStateStore.getState().setAuthToken('token');
|
||||
const apiURL = GlobalStateStore.getState().session.apiURL;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### MantineBetterMenusProvider
|
||||
**MantineBetterMenu**
|
||||
|
||||
The main provider component that wraps your application.
|
||||
- Provider: `MantineBetterMenusProvider`
|
||||
- Hook: `useMantineBetterMenus()` returns `{ show, hide, menus, setInstanceState }`
|
||||
- Key Props: `items[]`, `x`, `y`, `visible`, `menuProps`, `renderer`
|
||||
|
||||
**Props:**
|
||||
- `providerID?`: Optional unique identifier for the provider instance
|
||||
**Gridler**
|
||||
|
||||
### useMantineBetterMenus
|
||||
- Main Component: `Gridler`
|
||||
- Adaptors: `LocalDataAdaptor`, `APIAdaptorForGoLangv2`, `FormAdaptor`
|
||||
- Store Hook: `useGridlerStore()`
|
||||
- Key Props: `uniqueid`, `columns[]`, `data`
|
||||
|
||||
Hook to access menu functionality.
|
||||
**Former**
|
||||
|
||||
**Returns:**
|
||||
- `show(id: string, options?: Partial<MantineBetterMenuInstance>)`: Show a menu
|
||||
- `hide(id: string)`: Hide a menu
|
||||
- `menus`: Array of current menu instances
|
||||
- `setInstanceState`: Update specific menu instance properties
|
||||
- Main Component: `Former`
|
||||
- Wrappers: `FormerDialog`, `FormerModel`, `FormerPopover`
|
||||
- Ref Methods: `show()`, `close()`, `save()`, `reset()`, `validate()`
|
||||
- Key Props: `primeData`, `onSave`, `wrapper`
|
||||
|
||||
### MantineBetterMenuInstance
|
||||
**FormerControllers**
|
||||
|
||||
Interface for menu instances:
|
||||
- Controls: `TextInputCtrl`, `PasswordInputCtrl`, `TextAreaCtrl`, `NativeSelectCtrl`, `SwitchCtrl`, `ButtonCtrl`, `IconButtonCtrl`
|
||||
- Common Props: `name` (required), `label`, `disabled`
|
||||
|
||||
```typescript
|
||||
interface MantineBetterMenuInstance {
|
||||
id: string;
|
||||
items?: Array<MantineBetterMenuInstanceItem>;
|
||||
menuProps?: MenuProps;
|
||||
renderer?: ReactNode;
|
||||
visible: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
**Boxer**
|
||||
|
||||
- Provider: `BoxerProvider`
|
||||
- Store Hook: `useBoxerStore()`
|
||||
- Data Sources: `local`, `server`
|
||||
- Key Props: `data`, `dataSource`, `onAPICall`, `multiSelect`, `searchable`, `clearable`
|
||||
|
||||
**ErrorBoundary**
|
||||
|
||||
- Components: `ReactErrorBoundary`, `ReactBasicErrorBoundary`
|
||||
- Key Props: `namespace`, `reportAPI`, `onResetClick`, `onRetryClick`
|
||||
|
||||
**GlobalStateStore**
|
||||
|
||||
- Provider: `GlobalStateStoreProvider`
|
||||
- Hook: `useGlobalStateStore()` returns `{ program, session, owner, user, layout, navigation, app }`
|
||||
- Store Methods: `setAuthToken()`, `setApiURL()`, `fetchData()`, `login()`, `logout()`
|
||||
- Key Props: `apiURL`, `autoFetch`, `fetchOnMount`, `throttleMs`
|
||||
|
||||
## MCP Server
|
||||
|
||||
Oranguru includes a Model Context Protocol (MCP) server for AI-assisted development.
|
||||
|
||||
**Configuration:**
|
||||
|
||||
Add to `~/.claude/mcp_settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oranguru-docs": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@warkypublic/oranguru", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MantineBetterMenuInstanceItem
|
||||
**Tools:**
|
||||
|
||||
Interface for menu items:
|
||||
- `list_components` - List all components
|
||||
- `get_component_docs` - Get component documentation
|
||||
- `get_component_example` - Get code examples
|
||||
|
||||
```typescript
|
||||
interface MantineBetterMenuInstanceItem extends Partial<MenuItemProps> {
|
||||
isDivider?: boolean;
|
||||
label?: string;
|
||||
onClick?: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
onClickAsync?: () => Promise<void>;
|
||||
renderer?: ((props: MantineBetterMenuInstanceItem & Record<string, unknown>) => ReactNode) | ReactNode;
|
||||
}
|
||||
```
|
||||
**Resources:**
|
||||
|
||||
- `oranguru://docs/readme` - Full documentation
|
||||
- `oranguru://docs/components` - Component list
|
||||
|
||||
See `mcp/README.md` for details.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -174,6 +331,7 @@ interface MantineBetterMenuInstanceItem extends Partial<MenuItemProps> {
|
||||
- `pnpm lint`: Run ESLint
|
||||
- `pnpm typecheck`: Run TypeScript type checking
|
||||
- `pnpm clean`: Clean node_modules and dist folders
|
||||
- `pnpm mcp`: Run MCP server
|
||||
|
||||
### Building
|
||||
|
||||
@@ -189,9 +347,10 @@ See [LICENSE](LICENSE) file for details.
|
||||
|
||||
## About the Name
|
||||
|
||||
Oranguru is named after the Orangutan Pokémon (オランガ Oranga), a Normal/Psychic-type Pokémon introduced in Generation VII. Known as the "Sage Pokémon," Oranguru is characterized by its wisdom, intelligence, and ability to use tools strategically.
|
||||
Oranguru is named after the Orangutan Pokémon (オランガ Oranga), a Normal/Psychic-type Pokémon introduced in Generation VII. Known as the "Sage Pokémon," Oranguru is characterized by its wisdom, intelligence, and ability to use tools strategically.
|
||||
|
||||
In the Pokémon world, Oranguru is known for:
|
||||
|
||||
- Its exceptional intelligence and strategic thinking
|
||||
- Living deep in forests and rarely showing itself to humans
|
||||
- Using its psychic powers to control other Pokémon with its fan
|
||||
@@ -201,4 +360,4 @@ Just as Oranguru the Pokémon enhances and controls its environment with wisdom
|
||||
|
||||
## Author
|
||||
|
||||
**Warky Devs**
|
||||
Warky Devs
|
||||
|
||||
Reference in New Issue
Block a user