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

@tideorg/ui

v0.0.1

Published

Tide UI Framework - Reusable components for data management interfaces

Downloads

10

Readme

@tide/ui

Ready-to-use admin pages for Tide. Manage users, roles, approvals, logs, and templates with minimal setup.

Install

npm install @tide/ui @tidecloak/react @tidecloak/js

Quick Start

import { QueryClient, QueryClientProvider, UsersPage } from '@tide/ui';
import { TideCloakContextProvider, useTideCloak, Authenticated, Unauthenticated } from '@tidecloak/react';
import tidecloakConfig from './tidecloak.json';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <TideCloakContextProvider config={tidecloakConfig}>
        <Authenticated>
          <AdminDashboard />
        </Authenticated>
        <Unauthenticated>
          <LoginPage />
        </Unauthenticated>
      </TideCloakContextProvider>
    </QueryClientProvider>
  );
}

function AdminDashboard() {
  const { AdminAPI } = useTideCloak();

  return <UsersPage adminAPI={AdminAPI} title="Users" />;
}

function LoginPage() {
  const { login } = useTideCloak();

  return <button onClick={login}>Login</button>;
}

That's it. You have a working user management page.

Available Pages

UsersPage

Manage users and assign roles.

<UsersPage adminAPI={AdminAPI} />

RolesPage

Create roles with policy support and approval workflows.

import { createLocalStoragePolicyAPI, createLocalStorageTemplateAPI } from '@tide/ui';

<RolesPage
  adminAPI={AdminAPI}
  policyAPI={createLocalStoragePolicyAPI()}
  templateAPI={createLocalStorageTemplateAPI()}
  tideContext={tideContext}
  currentUsername={username}
/>

ApprovalsPage

Review and approve pending changes.

<ApprovalsPage
  adminAPI={AdminAPI}
  tideContext={tideContext}
  currentUsername={username}
/>

LogsPage

View access and policy activity.

import { createLocalStoragePolicyLogsAPI } from '@tide/ui';

<LogsPage
  adminAPI={AdminAPI}
  policyLogsAPI={createLocalStoragePolicyLogsAPI()}
/>

TemplatesPage

Manage policy templates.

import { createLocalStorageTemplateAPI } from '@tide/ui';

<TemplatesPage api={createLocalStorageTemplateAPI()} />

Full Example

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import {
  QueryClient, QueryClientProvider,
  UsersPage, RolesPage, ApprovalsPage, LogsPage, TemplatesPage,
  createLocalStorageTemplateAPI,
  createLocalStoragePolicyAPI,
  createLocalStorageAccessMetadataAPI,
  createLocalStoragePolicyLogsAPI,
  Tabs, TabsList, TabsTrigger, TabsContent,
} from '@tide/ui';
import { TideCloakContextProvider, useTideCloak, Authenticated, Unauthenticated } from '@tidecloak/react';
import tidecloakConfig from './tidecloak.json';

const queryClient = new QueryClient();
const templateAPI = createLocalStorageTemplateAPI();
const policyAPI = createLocalStoragePolicyAPI();
const accessMetadataAPI = createLocalStorageAccessMetadataAPI();
const policyLogsAPI = createLocalStoragePolicyLogsAPI();

function AdminDashboard() {
  const {
    AdminAPI,
    logout,
    isInitializing,
    getValueFromIdToken,
    initializeTideRequest,
    getVendorId,
    getResource,
    approveTideRequests,
  } = useTideCloak();

  const tideContext = { initializeTideRequest, getVendorId, getResource, approveTideRequests };
  const username = getValueFromIdToken('preferred_username') ?? undefined;

  if (isInitializing) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      <header>
        <h1>Admin Dashboard</h1>
        <p>Signed in as {username}</p>
        <button onClick={logout}>Logout</button>
      </header>

      <Tabs defaultValue="users">
        <TabsList>
          <TabsTrigger value="users">Users</TabsTrigger>
          <TabsTrigger value="roles">Roles</TabsTrigger>
          <TabsTrigger value="approvals">Approvals</TabsTrigger>
          <TabsTrigger value="logs">Logs</TabsTrigger>
          <TabsTrigger value="templates">Templates</TabsTrigger>
        </TabsList>

        <TabsContent value="users">
          <UsersPage adminAPI={AdminAPI} accessMetadataAPI={accessMetadataAPI} />
        </TabsContent>

        <TabsContent value="roles">
          <RolesPage
            adminAPI={AdminAPI}
            templateAPI={templateAPI}
            policyAPI={policyAPI}
            policyLogsAPI={policyLogsAPI}
            tideContext={tideContext}
            currentUsername={username}
          />
        </TabsContent>

        <TabsContent value="approvals">
          <ApprovalsPage
            adminAPI={AdminAPI}
            tideContext={tideContext}
            accessMetadataAPI={accessMetadataAPI}
            policyLogsAPI={policyLogsAPI}
            currentUsername={username}
          />
        </TabsContent>

        <TabsContent value="logs">
          <LogsPage adminAPI={AdminAPI} policyLogsAPI={policyLogsAPI} />
        </TabsContent>

        <TabsContent value="templates">
          <TemplatesPage api={templateAPI} />
        </TabsContent>
      </Tabs>
    </div>
  );
}

