14.0.1 • Published 2 months ago

@my-ul/ng-porcelain v14.0.1

Weekly downloads
259
License
-
Repository
-
Last release
2 months ago

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/ng-porcelain@14.0.1 \
	@fortawesome/angular-fontawesome@"^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0" \
	@fortawesome/fontawesome-svg-core@^1.2.27 \
	@fortawesome/free-solid-svg-icons@">= 5.0.0 || >= 6.0.0" \
	@w11k/angular-sticky-things@"^1.3.2 || ^1.4.0 || ^1.5.0 || ^1.5.2" \
	angular-mydatepicker@"^0.10.0 || >=0.11.0" \
	lodash-es@* \
	luxon@* \
	sprintf-js@* \
	uuid@*

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's sortKey.
  • Changes to the active sort are handled with three @Output() bindings. In order to use sort functionality, use bindings activeSortKeyChange, activeSortDirectionChange and sortChange.
  • Two output bindings, activeSortKeyChange and activeSortDirectionChange, 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.

PropertyTypeDescription
[success]booleanWhen set to true, a 3 pixel left border and .text-accent elements within the list item will be set to UL Medium Green.
[error]booleanWhen 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]booleanWhen 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]booleanWhen 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]booleanWhen 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
PropertyTypeDescription
[width]numberA number between zero and one. All cells within a List Item component should have width inputs that sum to 1.
[alignTop]booleanWhen true, the contents of the cells will be at the top of the row of cells. Cells are middle aligned by default.
[alignBottom]booleanWhen true, the contents of the cells will be at the bottom of the row of cells. Cells are middle aligned by default.
[padAll]booleanWhen true, the contents of the cell will be wrapped with ~20px of spacing. Default false.
[padTop]booleanWhen true, the contents of the cell will contain ~20px of padding at the top of the cell. Default false.
[padRight]booleanWhen true, the contents of the cell will contain ~20px of padding at the right of the cell. Default false.
[padBottom]booleanWhen true, the contents of the cell will contain ~20px of padding at the bottom of the cell. Default false.
[padLeft]booleanWhen 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
PropertyTypeDescription
[label]stringUser-visible label shown in the clickable portion of the sort header.
[sortKey]stringSets 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 5 IconDefinition
  • [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 on value
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: 'cory.ramer@ul.com',
			phone: '(309) 548-8281'
		},
		'eric.hawes': {
			username: 'eric.hawes',
			first: 'Eric',
			last: 'Hawes',
			email: 'eric.hawes@ul.com',
			phone: '(708) 826-6749'
		},
		'james.dock': {
			username: 'james.dock',
			first: 'James',
			last: 'Dock',
			email: 'james.dock@ul.com',
			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">
			&mdash;
		</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}} &bull; {{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
PropertyTypeDescription
[width]numberA number between zero and one. All cells within a List Item component should have width inputs that sum to 1.
[alignTop]booleanWhen true, the contents of the cells will be at the top of the row of cells. Cells are middle aligned by default.
[alignBottom]booleanWhen true, the contents of the cells will be at the bottom of the row of cells. Cells are middle aligned by default.
[padAll]booleanWhen true, the contents of the cell will be wrapped with ~0.5em of spacing. Default false.
[padTop]booleanWhen true, the contents of the cell will contain ~0.5em of padding at the top of the cell. Default false.
[padRight]booleanWhen true, the contents of the cell will contain ~0.5em of padding at the right of the cell. Default false.
[padBottom]booleanWhen true, the contents of the cell will contain ~0.5em of padding at the bottom of the cell. Default false.
[padLeft]booleanWhen 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-i
14.0.1

2 months ago

9.4.0

12 months ago

9.4.0-beta.3

12 months ago

9.4.0-beta.2

12 months ago

9.4.0-beta.1

12 months ago

9.4.0-beta.0

1 year ago

14.0.0-beta.1

11 months ago

14.0.0-beta.0

12 months ago

11.0.0-beta.0

12 months ago

12.0.0-beta.0

12 months ago

9.4.1-beta.0

12 months ago

9.4.1-beta.1

11 months ago

13.0.0-beta.0

12 months ago

10.0.0-beta.0

12 months ago

9.3.3-beta.9

1 year ago

9.3.3-beta.8

1 year ago

9.3.3-beta.7

1 year ago

9.3.3-beta.3

2 years ago

9.3.3-beta.2

2 years ago

9.3.3-beta.1

2 years ago

9.3.3-beta.0

2 years ago

9.3.2

2 years ago

9.3.1

2 years ago

9.3.0

2 years ago

9.3.3-beta.6

2 years ago

9.3.3-beta.5

2 years ago

9.3.3-beta.4

2 years ago

9.2.4-beta.1

2 years ago

9.2.3

2 years ago

9.2.4-beta.0

2 years ago

9.2.3-beta.6

2 years ago

9.2.3-beta.4

2 years ago

9.2.3-beta.3

2 years ago

9.2.3-beta.5

2 years ago

9.2.2

2 years ago

9.2.1

2 years ago

9.2.0

2 years ago

9.2.3-beta.0

2 years ago

9.2.3-beta.2

2 years ago

9.2.3-beta.1

2 years ago

9.1.0-beta.17

3 years ago

9.1.0-beta.15

3 years ago

9.1.0-beta.16

3 years ago

9.1.0-beta.14

3 years ago

9.1.0-beta.13

3 years ago

9.1.0-beta.12

3 years ago

9.0.9-beta.5

3 years ago

9.0.9-beta.4

3 years ago

9.0.9-beta.3

3 years ago

9.0.9-beta.2

3 years ago

9.0.9-beta.0

3 years ago

9.0.8

3 years ago

9.0.7

3 years ago

9.0.6

3 years ago

9.0.5

3 years ago

9.1.0-beta.10

3 years ago

9.1.0-beta.11

3 years ago

9.1.0-beta.9

3 years ago

9.1.0-beta.8

3 years ago

9.1.0-beta.7

3 years ago

9.1.0-beta.6

3 years ago

9.1.0-beta.5

3 years ago

9.1.0-beta.4

3 years ago

9.1.0-beta.3

3 years ago

9.1.0-beta.2

3 years ago

9.1.0-beta.1

3 years ago

9.0.4

3 years ago

9.0.3

4 years ago

9.0.2

4 years ago

9.0.1

4 years ago

9.0.0

4 years ago

1.13.6

4 years ago

1.13.5

4 years ago

1.13.4

4 years ago

1.13.3

4 years ago

1.13.2

4 years ago

1.13.1

4 years ago

1.13.0

4 years ago

1.12.1

4 years ago

1.12.0

4 years ago

1.11.1-beta.0

4 years ago

1.11.0

4 years ago

1.10.0

4 years ago

1.9.4

4 years ago

1.10.0-beta.2

4 years ago

1.10.0-beta.1

4 years ago

1.9.3

4 years ago

1.9.1

4 years ago

1.10.0-beta.0

4 years ago

1.9.2

4 years ago

1.9.0

4 years ago

1.8.6

4 years ago

1.8.4

4 years ago

1.8.3

4 years ago

1.8.2

4 years ago

1.9.0-beta.5

4 years ago

1.9.0-beta.4

4 years ago

1.8.1

4 years ago

1.9.0-beta.3

4 years ago

1.9.0-beta.2

5 years ago

1.9.0-beta.1

5 years ago

1.9.0-beta.0

5 years ago

1.8.0

5 years ago

1.7.0-beta.5

5 years ago

1.7.0-beta.4

5 years ago

1.7.0-beta.3

5 years ago

1.7.0-beta.2

5 years ago

1.7.0-beta.1

5 years ago

1.7.0-beta.0

5 years ago

1.6.0

5 years ago

1.5.0

5 years ago

1.6.0-beta.1

5 years ago

1.4.8

5 years ago

1.5.0-beta.0

5 years ago

1.4.7

5 years ago

1.4.6

5 years ago

1.4.5

5 years ago

1.4.4

5 years ago

1.4.3

5 years ago

1.4.2

5 years ago

1.4.1

5 years ago

1.4.0

5 years ago

1.3.2

5 years ago

1.3.1

5 years ago

1.3.0

5 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.1

5 years ago