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

@revenyu/ui

v0.15.1

Published

Shared Vue 3 components, app-shell, and Frappe API layer for Revenyu-suite apps (HR Recruiter, Payroll, CRM, ERP).

Readme

@revenyu/ui

Shared Vue 3 components, app-shell chrome, and Frappe API layer extracted from hr_recruiter, for reuse across Revenyu-suite apps (HR Recruiter, Payroll, CRM, ERP — see the logos in AppSwitcher). Ships raw .vue/.ts source, same as frappe-ui: consumers compile it with their own Vite/tsc, there is no prebuilt dist.

This package assumes a Frappe backend. It is not a framework/backend-agnostic UI kit — api/* calls whitelisted Frappe methods, lib/doppio/controllers/call.js talks to /api/method/* with Frappe's CSRF header, and several components read/write Frappe doctypes.

Install

Published to the public npm registry as @revenyu/ui. The GitHub repo (PROJECXIO/revenyu-ui) is private, but the npm package is not — installing needs no GitHub access and no auth token:

yarn add @revenyu/ui
// package.json
"dependencies": {
  "@revenyu/ui": "^0.15.0"
}

No build step runs on install — your own Vite/tsc compiles the .vue/.ts source directly out of node_modules/@revenyu/ui/src/..., same as frappe-ui.

Peer dependencies you must already have: vue >=3.5, vue-router >=4.

Pin >=0.15.0 if you use LinkSelect. 0.14.0 was published from the commit before LinkSelect landed, so that one version's tarball has no such export and consuming builds fail with [MISSING_EXPORT] "LinkSelect" is not exported by ".../@revenyu/ui/src/index.ts".

Package entrypoints

Only these five subpaths are importable — the exports map deliberately exposes no others, so api/*, utils/*, and composables/useAnchoredPosition are internal to the library and not part of the public surface:

| Import | What it is | | -------------------------------- | -------------------------------------------------------- | | @revenyu/ui | Every component, store, composable and type listed below | | @revenyu/ui/style.css | The design tokens every component reads via var(--x) | | @revenyu/ui/rv-list.css | The opt-in .rv-* list-page stylesheet | | @revenyu/ui/tailwind | Tailwind preset mapping the tokens to theme values | | @revenyu/ui/tsconfig.base.json | Shared strict TS compiler options |

Setup

// main.ts
import '@revenyu/ui/style.css'
import { setCallRouter } from '@revenyu/ui'
import router from './router'

setCallRouter(router) // lets the API layer redirect to your login route on 401/403

Your router needs a route named Login (used by the 401/403 redirect check).

Mount these once at your app root — they are singletons driven by the exported stores, not per-instance props/emits components:

<SelectSheet />
<DateSheet />
<ConfirmSheet />
<Toasts />

Optional Tailwind preset (maps the CSS-variable design tokens to Tailwind theme values):

// tailwind.config.js
import revenyuPreset from '@revenyu/ui/tailwind'
export default { presets: [revenyuPreset] }

What's in here

Every name below is exported from the package root: import { Badge, SideNav, AiRewriteSheet } from '@revenyu/ui'.

Primitives — form inputs

Button, Checkbox, Combobox, CustomSelect, DatePickerField, FormControl, FormLabel, IconButton, Password, Radio, Slider, Switch, TextInput, Textarea, TimePicker.

Primitives — overlays/floating

BottomSheet, ConfirmSheet, ContextMenu, DateSheet, Dialog, Dropdown, FileUploader, HoverCard, Popover, SelectSheet, SettingsDialog, Tooltip.

Primitives — feedback/loading

Alert, ErrorMessage, LoadingIndicator, LoadingText, Progress, Skeleton, Spinner, Toasts.

Primitives — layout/navigation

ActionBar, Breadcrumb, BreadcrumbChip, Breadcrumbs, CrmBreadcrumb, Divider, EmptyState, ItemListRow, ListToolbar, ListFilterFields, ListView, PageHeader, RangeSwitcher, ScrollArea, Section, SettingsMenu, Stepper, TabButtons, Tabs, Tree.

ListToolbar + ListFilterFields + ListView + BulkActionBar together are the "Revenyu list page" pattern — search, a responsive filter grid, a sortable/selectable table, column editing, pagination and a bulk-action bar. They render against @revenyu/ui/rv-list.css.

Primitives — misc

Avatar, Badge, Duration, EChart, Icon, KeyboardShortcut, KeyboardShortcutsDialog, KpiCard, Rating, RevenyuLogo, RevenyuUiProvider.

App shell

AppSwitcher, BottomNav, MoreDrawer, Notifications, PageCrumbs, ProfileMenu, Shell, ShellDesktop, Sidebar, SideNav, Topbar, TopbarDesktop, GlobalSearch.

Feature widgets

ActivityFeed, BulkActionBar, DeleteAction, DocViewers, EmailComposer, FieldValueInput, InterviewQuickCreate, LinkSelect, OnboardingFieldCard, OnboardingLauncher, TableColumnsEditor, TemplateEditor.

LinkSelect is a Link field's picker: everything CustomSelect does, plus a create action in the dropdown header, offered only to users the server says may create that doctype. The record it creates is selected and kept alongside the options the parent passed, so the surrounding form needs to know nothing about a record being created mid-edit.

AI wizards

AiActionButton, AiDescriptionWizard, AiOnboardingFieldsWizard, AiQuestionsWizard, AiRewriteSheet, AiSettingsSheet.

AiEmailTemplateWizard exists in the source but is intentionally not exported — its template only renders a permanent "Coming Soon" placeholder. See CHANGELOG.md.

Easily-confused pairs

Three pairs of similarly-named components are easy to mix up — pick based on this:

  • Breadcrumb (fixed parent→current pair) vs Breadcrumbs (an arbitrary-length, prop-driven trail) vs CrmBreadcrumb (a segmented chip trail, each segment clickable).
  • RangeSwitcher (hardcoded 7d/30d/90d/ytd/all date-range options) vs TabButtons (a generic segmented control taking any options array).
  • PageCrumbs (breadcrumb trail + subtitle, driven by this app's own route names) vs PageHeader (a generic title + subtitle + actions-slot header, unrelated to routing).

Imperative UI helpers

The singleton sheets are driven by function calls rather than props, so any code — including non-component code — can open them:

import { openSelect, openDate, askConfirm, askConfirmWithReason, notify } from '@revenyu/ui'

openSelect('Status', ['Open', 'Closed'], current, v => (current = v))
openDate(dateStr, v => (dateStr = v), { min: '2026-01-01', quickActions: true })

const ok = await askConfirm({ title: 'Delete job?', message: 'This cannot be undone.' })
const reason = await askConfirmWithReason({ title: 'Reject applicant?' }) // null if cancelled

notify('Job deleted', 'ok')

| Function | Signature / notes | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | openSelect | (label, options, value, onChange, opts?) — options accepts plain strings or { value, label }; opts takes { createLabel, onCreate } to offer the same create action CustomSelect has on desktop | | openDate | (value, onChange, opts?) — opts takes { min, quickActions, anchor } | | askConfirm | (ConfirmOpts) => Promise<boolean> | | askConfirmWithReason | (ConfirmOpts) => Promise<string \| null> — resolves to the typed reason, or null when cancelled | | closeAll | Closes the select/date/confirm sheets at once | | notify | (text, kind?, ms?, title?) — kind is 'ok' \| 'err' (default 'ok'), ms defaults to 2200 for 'ok' / 6000 for 'err', title is an optional bold heading (used by error toasts to name the failure kind) | | toggleSidebar | Flips ui.sidebarCollapsed, persists it to localStorage, and sets the --sidebar-w custom property (68px collapsed / 220px expanded) |

ConfirmOpts is { title, message?, confirmLabel?, cancelLabel?, danger?, icon?, reasonRequired?, reasonPlaceholder? }.

Stores

Plain Vue reactive() objects — no Pinia, nothing to install or register.

  • ui — the shared chrome state: the select/date/confirm sheets plus the open/closed flags every shell component reads (notifs, appSwitcher, profileMenu, more, search, aiSettings, aiDescriptionWizard, aiRewrite, aiEmailWizard, aiQuestionsWizard, sidebarCollapsed).
  • toasts — { items }, rendered by <Toasts />; push to it with notify().
  • userStore — { profile, loading, loaded }, with loadUser(force?), userInitial() and userImage() (which normalizes a Frappe user_image into a usable URL).
  • Notification count — unreadCount, plus refreshUnread(), setUnread(n), incUnread(by?) and decUnread(by?) so a screen that marks something read can correct the bell without a refetch.

Composables

  • useIsDesktop() — a ref<boolean>, true at window.innerWidth >= 900, initialised synchronously so the first render is already correct and kept current on resize.
  • useDialogLayer(step = 10) — returns a dialog's z-index and hands the next one down a layer. The depth travels down the component tree rather than through a module-level counter, so nested dialogs (a mandatory Link field inside a quick-create dialog opening another quick-create dialog) are ordered right by construction and closing one leaves nothing to unwind. MODAL_LAYER (2000) is the base; toasts stay above at 12000.
  • useRevenyuUiConfig() — reads the config provided by RevenyuUiProvider. Entirely optional: it falls back to built-in defaults when no provider is mounted.

Configuration

RevenyuUiProvider

Wrap your app to set library-wide defaults — toastDuration, iconSize, size. Every field is optional and components fall back to the defaults, so mounting the provider is never required.

configureNotifications

The shared Notifications panel and bell default to HR Recruiter's hiring-scoped backend and routes. Any other app calls this once at startup; partial overrides are merged over the default, so you can replace just routeFor and keep the rest:

import { configureNotifications } from '@revenyu/ui'

configureNotifications({
  list: limit => call('myapp.api.notifications.list', { limit }),
  routeFor: n => (n.document_type === 'Lead' ? `/leads/${n.document_name}` : null),
})

The full contract is NotificationsApi: list, unreadCount, markRead, markAllRead, dismiss, routeFor. getNotificationsApi() returns whatever is currently installed.

setCallRouter

Hands the lib/doppio fetch wrapper your router so a 401/403 from any API call can redirect to your app's own login route. Call it once at startup.

Exported types

ConfirmOpts, CrmBreadcrumbSegment, DropdownItem, ListFilterFieldDef, ListFilterOption, ListToolbarColumn, ListToolbarSortOption, ListViewColumn, NotificationRow, NotificationType, NotificationsApi, ProfileMenuItem, SearchEntityStyle, SearchResultGroup, SearchResultItem, SettingsMenuItem, StepperStep.

Design tokens and styles

src/style.css (exported as @revenyu/ui/style.css) defines the color/radius/shadow/typography CSS custom properties every component reads via var(--x). Import it once at your app root.

@revenyu/ui/rv-list.css is separate and opt-in: the list-page pattern (search + filter grid

  • sortable/checkbox table + popovers + pagination + bulk bar) ported verbatim from revenyu_v2's CRM Leads list, so every consuming app renders byte-identical output instead of hand-porting values per app. Its tokens are namespaced under --rv-*, so importing it never overwrites your app's own --primary, --border, and so on.

Shared TypeScript config

// tsconfig.json
{ "extends": "@revenyu/ui/tsconfig.base.json" }

Gives you the same strict, moduleResolution: "Bundler", isolatedModules baseline the library itself compiles under.

Known limitations

BottomNav, SideNav, MoreDrawer, and PageCrumbs are not yet generic — their nav items and page titles (paths, icons, labels — see src/utils/pageMeta.ts for PageCrumbs) are hardcoded to HR Recruiter's own routes (/jobs, /applicants, /interviews, /offers, /onboarding, /settings/team, …), not driven by props. A consumer app other than HR Recruiter that mounts Shell/ShellDesktop today will get HR Recruiter's nav and breadcrumbs pointing at routes it doesn't have. Treat these four as HR-Recruiter-only until they're made configurable (tracked in CHANGELOG.md).

Topbar, TopbarDesktop, AppSwitcher, Notifications, ProfileMenu, and GlobalSearch are not affected — they read their state from the shared stores, not from hardcoded nav/route data. Breadcrumb, Breadcrumbs, and CrmBreadcrumb take their items via props.

Component docs

yarn docs:dev runs a local VitePress site with a live, adjustable-prop playground for every component, plus a full composed page example. Not deployed anywhere yet — clone this repo and run it locally.

Development

yarn install
yarn lint         # eslint
yarn format:check # prettier
yarn type-check   # vue-tsc --noEmit
yarn test         # vitest
yarn pack:check   # confirm `exports`/`files` resolve correctly before publishing

Versioning

Semver: patch = fixes, minor = new components/props, major = breaking prop/behavior changes (e.g. what openSelect/askConfirm accept). Consumers bump with yarn upgrade @revenyu/ui.

Releasing: bump version in package.json in the same commit or later than the feature it ships, tag that commit vX.Y.Z, push the tag, npm publish, then cut a GitHub release. Bumping in an earlier commit than the feature is what produced the broken 0.14.0 described above.