@presencelearning/ui
Angular component library for Presence Learning. Provides reusable UI components built on Angular Material and @presencelearning/styles.
Current version: 1.2.2. Single flat entry point — everything is imported from @presencelearning/ui.
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';
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/uiSource: packages/ui