npm.io
17.0.2 • Published 3 weeks ago

@furystack/shades-common-components

Licence
GPL-2.0
Version
17.0.2
Deps
6
Size
6.2 MB
Vulns
0
Weekly
0
Stars
5

@furystack/shades-common-components

Common and reusable UI components for FuryStack Shades.

Installation

npm install @furystack/shades-common-components
# or
yarn add @furystack/shades-common-components

Components

Button

A styled button component with contained and outlined variants.

import { Button } from '@furystack/shades-common-components'

// Contained button (default)
<Button onclick={() => console.log('Clicked!')}>Click me</Button>

// Outlined button
<Button variant="outlined" color="primary">Outlined</Button>

// Disabled button
<Button disabled>Disabled</Button>

// With color
<Button variant="contained" color="error">Delete</Button>
Input

A styled text input with validation support.

import { Input } from '@furystack/shades-common-components'

// Basic input
<Input
  labelTitle="Username"
  placeholder="Enter your username"
  onTextChange={(value) => console.log(value)}
/>

// With validation
<Input
  labelTitle="Email"
  type="email"
  required
  variant="outlined"
  getValidationResult={({ state }) => {
    if (!state.value.includes('@')) {
      return { isValid: false, message: 'Please enter a valid email' }
    }
    return { isValid: true }
  }}
/>

// Contained variant
<Input
  labelTitle="Password"
  type="password"
  variant="contained"
  defaultColor="primary"
/>
Modal

A modal dialog component.

import { Modal } from '@furystack/shades-common-components'
import { ObservableValue } from '@furystack/utils'

const isVisible = new ObservableValue(false)

<Button onclick={() => isVisible.setValue(true)}>Open Modal</Button>

<Modal
  isVisible={isVisible}
  onClose={() => isVisible.setValue(false)}
  backdropStyle={{ background: 'rgba(0, 0, 0, 0.5)' }}
>
  <div onclick={(e) => e.stopPropagation()}>
    <h2>Modal Title</h2>
    <p>Modal content goes here</p>
    <Button onclick={() => isVisible.setValue(false)}>Close</Button>
  </div>
</Modal>
DataGrid

A data grid component for displaying tabular data.

import { DataGrid, CollectionService } from '@furystack/shades-common-components'

type User = { id: number; name: string; email: string }

const collectionService = new CollectionService<User>({ /* options */ })
const findOptions = { top: 10, skip: 0 }

<DataGrid<User, 'name' | 'email'>
  columns={['name', 'email']}
  collectionService={collectionService}
  findOptions={findOptions}
  onFindOptionsChange={(newOptions) => { /* handle options change */ }}
  headerComponents={{
    name: () => <span>Name</span>,
    email: () => <span>Email</span>,
  }}
  rowComponents={{
    name: (user) => <span>{user.name}</span>,
    email: (user) => <span>{user.email}</span>,
  }}
  styles={{ container: { minHeight: '400px' } }}
/>
AppBar

A top navigation bar component.

import { AppBar, AppBarLink } from '@furystack/shades-common-components'
;<AppBar>
  <h1>My App</h1>
  <AppBarLink href="/">Home</AppBarLink>
  <AppBarLink href="/about">About</AppBarLink>
</AppBar>
Tabs

A tabbed interface component.

import { Tabs } from '@furystack/shades-common-components'
;<Tabs
  tabs={[
    { header: <span>Tab 1</span>, component: <div>Content 1</div> },
    { header: <span>Tab 2</span>, component: <div>Content 2</div> },
  ]}
/>
CacheView

Renders the state of a cache entry. Takes a Cache instance and args, subscribes to the observable, and handles loading, error (with retry), and loaded/obsolete states.

import { CacheView } from '@furystack/shades-common-components'
import type { CacheWithValue } from '@furystack/cache'

const UserContent = Shade<{ data: CacheWithValue<User> }>({
  customElementName: 'user-content',
  render: ({ props }) => <div>{props.data.value.name}</div>,
})

// Basic usage
<CacheView cache={userCache} args={[userId]} content={UserContent} />

// With custom loader and error
<CacheView
  cache={userCache}
  args={[userId]}
  content={UserContent}
  loader={<Skeleton />}
  error={(err, retry) => <Alert severity="error"><Button onclick={retry}>Retry</Button></Alert>}
