Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Apply these rules to all new or modified Angular code:

- **Angular 21.x** with standalone components (no NgModules pattern)
- **TypeScript 5.9.3**
- **Bootstrap 5.3** + **Angular Material 21.x** (UI components)
- **Angular Material 21.x** (UI components). The library has no Bootstrap dependency: button directives and components keep their Bootstrap class names and ship their own CSS for them. Library CSS sits in the `ng-kit` cascade layer. The demo still loads Bootstrap 5.3 for its own layout.
- **RxJS 7.x**

### Package manager
Expand Down
4 changes: 2 additions & 2 deletions projects/ng-kit-demo/e2e/home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { expect, test } from '@playwright/test';
test('has success alert', async ({ page }) => {
await page.goto('/alert-demo');
await expect(page.getByText('Success Alert')).toHaveCount(2);
await expect(page.locator('.btn-close')).toHaveCount(2);
await expect(page.getByRole('button', { name: 'Close', exact: true })).toHaveCount(2);
});

test('has error alert', async ({ page }) => {
await page.goto('/alert-demo');
await expect(page.getByText('Error Alert')).toHaveCount(2);
await expect(page.locator('.btn-close')).toHaveCount(2);
await expect(page.getByRole('button', { name: 'Close', exact: true })).toHaveCount(2);
});
6 changes: 3 additions & 3 deletions projects/ng-kit-demo/src/app/pages/alert/alert.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ const alertTypesConfig = getAlertDemoConfig();
template: `
<doc-page title="Alert">
<p docLead>
Fully customizable, reusable alert component styled with Bootstrap CSS. Designed for standalone usage, tree-shakable imports, and
reactive state via Angular signals. Configure the alert type, visibility, dismiss behavior, and custom classes.
Fully customizable, reusable alert component with its own styles. Designed for standalone usage, tree-shakable imports, and reactive
state via Angular signals. Configure the alert type, visibility, dismiss behavior, and custom classes.
</p>

<div docOverview>
Expand Down Expand Up @@ -165,7 +165,7 @@ const alertTypesConfig = getAlertDemoConfig();
</tr>
</tbody>
</table>
<p class="api-note">Requires Bootstrap styles to be loaded for the alert to render correctly.</p>
<p class="api-note">No Bootstrap styles are required. The existing <code>alert</code> and <code>alert-*</code> classes remain.</p>
</div>
</doc-page>
`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ npm install`;
<ul>
<li><strong>Angular</strong>: Version 19 or later</li>
<li><strong>RxJS</strong>: Version 7.x</li>
<li><strong>Bootstrap</strong>: Version 5.x (if using)</li>
<li><strong>Bootstrap</strong>: Not required by the library</li>
<li><strong>Angular Material</strong>: Required for Material-based components</li>
</ul>
</section>
Expand Down Expand Up @@ -118,8 +118,12 @@ npm install`;
installed.
</li>
<li><strong>Clean install</strong>: Delete <code>node_modules</code> and lock files, then reinstall dependencies.</li>
<li><strong>Bootstrap styles</strong>: Verify Bootstrap CSS is properly imported if components appear unstyled.</li>
<li><strong>Library styles</strong>: Ensure the application allows component styles if buttons or alerts appear unstyled.</li>
<li><strong>Angular Material</strong>: Ensure Angular Material is installed if using Material-based components.</li>
<li>
<strong>CSS layers</strong>: If your application uses layers, declare <code>ng-kit</code> before your utility layer, for example
<code>&#64;layer components, ng-kit, utilities;</code> near the top of your stylesheet.
</li>
<li><strong>TypeScript errors</strong>: Ensure you're using a compatible TypeScript version (check Angular requirements).</li>
<li><strong>Update guide</strong>: Review the Angular Update Guide for breaking changes between versions.</li>
</ol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
<h1 class="page-title">Introduction</h1>
<p class="page-lead">
<strong>&#64;js-smart/ng-kit</strong> is a comprehensive collection of reusable Angular components, directives, and utilities built
with Angular Material and Bootstrap 5.x.
with Angular Material. The library provides its own button and alert styles without requiring Bootstrap.
</p>

