Sitelet https://github.com/sdcorejs/sdcorejs-angular/pull/73/files
Skip to content
Merged
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
1,261 changes: 1,261 additions & 0 deletions .sdcorejs/architecture/angular/2026-10-01-15-55-icon-shape-option.md

Large diffs are not rendered by default.

1,268 changes: 1,268 additions & 0 deletions .sdcorejs/architecture/angular/2026-10-01-16-44-icon-shape-option.md

Large diffs are not rendered by default.

543 changes: 543 additions & 0 deletions .sdcorejs/docs/angular/2026-10-01-15-39-icon-shape-option-spec.md

Large diffs are not rendered by default.

4,564 changes: 4,564 additions & 0 deletions .sdcorejs/docs/angular/2026-10-01-17-05-icon-shape-option-plan.md

Large diffs are not rendered by default.

4,575 changes: 4,575 additions & 0 deletions .sdcorejs/docs/angular/2026-10-01-17-58-icon-shape-option-plan.md

Large diffs are not rendered by default.

4,603 changes: 4,603 additions & 0 deletions .sdcorejs/docs/angular/2026-10-01-18-04-icon-shape-option-plan.md

Large diffs are not rendered by default.

5,795 changes: 5,795 additions & 0 deletions .sdcorejs/docs/angular/2026-10-01-23-16-icon-shape-option-plan.md

Large diffs are not rendered by default.

1,222 changes: 1,222 additions & 0 deletions .sdcorejs/docs/architecture/2026-10-01-15-55-icon-shape-option-architecture.md

Large diffs are not rendered by default.

1,231 changes: 1,231 additions & 0 deletions .sdcorejs/docs/architecture/2026-10-01-16-44-icon-shape-option-architecture.md

Large diffs are not rendered by default.

4,778 changes: 4,778 additions & 0 deletions .sdcorejs/plans/angular/2026-10-01-17-05-icon-shape-option.md

Large diffs are not rendered by default.

4,790 changes: 4,790 additions & 0 deletions .sdcorejs/plans/angular/2026-10-01-17-58-icon-shape-option.md

Large diffs are not rendered by default.

4,823 changes: 4,823 additions & 0 deletions .sdcorejs/plans/angular/2026-10-01-18-04-icon-shape-option.md

Large diffs are not rendered by default.

6,122 changes: 6,122 additions & 0 deletions .sdcorejs/plans/angular/2026-10-01-23-16-icon-shape-option.md

Large diffs are not rendered by default.

597 changes: 597 additions & 0 deletions .sdcorejs/specs/angular/2026-10-01-15-39-icon-shape-option.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ Planned release suffix `3.0` targets `19.3.0`, `20.3.0`, `21.3.0`, and `22.3.0`,
- Form builder: `<sd-form-builder>` is an embedded designer — toolbar Design / Preview / Schema, **Desktop / Tablet / Mobile** and undo / redo; presets palette (Text, Email, Phone, URL, Password, Integer, Decimal, Currency, Percent are `subtype` on `textfield` / `number`); canvas with in-place groups; inspector. Resize and the *Layout* tab write the span of the level in view and show the inherited value (*Same as Desktop* / *Default*); *Start a new row* sets `layout.newRow`. Drag and drop (Pointer Events, one planner for the indicator and the commit), 100-step undo, key rename that updates every structured reference (rules, catalog params/fill, upload params, html query, form validations), Undo toast after a delete, and keyboard paths for every drag. Preview is the real renderer forced to the level in view (tablet 768 px, mobile 390 px frames). Elements of an unknown `type` are kept unchanged and shown as unsupported.
- Form render: `[(value)]` model, `[variables]`, `[breakpoint]`, `[keys]`, `labelPlacement` (`'top'` default, `'float'`), `validate()` and `upload()`; rules and form validations evaluate `Filter` against `{ ...value, ...variables }`; select / radio options from a static list or a portal catalog (`load`, optional `search` and `labels`) with `params` and `fill`. `SdFormRenderService.viewEntities(schema, entities)` formats values for lists.
- Forms: `labelPlacement: 'float' | 'top'` (type `SdLabelPlacement` in `@sdcorejs/angular/forms/models`) on `sd-input`, `sd-input-number`, `sd-textarea`, `sd-select`, `sd-date`, `sd-datetime`, `sd-chip` and `sd-chip-calendar` — default `'float'`, so existing screens are unchanged. `sd-label` gains `for` and `labelId`. `sd-input` accepts `type="tel" | "url"`, `inputmode` and `autocomplete`; `sd-input-number` accepts `inputmode`; `sd-textarea` accepts `minlength`.
- Icon tile shape (`@sdcorejs/angular/modules/icon`): new type `SdIconShape = 'square' | 'circle' | 'none'` for the decorative icon tile of `<sd-section>` (header), `<sd-inform>`, `<sd-data-state>`, `SdNotifyService` toasts and `SdConfirmService` dialogs. Set it per instance with the `iconShape` input (`sd-section`, `sd-inform`, `sd-data-state`) or the `iconShape` option (`success`/`info`/`warning`/`error`, and all six confirm methods), or app-wide with `provideSdIcon({ defaultShape })`; the instance value wins. `none` drops the background but keeps the tile size. Each tile carries `data-icon-shape`, and the CSS custom property `--sd-icon-shape-radius` (default `var(--sd-radius-8, 8px)`) sets the square radius.

