Add comprehensive component library and demo application
Added extensive component library with feedback components (empty state, loading spinner, skeleton loader), enhanced form components (autocomplete, date picker, file upload, form field, time picker), navigation components (pagination), and overlay components (backdrop, drawer, modal, overlay container). Updated demo application with comprehensive showcase components and enhanced styling throughout the project. Excluded font files from repository to reduce size. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,584 @@
|
||||
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ProgressBarComponent } from '../../../../../ui-essentials/src/lib/components/data-display/progress';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-progress-demo',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ProgressBarComponent
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div style="padding: 2rem;">
|
||||
<h2>Progress Bar Component Showcase</h2>
|
||||
|
||||
<!-- Basic Progress Bars -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Basic Progress Bars</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 2rem;">
|
||||
<ui-progress-bar
|
||||
label="Small Progress"
|
||||
size="sm"
|
||||
[progress]="25"
|
||||
helperText="25% complete">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Medium Progress"
|
||||
size="md"
|
||||
[progress]="50"
|
||||
helperText="50% complete">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Large Progress"
|
||||
size="lg"
|
||||
[progress]="75"
|
||||
helperText="75% complete">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Progress Bar Variants -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Color Variants</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 2rem;">
|
||||
<ui-progress-bar
|
||||
label="Primary Progress"
|
||||
variant="primary"
|
||||
[progress]="60"
|
||||
helperText="Default brand color">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Secondary Progress"
|
||||
variant="secondary"
|
||||
[progress]="45"
|
||||
helperText="Secondary color variant">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Success Progress"
|
||||
variant="success"
|
||||
[progress]="80"
|
||||
helperText="Indicates successful completion">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Warning Progress"
|
||||
variant="warning"
|
||||
[progress]="30"
|
||||
helperText="Indicates caution or attention needed">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Danger Progress"
|
||||
variant="danger"
|
||||
[progress]="15"
|
||||
helperText="Indicates error or critical state">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Progress Bar Types -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Progress Bar Types</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 2rem;">
|
||||
<ui-progress-bar
|
||||
label="Determinate Progress"
|
||||
progressType="determinate"
|
||||
[progress]="determinateProgress()"
|
||||
helperText="Shows exact progress value">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Indeterminate Progress"
|
||||
progressType="indeterminate"
|
||||
variant="primary"
|
||||
helperText="Shows ongoing activity without specific progress">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Buffer Progress"
|
||||
progressType="buffer"
|
||||
[progress]="bufferProgress()"
|
||||
[buffer]="bufferValue()"
|
||||
variant="secondary"
|
||||
helperText="Shows buffered content loading">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Striped and Animated -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Striped and Animated Progress Bars</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 2rem;">
|
||||
<ui-progress-bar
|
||||
label="Striped Progress"
|
||||
[progress]="65"
|
||||
[striped]="true"
|
||||
helperText="With striped pattern">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Animated Striped Progress"
|
||||
[progress]="55"
|
||||
[striped]="true"
|
||||
[animated]="true"
|
||||
variant="success"
|
||||
helperText="With animated stripes">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Animated Indeterminate"
|
||||
progressType="indeterminate"
|
||||
[animated]="true"
|
||||
variant="primary"
|
||||
helperText="Smooth indeterminate animation">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- States -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Progress Bar States</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 2rem;">
|
||||
<ui-progress-bar
|
||||
label="Normal State"
|
||||
[progress]="70"
|
||||
helperText="Default active state">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
label="Disabled State"
|
||||
[progress]="40"
|
||||
state="disabled"
|
||||
helperText="Disabled progress bar">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Size Comparison -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Size and Variant Combinations</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; max-width: 1200px;">
|
||||
|
||||
<!-- Small sizes -->
|
||||
<div>
|
||||
<h4>Small Size Variants</h4>
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<ui-progress-bar size="sm" variant="primary" [progress]="25" label="Primary Small"></ui-progress-bar>
|
||||
<ui-progress-bar size="sm" variant="success" [progress]="50" label="Success Small"></ui-progress-bar>
|
||||
<ui-progress-bar size="sm" variant="warning" [progress]="75" label="Warning Small"></ui-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Large sizes -->
|
||||
<div>
|
||||
<h4>Large Size Variants</h4>
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<ui-progress-bar size="lg" variant="primary" [progress]="85" label="Primary Large"></ui-progress-bar>
|
||||
<ui-progress-bar size="lg" variant="success" [progress]="60" label="Success Large"></ui-progress-bar>
|
||||
<ui-progress-bar size="lg" variant="danger" [progress]="35" label="Danger Large"></ui-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Without Labels -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Progress Bars without Labels</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 1.5rem;">
|
||||
<ui-progress-bar
|
||||
[progress]="90"
|
||||
variant="success"
|
||||
[showLabel]="false"
|
||||
ariaLabel="File upload progress">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
progressType="indeterminate"
|
||||
variant="primary"
|
||||
[showLabel]="false"
|
||||
ariaLabel="Loading content">
|
||||
</ui-progress-bar>
|
||||
<ui-progress-bar
|
||||
[progress]="45"
|
||||
[buffer]="70"
|
||||
progressType="buffer"
|
||||
variant="secondary"
|
||||
[showLabel]="false"
|
||||
ariaLabel="Video buffering progress">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Interactive Demo -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Interactive Demo</h3>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 2rem; margin-bottom: 2rem;">
|
||||
|
||||
<!-- Controls -->
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem; min-width: 200px;">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">Progress Value:</label>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
[(ngModel)]="demoProgress"
|
||||
style="width: 100%;">
|
||||
<span style="font-size: 0.875rem; color: #666;">{{ demoProgress }}%</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">Buffer Value:</label>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
[(ngModel)]="demoBuffer"
|
||||
style="width: 100%;">
|
||||
<span style="font-size: 0.875rem; color: #666;">{{ demoBuffer }}%</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">Size:</label>
|
||||
<select [(ngModel)]="demoSize" style="padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; width: 100%;">
|
||||
<option value="sm">Small</option>
|
||||
<option value="md">Medium</option>
|
||||
<option value="lg">Large</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">Variant:</label>
|
||||
<select [(ngModel)]="demoVariant" style="padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; width: 100%;">
|
||||
<option value="primary">Primary</option>
|
||||
<option value="secondary">Secondary</option>
|
||||
<option value="success">Success</option>
|
||||
<option value="warning">Warning</option>
|
||||
<option value="danger">Danger</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">Type:</label>
|
||||
<select [(ngModel)]="demoType" style="padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; width: 100%;">
|
||||
<option value="determinate">Determinate</option>
|
||||
<option value="indeterminate">Indeterminate</option>
|
||||
<option value="buffer">Buffer</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">Options:</label>
|
||||
<div style="display: flex; flex-direction: column; gap: 0.25rem;">
|
||||
<label style="font-size: 0.875rem;">
|
||||
<input type="checkbox" [(ngModel)]="demoStriped" style="margin-right: 0.5rem;">
|
||||
Striped
|
||||
</label>
|
||||
<label style="font-size: 0.875rem;">
|
||||
<input type="checkbox" [(ngModel)]="demoAnimated" style="margin-right: 0.5rem;">
|
||||
Animated
|
||||
</label>
|
||||
<label style="font-size: 0.875rem;">
|
||||
<input type="checkbox" [(ngModel)]="demoShowPercentage" style="margin-right: 0.5rem;">
|
||||
Show Percentage
|
||||
</label>
|
||||
<label style="font-size: 0.875rem;">
|
||||
<input type="checkbox" [(ngModel)]="demoDisabled" style="margin-right: 0.5rem;">
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 1rem;">
|
||||
<button (click)="startDemo()" style="padding: 0.5rem 1rem; border: 1px solid #007bff; border-radius: 4px; background: #007bff; color: white; cursor: pointer; flex: 1;">
|
||||
Start Demo
|
||||
</button>
|
||||
<button (click)="resetDemo()" style="padding: 0.5rem 1rem; border: 1px solid #ccc; border-radius: 4px; background: white; cursor: pointer; flex: 1;">
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live Preview -->
|
||||
<div style="flex: 1; min-width: 300px;">
|
||||
<h4>Live Preview:</h4>
|
||||
<div style="padding: 2rem; border: 2px dashed #ccc; border-radius: 8px; background: #fafafa;">
|
||||
<ui-progress-bar
|
||||
[size]="demoSize"
|
||||
[variant]="demoVariant"
|
||||
[progressType]="demoType"
|
||||
[progress]="demoProgress"
|
||||
[buffer]="demoBuffer"
|
||||
[striped]="demoStriped"
|
||||
[animated]="demoAnimated"
|
||||
[showPercentage]="demoShowPercentage"
|
||||
[state]="demoDisabled ? 'disabled' : 'default'"
|
||||
label="Interactive Demo Progress"
|
||||
helperText="Customize the progress bar using the controls">
|
||||
</ui-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Usage Examples -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Usage Examples</h3>
|
||||
<div style="background: #f8f9fa; padding: 1.5rem; border-radius: 8px; border-left: 4px solid #007bff;">
|
||||
<h4>Basic Progress Bar:</h4>
|
||||
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code><ui-progress-bar
|
||||
label="Download Progress"
|
||||
size="md"
|
||||
variant="primary"
|
||||
[progress]="75"
|
||||
helperText="3 of 4 files downloaded">
|
||||
</ui-progress-bar></code></pre>
|
||||
|
||||
<h4>Indeterminate Progress:</h4>
|
||||
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code><ui-progress-bar
|
||||
label="Loading..."
|
||||
progressType="indeterminate"
|
||||
variant="primary"
|
||||
[animated]="true"
|
||||
helperText="Please wait while we process your request">
|
||||
</ui-progress-bar></code></pre>
|
||||
|
||||
<h4>Buffer Progress:</h4>
|
||||
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code><ui-progress-bar
|
||||
label="Video Loading"
|
||||
progressType="buffer"
|
||||
[progress]="currentProgress"
|
||||
[buffer]="bufferProgress"
|
||||
variant="secondary"
|
||||
helperText="Buffering video content">
|
||||
</ui-progress-bar></code></pre>
|
||||
|
||||
<h4>Striped Animated Progress:</h4>
|
||||
<pre style="background: #fff; padding: 1rem; border-radius: 4px; overflow-x: auto;"><code><ui-progress-bar
|
||||
label="Upload Progress"
|
||||
[progress]="uploadPercent"
|
||||
variant="success"
|
||||
[striped]="true"
|
||||
[animated]="true"
|
||||
size="lg"
|
||||
helperText="Uploading files...">
|
||||
</ui-progress-bar></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Programmatic Control Example -->
|
||||
<section style="margin-bottom: 3rem;">
|
||||
<h3>Programmatic Control Example</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<ui-progress-bar
|
||||
#programmableProgress
|
||||
label="File Processing"
|
||||
[progress]="programmableValue()"
|
||||
variant="success"
|
||||
helperText="Processing files..."
|
||||
size="lg">
|
||||
</ui-progress-bar>
|
||||
|
||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||
<button (click)="simulateProgress()" style="padding: 0.5rem 1rem; border: 1px solid #28a745; border-radius: 4px; background: #28a745; color: white; cursor: pointer;">
|
||||
Simulate Progress
|
||||
</button>
|
||||
<button (click)="pauseProgress()" style="padding: 0.5rem 1rem; border: 1px solid #ffc107; border-radius: 4px; background: #ffc107; color: black; cursor: pointer;">
|
||||
Pause
|
||||
</button>
|
||||
<button (click)="resetProgrammable()" style="padding: 0.5rem 1rem; border: 1px solid #6c757d; border-radius: 4px; background: #6c757d; color: white; cursor: pointer;">
|
||||
Reset
|
||||
</button>
|
||||
<button (click)="completeProgress()" style="padding: 0.5rem 1rem; border: 1px solid #007bff; border-radius: 4px; background: #007bff; color: white; cursor: pointer;">
|
||||
Complete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 0.875rem; color: #666;">
|
||||
<strong>Current Progress:</strong> {{ programmableValue() }}% |
|
||||
<strong>Status:</strong> {{ progressStatus() }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
h2 {
|
||||
color: hsl(279, 14%, 11%);
|
||||
font-size: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
border-bottom: 2px solid hsl(258, 100%, 47%);
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: hsl(279, 14%, 25%);
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: hsl(287, 12%, 35%);
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
section {
|
||||
border: 1px solid hsl(289, 14%, 90%);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
background: hsl(286, 20%, 99%);
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
color: #d63384;
|
||||
}
|
||||
|
||||
select, input[type="checkbox"], input[type="range"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class ProgressDemoComponent {
|
||||
|
||||
// Animated progress values
|
||||
determinateProgress = signal(35);
|
||||
bufferProgress = signal(25);
|
||||
bufferValue = signal(65);
|
||||
|
||||
// Interactive demo properties
|
||||
demoProgress = 60;
|
||||
demoBuffer = 80;
|
||||
demoSize: 'sm' | 'md' | 'lg' = 'md';
|
||||
demoVariant: 'primary' | 'secondary' | 'success' | 'warning' | 'danger' = 'primary';
|
||||
demoType: 'determinate' | 'indeterminate' | 'buffer' = 'determinate';
|
||||
demoStriped = false;
|
||||
demoAnimated = false;
|
||||
demoShowPercentage = true;
|
||||
demoDisabled = false;
|
||||
|
||||
// Programmable progress
|
||||
programmableValue = signal(0);
|
||||
progressStatus = signal('Ready');
|
||||
private progressInterval: any;
|
||||
private isPaused = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
// Start some animated progress bars
|
||||
this.animateProgress();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.progressInterval) {
|
||||
clearInterval(this.progressInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private animateProgress(): void {
|
||||
// Animate the determinate progress
|
||||
setInterval(() => {
|
||||
const current = this.determinateProgress();
|
||||
const next = (current + 1) % 101;
|
||||
this.determinateProgress.set(next);
|
||||
}, 100);
|
||||
|
||||
// Animate the buffer progress
|
||||
setInterval(() => {
|
||||
const currentBuffer = this.bufferValue();
|
||||
const currentProgress = this.bufferProgress();
|
||||
|
||||
const nextBuffer = (currentBuffer + 0.5) % 101;
|
||||
const nextProgress = Math.min(nextBuffer - 20, 100);
|
||||
|
||||
this.bufferValue.set(nextBuffer);
|
||||
this.bufferProgress.set(Math.max(0, nextProgress));
|
||||
}, 150);
|
||||
}
|
||||
|
||||
startDemo(): void {
|
||||
this.demoProgress = 0;
|
||||
let progress = 0;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
progress += Math.random() * 3;
|
||||
this.demoProgress = Math.min(100, progress);
|
||||
|
||||
if (progress >= 100) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
resetDemo(): void {
|
||||
this.demoProgress = 0;
|
||||
this.demoBuffer = 0;
|
||||
this.demoSize = 'md';
|
||||
this.demoVariant = 'primary';
|
||||
this.demoType = 'determinate';
|
||||
this.demoStriped = false;
|
||||
this.demoAnimated = false;
|
||||
this.demoShowPercentage = true;
|
||||
this.demoDisabled = false;
|
||||
}
|
||||
|
||||
simulateProgress(): void {
|
||||
if (this.progressInterval) {
|
||||
clearInterval(this.progressInterval);
|
||||
}
|
||||
|
||||
this.isPaused = false;
|
||||
this.progressStatus.set('Processing...');
|
||||
|
||||
this.progressInterval = setInterval(() => {
|
||||
if (!this.isPaused) {
|
||||
const current = this.programmableValue();
|
||||
if (current < 100) {
|
||||
this.programmableValue.set(current + Math.random() * 2);
|
||||
} else {
|
||||
this.progressStatus.set('Complete!');
|
||||
clearInterval(this.progressInterval);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
pauseProgress(): void {
|
||||
this.isPaused = !this.isPaused;
|
||||
this.progressStatus.set(this.isPaused ? 'Paused' : 'Processing...');
|
||||
}
|
||||
|
||||
resetProgrammable(): void {
|
||||
if (this.progressInterval) {
|
||||
clearInterval(this.progressInterval);
|
||||
}
|
||||
this.programmableValue.set(0);
|
||||
this.progressStatus.set('Ready');
|
||||
this.isPaused = false;
|
||||
}
|
||||
|
||||
completeProgress(): void {
|
||||
if (this.progressInterval) {
|
||||
clearInterval(this.progressInterval);
|
||||
}
|
||||
this.programmableValue.set(100);
|
||||
this.progressStatus.set('Complete!');
|
||||
this.isPaused = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user