/>
Loader

A loading spinner component.

import { Loader } from '@furystack/shades-common-components'
;<Loader />
Paper

A container component with elevation.

import { Paper } from '@furystack/shades-common-components'
;<Paper>
  <p>Content with elevated background</p>
</Paper>
Avatar

An avatar component for displaying user images or initials.

import { Avatar } from '@furystack/shades-common-components'
;<Avatar userName="John Doe" />
FAB (Floating Action Button)

A floating action button component.

import { Fab } from '@furystack/shades-common-components'
;<Fab onclick={() => console.log('FAB clicked')}>+</Fab>
Typography

A text component that renders semantic HTML elements (h1h6, p, span) based on the variant prop.

import { Typography } from '@furystack/shades-common-components'

// Heading
<Typography variant="h1">Page Title</Typography>

// Body text (default variant is 'body1')
<Typography>Regular paragraph text</Typography>

// With color
<Typography variant="h3" color="primary">Primary Heading</Typography>
<Typography color="textSecondary">Secondary text</Typography>

// Truncated with ellipsis (single line)
<Typography ellipsis>This text will be truncated if it overflows...</Typography>

// Multi-line clamp
<Typography ellipsis={3}>This text will be clamped to 3 lines...</Typography>

// Copyable
<Typography copyable>Click the icon to copy this text</Typography>

// Alignment and gutter
<Typography align="center" gutterBottom>Centered with bottom margin</Typography>

Variants: h1, h2, h3, h4, h5, h6, subtitle1, subtitle2, body1, body2, caption, overline

Colors: Any palette key (primary, secondary, error, warning, success, info) or textPrimary, textSecondary, textDisabled

Theming

The component library includes a CSS-variable-based theming system with runtime theme switching.

Available Themes

Two default themes are included in the main entry point:

Theme Export Description
Default Light defaultLightTheme Light theme with system fonts
Default Dark defaultDarkTheme Dark theme with system fonts

17 additional pop-culture-inspired themes are available as deep imports for tree-shaking:

Theme Import Path Inspiration
Architect @furystack/shades-common-components/themes/architect The Matrix
Auditore @furystack/shades-common-components/themes/auditore Assassin's Creed
Black Mesa @furystack/shades-common-components/themes/black-mesa Half-Life
Chieftain @furystack/shades-common-components/themes/chieftain Warcraft 1 Orc faction
Dragonborn @furystack/shades-common-components/themes/dragonborn Skyrim
Hawkins @furystack/shades-common-components/themes/hawkins Stranger Things
Jedi @furystack/shades-common-components/themes/jedi Star Wars (Jedi Order)
Neon Runner @furystack/shades-common-components/themes/neon-runner Cyberpunk
Paladin @furystack/shades-common-components/themes/paladin Warcraft 1 Human faction
Plumber @furystack/shades-common-components/themes/plumber Super Mario
Replicant @furystack/shades-common-components/themes/replicant Blade Runner
Sandworm @furystack/shades-common-components/themes/sandworm Dune
Shadow Broker @furystack/shades-common-components/themes/shadow-broker Mass Effect
Sith @furystack/shades-common-components/themes/sith Star Wars (Sith Order)
Vault Dweller @furystack/shades-common-components/themes/vault-dweller Fallout
Wild Hunt @furystack/shades-common-components/themes/wild-hunt The Witcher 3
Xenomorph @furystack/shades-common-components/themes/xenomorph Alien
Applying a Theme

Use useThemeCssVariables to set CSS variables on :root:

import { useThemeCssVariables, defaultLightTheme } from '@furystack/shades-common-components'

// Apply on startup
useThemeCssVariables(defaultLightTheme)

For reactive theme switching through the injector, resolve the ThemeProviderService token:

import { ThemeProviderService, defaultDarkTheme } from '@furystack/shades-common-components'

const themeProvider = injector.get(ThemeProviderService)
themeProvider.setAssignedTheme(defaultDarkTheme)

// Listen for changes
themeProvider.subscribe('themeChanged', (theme) => {
  console.log('Theme changed:', theme.name)
})

Deep-imported themes can be loaded lazily:

const { architectTheme } = await import('@furystack/shades-common-components/themes/architect')
themeProvider.setAssignedTheme(architectTheme)
Theme Structure

