Skip to content

Commit 02d430d

Browse files
committed
feat: add initials pipe with documentation and demo
1 parent 934e77e commit 02d430d

File tree

12 files changed

+262
-3
lines changed

12 files changed

+262
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class UserService {
9797
| **Directives** | `auto-focus`, `click-outside`, `click-throttle`, `hover-class`, `infinite-scroll`, `numbers-only`, `press-enter`, `repeat`, `resized`, `shortcut`, `typed-template`, `visibility-change` |
9898
| **Guards** | `param`, `query-param`, `unsaved-changes` |
9999
| **Interceptors** | `base-url`, `cache`, `correlation-id`, `encryption`, `jwt`, `performance`, `timeout` |
100-
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |
100+
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `initials`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |
101101
| **Rxjs** | `backoff-retry`, `catch-error-with-fallback`, `data-polling`, `debug`, `live-search`, `loading-status` |
102102
| **Services** | `cache`, `clipboard`, `cookie`, `device`, `event`, `jwt`, `logger`, `network-status`, `shortcut`, `storage` |
103103
| **Signals** | `breakpoint-matcher`, `debounced-signal`, `deep-computed`, `event-signal`, `interval-signal`, `route-param-signal`, `route-query-param-signal`, `router-event-signal`, `state-signal`, `storage-signal`, `throttled-signal`, `websocket-signal` |

projects/ngx-oneforall-docs/src/app/categories/getting-started/pages/introduction/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Fully typed APIs and consistent behavior make these utilities predictable and ea
5252
| **Directives** | `auto-focus`, `click-outside`, `click-throttle`, `hover-class`, `infinite-scroll`, `numbers-only`, `press-enter`, `repeat`, `resized`, `shortcut`, `typed-template`, `visibility-change` |
5353
| **Guards** | `param`, `query-param`, `unsaved-changes` |
5454
| **Interceptors** | `base-url`, `cache`, `correlation-id`, `encryption`, `jwt`, `performance`, `timeout` |
55-
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |
55+
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `initials`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |
5656
| **Rxjs** | `backoff-retry`, `catch-error-with-fallback`, `data-polling`, `debug`, `live-search`, `loading-status` |
5757
| **Services** | `cache`, `clipboard`, `cookie`, `device`, `event`, `jwt`, `logger`, `network-status`, `shortcut`, `storage` |
5858
| **Signals** | `breakpoint-matcher`, `debounced-signal`, `deep-computed`, `event-signal`, `interval-signal`, `route-param-signal`, `route-query-param-signal`, `router-event-signal`, `state-signal`, `storage-signal`, `throttled-signal`, `websocket-signal` |
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
.demo-container {
2+
max-width: 600px;
3+
margin: 2rem auto;
4+
padding: 2rem;
5+
background: #fafbfc;
6+
border-radius: 8px;
7+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
8+
font-family: Arial, sans-serif;
9+
}
10+
h2 {
11+
margin-bottom: 1rem;
12+
color: #2c3e50;
13+
}
14+
.demo-section {
15+
margin-top: 2rem;
16+
}
17+
.demo-section h3 {
18+
margin-bottom: 0.5rem;
19+
color: #34495e;
20+
}
21+
.result {
22+
background: #e8f5e9;
23+
padding: 0.75rem 1rem;
24+
border-radius: 4px;
25+
margin-top: 0.5rem;
26+
font-family: 'Fira Mono', monospace;
27+
font-size: 1.05em;
28+
color: #1b5e20;
29+
display: flex;
30+
align-items: center;
31+
}
32+
code {
33+
background: #f5f2f0;
34+
padding: 2px 6px;
35+
border-radius: 3px;
36+
font-size: 0.95em;
37+
color: #c7254e;
38+
}
39+
40+
.avatar {
41+
width: 40px;
42+
height: 40px;
43+
background-color: #3f51b5;
44+
color: white;
45+
border-radius: 50%;
46+
display: inline-flex;
47+
align-items: center;
48+
justify-content: center;
49+
font-weight: bold;
50+
margin-right: 15px;
51+
font-family: Arial, sans-serif;
52+
flex-shrink: 0;
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Component } from '@angular/core';
2+
import { InitialsPipe } from 'ngx-oneforall/pipes/initials';
3+
4+
@Component({
5+
selector: 'lib-initials-demo',
6+
standalone: true,
7+
imports: [InitialsPipe],
8+
template: `
9+
<div class="demo-container">
10+
<h2>Initials Pipe Demo</h2>
11+
12+
<div class="demo-section">
13+
<h3>Full Name (Default: 2)</h3>
14+
<code>'John Doe' | initials</code>
15+
<div class="result">
16+
<div class="avatar">{{ 'John Doe' | initials }}</div>
17+
<span>Output: {{ 'John Doe' | initials }}</span>
18+
</div>
19+
</div>
20+
21+
<div class="demo-section">
22+
<h3>Single Name</h3>
23+
<code>'John' | initials</code>
24+
<div class="result">
25+
<div class="avatar">{{ 'John' | initials }}</div>
26+
<span>Output: {{ 'John' | initials }}</span>
27+
</div>
28+
</div>
29+
30+
<div class="demo-section">
31+
<h3>Three Names (Limit 3)</h3>
32+
<code>'John Middle Doe' | initials:3</code>
33+
<div class="result">
34+
<div class="avatar">{{ 'John Middle Doe' | initials: 3 }}</div>
35+
<span>Output: {{ 'John Middle Doe' | initials: 3 }}</span>
36+
</div>
37+
</div>
38+
39+
<div class="demo-section">
40+
<h3>LowerCase Input</h3>
41+
<code>'jane doe' | initials</code>
42+
<div class="result">
43+
<div class="avatar">{{ 'jane doe' | initials }}</div>
44+
<span>Output: {{ 'jane doe' | initials }}</span>
45+
</div>
46+
</div>
47+
</div>
48+
`,
49+
styleUrl: './initials-demo.component.scss',
50+
})
51+
export class InitialsDemoComponent {}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
![Bundle Size](https://deno.bundlejs.com/badge?q=ngx-oneforall/pipes/initials&treeshake=[*]&config={"esbuild":{"external":["rxjs","@angular/core","@angular/common","@angular/forms","@angular/router"]}})
2+
3+
The `initials` pipe transforms a name or string into its initials. It handles single or multiple words and allows customizing the number of initials returned.
4+
5+
### Usage
6+
7+
Import the `InitialsPipe` from `ngx-oneforall/pipes/initials`.
8+
9+
```typescript
10+
import { InitialsPipe } from 'ngx-oneforall/pipes/initials';
11+
```
12+
13+
### Examples
14+
15+
#### Basic Usage
16+
17+
```html file="./snippets.html"#L2-L3
18+
```
19+
20+
#### Single Name
21+
22+
```html file="./snippets.html"#L6-L7
23+
```
24+
25+
#### Custom Limit (1 Initial)
26+
27+
```html file="./snippets.html"#L10-L11
28+
```
29+
30+
#### Custom Limit (3 Initials)
31+
32+
```html file="./snippets.html"#L14-L15
33+
```
34+
35+
### Parameters
36+
37+
| Name | Type | Default | Description |
38+
|------|------|---------|-------------|
39+
| `value` | `string` | - | The input string to transform |
40+
| `count` | `number` | `2` | The maximum number of initials to return |
41+
42+
### Live Demo
43+
44+
{{ NgDocActions.demo("InitialsDemoComponent") }}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { NgDocPage } from '@ng-doc/core';
2+
import PipesCategory from '../../ng-doc.category';
3+
import { InitialsDemoComponent } from './demo/initials-demo.component';
4+
5+
const Initials: NgDocPage = {
6+
title: 'Initials',
7+
mdFile: './index.md',
8+
category: PipesCategory,
9+
demos: {
10+
InitialsDemoComponent,
11+
},
12+
};
13+
14+
export default Initials;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- snippet: basic-usage -->
2+
<p>{{ 'John Doe' | initials }}</p>
3+
<!-- Output: JD -->
4+
5+
<!-- snippet: single-name -->
6+
<p>{{ 'John' | initials }}</p>
7+
<!-- Output: JO -->
8+
9+
<!-- snippet: limit-1 -->
10+
<p>{{ 'John Doe' | initials: 1 }}</p>
11+
<!-- Output: J -->
12+
13+
<!-- snippet: limit-3 -->
14+
<p>{{ 'John Middle Doe' | initials: 3 }}</p>
15+
<!-- Output: JMD -->

projects/ngx-oneforall-lib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class UserService {
9797
| **Directives** | `auto-focus`, `click-outside`, `click-throttle`, `hover-class`, `infinite-scroll`, `numbers-only`, `press-enter`, `repeat`, `resized`, `shortcut`, `typed-template`, `visibility-change` |
9898
| **Guards** | `param`, `query-param`, `unsaved-changes` |
9999
| **Interceptors** | `base-url`, `cache`, `correlation-id`, `encryption`, `jwt`, `performance`, `timeout` |
100-
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |
100+
| **Pipes** | `bytes`, `call`, `first-error-key`, `highlight-search`, `initials`, `pluralize`, `range`, `safe-html`, `time-ago`, `truncate` |
101101
| **Rxjs** | `backoff-retry`, `catch-error-with-fallback`, `data-polling`, `debug`, `live-search`, `loading-status` |
102102
| **Services** | `cache`, `clipboard`, `cookie`, `device`, `event`, `jwt`, `logger`, `network-status`, `shortcut`, `storage` |
103103
| **Signals** | `breakpoint-matcher`, `debounced-signal`, `deep-computed`, `event-signal`, `interval-signal`, `route-param-signal`, `route-query-param-signal`, `router-event-signal`, `state-signal`, `storage-signal`, `throttled-signal`, `websocket-signal` |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "lib": { "entryFile": "src/public_api.ts" } }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { InitialsPipe } from './initials.pipe';
2+
3+
describe('InitialsPipe', () => {
4+
let pipe: InitialsPipe;
5+
6+
beforeEach(() => {
7+
pipe = new InitialsPipe();
8+
});
9+
10+
it('create an instance', () => {
11+
expect(pipe).toBeTruthy();
12+
});
13+
14+
it('should return empty string for null/undefined/empty input', () => {
15+
expect(pipe.transform(null)).toBe('');
16+
expect(pipe.transform(undefined)).toBe('');
17+
expect(pipe.transform('')).toBe('');
18+
});
19+
20+
it('should extract initials from multi-word string', () => {
21+
expect(pipe.transform('John Doe')).toBe('JD');
22+
expect(pipe.transform('John Middle Doe')).toBe('JM'); // Default limit 2
23+
});
24+
25+
it('should respect the limit param', () => {
26+
expect(pipe.transform('John Middle Doe', 3)).toBe('JMD');
27+
expect(pipe.transform('John Doe', 1)).toBe('J');
28+
});
29+
30+
it('should handle single word string', () => {
31+
expect(pipe.transform('John')).toBe('JO'); // Takes first 2 chars by default
32+
expect(pipe.transform('John', 1)).toBe('J');
33+
});
34+
35+
it('should handle extra whitespace', () => {
36+
expect(pipe.transform(' John Doe ')).toBe('JD');
37+
});
38+
});

0 commit comments

Comments
 (0)