npm package: @polarityio/pi-shared
Description: Shared utilities, design tokens, base classes, and styles for the Polarity Integration Component Library
Latest version: 1.0.2

# Polarity Integration Component Library

## Shared Utilities

Foundation package providing design tokens, shared styles, base classes, and utilities for the Polarity Integration Component Library.

## Installation

```bash
npm install @polarityio/pi-shared
```

### Peer Dependencies

- [lit](https://lit.dev/) ^3.0.0

## Overview

`@polarityio/pi-shared` is the shared foundation used by all Polarity Integration components. It provides:

- **Design Tokens** — CSS custom properties for colors, spacing, typography, shadows, and more with dark/light theme support
- **Shared Styles** — Reusable Lit CSS for focus rings and typography
- **Base Classes** — `IntegrationComponentBase`, a LitElement subclass with built-in property bindings and service access
- **Utilities** — Helpers for defining, creating, and rendering custom elements
- **TypeScript Interfaces** — Contracts for blocks, configs, services, and component properties

### Package Exports

| Export Path | Description |
|---|---|
| `@polarityio/pi-shared` | Main entry — utilities, base classes, interfaces, tokens, and styles |
| `@polarityio/pi-shared/tokens` | Isolated design token exports (`paletteCSS`, `baseTokensCSS`, `darkThemeCSS`, `lightThemeCSS`, `injectPiTokens`) |
| `@polarityio/pi-shared/pi-tokens.css` | Pre-built CSS file containing all token definitions — use as a plain stylesheet |

## Design Tokens

The token system uses a three-layer architecture:

1. **Palette** — Raw color values (grays, blues, teals, greens, yellows, oranges, reds, purples). Theme-independent.
2. **Theme** — Dark and light theme layers that map palette colors to semantic theme variables.
3. **Base** — Semantic tokens that components consume. These reference theme tokens via `var()` and remain stable across themes.

Dark theme is active by default (applied at `:root`). Switch to light theme by adding `data-pi-theme="light"` to any ancestor element.

### Injecting Tokens

The simplest way to load all design tokens into the page:

```typescript
import { injectPiTokens } from '@polarityio/pi-shared';

// Injects palette, dark/light themes, and base tokens into <head>.
// Safe to call multiple times — will not duplicate the <style> element.
injectPiTokens();
```

Or import the pre-built CSS file directly:

```css
@import '@polarityio/pi-shared/pi-tokens.css';
```

### Using Tokens in CSS

Components should reference the semantic base tokens:

```css
.my-element {
  color: var(--pi-color-font-primary);
  background: var(--pi-color-background-application-0);
  padding: var(--pi-size-spacing-sm);
  border-radius: var(--pi-size-radius-base);
  font-family: var(--pi-font-family-base);
  font-size: var(--pi-size-font-base);
  transition: background var(--pi-transition-fast);
}
```

### Token Reference

#### Colors

| Category | Tokens |
|---|---|
| Background — Application | `--pi-color-background-application-0` through `application-4` |
| Background — Container | `-base`, `-danger`, `-disabled`, `-info`, `-neutral`, `-success`, `-warning` |
| Background — Semantic | `-primary`, `-secondary`, `-table-header`, `-transparent` |
| Border | `-container`, `-danger`, `-element`, `-info`, `-success`, `-warning` |
| Font | `-danger`, `-disabled`, `-info`, `-inverse`, `-primary`, `-secondary`, `-success`, `-urgent`, `-warning` |
| Interactive | `-hover`, `-active`, `-primary-hover` |
| Gradient | `-regenai-start`, `-regenai-end` |

#### Typography

| Token | Value |
|---|---|
| `--pi-size-font-xs` | 0.75rem |
| `--pi-size-font-sm` | 0.875rem |
| `--pi-size-font-base` | 0.8125rem |
| `--pi-size-font-md` | 1rem |
| `--pi-size-font-lg` | 1.25rem |
| `--pi-size-font-xl` | 1.5rem |
| `--pi-size-font-xxl` | 2rem |
| `--pi-font-family-base` | `'NotoSans', sans-serif` |
| `--pi-font-family-mono` | `'Courier New', monospace` |
| `--pi-font-weight-regular` | 400 |
| `--pi-font-weight-medium` | 500 |
| `--pi-font-weight-semibold` | 600 |
| `--pi-font-weight-bold` | 700 |

> **Font Size Scale:** Set `--pi-font-size-scale` to compensate for non-16px root font sizes.
> For example, if `html { font-size: 14px }`, set `--pi-font-size-scale: 1.143` (16 ÷ 14).

#### Spacing

| Token | Value |
|---|---|
| `--pi-size-spacing-xxs` | 0.125rem |
| `--pi-size-spacing-xs` | 0.25rem |
| `--pi-size-spacing-sm` | 0.5rem |
| `--pi-size-spacing-md` | 0.75rem |
| `--pi-size-spacing-lg` | 1rem |
| `--pi-size-spacing-xl` | 1.5rem |
| `--pi-size-spacing-xxl` | 2rem |

#### Border Radius

| Token | Value |
|---|---|
| `--pi-size-radius-sm` | 2px |
| `--pi-size-radius-base` | 4px |
| `--pi-size-radius-lg` | 8px |
| `--pi-size-radius-full` | 9999px |

#### Transitions

| Token | Value |
|---|---|
| `--pi-transition-fast` | 150ms ease-in-out |
| `--pi-transition-base` | 200ms ease-in-out |

#### Shadows

Composed shadow tokens at various elevation depths:

`--pi-shadow-sm`, `--pi-shadow-2`, `--pi-shadow-4`, `--pi-shadow-8`, `--pi-shadow-16`, `--pi-shadow-24`

#### Focus

| Token | Value |
|---|---|
| `--pi-color-border-focus` | `var(--pi-color-background-primary)` |
| `--pi-border-width-focus` | 2px |
| `--pi-outline-offset-focus` | -1px |

## Exports

### Utilities

#### `injectPiTokens()`

Injects all design tokens (palette, dark/light themes, and base semantic tokens) into the document `<head>`. Safe to call multiple times.

#### `libraryCustomElementName(name, version)`

Generates a versioned custom element name in the format `pi-{name}-v{version}` (e.g., `pi-my-component-v1-0-0`). Used internally by component packages to produce unique, collision-free element names.

```typescript
import { libraryCustomElementName } from '@polarityio/pi-shared';

const tagName = libraryCustomElementName('my-component', '1.0.0');
// → 'pi-my-component-v1-0-0'
```

#### `defineCustomElementOnce(name, elementClass)`

Registers a custom element only if it hasn't already been defined. Prevents duplicate registration errors.

```typescript
import { defineCustomElementOnce } from '@polarityio/pi-shared';

defineCustomElementOnce('pi-my-component-v1-0-0', MyComponent);
```

#### `appendCustomElement(parentElement, customElementName, properties)`

Creates a custom element via `document.createElement`, sets the given properties, and appends it to the parent.

```typescript
import { appendCustomElement } from '@polarityio/pi-shared';

appendCustomElement(container, 'pi-my-component-v1-0-0', [
  { name: 'block', value: blockData },
  { name: 'services', value: servicesProxy }
]);
```

#### `renderCustomElement(parentElement, customElementName, properties)`

Renders a custom element into a parent using Lit's `render()` and the `SpreadPropsDirective` for reactive property binding.

```typescript
import { renderCustomElement } from '@polarityio/pi-shared';

renderCustomElement(container, 'pi-my-component-v1-0-0', [
  { name: 'block', value: blockData },
  { name: 'services', value: servicesProxy }
]);
```

#### `getComponentElementName(config, type)`

Looks up a component's custom element name from an `IConfig` object by type (`'summary'` or `'details'`).

#### `SpreadPropsDirective`

A Lit directive that spreads an array of `ICustomElementProperty` objects onto an element as properties.

### Base Classes

#### `IntegrationComponentBase`

A LitElement subclass that all integration components should extend. Provides:

| Property | Type | Description |
|---|---|---|
| `isCollapsed` | `boolean` | Whether the component is in a collapsed state |
| `block` | `IBlock` | The integration block data (summary tags, details, acronym) |
| `services` | `IServicesProxy` | Proxy to all Polarity platform services |

| Member | Description |
|---|---|
| `integrationId` | Getter — returns `block.integrationId` |
| `sendIntegrationMessage(message)` | Sends a message to the integration's `onMessage` server hook |

The `connectedCallback` validates that `block` and `services` are set, and that `block.integrationId` is present.

```typescript
import { IntegrationComponentBase } from '@polarityio/pi-shared';
import { html, css } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('my-summary')
export class MySummary extends IntegrationComponentBase {
  static styles = css`
    :host { display: block; }
  `;

  render() {
    return html`<span>${this.block.acronym}</span>`;
  }
}
```

### Interfaces

#### Public Interfaces

| Interface | Description |
|---|---|
| `IBlock` | Integration block data — `integrationId`, `acronym`, and `data` (details + summary) |
| `IConfig` | Component configuration mapping types (`'summary'` \| `'details'`) to element names |
| `ICustomElementProperty` | Name/value pair for setting properties on custom elements |
| `IIntegrationBlockIntegration` | Shape of integration data containing `details` (object) and `summary` (string array) |

#### Component Interfaces

| Interface | Description |
|---|---|
| `IIntegrationComponentProperties` | Base component props — `isCollapsed`, `block`, `services` |
| `IDefaultSummaryComponentProperties` | Extends base props with optional `color` (`IPiTagColors`) |
| `IPiTagColors` | Tag color set — `background`, `text`, `textBold` |
| `IServicesProxy` | Read-only proxy to all Polarity platform services |

#### Service Interfaces

`IServicesProxy` exposes the following services:

| Service | Interface | Description |
|---|---|---|
| `currentServer` | `ICurrentServerService` | Server state and edition info |
| `currentUser` | `ICurrentUserService` | User loading and state |
| `flashMessages` | `IFlashMessagesService` | Success, info, warning, and danger notifications |
| `moment` | `IMomentService` | Date/time locale and timezone utilities |
| `notificationsData` | `INotificationsDataService` | Notification and block data management |
| `polarityx` | `IPolarityxService` | Core Polarity platform control (HUD, windows, lookups) |
| `searchData` | `ISearchDataService` | Search state and operations |
| `session` | `ISessionService` | Authentication and session management |
| `store` | `IStoreService` | Data store operations |
| `windowService` | `IWindowService` | Application window state and control |
| `log` | `ILogService` | Logging (trace, debug, info, warn, error, fatal) |
| `integrationMessenger` | `IIntegrationMessengerService` | Messaging with the integration server |

### Styles

#### `focusStyles`

A Lit `css` tagged template providing consistent focus ring styles. Import and spread into your component's `static styles`, then add the `pi-focus` class to focusable elements.

```typescript
import { focusStyles } from '@polarityio/pi-shared';
import { css } from 'lit';

class MyComponent extends LitElement {
  static styles = [
    focusStyles,
    css`/* component styles */`
  ];
}
```

The focus ring uses `outline` with a negative offset so there is no layout shift.

#### `typographyCSS`

A plain CSS string that styles native HTML elements (`h1`–`h6`, `p`, `small`, `code`, `pre`, `a`, `hr`) using Polarity design tokens. Intended for light DOM content.

#### `injectPiTypography(doc?)`

Injects the typography stylesheet into the document `<head>`. Safe to call multiple times.

```typescript
import { injectPiTypography } from '@polarityio/pi-shared';

injectPiTypography();
```

### Test Mocks

The package includes comprehensive service mocks under `src/base/mocks/` for use in tests:

- `ServicesProxyMock` — Complete mock of `IServicesProxy` composing all individual service mocks
- Individual mocks for each service (`CurrentServerServiceMock`, `FlashMessagesServiceMock`, `LogServiceMock`, etc.)

## Usage

### Building a Component Package

A typical integration component imports from `@polarityio/pi-shared` for registration, base class, and tokens:

```typescript
import {
  IntegrationComponentBase,
  libraryCustomElementName,
  defineCustomElementOnce,
  injectPiTokens,
  focusStyles
} from '@polarityio/pi-shared';
import { html, css } from 'lit';
import { version, name } from '../package.json';

// Inject design tokens into the page
injectPiTokens();

// Generate a versioned element name
const TAG = libraryCustomElementName(name, version);

export class MyDetails extends IntegrationComponentBase {
  static styles = [
    focusStyles,
    css`
      :host {
        display: block;
        color: var(--pi-color-font-primary);
        background: var(--pi-color-background-application-0);
        padding: var(--pi-size-spacing-sm);
      }
    `
  ];

  render() {
    return html`<pre>${JSON.stringify(this.block.data.details, null, 2)}</pre>`;
  }

  async onRefresh() {
    const result = await this.sendIntegrationMessage({ action: 'refresh' });
    // handle result
  }
}

// Register the element (safe to call multiple times)
defineCustomElementOnce(TAG, MyDetails);
```

### Theme Switching

Dark theme is active by default. To switch to light theme, set the `data-pi-theme` attribute:

```html
<!-- Dark theme (default) -->
<div>...</div>

<!-- Light theme -->
<div data-pi-theme="light">...</div>
```

## License

MIT