A Theme object contains design tokens for the entire UI:

  • palette — Semantic colors (primary, secondary, error, warning, success, info) each with light/main/dark variants and contrast colors
  • text — Text colors at primary, secondary, and disabled emphasis levels
  • background — Surface colors (default, paper) and an optional paperImage
  • button — Button state colors (active, hover, selected, disabled)
  • typography — Font family, size scale, weight scale, line heights, and letter spacing
  • spacing — Spacing scale (xs through xl)
  • shape — Border radius scale and border width
  • shadows — Elevation presets (none, sm, md, lg, xl)
  • transitions — Duration and easing presets
  • action — Interactive state colors (hover, selected, focus ring, backdrop)
  • zIndex — Stacking layers (drawer, appBar, modal, tooltip, dropdown)
  • effects — Blur values for glassy surfaces
CSS Variable System

All components reference tokens through cssVariableTheme, which resolves to CSS custom properties (e.g. var(--shades-theme-text-primary)). When you call useThemeCssVariables(theme), the actual theme values are written to :root, and all components update automatically.

import { cssVariableTheme, buildTransition } from '@furystack/shades-common-components'

// Use tokens in component styles
const style = {
  color: cssVariableTheme.text.primary,
  background: cssVariableTheme.background.paper,
  borderRadius: cssVariableTheme.shape.borderRadius.md,
  transition: buildTransition([
    'background',
    cssVariableTheme.transitions.duration.normal,
    cssVariableTheme.transitions.easing.default,
  ]),
}
Creating a Custom Theme

Define a palette and a theme object satisfying the Theme interface:

import type { Palette, Theme } from '@furystack/shades-common-components'

const myPalette: Palette = {
  primary: {
    light: '#6ec6ff',
    lightContrast: '#000',
    main: '#2196f3',
    mainContrast: '#fff',
    dark: '#0069c0',
    darkContrast: '#fff',
  },
  secondary: {
    light: '#ff79b0',
    lightContrast: '#000',
    main: '#ff4081',
    mainContrast: '#fff',
    dark: '#c60055',
    darkContrast: '#fff',
  },
  error: {
    light: '#ff6659',
    lightContrast: '#000',
    main: '#f44336',
    mainContrast: '#fff',
    dark: '#ba000d',
    darkContrast: '#fff',
  },
  warning: {
    light: '#ffb74d',
    lightContrast: '#000',
    main: '#ff9800',
    mainContrast: '#000',
    dark: '#f57c00',
    darkContrast: '#fff',
  },
  success: {
    light: '#81c784',
    lightContrast: '#000',
    main: '#4caf50',
    mainContrast: '#fff',
    dark: '#388e3c',
    darkContrast: '#fff',
  },
  info: {
    light: '#64b5f6',
    lightContrast: '#000',
    main: '#2196f3',
    mainContrast: '#fff',
    dark: '#1976d2',
    darkContrast: '#fff',
  },
}

const myTheme: Theme = {
  name: 'my-theme',
  palette: myPalette,
  text: { primary: '#fff', secondary: 'rgba(255,255,255,0.7)', disabled: 'rgba(255,255,255,0.5)' },
  // ... remaining tokens (see defaultLightTheme or defaultDarkTheme for a full reference)
}

Services

CollectionService

A service for managing collections of data with pagination, sorting, and filtering.

import { CollectionService } from '@furystack/shades-common-components'

const service = new CollectionService<MyModel>({
  loader: async (options) => {
    const response = await fetch('/api/items', {
      /* ... */
    })
    return response.json()
  },
})

// Subscribe to data changes
service.data.subscribe((items) => {
  console.log('Items updated:', items)
})
ThemeProviderService

A singleton service for managing the active theme. It updates CSS variables and emits a themeChanged event so components can react to theme switches.

import { ThemeProviderService, defaultDarkTheme } from '@furystack/shades-common-components'

const themeProvider = injector.get(ThemeProviderService)

// Access the CSS-variable-based theme reference (for use in styles)
const primaryColor = themeProvider.theme.palette.primary.main

// Switch theme at runtime
themeProvider.setAssignedTheme(defaultDarkTheme)

// Read the currently assigned theme
const current = themeProvider.getAssignedTheme()

// Listen for theme changes
themeProvider.subscribe('themeChanged', (theme) => {
  console.log('Switched to', theme.name)
})

Keywords