@grimoire-intel/yetzirah-angular
v1.0.0
Published
Angular wrappers for Yetzirah Web Components
Maintainers
Readme
@grimoire-intel/yetzirah-angular
Angular wrappers for Yetzirah Web Components - bringing Material Design behavior patterns to Angular 16+.
Installation
npm install @grimoire-intel/yetzirah-angular @grimoire-intel/yetzirah
# or
pnpm add @grimoire-intel/yetzirah-angular @grimoire-intel/yetzirah
# or
yarn add @grimoire-intel/yetzirah-angular @grimoire-intel/yetzirahUsage
Option 1: Standalone Components (Angular 16+, Recommended)
For apps using Angular's standalone component API:
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@grimoire-intel/yetzirah'; // Import Web Components
@Component({
selector: 'app-root',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<ytz-button>Click me</ytz-button>
<ytz-dialog>
<h2>Dialog Title</h2>
<p>Dialog content goes here</p>
</ytz-dialog>
`
})
export class AppComponent {}Alternatively, use the provided helper:
import { Component } from '@angular/core';
import { provideYetzirah } from '@grimoire-intel/yetzirah-angular';
import '@grimoire-intel/yetzirah';
@Component({
selector: 'app-root',
standalone: true,
schemas: [provideYetzirah()],
template: `<ytz-button>Click me</ytz-button>`
})
export class AppComponent {}Option 2: Traditional NgModule
For apps still using NgModule:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { YetzirahModule } from '@grimoire-intel/yetzirah-angular';
import '@grimoire-intel/yetzirah'; // Import Web Components
@NgModule({
imports: [
BrowserModule,
YetzirahModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}Loading Web Components
You must ensure @grimoire-intel/yetzirah is loaded before using the components. There are several ways to do this:
Method 1: Import in main.ts (Recommended)
// main.ts
import '@grimoire-intel/yetzirah';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent);Method 2: CDN in index.html
<!-- index.html -->
<script type="module" src="https://unpkg.com/@grimoire-intel/yetzirah/dist/yetzirah.js"></script>Method 3: Selective imports
// Import only the components you need for smaller bundle size
import '@grimoire-intel/yetzirah/button';
import '@grimoire-intel/yetzirah/dialog';
import '@grimoire-intel/yetzirah/tabs';Available Components
All Yetzirah Web Components are available in Angular templates with dedicated standalone components.
Tier 1 Standalone Components
| Component | Selector | Import |
|-----------|----------|--------|
| Button | ytz-button | ButtonComponent |
| Dialog | ytz-dialog | DialogComponent |
| Drawer | ytz-drawer | DrawerComponent |
| Tabs | ytz-tabs, ytz-tab-list, ytz-tab, ytz-tab-panel | TabsComponent, TabListComponent, TabComponent, TabPanelComponent |
| Menu | ytz-menu, ytz-menu-item, ytz-menu-trigger | MenuComponent, MenuItemComponent, MenuTriggerComponent |
| Accordion | ytz-accordion, ytz-accordion-item | AccordionComponent, AccordionItemComponent |
| Disclosure | ytz-disclosure | DisclosureComponent |
| Tooltip | ytz-tooltip | TooltipComponent |
| Popover | ytz-popover | PopoverComponent |
| Autocomplete | ytz-autocomplete, ytz-autocomplete-option | AutocompleteComponent, AutocompleteOptionComponent |
| Listbox | ytz-listbox, ytz-listbox-option | ListboxComponent, ListboxOptionComponent |
| Select | ytz-select, ytz-select-option | SelectComponent, SelectOptionComponent |
Tier 2 Standalone Components
| Component | Selector | Forms Support |
|-----------|----------|---------------|
| Toggle | ytz-toggle | ControlValueAccessor, [(ngModel)], formControlName |
| Chip | ytz-chip | - |
| IconButton | ytz-icon-button | - |
| Slider | ytz-slider | ControlValueAccessor, [(ngModel)], formControlName |
| DataGrid | ytz-datagrid, ytz-datagrid-column | - |
| ThemeToggle | ytz-theme-toggle | - |
Tier 1 Component API
Dialog
import { Component } from '@angular/core';
import { DialogComponent, ButtonComponent } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [DialogComponent, ButtonComponent],
template: `
<ytz-button (click)="isOpen = true">Open Dialog</ytz-button>
<ytz-dialog [open]="isOpen" (close)="isOpen = false">
<div class="pa4 bg-white br3">
<h2>Dialog Title</h2>
<p>Dialog content goes here.</p>
<ytz-button (click)="isOpen = false">Close</ytz-button>
</div>
</ytz-dialog>
`
})
export class MyComponent {
isOpen = false;
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| open | boolean | false | Controls dialog visibility |
| static | boolean | false | Prevent backdrop dismiss |
| Output | Type | Description |
|--------|------|-------------|
| close | EventEmitter<void> | Emitted when dialog closes |
Select
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SelectComponent, SelectOptionComponent } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [FormsModule, SelectComponent, SelectOptionComponent],
template: `
<ytz-select [(ngModel)]="selected" placeholder="Choose an option...">
<ytz-select-option value="1">Option 1</ytz-select-option>
<ytz-select-option value="2">Option 2</ytz-select-option>
<ytz-select-option value="3">Option 3</ytz-select-option>
</ytz-select>
`
})
export class MyComponent {
selected = '';
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| ngModel | string \| string[] | '' | Two-way bound value |
| multiple | boolean | false | Enable multi-select |
| disabled | boolean | false | Disable the select |
| placeholder | string | - | Placeholder text |
Tabs
import { Component } from '@angular/core';
import {
TabsComponent,
TabListComponent,
TabComponent,
TabPanelComponent
} from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [TabsComponent, TabListComponent, TabComponent, TabPanelComponent],
template: `
<ytz-tabs>
<ytz-tab-list>
<ytz-tab>Tab 1</ytz-tab>
<ytz-tab>Tab 2</ytz-tab>
<ytz-tab>Tab 3</ytz-tab>
</ytz-tab-list>
<ytz-tab-panel>Content for Tab 1</ytz-tab-panel>
<ytz-tab-panel>Content for Tab 2</ytz-tab-panel>
<ytz-tab-panel>Content for Tab 3</ytz-tab-panel>
</ytz-tabs>
`
})
export class MyComponent {}Menu
import { Component } from '@angular/core';
import {
MenuComponent,
MenuTriggerComponent,
MenuItemComponent,
ButtonComponent
} from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [MenuComponent, MenuTriggerComponent, MenuItemComponent, ButtonComponent],
template: `
<ytz-menu>
<ytz-menu-trigger>
<ytz-button>Open Menu</ytz-button>
</ytz-menu-trigger>
<ytz-menu-item (click)="handleEdit()">Edit</ytz-menu-item>
<ytz-menu-item (click)="handleDelete()">Delete</ytz-menu-item>
</ytz-menu>
`
})
export class MyComponent {
handleEdit() { console.log('Edit'); }
handleDelete() { console.log('Delete'); }
}Autocomplete
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AutocompleteComponent, AutocompleteOptionComponent } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [FormsModule, AutocompleteComponent, AutocompleteOptionComponent],
template: `
<ytz-autocomplete [(ngModel)]="value" placeholder="Search fruits...">
@for (option of options; track option) {
<ytz-autocomplete-option [value]="option">{{ option }}</ytz-autocomplete-option>
}
</ytz-autocomplete>
`
})
export class MyComponent {
value = '';
options = ['Apple', 'Banana', 'Cherry', 'Date'];
}Tier 2 Component API
Toggle
Implements ControlValueAccessor for Angular forms integration.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Toggle } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [FormsModule, Toggle],
template: `
<ytz-toggle [(ngModel)]="enabled" [disabled]="false" (change)="onChange($event)"></ytz-toggle>
`
})
export class MyComponent {
enabled = false;
onChange(event: Event) { console.log(event); }
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| ngModel | boolean | false | Two-way bound checked state |
| disabled | boolean | false | Disables the toggle |
| Output | Type | Description |
|--------|------|-------------|
| ngModelChange | boolean | Emits when checked state changes |
| change | Event | Native change event |
Chip
import { Component } from '@angular/core';
import { Chip } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [Chip],
template: `
<ytz-chip [deletable]="true" (delete)="onDelete()">Tag Name</ytz-chip>
`
})
export class MyComponent {
onDelete() { console.log('deleted'); }
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| deletable | boolean | false | Shows delete button |
| disabled | boolean | false | Disables the chip |
| Output | Type | Description |
|--------|------|-------------|
| delete | EventEmitter<void> | Emits when delete button clicked |
IconButton
import { Component } from '@angular/core';
import { IconButton } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [IconButton],
template: `
<ytz-icon-button ariaLabel="Close" tooltip="Close dialog" (click)="onClick()">
<svg><!-- icon --></svg>
</ytz-icon-button>
`
})
export class MyComponent {
onClick() { console.log('clicked'); }
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| ariaLabel | string | required | Accessible label (maps to aria-label) |
| tooltip | string | - | Tooltip text |
| disabled | boolean | false | Disables the button |
Slider
Implements ControlValueAccessor for Angular forms integration.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Slider } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [FormsModule, Slider],
template: `
<ytz-slider [(ngModel)]="volume" [min]="0" [max]="100" [step]="1"></ytz-slider>
`
})
export class MyComponent {
volume = 50;
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| ngModel | number | 0 | Two-way bound value |
| min | number | 0 | Minimum value |
| max | number | 100 | Maximum value |
| step | number | 1 | Step increment |
| disabled | boolean | false | Disables the slider |
| Output | Type | Description |
|--------|------|-------------|
| ngModelChange | number | Emits on value change |
| input | Event | Live value change during drag |
| change | Event | Committed value change on release |
DataGrid
import { Component } from '@angular/core';
import { DataGrid, DataGridColumn } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [DataGrid, DataGridColumn],
template: `
<ytz-datagrid [data]="data" [rowHeight]="40" (sort)="onSort($event)" (rowSelect)="onSelect($event)">
<ytz-datagrid-column field="id" header="ID" [width]="80"></ytz-datagrid-column>
<ytz-datagrid-column field="name" header="Name" [sortable]="true"></ytz-datagrid-column>
<ytz-datagrid-column field="email" header="Email"></ytz-datagrid-column>
</ytz-datagrid>
`
})
export class MyComponent {
data = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob', email: '[email protected]' }
];
onSort(event: any) { console.log(event); }
onSelect(event: any) { console.log(event); }
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| data | any[] | [] | Row data array |
| columns | Column[] | [] | Column definitions (alternative to children) |
| rowHeight | number | 40 | Row height in pixels |
| Output | Type | Description |
|--------|------|-------------|
| sort | { column, direction } | Sort requested |
| rowSelect | { row, index } | Row selected |
| rowActivate | { row, index } | Row double-clicked |
ThemeToggle
import { Component } from '@angular/core';
import { ThemeToggle } from '@grimoire-intel/yetzirah-angular';
@Component({
standalone: true,
imports: [ThemeToggle],
template: `
<ytz-theme-toggle [storageKey]="'my-app-theme'" (themeChange)="onThemeChange($event)"></ytz-theme-toggle>
`
})
export class MyComponent {
onThemeChange(event: CustomEvent) { console.log(event.detail); }
}| Input | Type | Default | Description |
|-------|------|---------|-------------|
| storageKey | string | 'theme' | localStorage key |
| noPersist | boolean | false | Disable persistence |
| Output | Type | Description |
|--------|------|-------------|
| themeChange | { theme, isDark } | Theme changed |
TypeScript Support
This package includes TypeScript definitions for all components. However, since these are Web Components, you may need to add custom element type declarations for strict type checking:
// src/custom-elements.d.ts
declare namespace JSX {
interface IntrinsicElements {
'ytz-button': any;
'ytz-dialog': any;
// ... add other components as needed
}
}Two-Way Binding
Angular's two-way binding works with Web Component properties and events:
@Component({
template: `
<ytz-toggle
[checked]="isEnabled"
(change)="onToggleChange($event)">
</ytz-toggle>
`
})
export class MyComponent {
isEnabled = false;
onToggleChange(event: Event) {
this.isEnabled = (event.target as any).checked;
}
}Styling
Yetzirah components are unstyled by default. Import the optional CSS if desired:
// main.ts or styles.css
import '@grimoire-intel/yetzirah/button.css';
import '@grimoire-intel/yetzirah/dialog.css';
import '@grimoire-intel/yetzirah/disclosure.css';Or use the dark theme:
import '@grimoire-intel/yetzirah/dark.css';Testing
# Run all tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run with coverage
pnpm test:coverageTests use Jest with jest-preset-angular for Angular-specific testing utilities.
Requirements
- Angular 16 or higher
- Modern browsers with Web Components support (all evergreen browsers)
How It Works
These wrappers integrate Yetzirah web components with Angular's patterns:
ControlValueAccessor: Form controls work withngModeland reactive formsCUSTOM_ELEMENTS_SCHEMA: Allowsytz-*elements in templates- Event binding: Component events emit as Angular
EventEmitters
See @grimoire-intel/yetzirah for full component documentation.
License
MIT
Repository
https://github.com/grimoire-intelligence/yetzirah