### Changed
- Icon tiles of `<sd-section>`, `<sd-inform>`, `<sd-data-state>`, notify toasts and confirm dialogs are square with an 8px radius instead of round. Keep the round tiles app-wide with `provideSdIcon({ defaultShape: 'circle' })`, or per instance with `iconShape="circle"`. The inform tip variant stays flat and transparent. `ISdIconResolvedConfiguration` now includes `defaultShape`: build hand-made `SD_ICON_CONFIGURATION` values with `resolveSdIconConfig(...)`, which fills it.
- API handler selection: when several handlers match a URL, the one with the **longest** matching host prefix (origin plus path) wins. It used to be the first match in registration order, so when libraries share a gateway and differ by context path, provider order decided which handler received a request. Now `https://gw.example/bpm` wins over `https://gw.example` in either order; equally long prefixes keep registration order, with root providers first. If a broad host listed first was shadowing a more specific one on purpose, remove the broad host from that handler. Host matching itself (origin plus segment-aware path, blank hosts match nothing) is unchanged. `@sd-angular/core` 19.0.41 ships the same registry API and the same longest-prefix rule, but it keeps a raw `url.startsWith(host)` match.
- `@sdcorejs/utils` is upgraded from `1.1.4` to `1.2.5` on every line (a runtime dependency of the package, pinned exactly). Consumers that import `@sdcorejs/utils` directly get the 1.2 contract: see its `MIGRATION-1.2.md`. The notable changes are that `getNestedValue` returns `T | undefined` and rejects property paths with whitespace, `normalizeAsync` returns a structural subscribable rather than an RxJS `Observable`, `BrowserUtilities.upload` rejects with `FilePickerCancelledError` when the picker is cancelled or times out instead of resolving empty, the date and number helpers are stricter, and a class that extends a `@sdcorejs/utils` error must declare its own `static override readonly errorName: string` (1.2.5). Behaviour of `@sdcorejs/angular` itself is kept:
- Cancelling the file picker still produces no error: `sd-upload-file` shows no toast, `SdApiService.upload()` resolves `undefined` without a request, and `SdExcelService.upload()` resolves `{ items: [], file: null }`. 1.2.5 is needed for this in a bundled application: with 1.2.4 the cancel error thrown by one utils entry point was not `instanceof` the class the library imports from another, so the cancel surfaced as an error.
Expand Down
6 changes: 3 additions & 3 deletions showcase/src/app/docs/core/documentation.registry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('documentation registry', () => {
for (const category of DOC_CATEGORIES) {
expect(getDocPagesByCategory(category)).withContext(category).toHaveSize(EXPECTED_CATEGORY_COUNTS[category]);
}
expect(DOC_PAGES.reduce((total, page) => total + page.demoSectionCount, 0)).toBe(391);
expect(DOC_PAGES.reduce((total, page) => total + page.demoSectionCount, 0)).toBe(396);
});

