This repository has been archived by the owner on Dec 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 401
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a "Styling" page to each component that tells users how to customize the component's styles.
- Loading branch information
Showing
12 changed files
with
357 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
@let item = docItem | async; | ||
@let data = dataStream | async; | ||
@let example = exampleStream | async; | ||
@let hasData = hasDataStream | async; | ||
|
||
@if (!item || !data) { | ||
Loading... | ||
} @else if (!hasData) { | ||
This component does not support style overrides | ||
} @else { | ||
<h2 class="cdk-visually-hidden" tabindex="-1">How to style {{item.id}}</h2> | ||
Styles from the <code>{{item.packageName}}/{{item.id}}</code> package can be customized using | ||
@if (data.length === 1) { | ||
the <code>{{data[0].overridesMixin}}</code> mixin. | ||
} @else { | ||
the @for (current of data; track current.name) {{{$last ? ' and ' : ($first ? '' : ', ')}}<code>{{current.overridesMixin}}</code>} mixins. | ||
} | ||
{{data.length === 1 ? 'This mixin accepts' : 'These mixins accept'}} a set of tokens that control how the components will look, either for the entire app or under a specific selector. {{example ? 'For example:' : ''}} | ||
|
||
@if (example) { | ||
<div class="docs-markdown"> | ||
<pre>{{example}}</pre> | ||
</div> | ||
} | ||
|
||
You can find the full list of supported mixins and tokens below. | ||
|
||
<div class="docs-markdown"> | ||
@for (current of data; track current.name) { | ||
<h3>Tokens supported by <code>{{current.overridesMixin}}</code></h3> | ||
<token-table [tokens]="current.tokens"/> | ||
} | ||
</div> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import {Component, inject, Injectable} from '@angular/core'; | ||
import {HttpClient} from '@angular/common/http'; | ||
import {AsyncPipe} from '@angular/common'; | ||
import {Observable} from 'rxjs'; | ||
import {map, shareReplay, switchMap} from 'rxjs/operators'; | ||
import {ComponentViewer} from './component-viewer'; | ||
import {DocItem} from '../../shared/documentation-items/documentation-items'; | ||
import {Token, TokenTable} from './token-table'; | ||
|
||
interface StyleOverridesData { | ||
name: string; | ||
overridesMixin: string; | ||
tokens: Token[]; | ||
} | ||
|
||
@Injectable({providedIn: 'root'}) | ||
class TokenService { | ||
private _cache: Record<string, Observable<StyleOverridesData[]>> = {}; | ||
|
||
constructor(private _http: HttpClient) {} | ||
|
||
getTokenData(item: DocItem): Observable<StyleOverridesData[]> { | ||
const url = `/docs-content/tokens/${item.packageName}/${item.id}/${item.id}.json`; | ||
|
||
if (this._cache[url]) { | ||
return this._cache[url]; | ||
} | ||
|
||
const stream = this._http.get<StyleOverridesData[]>(url).pipe(shareReplay(1)); | ||
this._cache[url] = stream; | ||
return stream; | ||
} | ||
} | ||
|
||
@Component({ | ||
selector: 'component-styling', | ||
templateUrl: './component-styling.html', | ||
standalone: true, | ||
imports: [AsyncPipe, TokenTable], | ||
}) | ||
export class ComponentStyling { | ||
private componentViewer = inject(ComponentViewer); | ||
private tokenService = inject(TokenService); | ||
protected docItem = this.componentViewer.componentDocItem; | ||
protected dataStream = | ||
this.docItem.pipe(switchMap(item => this.tokenService.getTokenData(item))); | ||
protected hasDataStream = this.dataStream.pipe( | ||
map(data => data.length > 0 && data.some(d => d.tokens.length > 0))); | ||
|
||
protected exampleStream = this.dataStream.pipe(map(data => { | ||
const mixin = data.find(d => d.tokens.length > 0); | ||
|
||
if (!mixin) { | ||
return null; | ||
} | ||
|
||
// Pick out a couple of color tokens to show as examples. | ||
const firstToken = mixin.tokens.find(token => token.type === 'color'); | ||
const secondToken = mixin.tokens.find(token => token.type === 'color' && token !== firstToken); | ||
|
||
if (!firstToken) { | ||
return null; | ||
} | ||
|
||
const lines = [ | ||
`@use '@angular/material' as mat;`, | ||
``, | ||
`// Customize the entire app. Change :root to your selector if you want to scope the styles.`, | ||
`:root {`, | ||
` @include mat.${mixin.overridesMixin}((`, | ||
` ${firstToken.overridesName}: orange,`, | ||
...(secondToken ? [` ${secondToken.overridesName}: red,`] : []), | ||
` ));`, | ||
`}`, | ||
]; | ||
|
||
return lines.join('\n'); | ||
})); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import {Component, input, inject} from '@angular/core'; | ||
import {MatIconButton} from '@angular/material/button'; | ||
import {Clipboard} from '@angular/cdk/clipboard'; | ||
import {MatIcon} from '@angular/material/icon'; | ||
import {MatSnackBar} from '@angular/material/snack-bar'; | ||
import {MatTooltip} from '@angular/material/tooltip'; | ||
|
||
@Component({ | ||
selector: 'token-name', | ||
standalone: true, | ||
template: ` | ||
<code>{{name()}}</code> | ||
<button | ||
mat-icon-button | ||
matTooltip="Copy name to the clipboard" | ||
(click)="copy(name())"> | ||
<mat-icon>content_copy</mat-icon> | ||
</button> | ||
`, | ||
styles: ` | ||
:host { | ||
display: flex; | ||
align-items: center; | ||
button { | ||
margin-left: 8px; | ||
} | ||
} | ||
`, | ||
imports: [MatIconButton, MatIcon, MatTooltip], | ||
}) | ||
export class TokenName { | ||
private clipboard = inject(Clipboard); | ||
private snackbar = inject(MatSnackBar); | ||
|
||
name = input.required<string>(); | ||
|
||
protected copy(name: string): void { | ||
const message = this.clipboard.copy(name) ? 'Copied token name' : 'Failed to copy token name'; | ||
this.snackbar.open(message, undefined, {duration: 2500}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<div class="filters"> | ||
<mat-form-field class="name-field" subscriptSizing="dynamic" appearance="outline"> | ||
<mat-label>Filter by name</mat-label> | ||
<input | ||
#nameInput | ||
matInput | ||
[value]="nameFilter()" | ||
(input)="nameFilter.set(nameInput.value)"/> | ||
</mat-form-field> | ||
|
||
<mat-form-field subscriptSizing="dynamic" appearance="outline"> | ||
<mat-label>Filter by type</mat-label> | ||
<mat-select (selectionChange)="typeFilter.set($event.value)"> | ||
@for (type of types; track $index) { | ||
<mat-option [value]="type">{{type | titlecase}}</mat-option> | ||
} | ||
</mat-select> | ||
</mat-form-field> | ||
|
||
<button mat-button (click)="reset()">Reset filters</button> | ||
</div> | ||
|
||
<div class="docs-markdown"> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Name</th> | ||
<th class="type-header">Type</th> | ||
<th class="system-header">Based on system token</th> | ||
</tr> | ||
</thead> | ||
|
||
<tbody> | ||
@for (token of filteredTokens(); track token.overridesName) { | ||
<tr> | ||
<td><token-name [name]="token.overridesName"/></td> | ||
<td>{{token.type | titlecase}}</td> | ||
<td> | ||
@if (token.derivedFrom) { | ||
<token-name [name]="token.derivedFrom"/> | ||
} @else { | ||
None | ||
} | ||
</td> | ||
</tr> | ||
} @empty { | ||
<tr> | ||
<td>No tokens match the current set of filters</td> | ||
</tr> | ||
} | ||
</tbody> | ||
</table> | ||
</div> |
Oops, something went wrong.