npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ebig-library

v0.0.10

Published

A modern UI component library for React by eBig

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.

npm version License: MIT


Table of Contents


Installation

npm install ebig-library

Import 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 EbigProvider with loadResources={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: EbigProvider wraps BrowserRouter internally. Do not add another BrowserRouter in 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-1heading-8, body-1body-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 | |---|---|---| | col1col24 | 1/24 – 24/24 | Always applied | | remain | fills rest | flex: 1 | | col1-mincol24-min | at < 576px | phones | | col1-smcol24-sm | at ≥ 576px | small devices | | col1-mdcol24-md | at ≥ 768px | tablets | | col1-lgcol24-lg | at ≥ 992px | laptops | | col1-xlcol24-xl | at ≥ 1200px | desktops | | col1-xxlcol24-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