ebig-library
v0.0.10
Published
A modern UI component library for React by eBig
Maintainers
Readme
ebig-library
A modern, lightweight React + TypeScript UI component library by eBig — 35+ ready-to-use components, a responsive layout system, design-token theming, built-in i18n (en/vi), and optional eBig backend integration for dynamic form/table/page rendering.
Table of Contents
- Installation
- Setup: EbigProvider
- Global Context
- Layout System
- Skin / CSS
- Components
- Button & SimpleButton
- Ebigicon
- TextField
- TextArea
- SelectDropdown
- Checkbox
- Switch
- RadioButton
- DateTimePicker
- NumberPicker
- Slider
- Rating
- InputOtp
- ColorPicker
- Tag
- Pagination
- InfiniteScroll
- Dialog
- Popup
- ToastMessage
- ProgressBar
- ProgressCircle
- Calendar
- Carousel
- VideoPlayer / AudioPlayer / IframePlayer
- ImportFile / UploadFiles
- CustomCkEditor5
- EbigEditor
- IconPicker
- EmptyPage
- Text
- ComponentStatus
- Form Components (react-hook-form)
- Utility Class (Util)
- Controllers
- Backend-Driven Modules
- Design Tokens & Theming
- Responsive Grid Classes
Installation
npm install ebig-libraryImport the CSS skin files (required for layout and typography utilities):
<!-- In your index.html or root CSS -->
<link rel="stylesheet" href="https://cdn.ebig.co/ebig-library/src/skin/root.min.css" />
<link rel="stylesheet" href="https://cdn.ebig.co/ebig-library/src/skin/layout.min.css" />
<link rel="stylesheet" href="https://cdn.ebig.co/ebig-library/src/skin/typography.min.css" />
<link rel="stylesheet" href="https://cdn.ebig.co/ebig-library/src/skin/toast-noti.min.css" />These are loaded automatically if you use
EbigProviderwithloadResources={true}(default).
Setup: EbigProvider
Wrap your app's root with EbigProvider. It sets up routing (BrowserRouter internally), toast notifications, dialogs, token refresh, and optional design token + i18n loading from the eBig backend.
// main.tsx
import ReactDOM from 'react-dom/client'
import EbigProvider, { Route } from 'ebig-library'
ReactDOM.createRoot(document.getElementById('root')!).render(
<EbigProvider
pid="your-project-id"
url="https://your-ebig-api.com/"
fileUrl="https://your-file-server.com/"
imgUrlId="https://your-cdn.com/"
theme="light" // "light" | "dark"
loadResources={true} // false → skip backend token/i18n loading
>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</EbigProvider>
)Important:
EbigProviderwrapsBrowserRouterinternally. Do not add anotherBrowserRouterin your app.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
| pid | string | ✅ | eBig project ID (32-char) |
| url | string | ✅ | eBig API base URL |
| fileUrl | string | ✅ | File server URL |
| imgUrlId | string | ✅ | CDN/image URL prefix |
| theme | "light" \| "dark" | — | Default "light" |
| loadResources | boolean | — | Default true. Set to false to skip backend loading |
| onInvalidToken | () => void | — | Called on 401 — override to redirect to login |
| children | ReactNode | — | <Route> elements |
Global Context
import { useEbigContext } from 'ebig-library'
function MyComponent() {
const {
theme, // "light" | "dark"
setTheme,
userData, // current user object (set by you)
setUserData,
globalData, // any global state bag
setGlobalData,
i18n, // i18next instance
} = useEbigContext()
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
)
}Layout System
Use row and col CSS classes for flex layouts. Add remain to let a child fill remaining space.
// Horizontal (flex-direction: row, align-items: center)
<div className="row" style={{ gap: 12 }}>
<span>Left</span>
<span className="remain">Stretches to fill space</span>
<span>Right</span>
</div>
// Vertical (flex-direction: column)
<div className="col" style={{ gap: 8 }}>
<span>Top</span>
<span>Bottom</span>
</div>Skin / CSS
| Resource | URL |
|---|---|
| Design tokens (CSS variables) | https://cdn.ebig.co/ebig-library/src/skin/root.min.css |
| Layout utilities (row, col, grids) | https://cdn.ebig.co/ebig-library/src/skin/layout.min.css |
| Typography (heading-1…heading-8, body-1…body-3) | https://cdn.ebig.co/ebig-library/src/skin/typography.min.css |
| Toast styles | https://cdn.ebig.co/ebig-library/src/skin/toast-noti.min.css |
Components
Button & SimpleButton
import { Button, SimpleButton, Ebigicon } from 'ebig-library'
// Basic
<Button label="Click me" onClick={() => alert('clicked!')} />
// Size + color variant
<Button label="Save" className="size40 button-primary" onClick={handleSave} />
// With prefix icon
<Button
label="Upload"
prefix={<Ebigicon src="outline/arrows/cloud-upload" size={16} />}
className="size40 button-neutral"
onClick={handleUpload}
/>
// As a link
<Button label="Docs" linkTo="https://ebig.co" target="_blank" className="size40 button-grey" />
// With tooltip
<Button label="Info" tooltip={{ message: 'More details', position: 'top' }} />
// Submit — also triggers on Enter key
<Button label="Submit" type="submit" className="size40 button-primary" onClick={handleSubmit} />
// SimpleButton — auto-disables itself during async onClick to prevent double-clicks
<SimpleButton label="Save" className="size40 button-primary" onClick={async () => { await save() }} />Size classes: size24 · size32 (default) · size40 · size48 · size56 · size64
Color classes: button-primary · button-grey · button-neutral · button-black · button-white · button-infor · button-warning · button-error · button-success · button-infor-main · button-warning-main · button-error-main · button-success-main
Ebigicon
SVG icon component. Icons are fetched from https://cdn.ebig.co/icon-library/ and cached in the browser via the Cache API after first load.
import { Ebigicon } from 'ebig-library'
<Ebigicon src="outline/user/user" size={24} />
<Ebigicon src="fill/actions/trash" size={20} color="#E14337" onClick={handleDelete} className="icon-button" />
// With tooltip
<Ebigicon src="outline/essential/info-circle" size={20} tooltip={{ message: 'Help', position: 'bottom' }} />
// From a custom link
<Ebigicon link="https://your-cdn.com/custom-icon.svg" size={24} />Style classes: icon-button (adds hover/active styles) · icon-button light · border · dashed
Size classes: size24 · size32 · size40 · size48 · size56 · size64
TextField
import { TextField, Ebigicon } from 'ebig-library'
<TextField placeholder="Enter name" onChange={(e) => setName(e.target.value)} />
// With prefix icon and helper text (shown in red below)
<TextField
prefix={<Ebigicon src="outline/user/user" size={16} />}
placeholder="Email"
type="email"
helperText="Invalid email"
className="size40 body-3"
/>
// With react-hook-form
const { register } = useForm()
<TextField placeholder="Username" register={register('username', { required: true })} />Size classes: size24 · size32 · size40 (default) · size48
TextArea
import { TextArea } from 'ebig-library'
<TextArea placeholder="Write something..." onChange={(e) => setText(e.target.value)} />SelectDropdown
Single-value dropdown. Supports static options, async lazy-loading, hierarchical (parent/child) options, and react-hook-form.
import { SelectDropdown } from 'ebig-library'
const options = [
{ id: '1', name: 'Option A' },
{ id: '2', name: 'Option B' },
{ id: '3', name: 'Option C', disabled: true },
]
// Static
<SelectDropdown
value={selected}
options={options}
placeholder="Choose one"
onChange={(item) => setSelected(item?.id)}
/>
// Async (loads on open / search)
<SelectDropdown
options={[]}
getOptions={async ({ length, search }) => {
const res = await fetchItems({ page: Math.floor(length / 10) + 1, search })
return { data: res.items, totalCount: res.total }
}}
onChange={(item) => setSelected(item?.id)}
/>Checkbox
import { Checkbox } from 'ebig-library'
<Checkbox value={isChecked} onChange={(val) => setIsChecked(val)} />
<Checkbox value={null} /> {/* null = indeterminate */}Switch
import { Switch } from 'ebig-library'
<Switch value={isOn} onChange={(val) => setIsOn(val)} />
<Switch value={isOn} size="2.4rem" onBackground="#287CF0" onChange={setIsOn} />RadioButton
import { RadioButton } from 'ebig-library'
<RadioButton value={selected === 'a'} onChange={() => setSelected('a')} label="Option A" />
<RadioButton value={selected === 'b'} onChange={() => setSelected('b')} label="Option B" />DateTimePicker
import { DateTimePicker } from 'ebig-library'
<DateTimePicker value={date} onChange={(val) => setDate(val)} placeholder="Pick a date" />NumberPicker
import { NumberPicker } from 'ebig-library'
<NumberPicker value={count} onChange={(val) => setCount(val)} min={0} max={100} />Slider
import { Slider } from 'ebig-library'
<Slider value={volume} min={0} max={100} onChange={(val) => setVolume(val)} />Rating
import { Rating } from 'ebig-library'
<Rating value={3} onChange={(val) => setRating(val)} />InputOtp
import { InputOtp } from 'ebig-library'
<InputOtp length={6} onComplete={(code) => verifyOtp(code)} />ColorPicker
import { ColorPicker } from 'ebig-library'
<ColorPicker value={color} onChange={(hex) => setColor(hex)} />Tag
import { Tag } from 'ebig-library'
<Tag label="Active" color="#287CF0" />
<Tag label="Error" color="#E14337" onRemove={() => handleRemove()} />Pagination
import { Pagination } from 'ebig-library'
<Pagination
pageIndex={page}
totalCount={total}
pageSize={10}
onChange={(newPage) => setPage(newPage)}
/>InfiniteScroll
import { InfiniteScroll } from 'ebig-library'
<InfiniteScroll
data={items}
render={(item) => <div key={item.id}>{item.name}</div>}
totalCount={total}
onLoadMore={() => loadNextPage()}
/>Dialog
import { showDialog, DialogAlignment } from 'ebig-library'
showDialog({
title: 'Confirm delete',
content: 'Are you sure?',
alignment: DialogAlignment.center,
onConfirm: () => handleDelete(),
})Popup
import { Popup, showPopup, closePopup } from 'ebig-library'
import { useRef } from 'react'
const ref = useRef()
<button onClick={(ev) => showPopup({ ref, id: 'my-popup', clickTarget: ev.currentTarget, content: () => <div>Content</div> })}>
Open popup
</button>
<Popup ref={ref} />ToastMessage
import { ToastMessage } from 'ebig-library'
ToastMessage.success('Saved successfully')
ToastMessage.errors('Something went wrong')
ToastMessage.warning('Check your input')ProgressBar
import { ProgressBar } from 'ebig-library'
<ProgressBar percent={75} label="Uploading..." />ProgressCircle
import { ProgressCircle } from 'ebig-library'
<ProgressCircle percent={60} size={80} />Calendar
import { Calendar } from 'ebig-library'
<Calendar value={date} onChange={(val) => setDate(val)} />Carousel
import { Carousel } from 'ebig-library'
<Carousel>
<img src="/slide1.jpg" alt="Slide 1" />
<img src="/slide2.jpg" alt="Slide 2" />
</Carousel>VideoPlayer / AudioPlayer / IframePlayer
import { VideoPlayer, AudioPlayer, IframePlayer } from 'ebig-library'
<VideoPlayer src="https://example.com/video.mp4" />
<AudioPlayer src="https://example.com/audio.mp3" />
<IframePlayer src="https://www.youtube.com/embed/xxxxx" />ImportFile / UploadFiles
import { ImportFile, UploadFiles } from 'ebig-library'
// File input with preview
<ImportFile
value={files}
onChange={(fileList) => setFiles(fileList)}
maxSize={5 * 1024 * 1024} // 5 MB
accept={['.png', '.jpg', '.pdf']}
/>
// Full upload flow with progress and CDN upload
<UploadFiles
value={uploadedFiles}
onChange={(files) => setUploadedFiles(files)}
/>CustomCkEditor5
Rich-text editor powered by CKEditor 5. Requires ckeditor5 and @ckeditor/ckeditor5-react to be installed by the consumer.
import { CustomCkEditor5, CkEditorUploadAdapter } from 'ebig-library'
<CustomCkEditor5
value={html}
onChange={(val) => setHtml(val)}
uploadAdapter={CkEditorUploadAdapter}
/>EbigEditor
Lightweight rich-text editor (no CKEditor dependency). Supports emoji, bold, italic, underline, hyperlinks, and @mention-style suggestion hooks.
import { EbigEditor } from 'ebig-library'
<EbigEditor
placeholder="Write a comment..."
onChange={(value, el) => setContent(value)}
onBlur={(value, el) => handleBlur(value)}
/>
// Custom toolbar subset
<EbigEditor
customToolbar={['bold', 'italic', 'emoji']}
onChange={(val) => setContent(val)}
/>
// Mention suggestions
<EbigEditor
onSuggest={[{
triggerPattern: '@',
render: (offset, match, select) => (
<MentionList offset={offset} query={match} onSelect={select} />
)
}]}
onChange={(val) => setContent(val)}
/>IconPicker
import { IconPicker } from 'ebig-library'
<IconPicker value={iconName} onChange={(name) => setIconName(name)} />EmptyPage
import { EmptyPage } from 'ebig-library'
<EmptyPage
title="No data found"
subtitle="Try adjusting your filters"
button={<Button label="Reset" onClick={reset} />}
/>Text
Typography component that renders semantic HTML elements with typography class helpers.
import { Text } from 'ebig-library'
<Text className="heading-3">Page title</Text>
<Text className="body-2" maxLine={2}>Truncated body text that wraps at two lines...</Text>ComponentStatus
import { ComponentStatus, getStatusIcon } from 'ebig-library'
<ComponentStatus status="success" label="Completed" />
const icon = getStatusIcon('warning')Form Components (react-hook-form)
Pre-built form field wrappers integrating react-hook-form. All accept methods (from useForm()), name, label, required, disabled, placeholder, and className.
import { useForm } from 'react-hook-form'
import {
TextFieldForm,
TextAreaForm,
Select1Form,
SelectMultipleForm,
CheckboxForm,
SwitchForm,
DateTimePickerForm,
NumberPickerForm, // NumberPicker
RateForm, // Rating
ColorPickerForm,
GroupCheckboxForm,
GroupRadioButtonForm,
RangeForm, // Slider
CKEditorForm,
EbigEditorForm,
InputPasswordForm,
IconPickerForm,
UploadMultipleFileTypeForm,
} from 'ebig-library'
const methods = useForm()
<form onSubmit={methods.handleSubmit(onSubmit)}>
<TextFieldForm methods={methods} name="username" label="Username" required />
<TextFieldForm methods={methods} name="price" label="Price" type="money" />
<Select1Form
methods={methods}
name="category"
label="Category"
options={[{ id: '1', name: 'Tech' }, { id: '2', name: 'Finance' }]}
/>
<DateTimePickerForm methods={methods} name="dueDate" label="Due date" />
<EbigEditorForm methods={methods} name="description" label="Description" />
<Button label="Submit" type="submit" className="size40 button-primary" />
</form>Utility Class (Util)
import { Util } from 'ebig-library'
// Date & Time
Util.dateTime_stringToDecimal('2026-04-08T10:00:00Z') // → Unix seconds
Util.stringToDate('08/04/2026', 'dd/mm/yyyy') // → Date
Util.calculateAge('01/01/2000') // → number
// Number & Currency
Util.formatCurrency(1500000, 'VND') // → "1,500,000.00 ₫"
Util.formatCurrency(99.9, 'USD') // → "$99.90"
Util.convertCurrency(100, 'USD', 'VND') // → 2450000
// Color
Util.hexToRgb('#287CF0') // → { r: 40, g: 124, b: 240 }
Util.rgbToHex(40, 124, 240) // → "#287cf0"
// String
Util.toSlug('Hello World!') // → "hello-world"
Util.randomGID() // → 32-char random hex ID
// Cookie
Util.getCookie('accessToken')
Util.setCookie('accessToken', token)
Util.clearCookie()
// File
Util.formatFileSize(1048576) // → "1 MB"
Util.stringToFile('content', 'file.txt')
// Auth
Util.encodeBase64('string')
Util.decodeBase64('encoded')Controllers
All controllers require ConfigData.url and ConfigData.pid to be set (done automatically by EbigProvider).
DataController
Generic CRUD for any project data module.
import { DataController } from 'ebig-library'
const ctrl = new DataController('Product')
// Fetch list (RediSearch query syntax)
const res = await ctrl.getListSimple({ page: 1, size: 20, query: '@Category:{Electronics}' })
// Get by ID
const item = await ctrl.getById('abc123')
// Add
await ctrl.add([{ Name: 'Product A', Price: 100 }])
// Edit
await ctrl.edit([{ Id: 'abc123', Price: 120 }])
// Delete
await ctrl.delete(['abc123', 'def456'])
// Aggregate / group
await ctrl.aggregateList({ filter: '@Price:[100 200]', sortby: [{ prop: 'Price', direction: 'ASC' }] })SettingDataController
For system-level setting entities (report, chart, form, card, view).
import { SettingDataController } from 'ebig-library'
const ctrl = new SettingDataController('form')
await ctrl.getListSimple({ page: 1, size: 10 })
await ctrl.action('add', { data: [{ Name: 'My Form' }] })
await ctrl.getByIds(['id1', 'id2'])AccountController
Login, get user info, and password utilities.
import { AccountController } from 'ebig-library'
const acc = new AccountController('Customer') // or 'User'
// Login with account/password
const res = await acc.login({ type: 'account', username: 'admin', password: 'pass' })
// Login with Google OAuth token
await acc.login({ type: 'google', token: googleToken })
// Get current user info
const info = await acc.getInfor()
// Hash a password
const hashed = await acc.hashPassword('mypassword')EbigController
Project-level queries (fetches eBig project metadata). Do not change the internal API paths.
import { EbigController } from 'ebig-library'
const ctrl = new EbigController('Project')
const project = await ctrl.getById('your-project-id')
const results = await ctrl.getListSimple({ query: '@Domain:{ebig.co}', size: 1 })TableController
CRUD for schema-level settings (table, column, rel, menu, page, layout, designtoken, workflow, process, step).
import { TableController } from 'ebig-library'
const ctrl = new TableController('table')
const tables = await ctrl.getAll()
await ctrl.add([{ Name: 'Orders' }])
await ctrl.edit([{ Id: 'xxx', Name: 'OrdersV2' }])
await ctrl.delete(['xxx'])IntegrationController
import { IntegrationController } from 'ebig-library'
const integration = new IntegrationController()
await integration.sendEmail({
templateId: 'welcome-email',
templateParams: { to: '[email protected]', name: 'Alice' }
})BaseDA
Low-level HTTP helpers used by all controllers. Use directly only when you need calls outside the standard controller pattern.
import { BaseDA, ConfigData } from 'ebig-library'
// GET (auto-injects auth headers for ConfigData.url requests)
const data = await BaseDA.get(`${ConfigData.url}data/custom-endpoint`, {
headers: { pid: ConfigData.pid }
})
// POST
await BaseDA.post(`${ConfigData.url}data/action?action=add`, {
headers: { pid: ConfigData.pid, module: 'Product' },
body: { data: [{ Name: 'Item' }] }
})
// Upload files (auto-batches: max 12 files / 200 MB per batch)
const uploaded = await BaseDA.uploadFiles(fileList)
// uploaded → [{ Id, Url, Name, ... }]Backend-Driven Modules
Render entire UI sections driven by eBig backend configuration. Requires a valid pid/url in EbigProvider.
import { PageById, PageByUrl, FormById, CardById, ViewById, ChartById, ChartByType } from 'ebig-library'
// Render a full page by its backend ID
<PageById id="page-id" />
// Render a page by URL path (matches current URL)
<PageByUrl url="location.pathname" />
// Render a form by ID
<FormById id="form-id" onSuccess={(data) => console.log(data)} />
// Render a card layout by ID
<CardById id="card-id" />
// Render a data view by ID
<ViewById id="view-id" />
// Chart by backend config ID
<ChartById id="chart-id" />
// Chart by type with your own data
<ChartByType type="bar" data={chartData} />Design Tokens & Theming
Design tokens are loaded from the eBig backend at runtime and injected as CSS custom properties into <head>. Toggle dark mode via setTheme:
const { theme, setTheme } = useEbigContext()
setTheme('dark') // adds class="dark" to <html>Tokens follow the pattern:
/* Light mode */
--primary-main-color: #287CF0;
--neutral-text-title-color: #18181B;
/* Dark mode (html.dark) */
--primary-main-color: #4A90E2;
--neutral-text-title-color: #EAEAEC;Responsive Grid Classes
The layout system uses a 24-column grid. Add these classes to children inside a row.
| Class | Columns | Notes |
|---|---|---|
| col1 – col24 | 1/24 – 24/24 | Always applied |
| remain | fills rest | flex: 1 |
| col1-min – col24-min | at < 576px | phones |
| col1-sm – col24-sm | at ≥ 576px | small devices |
| col1-md – col24-md | at ≥ 768px | tablets |
| col1-lg – col24-lg | at ≥ 992px | laptops |
| col1-xl – col24-xl | at ≥ 1200px | desktops |
| col1-xxl – col24-xxl | at > 1200px | wide screens |
// 2-column on desktop, stacks on mobile
<div className="row" style={{ gap: 16 }}>
<div className="col24 col12-lg" style={{ "--gutter": "16px" }}>Left panel</div>
<div className="col24 col12-lg" style={{ "--gutter": "16px" }}>Right panel</div>
</div>License
MIT © eBig / FDITECH
