@my-ul/ng-porcelain
v14.0.2
Published
## Quick Start
Downloads
385
Readme
Porcelain for Angular
Quick Start
Install Porcelain and its dependencies
It is assumed that you have already used the Angular CLI to install and instantiate an Angular application. Refer to the Angular documentation for help setting up an Angular project.
npm install --save @my-ul/[email protected] \
@fortawesome/angular-fontawesome@* \
@fortawesome/fontawesome-svg-core@* \
@fortawesome/free-solid-svg-icons@* \
@w11k/angular-sticky-things@* \
angular-mydatepicker@* \
lodash-es@* \
luxon@* \
sprintf-js@*
Import and use Porcelain modules
import {
ApplicatorModule,
DateRefinerModule,
FooterModule,
InterpolateModule,
RefinersModule,
SearchInputModule
SimpleRefinerModule,
SpinnerModule,
TruncateModule
} from '@my-ul/ng-porcelain';
// YourModule.ts
@Module({
imports: [
ApplicatorModule,
DateRefinerModule,
FooterModule,
InterpolateModule,
RefinersModule,
SearchInputModule,
SimpleRefinerModule,
SpinnerModule,
TruncateModule
]
})
export class AppModule {}
Components
Checkbox
The checkbox provides an accessible, labeled and styled checkbox functionality that can be used almost exactly like a standard HTML checkbox. The checkbox does not support indeterminate states.
Inputs
[(checked)]: boolean
Boolean representing whether or not the checkbox is in the checked state. Can be split into [checked]
and (checkedChange)
.
[(disabled)]: boolean
Boolean that can be used to disable the checkbox. When disabled, the checkbox will not respond to the user's attempts to change the value. The value can still be changed programatically. Can be split into [disabled]
and (disabledChange)
.
Combobox
Allow a user to search and select pre-populated values.
Usage
Array of strings as items
export class MyComponent {
fruits: ['Apple', 'Banana', 'Cherry', 'Durian'];
currentFruit: 'Apple';
}
<porcelain-combobox [items]="fruits" [(value)]="currentFruit"></porcelain-combobox>
Array of objects as items
Use an array of objects as items. Selecting a value returns the entire object, not just the label.
export class MyComponent {
fruits: [
{
color: 'red';
name: 'Apple';
},
{
color: 'yellow';
name: 'Banana';
},
{
color: 'darkred';
name: 'Cherry';
},
{
color: 'lightyellow';
name: 'Durian';
}
];
currentFruit: 'Apple';
}
<porcelain-combobox
[items]="fruits"
[isObjectArray]="true"
[labelProp]="name"
[(value)]="currentFruit"
></porcelain-combobox>
Expando
The Expando can be used to create content areas that can be opened or closed with a disclosure triangle.
The Expando's expand/collapse behavior supports animations. These actions will be animated if you import BrowserAnimationsModule
in the root of your app.
Supplementary Types
ExpandoIconPosition
= 'before' | 'after'
Used to change the expando's icon position relative to the expando title.
Basic Usage
title
Input: string
The title to be shown next to the icon.
Special note about [title] binding
You can use non-bracketed syntax to define the title. If you do this, you must use string interpolation to use variables.
The following bindings are equivalent:
<p-expando [title]="myVariable"></p-expando> <p-expando title="{{myVariable}}"></p-expando>
[iconPosition]
Input: ExpandoIconPosition
Accepts string values "before" or "after" as defined by ExpandoIconPosition
[icon]
Input: FaIconDefinition
Accepts any Font Awesome icon definition, from the @fortawesome/free-*-svg-icons
packages. By default, this is faCaretDown
.
[isOpen]
Input: boolean
Boolean used to control the current state of the expando. Set true to reveal included content, otherwise, false to hide.
<p-expando
title="My Expando Title"
[iconPosition]="ExpandoIconPosition"
[icon]="faIconDefinition"
[isOpen]="trueOrFalse"
>
<p>Your content goes here.</p>
</p-expando>
<!-- or -->
<porcelain-expando
title="My Expando Title"
[iconPosition]="ExpandoIconPosition"
[icon]="faIconDefinition"
[isOpen]="trueOrFalse"
>
<p>Your content goes here.</p>
</porcelain-expando>
Advanced Usage
If the [title]
input is omitted or set to the empty string ''
, the Expando will use Expando Body and Expando Header components to populate these regions.
<p-expando>
<p-expando-header>
<h2>My Header Area</h2>
</p-expando-header>
<p-expando-body>
<p>My content area</p>
</p-expando-body>
</p-expando>
<!-- or -->
<porcelain-expando>
<porcelain-expando-header>
<h2>My Header Area</h2>
</porcelain-expando-header>
<porcelain-expando-body>
<p>My content area</p>
</porcelain-expando-body>
</porcelain-expando>
Footer
Shows the UL footer.
<porcelain-footer></porcelain-footer>
Customizing the footer links
Option 1: Using the links
input
You can customize the footer links by passing an array of [url, label] entries to the links
input.
<porcelain-footer
[links]="[
['https://example.com/link1', 'Link 1'],
['https://example.com/link2', 'Link 2'],
['https://example.com/link3', 'Link 3']
]"
></porcelain-footer>
Option 2: Provide as content children
You can also provide the links as content children. This is useful when you want to use Angular components as links. Ensure the porcelain-link
directive is used as a Structural Directive (include the asterisk, i.e. *porcelain-link
).
<porcelain-footer>
<a *porcelain-link href="https://example.com/link1">Link 1</a>
<a *porcelain-link href="https://example.com/link2">Link 2</a>
<a *porcelain-link href="https://example.com/link3">Link 3</a>
</porcelain-footer>
Legacy Search Input
Since 1.4.0
Deprecated Use the new p-search-input component that supports two-way binding.
The Search Sort Component provides keyword search function as needed.
Install Porcelain 1.4 and its dependencies
npm install --save @my-ul/ng-porcelain@^1.4.0
Import the LegacySearchInputModule
import { LegacySearchInputModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [YourComponent],
imports: [CommonModule, LegacySearchInputModule],
exports: []
})
class YourModule {}
Write a function to handle new values from the component...
@Component()
class YourComponent {
handleNewValue(newValue: string): void {
this.value = newValue;
}
}
Place the component in your template, with reference to the handler...
<porcelain-search-input (submitHandler)="handleNewValue($event)"></porcelain-search-input>
Change the Placeholder Text to change the displayed text. Useful for i18n/translation.
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[placeholderLabel]="'Volume'"
></porcelain-search-input>
Just use uservalue to assign value to it in the HTML
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[userValue]="searchTerm"
></porcelain-search-input>
For Search box border Toggle set borders for
1.)true for enabling border 2.)false for disabling border
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[borders]="false"
></porcelain-search-input>
For getting just empty value when search cancel is clicked, use emptyhandler
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[borders]="false"
[emptyHandler]="clearSearchClick($event)"
></porcelain-search-input>
Alternative Font Awesome icons can be used instead of the defaults for 'Clear' and 'Submit'. See Font Awesome for Angular docs for more information.
<porcelain-search-input
(submitHandler)="..."
[submitIcon]="mySubmitIcon"
[clearIcon]="myClearIcon"
></porcelain-search-input>
Refiners
Simple Refiner
The Simple Refiner component provides an interface for the user to pick many options to begin refining a search.
The behavior of the Simple Refiner is best defined by the SimpleRefinerDefinition
class in lib/shared
.
Basic Usage
<porcelain-simple-refiner
[refiner]="mySimpleRefiner"
(onRefinerChange)="myRefinerUpdateCallbackFunction($event)"
></porcelain-simple-refiner>
Inside the component, use the SimpleRefiner
class to define your refiner's appearance. Write a callback function to be called when the value is updated.
import { SimpleRefiner } from '@my-ul/ng-porcelain';
class MyComponent {
// Provided to the <porcelain-simple-refiner> component
mySimpleRefiner = new SimpleRefiner({
slug: 'mySimpleRefiner',
title: 'Select three options',
options: {
optionKey1: 'Option Value One',
optionKey2: 'Option Value Two',
optionKey3: 'Option Value Three'
}
});
// called when the refiner has a new value.
myRefinerUpdateCallbackFunction([refinerKey, refinerValue]: [string, string[]]) {
console.log('SimpleRefiner change', refinerKey, refinerValue);
// => 'simple refiner values have changed', 'mySimpleRefiner', ['optionKey1', 'optionKey2', 'optionKey3']
}
}
Enabling Default preselected Values
Incase some options needs to enabled by default then use preSelectedValues property of SimpleRefinerDefinition while creating refiner object or in the created refiner object update preSelectedValues before loaded onto applicator component. Only slugs must be sent and should properly be mapped.
import { SimpleRefiner } from '@my-ul/ng-porcelain';
class MyComponent {
// Provided to the <porcelain-simple-refiner> component
mySimpleRefiner = new SimpleRefiner({
slug: 'mySimpleRefiner',
title: 'Select three options',
preSelectedValues: ['optionKey1', 'optionKey2'],
options: {
optionKey1: 'Option Value One',
optionKey2: 'Option Value Two',
optionKey3: 'Option Value Three'
}
});
// called when the refiner has a new value.
myRefinerUpdateCallbackFunction([refinerKey, refinerValue]: [string, string[]]) {
console.log('SimpleRefiner change', refinerKey, refinerValue);
// => 'simple refiner values have changed', 'mySimpleRefiner', ['optionKey1', 'optionKey2', 'optionKey3']
}
//optionKey1 and optionKey2 are enabled by default. NOTE SEND ONLY PROPER MAPPED SLUGS IN THE preSelectedValues!! preSelectedValues property takes array of strings i.e slugs
}
Date Refiner
The Date Refiner component provides an interface for the user to specify an applicable date range for refining a search.
The behavior of the Simple Refiner is defined by the DateRefinerDefinition
class in lib/shared
.
Basic Usage
<!-- When using callback API -->
<porcelain-date-refiner
[refiner]="myDateRefiner"
(onRefinerChange)="myRefinerUpdateCallbackFunction($event)"
></porcelain-date-refiner>
<!-- When using subscription API -->
<porcelain-date-refiner [refiner]="myDateRefiner"></porcelain-date-refiner>
Write a refiner definition and a callback function that will receive the updated refiner value.
import { DateRefinerValue } from '@my-ul/ng-porcelain';
class MyComponent implements OnInit {
myDateRefiner = new DateRefiner({
slug: 'modifiedRange',
title: 'Modified'
// options: null // OMIT to use the default ranges
});
ngOnInit() {
/**
* Subscription API Option
* --
* Add one or many subscriptions to perform tasks when the Refiner value changes
*
* */
this.myDateRefiner.valueSubject.subscribe(newDateRefinerValue => {
console.log({
refinerSlug: this.myDateRefiner.slug,
refinerValue: newDateRefinerValue
});
});
}
/**
* Callback API Option
* --
* Callback fired when the Refiner value changes.
*
* */
myRefinerUpdateCallbackFunction([refinerKey, refinerValue]: [string, DateRefinerValue]) {
console.log({ refinerKey, refinerValue });
/*
'DateRefiner change',
'modifiedBefore',
{
from: Date(...) or null if unbounded
to: Date(...) or null if unbounded
}
*/
}
}
The updated value is an instance of DateRefinerValue. The date will be in the user's current timezone. Use the returned date objects to
work with the date provided using getUTCMonth()
, getUTCDay()
, and getUTCYear()
as appropriate to build date strings.
Working with Time Zones
The Date Refiner Component will emit values in UTC. Be sure to handle these values appropriately so that your dates are returned properly.
If you serialize the value (into JSON or by using Date.toString()), the values for month, day and year may not be correct. As such, it is recommended to use JavaScript's UTC functions, such as getUTCFullYear
, getUTCMonth
, and getUTCDate
. Refer to MDN Date documentation for further details.
If you are using moment
to handle dates, ensure you call .utc()
before calling format. Refer to moment.js documentation for more details
moment.utc(refinerValue).format('YYYY-MM-DD');
A slug representing the preset chosen by the user is also returned.
let { optionSlug, to, from } = args[0];
Optionally, use moment
to work with the dates.
let { from, to } = args[0],
mTo = moment(to),
mFrom = moment(from),
fmtTo = mTo.format('YYYY-MM-DD'),
fmtFrom = mFrom.format('YYYY-MM-DD');
Radio Refiner
The Simple radio Refiner component is used to display a set of limited options for applicator component
Basic Usage
<porcelain-simple-radio-refiner
[refiner]="mySimpleRefiner"
(onRefinerChange)="myRefinerUpdateCallbackFunction($event)"
></porcelain-simple-radio-refiner>
Inside the component, use the SimpleRefiner
class to define your refiner's appearance. Write a callback function to be called when the value is updated.
####### Note:- Update the type as radio!!!
import { SimpleRefiner } from '@my-ul/ng-porcelain';
class MyComponent {
// Provided to the <porcelain-simple-refiner> component
mySimpleRefiner = new SimpleRefiner({
type: 'radio',
slug: 'mySimpleRefiner',
title: 'Select three options',
options: {
optionKey1: 'Option Value One',
optionKey2: 'Option Value Two',
optionKey3: 'Option Value Three'
}
});
// called when the refiner has a new value.
myRefinerUpdateCallbackFunction([refinerKey, refinerValue]: [string, string[]]) {
console.log('SimpleRefiner change', refinerKey, refinerValue);
// => 'simple refiner values have changed', 'mySimpleRefiner', ['optionKey1', 'optionKey2', 'optionKey3']
}
}
ToolTip Addition in Radio Refiner
tooltipText
is feild used to input the text to be show
when refiner option is inserted into refiner definition, update the tooltipText feild like below
refiner: new SimpleRefinerDefinition({
slug: 'simple',
type: 'radio',
title: 'United States of America (full definitions; see notes)',
options: {
AL: new SimpleOption({
badge: 4888949,
label: 'Compliance summary',
slug: 'AL',
isSelected: true,
tooltipText: 'Unified view of all compliance impacts'
})
})
####### In case you want to change tooltip icon, In the UI the icon is rendered via CSS content property. Use feild customToolTipImageUrl
refiner: new SimpleRefinerDefinition({
slug: 'simple',
type: 'radio',
title: 'United States of America (full definitions; see notes)',
options: {
AL: new SimpleOption({
badge: 4888949,
label: 'Compliance summary',
slug: 'AL',
isSelected: true,
tooltipText: 'Unified view of all compliance impacts',
customToolTipImageUrl: "/assets/info-icon.png"
})
})
####### NOTE!!! Important!!:- only get relevant path file of where image is location application project. Any other value will not work!!
Applicator
The Applicator component allows a user to defer updates on an expensive operation (such as querying a server for search results) by staging a series of changes and then clicking apply.
The Applicator uses the Expando component to provide accordion behavior. These actions will be animated if you import BrowserAnimationsModule
in the root of your app.
<porcelain-applicator
[refiners]="refiners"
(onApply)="myApplyHandler($event)"
(onReset)="myResetHandler($event)"
></porcelain-applicator>
class MyComponent implements OnInit {
refiners = [
new DateRefiner(/* ... */),
new SimpleRefiner(/* ... */),
new SimpleRefiner(/* ... */),
new DateRefiner(/* ... */)
];
// Using Callback API
myApplyHandler(indexedValues, initialLoad) {
// initialLoad sets to true when refiner emits event on ngOninit
// and it sets to false when user clicks on apply/reset button
console.log(indexedValues, initialLoad);
}
myResetHandler(resetClicked) {
// resetClicked sets to true when reset button is clicked
// and it sets to false when user clicks on apply button
console.log(resetClicked);
}
// Using Subscription API
ngOnInit() {
// Subscribe to each refiner's value subject
this.refiners.forEach(refiner => {
refiner.valueSubject.subscribe(newRefinerValue => {
console.log({
refinerSlug: refiner.slug,
refinerValue: newRefinerValue
});
});
});
// Use combine to gather values for generating a query
combineLatest(this.refiners.map(refiner => refiner.valueSubject)).subscribe(
([date1, simple1, simple2, date2]) => {
/*
Called...
- once every subscription has emitted
- if any subscription emits again (refiner changes)
- great for combining search params
*/
}
);
}
}
Refiners Macro
The Refiners component is a macro that will automatically build a series of Refiners (Simple and Date), resulting in less template code, allowing developers to rely on type-checked definitions.
Basic Usage
<porcelain-refiners
[refiners]="refiners"
(onRefinersChange)="refinersChange($event)"
></porcelain-refiners>
Your component should include a refiner array definition and a callback function to be called when a values have changed.
class MyComponent {
refiners = [
new DateRefiner({
slug: 'modifiedRange',
title: 'Modified'
// options: null // OMIT to use the default ranges
}),
new SimpleRefiner({
slug: 'mySimpleRefiner',
title: 'Select three options',
options: {
optionKey1: 'Option Value One',
optionKey2: 'Option Value Two',
optionKey3: 'Option Value Three'
}
})
];
refinersChange() {
console.log('refiners update', args);
}
}
Spinner
Shows a spinner, suitable for loading, or activity indication.
<porcelain-spinner></porcelain-spinner>
Truncate
Truncates a string to the width of its container. Any truncation will show ellipses.
<porcelain-truncate [value]="'stringToTruncate'"></porcelain-truncate>
Pipes
Pipes provide common operations to be used in templates. To use pipes, import the PipesModule
into your @NgModule
.
import { PipesModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [
/* */
],
imports: [PipesModule],
exports: [
/* */
]
})
export class YourModuleNameHere {}
ceil
Pipe
Performs a mathematic ceil
operation on a numeric value. Non-numeric values are passed through.
{{ 1234.01 | ceil }}
<!-- 1235 -->
floor
Pipe
Performs a mathematic floor
operation on a numeric value. Non-numeric values are passed through.
{{ 1234.99 | floor }}
<!-- 1234 -->
round
Pipe
Performs a mathematic round
operation on a numeric value. Non-numeric values are passed through.
{{ 1234.01 | round }}
<!-- 1234 -->
{{ 1234.50 | round }}
<!-- 1235 -->
{{ 1234.99 | round }}
<!-- 1235 -->
sprintf
Pipe
Formats a string using unix-style sprintf syntax.
const currentLocale = getLocale(userLocale);
const projectCount = 1234;
const taskCount = 4321;
Here, the translate
pipe would provide a translation for the string it was provided.
{{ 'There are %1$u projects and %2$u tasks available.' | translate : currentLocale | sprintf :
projectCount : taskCount }}
<!--
en: There are 1234 projects and 4321 tasks available
es: Hay 1234 y 4321 pryectos de tareas disponibles
fr: Il y a 1234 projets et 4321 tâches disponibles.
-->
filter
Pipe
Filter an array of items using an Angular pipe.
Arguments
query: string
Search Query
A string representing the search query.
isObjectArray: boolean
Set to true to filter each item on its labelProp
property.
labelProp: string
Property on each item to use for the filter.
Usage
Filter an array of strings (default)
export class MyComponent {
searchQuery = '';
items = ['Apple', 'Banana', 'Cherry', 'Durian'];
}
<input [(ngModel)]="searchQuery" placeholder="filter the list..." />
<li *ngFor="let item of items | filter : searchQuery">
{{item}}
</li>
Filter an array of objects
Note the use of the third (true
) and fourth ('type'
) properties in *ngFor
.
export class MyComponent {
searchQuery = '';
items = [
{ id: '5001', type: 'None' },
{ id: '5002', type: 'Glazed' },
{ id: '5005', type: 'Sugar' },
{ id: '5007', type: 'Powdered Sugar' },
{ id: '5006', type: 'Chocolate with Sprinkles' },
{ id: '5003', type: 'Chocolate' },
{ id: '5004', type: 'Maple' }
];
}
<input [(ngModel)]="searchQuery" placeholder="filter the list..." />
<li *ngFor="let item of items | filter : searchQuery : true : 'type' ">
{{item.type}} ({{item.id}})
</li>
highlight
Pipe
Highlight text in a string.
Usage
Import the Pipes Module.
import { PipesModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [MyComponent],
imports: [PipesModule],
exports: [MyComponent]
})
export class MyModule {}
Use the highlight pipe by binding to an innerHTML
attribute.. You must bind to innerHTML
, or you may see raw escaped HTML.
<input [(ngModel)]="myQuery" type="text" /> <span innerHTML="{{myString | highlight : myQuery}}"></span>
toLocaleString
Pipe
Formats a date or number for the current locale (or a specified locale).
{{ 1234.56 | toLocaleString : 'en-US' }}
<!-- 1,234.56 -->
{{ new Date() || toLocaleString }}
<!-- 1/2/2020, 12:00:00 AM -->
Services
Services can be used to provide application-wide functionalities like translation and analytics to your application.
Inject services directly into your components.
export class MyComponent {
constructor(
public translationService: TranslationService,
public googleAnalyticsService: GoogleAnalyticsService
) {}
}
Google Analytics Service
The Google Analytics service is a proper Angular service wrapping the async Google Analytics API. When Angular is in dev mode, events will be output to the console.
Replace references to window._gaq
like this...
declare _gaq;
@Component({
// ...
})
export class MyComponent {
constructor() {
_gaq.push(['_trackPageview']);
}
}
with
import { GoogleAnalyticsService } from '@my-ul/ng-porcelain';
@Component({
// ...
providers: [GoogleAnalyticsService]
})
export class MyComponent {
constructor(ga: GoogleAnalyticsService) {
this.ga.push(['_trackPageview']);
}
}
Translation Service
Use the translation to reliably subscribe to a translation dictionary.
import { TranslationService } from '@my-ul/ng-porcelain';
@Component({
// ... //
providers: [TranslationService]
})
class MyComponent {
// Define labels as defaults
applyLabel: string = 'Apply';
cancelLabel: string = 'Cancel';
resetLabel: string = 'Reset';
constructor(private translationService: TranslationService) {
translationService.getTranslations().subscribe(
// Optional static translate method makes installing translations simple
TranslationService.translate(this, {
label_Apply: 'applyLabel',
label_Cancel: 'cancelLabel',
label_Reset: 'resetLabel'
})
);
}
}
Modules
Breadcrumbs
Renders consistent, manageable breadcrumbs.
Usage
Import the module
import { BreadcrumbModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [
/**/
],
imports: [BreadcrumbModule],
exports: [
/**/
]
})
export class BreadcrumbModule {}
Use the components. Use links within the breadcrumb-item to show clickable links, if required.
<porcelain-breadcrumbs>
<porcelain-breadcrumb-item>
<a [routerLink]="['/']">{{translations.label_Orders}}</a>
</porcelain-breadcrumb-item>
<porcelain-breadcrumb-item>
{{translations.label_OrderDetails}}
</porcelain-breadcrumb-item>
</porcelain-breadcrumbs>
Inputs
since 1.13.0
The Inputs Module contains form input controls for use in form building and data collection.
import { InputsModule } from '@my-ul/ng-porcelain';
@NgModule({
/* ... */
imports: [InputsModule]
/* ... */
})
export class MyModule {}
Search Input
Since 1.4.0
The Search Input Component provides a familiar search control with a search and a clear button. In addition to clickable clear/search buttons, the Search Input will also provide clear when escape is pressed and submit when enter is pressed (as long as the component has focus)
Usage
Import the InputsModule
. Prior to Porcelain 1.13.0, import the SearchInputModule
.
import { InputsModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [YourComponent],
imports: [CommonModule, InputsModule],
exports: []
})
class YourModule {}
Use with Two-Way Binding
As of 1.13.0, the Search Input supports two-way binding. This makes it easy to provide and consume values in both directions.
In your template...
<porcelain-search-input [(value)]="myValue"></porcelain-search-input>
In your controller...
@Component(/* ... */)
export class MyComponent {
myValue: string = '';
}
Use with Split Binding
If you prefer manual control over how values are provided and consumed by the Search Input, you can split the binding...
In your template...
<porcelain-search-input [value]="myValue" (valueChanged)="myHandler($event)"></porcelain-search-input>
In your controller...
@Component()
export class MyComponent {
myValue: string = '';
myHandler(newValue: string): void {
if (this.myValue !== newValue) {
this.myValue = newValue;
}
}
}
Binding to the (clear)
and (submit)
button events
In addition to two-way binding, the Search Input provides two buttons that emit when clicked (or used via keyboard): (submit)
and (clear)
. Note that in this example, myValue
is bound to the Search Input with split binding, but myValue
could also be bound with Two-Way Binding to simplify the marshalling of values between the parent component and the Search Input.
In your template...
<porcelain-search-input
[value]="myValue"
(valueChanged)="valueChangeHandler($event)"
(submit)="submitHandler($event)"
(clear)="clearHandler($event)"
></porcelain-search-input>
In your controller...
@Component()
export class MyComponent {
myValue: string = '';
valueChangeHandler(changedValue: string): void {
if (this.myValue !== changedValue) {
this.myValue = changedValue;
}
}
submitHandler(submittedValue: string): void {
// do something with submittedValue
}
clearHandler(): void {
// do something to "clear"
this.myValue = '';
}
}
Install Porcelain 1.4 and its dependencies
npm install --save @my-ul/ng-porcelain@^1.4.0
Import the SearchInputModule
import { SearchInputModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [YourComponent],
imports: [CommonModule, SearchInputModule],
exports: []
})
class YourModule {}
Write a function to handle new values from the component...
@Component()
class YourComponent {
handleNewValue(newValue: string): void {
this.value = newValue;
}
}
Place the component in your template, with reference to the handler...
<porcelain-search-input (submitHandler)="handleNewValue($event)"></porcelain-search-input>
Input Properties
Change Placeholder Text
Change the Placeholder Text to change the displayed text. Useful for i18n/translation.
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[placeholderLabel]="'Volume'"
></porcelain-search-input>
Default value in Search Box
Just use uservalue to assign value to it in the HTML
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[userValue]="searchTerm"
></porcelain-search-input>
Search Box Border Toggle
For Search box border Toggle set borders for
1.)true for enabling border 2.)false for disabling border
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[borders]="false"
></porcelain-search-input>
Search Box Cancel button action
For getting just empty value when search cancel is clicked, use emptyhandler
<porcelain-search-input
[submitHandler]="handleNewValue($event)"
[borders]="false"
[emptyHandler]="clearSearchClick($event)"
></porcelain-search-input>
Customize icons
Alternative Font Awesome icons can be used instead of the defaults for 'Clear' and 'Submit'. See Font Awesome for Angular docs for more information.
<porcelain-search-input
(submitHandler)="..."
[submitIcon]="mySubmitIcon"
[clearIcon]="myClearIcon"
></porcelain-search-input>
Password Input Component
The Password Input allows a user to input a password. The control allows the user to optionally reveal the password to make managing strong passwords easier.
<porcelain-password-input [(value)]="myPassword"></porcelain-password-input>
In your controller...
export class MyController {
myPassword: string = '';
}
Text Input Component
The Text Input control allows a user to enter text. It is not validated.
In your template...
<porcelain-text-input [(value)]="textValue"></porcelain-text-input>
In your controller...
export class MyController {
textValue: string = '';
}
Lists
since 1.10.0
The lists module is used for building styled list items with aligned headers and the ability to sort columns.
To use the List Module within your application, import the ListsModule
.
import { ListsModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [
/* ... */
],
imports: [ListsModule],
exports: [
/* ... */
]
})
export class MyApplicationModule {}
Dynamic Header Component
since 1.13.0
The Dynamic Header can be used to allow the end user to reorder columns.
Where the List Header is populated with static instances of Sort Header and Text Header, the Dynamic Header uses an array JavaScript objects to control appearance. These objects implement the DynamicHeader interface. These header references are built in your application, and can also be used to render the List Item Cell components.
The DynamicColumn
Interface
The DynamicColumn
provides a thorough, easy-to-use type that makes it easy to compose columns for use in the Dynamic Header.
export interface DynamicColumn {
/**
* The user-visible label for the column
*/
label: string;
/**
* The internal identifier used to identify/track the column.
* These values should be unique.
*/
key: string;
/**
* Locks a column in the active state. When `locked` is true, a column cannot be removed
*/
locked: boolean;
/**
* The type of control to display for representing the column. Either 'text', 'search', or 'sort'.
*/
type: DynamicColumnType;
/**
* A numeric representation of how wide the column should be.
* The sum of the `width` values should be less than or equal to 1
*/
width: number;
}
Working with the column order
The Dynamic Header uses two-way binding to manage changes to the columns. Any external changes to the column order will be reflected immediately, and any changes made using drag-drop functionality will be published.
The simplest way to bind columns is with two-way binding.
In your template...
<porcelain-dynamic-header [(columns)]="activeColumns"></porcelain-dynamic-header>
In your controller...
import { DynamicColumn } from '@my-ul/ng-porcelain';
@Component(/* ... */)
export class MyComponent {
activeColumns: DynamicColumn[] = [
{
label: 'Description',
key: 'description',
type: 'text',
locked: false,
width: 0.5
},
{
label: 'Price',
key: 'price',
type: 'text',
locked: false,
width: 0.5
}
];
}
Using Sort Headers
The Dynamic Header will aggregate changes to Sort Header instances and publish them via a number of bindings.
Key Concepts
- The Dynamic Column's
key
will be used as each column'ssortKey
. - Changes to the active sort are handled with three
@Output()
bindings. In order to use sort functionality, use bindingsactiveSortKeyChange
,activeSortDirectionChange
andsortChange
. - Two output bindings,
activeSortKeyChange
andactiveSortDirectionChange
, can be used with two-way bindings to simplify implementation.
Binding to (sortChange)
Sort change fires once per change to sort, and includes sortKey
and sortDirection
. The $event
type is SortTuple
. It's usage is similar to the other bindings.
In these examples, notice that activeSortKey
and activeSortDirection
are not using two-way binding.
In your template...
<porcelain-dynamic-header
[(columns)]="activeColumns"
[activeSortKey]="activeSortKey"
[activeSortDirection]="activeSortDirection"
(sortChange)="updateSort($event)"
></porcelain-dynamic-header>
In your controller...
import { DynamicColumn, SortTuple, SortDirection } from '@my-ul/ng-porcelain';
@Component(/* ... */)
export class MyComponent {
activeColumns: DynamicColumn[] = [
{
label: 'Description',
key: 'description',
type: 'text',
locked: false,
width: 0.5
}
/** ... **/
];
activeSortKey: string = null;
activeSortDirection: SortDirection = null;
updateSort([activeSortKey, activeSortDirection]: SortTuple): void {
this.activeSortKey = activeSortKey;
this.activeSortDirection = activeSortDirection;
this.updateList();
}
updateList() {
this.listService.getList(this.activeSortKey, this.activeSortDirection).subscribe(updatedList => {
this.list = updatedList;
});
}
}
Alternative Bindings
Although most cases will probably use (onSort)
bindings, it is possible to bind to sortKey and sortDirection changes separately.
####### Binding to (activeSortKeyChange)
Whenever the sort column key changes, this output will receive an update.
In your template...
<porcelain-dynamic-header
[activeSortKey]="activeSortKey"
(activeSortKeyChange)="updateActiveSortKey($event)"
></porcelain-dynamic-header>
In your controller...
import { DynamicColumn } from '@my-ul/ng-porcelain';
@Component(/* ... */)
export class MyComponent {
activeColumns: DynamicColumn[] = [
{
label: 'Description',
key: 'description',
type: 'text',
locked: false,
width: 0.5
}
/** ... **/
];
activeSortKey: string = null;
updateActiveSortKey(activeSortKey: string): void {
this.activeSortKey = activeSortKey;
this.updateList();
}
updateList() {
this.listService.getList(this.activeSortKey).subscribe(updatedList => {
this.list = updatedList;
});
}
}
####### Binding to (activeSortDirectionChange)
Whenever the active sort direction changes, this output will receive an update.
In your template...
<porcelain-dynamic-header
[activeSortDirection]="activeSortDirection"
(activeSortDirectionChange)="updateActiveSortDirection($event)"
></porcelain-dynamic-header>
In your controller...
import { DynamicColumn, SortDirection } from '@my-ul/ng-porcelain';
@Component(/* ... */)
export class MyComponent {
activeColumns: DynamicColumn[] = [
{
label: 'Description',
key: 'description',
type: 'text',
locked: false,
width: 0.5
}
/** ... **/
];
activeSortKey: string = null;
updateActiveSortKey(activeSortKey: string): void {
this.activeSortKey = activeSortKey;
// updateActiveSortDirection also calls this
// this may result in two calls to updateList()
// consider binding to (sortChange) instead.
this.updateList();
}
activeSortDirection: SortDirection = null;
updateActiveSortDirection(activeSortDirection: SortDirection): void {
this.activeSortDirection = activeSortDirection;
// updateActiveSortKey also calls this
// this may result in two calls to updateList()
// consider binding to (sortChange) instead.
this.updateList();
}
updateList() {
this.listService.getList(this.activeSortKey, this.activeSortDirection).subscribe(updatedList => {
this.list = updatedList;
});
}
}
####### Timing problems with activeSortKeyChange
and activeSortDirectionChange
You may notice that when using the split bindings for activeSortDirection
and activeSortKey
, it becomes difficult to sanely trigger updates. Commonly, both of these may fire at once, which might cause excessive API calls. You might consider using a throttle/debounce to ensure that these aren't called too quickly, but these will rely on arbitrary timeouts.
For most cases, it is most sensible to use the (sortChange)
output binding, as it emits a tuple containing both updated activeSortKey
and activeSortDirection
values. Implementations using (sortChange)
do not need to rely on arbitrary timeouts (such as throttle/debounce). As an added benefit, there is typically less code needed for this implementation.
####### Two-Way Binding with [(activeSortKey)]
and [(activeSortDirection)]
It is possible to use two-way binding for activeSortKey and activeSortDirection. In most cases, you will need to use the (sortChange) output to trigger a list reload, so two-way binding, for most cases, is not recommended.
In your template...
<porcelain-dynamic-header
[(columns)]="activeColumns"
[(activeSortKey)]="activeSortKey"
[(activeSortDirection)]="activeSortDirection"
(sortChange)="updateSort($event)"
></porcelain-dynamic-header>
In your controller...
export class MyController {
activeSortKey: string = null;
activeSortDirection: SortDirection = null;
updateSort(sort: SortTuple) {
// activeSortKey and activeSortDirection
// are assumed to be updated, but this isn't
// a guarantee. The `sort` argument IS
// guaranteed to be most-recent.
this.updateList();
}
}
Using Search Headers
In addition to offering controls to sort, the Search Header can be toggled to offer keyword search for values in a column. These values can perform in-memory searches or reach out to an API for a more exhaustive search.
Since the Search Header offers support for Sort, it offers the same event bindings as the Sort Header, but also offers two Angular @Output
bindings to support searching.
Key Concepts
- The Search header offers a toggle to switch between Search and Sort functionality.
- The Sort outputs of the Search Header are bound using the Sort Header API. Your application will need to bind an adequate combination of
[activeSortKey]
,[activeSortDirection]
,(activeSortKeyChange)
,(activeSortDirectionChange)
, and(sortChange)
in addition to search bindings. - Most sort cases can be supported with just
[activeSortKey]
,[activeSortDirection]
and(sortChange)
. - Search functionality provides two-way binding for the query, as well as @Output bindings for the search submit and search clear events.
Binding the search query
The search query field supports two-way binding. This will keep your application up-to-date with internal state of the search input.
In your template...
<porcelain-dynamic-header [(query)]="searchQuery"></porcelain-dynamic-header>
In your controller...
export class MyController {
searchQuery: string = '';
}
####### Using split binding
If you do not want to use the two-way binding syntax for the search query, you can split it.
The (queryChange)
binding will be called with every keystroke, so do not bind it to any expensive operations, such as an API call. If you are building live search functionality, you may benefit from rxjs debounce.
In your template...
<porcelain-dynamic-header
[query]="searchQuery"
(queryChange)="queryChanged($event)"
></porcelain-dynamic-header>
In your controller...
export class MyController {
searchQuery: string = '';
queryChanged(searchQuery: string) {
if (this.searchQuery !== searchQuery) {
this.searchQuery = searchQuery;
}
}
}
####### Debouncing the (queryChange)
Output.
Some applications of the Search Header might want semi-live updating of search results. To safely implement this, using rxjs debounceTime
and throttleTime
may help.
The debounceTime
operator will emit the last value over a specified interval, whereas the throttleTime
operator will emit the first value and wait the specified interval before emitting again.
In your template...
<porcelain-dynamic-header
[query]="searchQuery"
(queryChange)="queryChanged($event)"
></porcelain-dynamic-header>
In your controller...
import { Subject } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';
class Widget {
widget_id?: number;
name: string;
}
export class MyController {
query$: Subject<string> = new Subject();
results: any[] = null;
constructor(public widgetService: WidgetService) {
// Use a debounced observable to slow down queries
this.query$
.pipe(
// after 200 ms, take the last update
debounceTime(200),
// turn this observable into another
map(query => this.search(query))
)
.subscribe(results => (this.results = results));
}
queryChanged(query: string) {
this.query$.next(query);
}
search(query: string): Observable<Widget[]> {
return this.widgetService.search(query);
}
}
Binding the (queryChange)
Output
The queryChange Output will fire when a new search query is available. The emitted $event
is a dictionary containing column search values. The dictionary will be keyed with the column.key
property. If a user clears a search input, the key will be removed from the dictionary.
Take the following column, for example.
let column = {
label: 'Description',
key: 'description',
locked: false,
type: 'search',
width: 1 / 2
};
If a user typed "television" as a search query, the following object would be returned on the (queryChange)
output:
{
"description": "television"
}
You can do whatever you need to with this object. Commonly, you might want to lowercase or uppercase the value so that the string will be matched for any case.
In your template...
<porcelain-dynamic-header (queryChange)="search($event)"></porcelain-dynamic-header>
In your controller...
export class MyController {
searchQuery: string = '';
search(searchQuery: DynamicSearchQuery): void {
// use DynamicSearchQuery
}
}
List Component
The List Component is the outer-most wrapper for the List Component System
<porcelain-list>
<!-- children here -->
</porcelain-list>
List Body Component
The List Body Component wraps a series of items. Although there is no technical requirement to use List Item components (use any elements necessary), it is recommended. The List Body Component provides padding and provides spacing between List Item components.
<porcelain-list>
<porcelain-list-header>
<!-- porcelain-list-header-cell instances here -->
</porcelain-list-header>
<porcelain-list-body>
<!-- porcelain-list-item instances here -->
</porcelain-list-body>
</porcelain-list>
List Header Component
The List Header Component wraps a series of List Header Cells. The List Header Component can be made sticky using the @w11k/angular-sticky-things library.
<porcelain-list>
<porcelain-list-header>
<!-- porcelain-list-header-cell instances here -->
</porcelain-list-header>
</porcelain-list>
List Header Cell Component
The List Header Cell Component is used to define table headers for a List. This is considered a container.
[width] input
The width
input is mandatory, and is a number between zero and one. For all list-header-cell components in a list-header, the total of the "width" inputs should be 1
.
Example
<porcelain-list>
<porcelain-list-header>
<porcelain-list-header-cell [width]="2 / 3">
<!-- Header here -->
</porcelain-list-header-cell>
<porcelain-list-header-cell [width]="1 / 3">
<!-- Header here -->
</porcelain-list-header-cell>
</porcelain-list-header>
</porcelain-list>
List Item Component
The List Item Component is used to markup the display of a series of items. Since it is designed to be a single atomic instance of an item, it is designed to be used with the *ngFor
directive.
The List Item component is responsible for drawing borders between List Item Cell components.
| Property | Type | Description |
| ------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| [success]
| boolean | When set to true
, a 3 pixel left border and .text-accent
elements within the list item will be set to UL Medium Green. |
| [error]
| boolean | When set to true
, a 3 pixel left border and .text-accent
elements within the List Item will have their color set to UL Medium Red. |
| [warning]
| boolean | When set to true
, a 3 pixel left border and .text-accent
elements within the List Item will have their color set to UL Medium Yellow. |
| [primary]
| boolean | When set to true
, a 3 pixel left border and .text-accent
elements within the List Item will have their color set to UL Action Blue. |
| [secondary]
| boolean | When set to true
, a 3 pixel left border and .text-accent
elements within the List Item will have their color set to UL Medium Teal. |
<porcelain-list>
<porcelain-list-header>
<!-- porcelain-list-header-cell instances here -->
</porcelain-list-header>
<porcelain-list-body>
<porcelain-list-item>
<!-- porcelain-list-item-cell instances here -->
</porcelain-list-item>
</porcelain-list-body>
</porcelain-list>
List Item Cell Component
The List Item Cell defines a cell within a List Item. This typically corresponds to a property within an entity that is being looped.
Properties
| Property | Type | Description |
| --------------- | --------- | ---------------------------------------------------------------------------------------------------------------------- |
| [width]
| number
| A number between zero and one. All cells within a List Item component should have [width] inputs that sum to 1. |
| [alignTop]
| boolean
| When true
, the contents of the cells will be at the top of the row of cells. Cells are middle aligned by default. |
| [alignBottom]
| boolean
| When true
, the contents of the cells will be at the bottom of the row of cells. Cells are middle aligned by default. |
| [padAll]
| boolean
| When true
, the contents of the cell will be wrapped with ~20px of spacing. Default false
. |
| [padTop]
| boolean
| When true
, the contents of the cell will contain ~20px of padding at the top of the cell. Default false
. |
| [padRight]
| boolean
| When true
, the contents of the cell will contain ~20px of padding at the right of the cell. Default false
. |
| [padBottom]
| boolean
| When true
, the contents of the cell will contain ~20px of padding at the bottom of the cell. Default false
. |
| [padLeft]
| boolean
| When true
, the contents of the cell will contain ~20px of padding at the left of the cell. Default false
. |
Example
<porcelain-list-item>
<porcelain-list-item-cell [width]="1/5" [padAll]="true">
{{ item.price | currency : 'USD' }}
</porcelain-list-item-cell>
</porcelain-list-item>
Sort Header Component
The Sort Header can be used to control sort variables within a list view.
Dual-Binding/Banana Box Sort Direction
This component supports banana-box/two-way binding notation for updating the values of activeSortKey
and activeSortDirection
.
Inputs
| Property | Type | Description |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [label]
| string
| User-visible label shown in the clickable portion of the sort header. |
| [sortKey]
| string
| Sets the key that this sort header is responsible for toggling. When equal to activeSortKey
are equal, the component will be in the active
state. Typically, this is a constant value, wrapped in single quotes, but it can be a variable, which is useful for loops. This value should NOT change often, if ever, after the component loads. |
<porcelain-sort-header [label]=" 'Date Modified' " [sortKey]=" 'date' "></porcelain-sort-header>
Outputs
(onSortChange)
Output
The (onSortChange) output will emit the sortKey
and the sortDirection
in a tuple: [sortKey, sortDirection]
. Use this to trigger any page load updates, as binding to the activeSortKeyChange
and activeSortColumnChange
may cause any bound actions to execute sporadically (twice in some cases, or not at all in others).
<porcelain-sort-header
(onSortChange)=" updateList() "
[(activeSortKey)]=" currentSortKey "
[(activeSortDirection)]=" currentSortDirection "
></porcelain-sort-header>
The following code pattern can be used to refresh a list after the sort fields are modified.
@Component({
// ...
})
export class MyComponent {
public currentSortKey;
public currentSortDirection;
constructor(public listService: ListService) {
this.resetSort();
}
resetSort() {
this.currentSortKey = 'price';
this.currentSortDirection = 'asc';
}
updateList() {
this.listService
.getList({
sortKey: this.currentSortKey,
sortDirection: this.currentSortDirection
})
.subscribe(newListItems => {
this.items = newListItems;
});
}
}
activeSortKey
Two-Way Binding
Binds the current active sort column from your component. Can be split into @Input
/@Output
bindings if you need a callback, although this is not recommended in favor of (onSortChange)
, which fires a callback once per toggle.
[(activeSortKey)]
Two-Way Binding
Set to the application's current sort column. When equal to [sortKey]
, the component will be in the active
state.
<porcelain-sort-header [(activeSortKey)]=" currentSortColumn "></porcelain-sort-header>
[activeSortKey]
Input
Set this to your application's current sort column. If [sortKey]
and [activeSortKey]
are equal with a valid [activeSortDirection]
, the header will appear active.
Valid values are null
, 'asc'
and 'desc'
.
(activeSortKeyChange)
Output
Fires whenever the [activeSortKey]
is set to [sortKey]
. Value can be accessed with $event
.
<porcelain-sort-header (activeSortKeyChange)=" handleSortKeyChange($event) "></porcelain-sort-header>
[(activeSortDirection)]
Two-Way Binding
Banana-Box Binding
Use two-way binding to keep the sort-header and your application's sort column state in sync.
<porcelain-sort-header [(activeSortDirection)]=" myCurrentSortDirection "></porcelain-sort-header>
[activeSortDirection]
Input Binding
Set the activeSortDirection
from your application.
<porcelain-sort-header [activeSortDirection]=" myCurrentSortDirection "></porcelain-sort-header>
(activeSortDirectionChange)
Output Binding
Bind a callback that will fire upon changes to activeSortDirection
. $event
will contain the new sort direction.
<porcelain-sort-header (activeSortDirectionChange)=" handleSortDirectionChange($event)">
</porcelain-sort-header>
Skeletons
Skeletons can be used to improve perceived load time by providing a hint about the content that is about to load.
Skeleton Block
Creates an animated skeleton region, suitable as a placeholder for images. The Skeleton Block tries to fill all vertical and horizontal space available to it, so wrap it with another element to specify the dimensions.
The Skeleton Block has no @Input()
or @Output()
decorators.
Usage
<p-skeleton-block></p-skeleton-block> <porcelain-skeleton-block></porcelain-skeleton-block>
Skeleton Word
Creates an animated Skeleton Block that can be sized in characters (ems). Internally used by the Skeleton Line.
Inputs
[characters]
Input
A number
specifying how many characters (ems) the word skeleton should be.
Usage
<porcelain-skeleton-block [characters]="6"></porcelain-skeleton-block>
<p-skeleton-block [characters]="6"></p-skeleton-block>
Skeleton Line
<porcelain-skeleton-line [minLength]="7" [maxLength]="13"></porcelain-skeleton-line>
<p-skeleton-line [minLength]="7" [maxLength]="13"></p-skeleton-line>
Automatically creates a line with randomly-sized words.
Inputs
[minLength]
Input: number
Minimum character width for the words placed in the line. Defaults to 7
.
[maxLength]
Input: number
Maximum character width for the words placed in the line. Defaults to 13
.
Skeleton Paragraph
<porcelain-skeleton-paragraph></porcelain-skeleton-paragraph>
<p-skeleton-paragraph></p-skeleton-paragraph>
Automatically creates a paragraph of Skeleton Word elements.
Inputs
[lines]
Input: number
Number of lines of skeleton text to generate. Lines will be generated to the full width of the block. If this is undesired, use the Skeleton Word.
Toolbars
The toolbar module consists of several components that can be used to quickly compose toolbars within an Angular application.
To use the Toolbar Module, import it into your module. Subtle dropdown animations can be used by the Toolbar system. To enable these animations, import BrowserAnimationsModule
in the root module of your app.
import { ToolbarModule } from '@my-ul/ng-porcelain';
@NgModule({
imports: [ToolbarModule]
})
export class AppModule {}
Toolbar Component
The Toolbar component composes one row of Toolbar Cells to create a toolbar. To stack more than one Toolbar, wrap with the ToolbarsComponent
(note the added 's').
Permitted Children
- Toolbar Cell Component -
<porcelain-toolbar-cell>
<porcelain-toolbar>
<porcelain-toolbar-cell>...</porcelain-toolbar-cell>
</porcelain-toolbar>
Toolbar Cell Component
The Toolbar Cell component builds cells within a Toolbar. Adjacent cells are divided with a thin line.
Props
[flex]
- a flex definition, specifying the grow/shrink/basis property for element sizing.
Permitted Children
- any
<porcelain-toolbar-cell [flex]="'0 0 auto'">
...
</porcelain-toolbar-cell>
Toolbar Text Component
The Toolbar Text component allows the placement of arbitrary text within a toolbar cell. This can be used near other controls, such as a Toolbar Option or Toolbar Button, to provide context about the purpose of those controls. For example, a Toolbar Text could be placed near a Toolbar Button to describe the state of pagination.
Props
[alignRight]
- true if you want the text aligned right[alignCenter]
- true if you want the text centered[noWrap]
- true if you don't want your text to break into lines
<porcelain-toolbar-cell ...>
<porcelain-toolbar-text [alignRight]="true" [alignCenter]="true" [noWrap]="true">
{{totalResultCount.toLocaleString()}} Results
</porcelain-toolbar-text>
</porcelain-toolbar-cell>
Toolbar Button Component
The Toolbar Button component is an accessible button that includes an optional icon.
Props
[icon]
- a Font Awesome 5IconDefinition
[isLabelSrOnly]
-true
if you only want to hide the button text (except for screen readers).(onClick)
- a callback function to be called when the button is clicked or triggered with enter/space.
Children
Text will be used for the button label.
<porcelain-toolbar-cell ...>
<porcelain-toolbar-button [icon]="faSave" [isLabelSrOnly]="false" (onClick)="mySaveHandler($event)">
Save
</porcelain-toolbar-button>
</porcelain-toolbar-cell>
Toolbar Select Component
The Toolbar Select component closely mimics an HTML <select>
element. The Toolbar Select component provides a highly-customizable, yet accessible dropdown implementation. The component allows for a HTML-templated dropdown options, as well as in the currently-selected-item window.
Props
[fullWidth]
-true
to enable the full-width presentation, which is left-aligned, and spans the full width of the Toolbar Select Component.[label]
- Label to be displayed to the left of the currently-selected item.[(value)]
- two-way binding property for the current value.
Two-WayBinding vs. Split Binding
The two-way binding of value can be broken apart if you would like to register callbacks to handle updating your values. This syntax would use the following properties instead of [(value)]="myValue"
. When split, the (valueChange)
callback is responsible for updating the values passed to [value]
.
<porcelain-toolbar-select [value]="myValue" (valueChange)="myValueChangeHandler($event)"
><!-- ... --></porcelain-toolbar-select
>
Children
<porcelain-toolbar-option>
- used to define options and the template used to represent the option in the dropdown list.<porcelain-toolbar-selected-template>
- used to define the display of the currently-selected option when the component is closed. Use more than one to define null states based onvalue
Annotated Example
The following code could be used to create a people picker.
TypeScript Component
@Component({
// ...
})
export class MyComponent {
/**
* The current person selected by the Component
*/
currentPerson = null;
/**
* People, indexed by their username to make the selected-template easy to populate.
* Sample data from https://fakepersongenerator.com/
*/
people = {
'cory.ramer': {
username: 'cory.ramer',
first: 'Cory',
last: 'Ramer',
email: '[email protected]',
phone: '(309) 548-8281'
},
'eric.hawes': {
username: 'eric.hawes',
first: 'Eric',
last: 'Hawes',
email: '[email protected]',
phone: '(708) 826-6749'
},
'james.dock': {
username: 'james.dock',
first: 'James',
last: 'Dock',
email: '[email protected]',
phone: '(801) 638-3830'
}
};
/**
* Turns an object into an array for looping
*/
public getValues(obj) {
return Object.keys(obj).map(key => obj[key]);
// or Object.values(obj) if you have the polyfill enabled
}
}
HTML Template
<porcleain-toolbar-cell ...>
<porcelain-toolbar-select
[fullWidth]="true"
[label]="'To:'"
[(value)]="currentPerson">
<!-- define a template for the currently selected item -->
<porcelain-toolbar-selected-template *ngIf="currentPerson">
{{recipients[currentPerson].email}}
</porcelain-toolbar-selected-template>
<!-- define a template for the current window for null/undefined selection -->
<porcelain-toolbar-selected-template *ngIf="currentPerson === null">
—
</porcelain-toolbar-selected-template>
<!--
- define the people dictionary in the TypeScript Component, above.
- loop with *ngFor, if necessary. Rendering static options is also possible.
- bind to [value] to specify what value is returned
- use HTML for rich formatting control over the option
-->
<porcelain-toolbar-option
*ngFor="let person of getValues(people)"
[value]="person.username"
>
<strong>{{person.first}} {{person.last}}</strong><br>
{{person.email}} • {{person.phone}}
</porcelain-toolbar-option>
</porcelain-toolbar-select>
</porcelain-toolbar-cell>
TableViewModule
table View System
The table view module is used for building styled table view list items with aligned headers and the ability to sort columns and interchange columns if rack component is used.
To use the List Module within your application, import the ListsModule
.
import { TableviewModule } from '@my-ul/ng-porcelain';
@NgModule({
declarations: [
/* ... */
],
imports: [TableviewModule],
exports: [
/* ... */
]
})
export class MyApplicationModule {}
Table View Header
The parent component is table view header and insdie it each block is table view header item. The tableview header item takes an input width which should be same as for tableview columns so that they neatly aling in UI
<p-tableview-header>
<p-tableview-header-item [width]="column.width"
*ngFor="let column of getColumnValues()">
<ng-container>
<p-tableview-text-header>
<span innerHTML="{{column.label}}" title="{{column.label}}"></span>
</p-tableview-text-header>
</ng-container>
</p-tableview-header-item>
</p-tableview-header>
Table View Header Item
Properties
| Property | Type | Description |
| --------------- | --------- | ---------------------------------------------------------------------------------------------------------------------- |
| [width]
| number
| A number between zero and one. All cells within a List Item component should have [width] inputs that sum to 1. |
| [alignTop]
| boolean
| When true
, the contents of the cells will be at the top of the row of cells. Cells are middle aligned by default. |
| [alignBottom]
| boolean
| When true
, the contents of the cells will be at the bottom of the row of cells. Cells are middle aligned by default. |
| [padAll]
| boolean
| When true
, the contents of the cell will be wrapped with ~0.5em of spacing. Default false
. |
| [padTop]
| boolean
| When true
, the contents of the cell will contain ~0.5em of padding at the top of the cell. Default false
. |
| [padRight]
| boolean
| When true
, the contents of the cell will contain ~0.5em of padding at the right of the cell. Default false
. |
| [padBottom]
| boolean
| When true
, the contents of the cell will contain ~0.5em of padding at the bottom of the cell. Default false
. |
| [padLeft]
| boolean
| When true
, the contents of the cell will contain ~0.5em of padding at the left of the cell. Default false
. |
<p-tableview-header>
<p-tableview-header-item [width]="1/5" [padAll]="true"> </p-tableview-header-item>
</p-tableview-header>
Table View Text Header
Table view text header is just used to display the title value of the header. This should always be en