<section class="page-section">
Expand All @@ -28,8 +28,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
<h2>Key features</h2>
<ul class="feature-list">
<li>
<strong>Low dependencies</strong> — only one third-party dependency (Bootstrap). Fewer dependencies reduce security risk, simplify
updates, and prevent version conflicts.
<strong>No Bootstrap dependency</strong> — button directives and alerts include their own styles, so consuming applications do not
need Bootstrap CSS.
</li>
<li>
<strong>Built for modern Angular</strong> — designed around Angular Signals and standalone components. Not a legacy port — built
Expand Down
25 changes: 12 additions & 13 deletions projects/ng-kit-demo/src/app/pages/spinner/spinner.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { SpinnerComponent } from '@js-smart/ng-kit';
selector: 'app-spinner-demo',
imports: [SpinnerComponent],
template: \`
<spinner [bootstrapSpinner]="false" [diameter]="40" color="accent" [strokeWidth]="4" />
<spinner [diameter]="40" color="accent" [strokeWidth]="4" />
\`,
})
export class SpinnerDemoComponent {}`;
Expand All @@ -23,7 +23,7 @@ import { SpinnerComponent } from '@js-smart/ng-kit';
selector: 'app-spinner-default',
imports: [SpinnerComponent],
template: \`
<spinner [bootstrapSpinner]="false" />
<spinner />
\`,
})
export class SpinnerDefaultComponent {}`;
Expand Down Expand Up @@ -56,17 +56,16 @@ const materialConfig = buildDemoConfig({
<doc-page title="Spinner">
<p docLead>
A thin wrapper around the Angular Material progress spinner. Provides a simple way to customize size, color, and stroke width, with
an optional Bootstrap spinner fallback.
an indeterminate loading indicator without requiring Bootstrap.
</p>

<div docOverview>
<section class="page-section">
<h2>Overview</h2>
<p>
Import <code>SpinnerComponent</code> and drop the <code>&lt;spinner&gt;</code> element into any standalone component. By default
<code>bootstrapSpinner</code> is <code>true</code>, which renders a Bootstrap spinner (requires Bootstrap CSS). Set
<code>bootstrapSpinner</code> to <code>false</code> to render the Angular Material <code>mat-spinner</code> instead, then tune
it with <code>diameter</code>, <code>color</code>, and <code>strokeWidth</code>.
The component always renders an indeterminate Angular Material progress spinner. Tune it with <code>diameter</code>,
<code>color</code>, and <code>strokeWidth</code>.
</p>
<ul>
<li>Tree-shakable: only the imported features are included in your bundle.</li>
Expand All @@ -81,20 +80,20 @@ const materialConfig = buildDemoConfig({
title="Material spinner"
anchorId="material-spinner"
description="The Angular Material spinner with default diameter, primary color, and default stroke width."
[props]="['bootstrapSpinner']"
[props]="[]"
[code]="defaultCode"
[stackblitz]="defaultConfig">
<spinner [bootstrapSpinner]="false" />
<spinner />
</demo-card>

<demo-card
title="Custom size, color &amp; stroke"
anchorId="custom-size-color-stroke"
description="Tune the Material spinner with a smaller diameter, an accent color, and a thinner stroke."
[props]="['bootstrapSpinner', 'diameter', 'color', 'strokeWidth']"
[props]="['diameter', 'color', 'strokeWidth']"
[code]="materialCode"
[stackblitz]="materialConfig">
<spinner [bootstrapSpinner]="false" [diameter]="40" color="accent" [strokeWidth]="4" />
<spinner [diameter]="40" color="accent" [strokeWidth]="4" />
</demo-card>
</div>

Expand All @@ -117,7 +116,7 @@ const materialConfig = buildDemoConfig({
<td><code>bootstrapSpinner</code></td>
<td><code>boolean</code></td>
<td><code>true</code></td>
<td>Use the Bootstrap spinner when <code>true</code>, otherwise the Angular Material spinner</td>
<td>Deprecated compatibility input; has no effect</td>
</tr>
<tr>
<td><code>diameter</code></td>
Expand All @@ -140,8 +139,8 @@ const materialConfig = buildDemoConfig({
</tbody>
</table>
<p class="api-note">
Set <code>bootstrapSpinner</code> to <code>false</code> to use the Angular Material spinner. The Bootstrap variant requires
Bootstrap CSS to be present in your application.
The default changed from a Bootstrap spinner to an Angular Material spinner. Existing
<code>[bootstrapSpinner]</code> bindings still compile but do not change the result.
</p>
</div>
</doc-page>
Expand Down
119 changes: 119 additions & 0 deletions projects/ng-kit/src/lib/components/alert/alert.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Alert styles: the Bootstrap 5 alert look, without Bootstrap. The existing class names are scoped to this component's
* host. Colours use Bootstrap's CSS variables when present and fall back to Bootstrap 5's defaults otherwise.
*/
@layer ng-kit {
:is(alert, lib-alert) > .row {
display: flex;
flex-wrap: wrap;
margin-inline: -0.75rem;
}

:is(alert, lib-alert) > .row > .col-xs-12 {
box-sizing: border-box;
width: 100%;
margin-inline: auto;
padding-inline: 0.75rem;
}

:is(alert, lib-alert) .alert {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 100%;
margin-bottom: 1rem;
padding: 1rem 3rem 1rem 1rem;
line-height: 1.5;
color: var(--ngk-alert-color);
background-color: var(--ngk-alert-bg);
border: 1px solid var(--ngk-alert-border-color);
border-radius: 0.375rem;
}

/* Full width on small screens, shrink-wrapped and centred from 768px up */
@media (width >= 768px) {
:is(alert, lib-alert) > .row > .col-md-auto {
width: auto;
}
}

:is(alert, lib-alert) .alert-primary {
--ngk-alert-color: var(--bs-primary-text-emphasis, #052c65);
--ngk-alert-bg: var(--bs-primary-bg-subtle, #cfe2ff);
--ngk-alert-border-color: var(--bs-primary-border-subtle, #9ec5fe);
}

:is(alert, lib-alert) .alert-secondary {
--ngk-alert-color: var(--bs-secondary-text-emphasis, #2b2f32);
--ngk-alert-bg: var(--bs-secondary-bg-subtle, #e2e3e5);
--ngk-alert-border-color: var(--bs-secondary-border-subtle, #c4c8cb);
}

:is(alert, lib-alert) .alert-success {
--ngk-alert-color: var(--bs-success-text-emphasis, #0a3622);
--ngk-alert-bg: var(--bs-success-bg-subtle, #d1e7dd);
--ngk-alert-border-color: var(--bs-success-border-subtle, #a3cfbb);
}

:is(alert, lib-alert) .alert-info {
--ngk-alert-color: var(--bs-info-text-emphasis, #055160);
--ngk-alert-bg: var(--bs-info-bg-subtle, #cff4fc);
--ngk-alert-border-color: var(--bs-info-border-subtle, #9eeaf9);
}

:is(alert, lib-alert) .alert-warning {
--ngk-alert-color: var(--bs-warning-text-emphasis, #664d03);
--ngk-alert-bg: var(--bs-warning-bg-subtle, #fff3cd);
--ngk-alert-border-color: var(--bs-warning-border-subtle, #ffe69c);
}

:is(alert, lib-alert) .alert-danger {
--ngk-alert-color: var(--bs-danger-text-emphasis, #58151c);
--ngk-alert-bg: var(--bs-danger-bg-subtle, #f8d7da);
--ngk-alert-border-color: var(--bs-danger-border-subtle, #f1aeb5);
}

:is(alert, lib-alert) .alert-light {
--ngk-alert-color: var(--bs-light-text-emphasis, #495057);
--ngk-alert-bg: var(--bs-light-bg-subtle, #fcfcfd);
--ngk-alert-border-color: var(--bs-light-border-subtle, #e9ecef);
}

:is(alert, lib-alert) .alert-dark {
--ngk-alert-color: var(--bs-dark-text-emphasis, #495057);
--ngk-alert-bg: var(--bs-dark-bg-subtle, #ced4da);
--ngk-alert-border-color: var(--bs-dark-border-subtle, #adb5bd);
}

:is(alert, lib-alert) .btn-close {
position: absolute;
top: 0;
right: 0;
z-index: 2;
box-sizing: content-box;
width: 1em;
height: 1em;
margin: 0;
padding: 1.25rem 1rem;
font: inherit;
cursor: pointer;
background: transparent
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e")
center / 1em auto no-repeat;
border: 0;
border-radius: 0.375rem;
opacity: 0.5;

&:hover {
opacity: 0.75;
}

&:focus-visible {
outline: 0;
box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%);
opacity: 1;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="alert alert-{{ type() }} alert-dismissible alert_div" role="alert" aria-live="polite">
<ng-content></ng-content>
@if (dismissible()) {
<button (click)="closeAlert()" aria-label="Close" class="btn-close" data-bs-dismiss="alert" type="button"></button>
<button (click)="closeAlert()" aria-label="Close" class="btn-close" type="button"></button>
}
</div>
</div>
Expand Down
5 changes: 0 additions & 5 deletions projects/ng-kit/src/lib/components/alert/alert.component.scss

This file was deleted.

Loading
Loading