@presencelearning/ui
Angular component library for Presence Learning. Provides reusable UI components built on Angular Material and @presencelearning/styles.
Current version: 1.2.2. Ships as a root entry point plus per-group secondary entry points (@presencelearning/ui/topbar, /theme, /rich-text-editor, etc.) — most components are still importable from the bare @presencelearning/ui root, but command-palette and rich-text-editor are secondary-entry-only. See the “Adding a new secondary entry point” section below for how entries are wired up.
Install
Section titled “Install”npm install @presencelearning/uiPeer dependencies
Section titled “Peer dependencies”Declared in package.json:
@angular/core>=18.0.0 <22.0.0@angular/common>=18.0.0 <22.0.0@angular/cdk>=18.0.0 <22.0.0@angular/material>=18.0.0 <22.0.0@presencelearning/styles^1.0.0
Also imported by the library but not currently declared as peers — your app needs them installed: @angular/forms (rich text editor), @angular/router (NavigationLinkComponent), @angular/animations (NavigationComponent), and rxjs.
The rich text editor bundles its own runtime dependencies (17 @tiptap/* packages, dompurify, unicode-emoji-json), so they install transitively rather than as peers.
Components
Section titled “Components”Note that the exported class names have no Pl prefix — the pl- prefix appears only on the selectors.
| Component | Selector | Description |
|---|---|---|
NavigationComponent | pl-navigation | Main navigation with expandable/collapsible sidebar |
NavigationLinkComponent | pl-navigation-link | Navigation link with icon and label |
HelpMenuComponent | pl-help-menu | Help menu dropdown |
BannerComponent | pl-banner | Inline info/success banner with optional link and action buttons |
RichTextEditorComponent | pl-rich-text-editor | Tiptap-based rich text editor with mentions, emoji, tables, and images |
PlaceholderComponent | pl-placeholder | Dashed-border box for verifying that the library is wired up correctly |
NavigationTriggerDirective ([plNavigationTrigger], exportAs: 'plNavigationTrigger') is also exported, for toggling the sidebar from outside it.
NavigationComponent
Section titled “NavigationComponent”| Input | Type | Default | Description |
|---|---|---|---|
pages | NavLink[] | [] | Primary navigation links |
resources | NavLink[] | [] | Secondary/resource links |
hiddenRoutes | string[] | [] | Routes on which the nav hides itself |
currentUrl | string | '' | Drives active-link highlighting |
Outputs: linkClicked: NavLink, navigationToggled: boolean. Public methods: toggle(skipSave = false), open(), close(), toggleHelpMenu(). Project help menu content via a #helpMenuContent template.
NavLink resolution priority is routerLink → href → plain button. Collapsed state persists through the PL_NAVIGATION_STORAGE injection token, which defaults to defaultNavigationStorage (localStorage under the key pl-navigation). Override the token to change or disable persistence.
BannerComponent
Section titled “BannerComponent”| Input | Type | Default | Description |
|---|---|---|---|
type | BannerType ('info' | 'success') | 'info' | Visual treatment |
title | string | — | Banner heading |
body | string | — | Banner body text |
bodyLinkLabel | string | — | Label for an inline link in the body |
bodyLinkHref | string | — | Href for the inline body link |
primaryButtonLabel | string | — | Renders the primary action when set |
secondaryButtonLabel | string | — | Renders the secondary action when set |
showCloseButton | boolean | false | Renders a close affordance |
Outputs: primaryClicked, secondaryClicked, bodyLinkClicked, closeClicked (all void).
RichTextEditorComponent
Section titled “RichTextEditorComponent”Implements ControlValueAccessor, so it works with formControl/ngModel as well as with the value/valueChange pair.
| Input | Type | Default | Description |
|---|---|---|---|
value | string | '' | Editor HTML |
readonly | boolean | false | Renders read-only |
placeholder | string | '' | Empty-state placeholder |
mentionsFetch | MentionsFetch | null | null | Supplies @-mention candidates |
Output: valueChange: string. Public method: print(): void.
Register the editor engine once at app level:
import { ApplicationConfig } from '@angular/core';import { provideRichTextEditor } from '@presencelearning/ui/rich-text-editor';
export const appConfig: ApplicationConfig = { providers: [provideRichTextEditor()],};Exported types and tokens
Section titled “Exported types and tokens”| Export | Kind | Description |
|---|---|---|
BannerType | type | 'info' | 'success' |
NavLink | type | Navigation entry — label, optional icon, href, routerLink, external, active, children, enabled |
HelpMenuLink | type | Help menu entry — title, subtitle, optional icon, href, external, local |
HelpMenuEmail | type | Help menu email entry — address, label |
PL_NAVIGATION_STORAGE | InjectionToken<NavigationStorage> | Persistence for the sidebar collapsed state |
defaultNavigationStorage | const | localStorage adapter used by default |
NavigationStorage | interface | Contract for a custom storage adapter |
provideRichTextEditor | function | () => EnvironmentProviders — registers the Tiptap engine |
ActiveState | interface | Current editor formatting state |
MentionItem | interface | A single mention candidate |
MentionsFetch | type | Mention lookup callback |
HeadingLevel | type | Heading levels the editor supports |
Storybook
Section titled “Storybook”Stories exist for BannerComponent, NavigationComponent, PlaceholderComponent, and RichTextEditorComponent. Run locally:
npm run storybook --workspace=@presencelearning/uiAdding a new secondary entry point
Section titled “Adding a new secondary entry point”The hand-written exports map in packages/ui/package.json doesn’t get generated — it has to be kept in sync by hand whenever a new secondary entry point is added. To add one:
- Create
packages/ui/<name>/ng-package.jsonpointing at a barrel insrc/lib/<name>/index.ts. - Add an entry for
./<name>to theexportsmap inpackages/ui/package.json, prefixed withdist/(matching the pattern of the existing entries). - If the group should also remain public from the root, add
export * from './lib/<name>';topackages/ui/src/index.ts.
Skipping any of these steps leaves the hand-written exports map silently out of sync with the actual build output.
Source: packages/ui