it('uses unique stable page ids and category/slug pairs', () => {
Expand All @@ -47,7 +47,7 @@ describe('documentation registry', () => {
const exampleIds = DOC_PAGES.flatMap(page => page.examples.map(example => example.id));

expect(new Set(exampleIds).size).toBe(exampleIds.length);
expect(exampleIds).toHaveSize(391);
expect(exampleIds).toHaveSize(396);
const switchSections = findDocPage('forms', 'switch')?.examples.map(example => example.sectionId);
expect(switchSections).toContain('example-kich-thuoc');
expect(switchSections).toContain('example-ben-trong-bang');
Expand Down Expand Up @@ -85,7 +85,7 @@ describe('documentation registry', () => {
expect(findDocPage('components', 'job-progress')?.selector).toBe('sd-job-progress');
expect(findDocPage('components', 'audit-diff')?.demoSectionCount).toBe(4);
expect(findDocPage('components', 'breadcrumb')?.demoSectionCount).toBe(3);
expect(findDocPage('components', 'data-state')?.demoSectionCount).toBe(7);
expect(findDocPage('components', 'data-state')?.demoSectionCount).toBe(8);
expect(findDocPage('pipes-utilities', 'read-state')?.publishedDocId).toBe('utilities/read-state/sd-read-state');
expect(findDocPage('pipes-utilities', 'read-state')?.importPath).toBe('@sdcorejs/angular/utilities/read-state');
expect(findDocPage('services', 'missing')).toBeUndefined();
Expand Down
10 changes: 5 additions & 5 deletions showcase/src/app/docs/core/documentation.registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ const COMPONENT_PAGES = [
publishedDocId: 'components/data-state/sd-data-state',
keywords: ['loading', 'empty', 'error', 'forbidden', 'retry'],
status: 'stable',
demoSectionCount: 7,
demoSectionCount: 8,
loadComponent: () => import('../../pages/components/data-state/data-state-demo.component').then(m => m.DataStateDemoComponent),
}),
defineDocPage({
Expand Down Expand Up @@ -377,7 +377,7 @@ const COMPONENT_PAGES = [
publishedDocId: 'components/inform/sd-inform',
keywords: ['alert', 'banner', 'warning', 'message'],
status: 'stable',
demoSectionCount: 12,
demoSectionCount: 13,
loadComponent: () => import('../../pages/components/inform/inform-demo.component').then(m => m.InformDemoComponent),
}),
defineDocPage({
Expand Down Expand Up @@ -523,7 +523,7 @@ const COMPONENT_PAGES = [
publishedDocId: 'components/section/sd-section',
keywords: ['card', 'details', 'header', 'footer'],
status: 'stable',
demoSectionCount: 7,
demoSectionCount: 8,
loadComponent: () => import('../../pages/components/section/section-demo.component').then(m => m.SectionDemoComponent),
}),
defineDocPage({
Expand Down Expand Up @@ -906,7 +906,7 @@ const SERVICE_PAGES = [
publishedDocId: 'services/confirm/sd-confirm',
keywords: ['dialog', 'confirmation', 'prompt', 'promise'],
status: 'stable',
demoSectionCount: 9,
demoSectionCount: 10,
loadComponent: () => import('../../pages/services/confirm/confirm-demo.component').then(m => m.ConfirmDemoComponent),
}),
defineDocPage({
Expand Down Expand Up @@ -958,7 +958,7 @@ const SERVICE_PAGES = [
publishedDocId: 'services/notify/sd-notify',
keywords: ['toast', 'notification', 'feedback', 'message'],
status: 'stable',
demoSectionCount: 4,
demoSectionCount: 5,
loadComponent: () => import('../../pages/services/notify/notify-demo.component').then(m => m.NotifyDemoComponent),
}),
defineDocPage({
Expand Down
40 changes: 40 additions & 0 deletions showcase/src/app/docs/generated/example-manifest.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type ShowcaseExampleSourceKey =
| "components/data-state/example-empty"
| "components/data-state/example-error"
| "components/data-state/example-forbidden"
| "components/data-state/example-icon-shape"
| "components/data-state/example-loading"
| "components/data-state/example-loi-va-retry-tren-ba-control"
| "components/data-state/example-panel-hep-va-lua-chon-dang-giu"
Expand Down Expand Up @@ -90,6 +91,7 @@ export type ShowcaseExampleSourceKey =
| "components/inform/example-bang-mau"
| "components/inform/example-chi-co-tieu-de"
| "components/inform/example-dong-duoc-action"
| "components/inform/example-icon-shape"
| "components/inform/example-line-clamp"
| "components/inform/example-noi-dung-tuy-bien"
| "components/inform/example-tip-bang-mau"
Expand Down Expand Up @@ -143,6 +145,7 @@ export type ShowcaseExampleSourceKey =
| "components/section/example-footer-left-right"
| "components/section/example-full-width-content-with-body-padding-0"
| "components/section/example-headerless-card-with-manual-body-padding"
| "components/section/example-icon-shape"
| "components/section/example-section-item-rich-values"
| "components/side-drawer/example-create-drawer-split-footer"
| "components/side-drawer/example-custom-header-left-right"
Expand Down Expand Up @@ -360,6 +363,7 @@ export type ShowcaseExampleSourceKey =
| "services/confirm/example-chon-ngay-gio"
| "services/confirm/example-chon-phong-ban"
| "services/confirm/example-chon-radio-dang-doc"
| "services/confirm/example-icon-shape"
| "services/confirm/example-nhap-ly-do"
| "services/confirm/example-nhat-ky-gan-nhat"
| "services/confirm/example-xac-nhan-co-ban"
Expand All @@ -373,6 +377,7 @@ export type ShowcaseExampleSourceKey =
| "services/loading/example-nhieu-host-cung-selector-multi-tab"
| "services/notify/example-4-loai-toast"
| "services/notify/example-don-dep"
| "services/notify/example-icon-shape"
| "services/notify/example-thoi-luong-tuy-chinh"
| "services/notify/example-toast-co-action"
| "services/persistence/example-deterministic-identity"
Expand Down Expand Up @@ -802,6 +807,13 @@ export const SHOWCASE_EXAMPLE_MANIFEST = [
title: "Panel hẹp và lựa chọn đang giữ",
description: "Lỗi nằm giữa, dưới các mục đã chọn. Panel 180px vẫn đọc được thông báo và bấm Thử lại.",
},
{
sourceKey: "components/data-state/example-icon-shape",
pageKey: "components/data-state",
sectionId: "example-icon-shape",
title: "Icon shape",
description: "Mặc định square bo 8px (token --sd-icon-shape-radius). circle giữ kiểu tròn cũ; none bỏ nền, giữ kích thước ô.",
},
{
sourceKey: "components/editor/example-soan-noi-dung",
pageKey: "components/editor",
Expand Down Expand Up @@ -1068,6 +1080,13 @@ export const SHOWCASE_EXAMPLE_MANIFEST = [
title: "Tip trong form và bảng",
description: "Existing “Tip trong form và bảng” scenario preserved from the showcase.",
},
{
sourceKey: "components/inform/example-icon-shape",
pageKey: "components/inform",
sectionId: "example-icon-shape",
title: "Icon shape",
description: "Mặc định square bo 8px (token --sd-icon-shape-radius). circle giữ kiểu tròn cũ; none bỏ nền, giữ kích thước ô.",
},
{
sourceKey: "components/job-progress/example-determinate-bar",
pageKey: "components/job-progress",
Expand Down Expand Up @@ -1397,6 +1416,13 @@ export const SHOWCASE_EXAMPLE_MANIFEST = [
title: "Section item rich values",
description: "Existing “Section item rich values” scenario preserved from the showcase.",
},
{
sourceKey: "components/section/example-icon-shape",
pageKey: "components/section",
sectionId: "example-icon-shape",
title: "Icon shape",
description: "Mặc định square bo 8px (token --sd-icon-shape-radius). circle giữ kiểu tròn cũ; none bỏ nền, giữ kích thước ô.",
},
{
sourceKey: "components/side-drawer/example-create-drawer-split-footer",
pageKey: "components/side-drawer",
Expand Down Expand Up @@ -2937,6 +2963,13 @@ export const SHOWCASE_EXAMPLE_MANIFEST = [
title: "Nhật ký gần nhất",
description: "Existing “Nhật ký gần nhất” scenario preserved from the showcase.",
},
{
sourceKey: "services/confirm/example-icon-shape",
pageKey: "services/confirm",
sectionId: "example-icon-shape",
title: "Icon shape",
description: "Mặc định square bo 8px (token --sd-icon-shape-radius). circle giữ kiểu tròn cũ; none bỏ nền, giữ kích thước ô.",
},
{
sourceKey: "services/excel/example-xuat-file-xlsx",
pageKey: "services/excel",
Expand Down Expand Up @@ -3014,6 +3047,13 @@ export const SHOWCASE_EXAMPLE_MANIFEST = [
title: "Dọn dẹp",
description: "clearAll() xóa toàn bộ; clearByType('error') xóa theo loại.",
},
{
sourceKey: "services/notify/example-icon-shape",
pageKey: "services/notify",
sectionId: "example-icon-shape",
title: "Icon shape",
description: "Mặc định square bo 8px (token --sd-icon-shape-radius). circle giữ kiểu tròn cũ; none bỏ nền, giữ kích thước ô.",
},
{
sourceKey: "services/persistence/example-graph-round-trip",
pageKey: "services/persistence",
Expand Down
Loading
Loading