function LoginPage() {
  const { login } = useTideCloak();

  return (
    <div>
      <h1>Welcome</h1>
      <button onClick={login}>Login</button>
    </div>
  );
}

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <TideCloakContextProvider config={tidecloakConfig}>
        <Authenticated>
          <AdminDashboard />
        </Authenticated>
        <Unauthenticated>
          <LoginPage />
        </Unauthenticated>
      </TideCloakContextProvider>
    </QueryClientProvider>
  </StrictMode>
);

Storage Adapters

For development, use the localStorage adapters. They work instantly but show a warning that data won't persist across devices.

createLocalStorageTemplateAPI()        // Policy templates
createLocalStoragePolicyAPI()          // Role policies
createLocalStorageAccessMetadataAPI()  // Change set metadata
createLocalStoragePolicyLogsAPI()      // Policy activity logs
createLocalStoragePolicyApprovalsAPI() // Policy approvals

For production, implement your own:

const myPolicyAPI: PolicyAPI = {
  getPolicy: (roleName) => fetch(`/api/policies/${roleName}`).then(r => r.json()),
  upsertPolicy: (policy) => fetch('/api/policies', { method: 'POST', body: JSON.stringify(policy) }),
  deletePolicy: (roleName) => fetch(`/api/policies/${roleName}`, { method: 'DELETE' }),
};

Tide Enclave Integration

Pass tideContext to enable the Tide approval enclave popup for cryptographic signing:

const {
  initializeTideRequest,
  getVendorId,
  getResource,
  approveTideRequests,
} = useTideCloak();

const tideContext = { initializeTideRequest, getVendorId, getResource, approveTideRequests };

<ApprovalsPage tideContext={tideContext} />
<RolesPage tideContext={tideContext} />

Custom Data Fetching

Need full control? Use the base components:

import { UsersPageBase } from '@tide/ui';

<UsersPageBase
  fetchUsers={() => myApi.getUsers()}
  fetchRoles={() => myApi.getRoles()}
  onCreate={(data) => myApi.createUser(data)}
  onUpdateProfile={(data) => myApi.updateUser(data)}
  onUpdateRoles={(data) => myApi.updateRoles(data)}
  onDelete={(id) => myApi.deleteUser(id)}
/>

Available base components: UsersPageBase, RolesPageBase, ApprovalsPageBase, LogsPageBase, TemplatesPageBase

Built-in UI Components

Works out of the box with inline styles. No Tailwind or CSS required.

import {
  Card, CardContent, Button, Badge, Input, Label,
  Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
  Dialog, DialogContent, DialogHeader, DialogTitle,
  Tabs, TabsList, TabsTrigger, TabsContent,
} from '@tide/ui';

Or pass your own shadcn/ui components:

import { Button } from '@/components/ui/button';

<RolesPage components={{ Button }} />

Theming

Inject theme CSS variables for dark mode support and consistent styling:

import { injectThemeCSS } from '@tide/ui';

// Call once at app init
injectThemeCSS();

Or use the theme constants directly:

import { colors, spacing, radius, focusRing } from '@tide/ui';

const myStyle = {
  color: colors.foreground.primary,
  padding: spacing.md,
  borderRadius: radius.lg,
};

Supports prefers-color-scheme: dark and prefers-reduced-motion: reduce automatically.

Hooks

useAutoRefresh

Auto-refresh data at configurable intervals with error handling:

import { useAutoRefresh } from '@tide/ui';

const { secondsRemaining, refreshNow, lastError, failureCount, isStopped } = useAutoRefresh({
  intervalSeconds: 15,
  refresh: fetchData,
  isBlocked: isLoading,
  onError: (err) => console.error('Refresh failed:', err),
  maxRetries: 3, // stops after 3 consecutive failures
});

// Show countdown or error state
{lastError && <span>Refresh failed: {lastError.message}</span>}
{secondsRemaining && <span>Refreshing in {secondsRemaining}s</span>}

Accessibility

All components include proper ARIA attributes:

  • role="status" and aria-busy for loading states
  • role="alertdialog" with aria-modal for confirmation dialogs
  • aria-label on interactive elements
  • prefers-reduced-motion support for animations

API Reference

| Adapter | Purpose | |---------|---------| | TemplateAPI | Store policy templates | | PolicyAPI | Store role policies | | PolicyLogsAPI | Log policy activity | | PolicyApprovalsAPI | Track pending policy approvals | | AccessMetadataAPI | Track change set metadata |

License

MIT