Skip to content

feat(layout-gap): use CSS gap instead of child margins (#43) - #110

Merged
DuncanFaulkner merged 3 commits into
22.x.xfrom
feat/layout-gap-css-gap-43
Jun 15, 2026
Merged

feat(layout-gap): use CSS gap instead of child margins (#43)#110
DuncanFaulkner merged 3 commits into
22.x.xfrom
feat/layout-gap-css-gap-43

Conversation

@DuncanFaulkner

Copy link
Copy Markdown
Contributor

Summary

Fixes #43fxLayout="row wrap" + fxLayoutGap rendering inconsistencies.

fxLayoutGap previously applied margin to each child except the last. This caused two bugs the reporter documented:

  1. No spacing between wrapped rows (gap only applied along the main axis).
  2. Extra leading margin on the last/short wrapped row.

This PR switches fxLayoutGap to set the native CSS gap property on the flex container itself. gap spaces items both within a row/column and between wrapped rows, which fixes both issues. It's also direction-agnostic, so RTL and reversed flows need no special casing.

Changes

  • layout-gap.tsLayoutGapStyleBuilder.buildStyles now returns { gap } applied to the host. Removed the MutationObserver, child querying/sorting, layout-direction tracking, and the grid negative-margin/padding machinery (buildGridMargin/buildGridPadding/getMarginType/buildGapCSS/CLEAR_MARGIN_CSS). BaseDirective2's default update path applies the gap. Net −820 lines.
    • One value → gap: X (row + column). Two values → CSS gap shorthand <row-gap> <column-gap>.
    • Multiplier and default unit applied per token.
  • layout-gap.spec.ts — replaced the margin/child-based suite with a focused suite asserting gap on the container (static values, multiplier, default unit, two-value, responsive activate/clear, RTL no-op, legacy grid suffix).
  • fxLayoutGap-API.md — documented the new behavior and the breaking change.

Breaking change

fxLayoutGap now emits CSS gap on the container rather than margin on children, so the element must be a flex container (apply fxLayout) for the gap to take effect. The legacy " grid" suffix (e.g. fxLayoutGap="10px grid") is still accepted but is now equivalent to a plain gap, since gap already handles wrapped rows.

Verification

  • ng test @ngbracket/ngx-layout464 tests pass (incl. rewritten layout-gap suite; flex/layout-align/show-hide/grid specs that interact via the layout-gap marshaller key still pass)
  • ng build @ngbracket/ngx-layout — builds clean

cc @Alexi996 — this implements the CSS gap approach you proposed.

🤖 Generated with Claude Code

DuncanFaulkner and others added 2 commits June 11, 2026 22:23
fxLayoutGap previously applied `margin` to each child except the last,
which left two rendering bugs with `fxLayout="row wrap"`:
  - no spacing between wrapped rows, and
  - an extra leading margin on the last/short row.

Switch fxLayoutGap to set the native CSS `gap` property on the flex
container itself. `gap` spaces items within a row/column AND between
wrapped rows, fixing both issues, and is direction-independent so RTL
and reversed flows need no special handling.

This removes the MutationObserver, child sorting, layout tracking, and
the grid negative-margin/padding machinery; BaseDirective2's default
update path now applies the gap to the host.

BREAKING CHANGE: fxLayoutGap now emits CSS `gap` on the container rather
than `margin` on children, so the element must be a flex container
(apply fxLayout) for the gap to take effect. The legacy " grid" suffix
is still accepted but is now equivalent to a plain gap.

Closes #43

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Layout Gap" demo used the legacy `fxLayoutGap="10px 5px grid"` with
`fxFlex="25"` children, relying on the old negative-margin grid hack so
four 25%-basis items fit per row with gutters.

With CSS `gap`, the gap is added on top of the flex-basis, so size the
children with `calc(25% - 7.5px)` to keep four per row, and use a plain
`fxLayoutGap="10px"` (the ` grid` suffix is now a no-op).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DuncanFaulkner

Copy link
Copy Markdown
Contributor Author

Runtime verification — Layout Gap demo ✅

Ran the built updated-demo (served with SPA fallback) and drove headless Chrome to /layout/layout-gap.

Layout Gap page

Verdict: PASS — the page renders a 4-per-row wrapping grid (A B C D / E F G) with visible spacing between items and between the wrapped rows, and the last row starts flush-left (no extra leading margin). These are exactly the two symptoms reported in #43.

Confirmed the mechanism from the live DOM, not just pixels:

  • The fxLayoutGap container has inline gap: 10px; display: flex; flex-flow: wrap — and there are zero margin-inline/margin-block styles anywhere. The old per-child margin path is gone.
  • Children carry flex: 1 1 calc(25% - 7.5px), which is what keeps four per row across the 10px column gaps.

Note (pre-existing, not a regression): the last-row tiles render wider because fxFlex defaults to flex-grow: 1; the previous grid-mode demo behaved identically. Out of scope for the gap change.

Screenshot hosted on the throwaway pr-110-verification branch; safe to delete after review.

@DuncanFaulkner
DuncanFaulkner merged commit 4fec598 into 22.x.x Jun 15, 2026
1 check passed
DuncanFaulkner added a commit that referenced this pull request Jun 15, 2026
* feat: update universal-demo-app to support BootstrapContext in main.server.ts

chore: update peer dependency for @angular/cdk to version 21.0.0 in flex-layout package.json

fix: correct schema path in flex-layout project.json and add tsConfig option for packaging

* chore: update version to 21.0.0-rc.1 in package.json

* Refactor code structure for improved readability and maintainability

* feat: add markdown page and sitemap, implement section page component

- Created a new markdown page (index.html) for standalone content.
- Added a sitemap.xml for better SEO and navigation.
- Implemented SectionPageComponent with dynamic content rendering and layout using Angular Material and ngx-layout directives.

* feat: enhance components with ChangeDetectionStrategy.Eager for improved performance

- Updated multiple components in the responsive and stackoverflow pages to use ChangeDetectionStrategy.Eager.
- Modified tsconfig.app.json to include angularCompilerOptions for nullish coalescing and optional chaining checks.
- Adjusted various test files to import necessary components instead of declaring them.
- Added .npmrc file to set legacy-peer-deps to true for compatibility.
- Changed module resolution strategy in tsconfig.json from node to bundler.

* refactor: migrate tests from Jasmine to Vitest and remove custom matchers

- Replaced Jasmine spies with Vitest spies in media-marshaller tests.
- Removed custom matchers from various spec files and set up Vitest environment.
- Updated TypeScript configuration to use Vitest types instead of Jasmine.
- Deleted obsolete Karma configuration files and test setup.

* fix: update output paths for updated-demo and universal-demo-app builds

* chore: remove Netlify configuration file

* fix: update output paths for updated-demo and universal-demo-app builds

* fix: remove duplicate dependencies from package.json

* fix: update version to 22.0.0-rc in package.json

* fix: add ignoreDeprecations option to tsconfig.json

* chore: update dependencies and refactor tests

- Updated @docusaurus/preset-classic to ^3.10.1 and related Docusaurus packages.
- Replaced conventional-changelog-cli with conventional-changelog.
- Added jsdom as a dependency.
- Removed unused expect imports from various test files.
- Refactored test imports to use Default directives from ngx-layout.
- Updated test setup for Vitest compatibility, including ProxyZone handling.
- Added vitest-base.config.ts for base Vitest configuration.
- Adjusted tsconfig.spec.json to include test-setup.ts.

* feat: add SSR testing support for flex-layout library

- Introduced a new test target for server-side rendering (SSR) in angular.json.
- Updated package.json to use the new SSR test command.
- Removed deprecated SSR test files and configurations.
- Added a new provider file for SSR tests to set PLATFORM_ID.
- Adjusted flex directive tests to handle null styles during SSR.
- Updated TypeScript configuration to include the new SSR provider file.

* fix: update CI badge and remove deprecated dependencies from package.json and package-lock.json

* fix(layout-gap): use direction-independent CSS properties for fxLayoutGap (#95) (#106)

fxLayoutGap emitted physical margin properties (margin-right/left) and
selected between them by reading the CDK Directionality (the `dir`
attribute). That broke layouts when the writing direction was set via CSS
`direction: rtl`, and required LTR/RTL branching throughout the directive.

Switch to logical properties, which the browser resolves against the
writing direction automatically:

  row          -> margin-inline-end
  row-reverse  -> margin-inline-start
  column       -> margin-block-end
  column-reverse -> margin-block-start
  grid margin  -> margin-inline / margin-block
  grid padding -> padding-inline / padding-block

This both fixes the RTL bug and removes now-dead direction handling: the
Directionality injection and its change trigger, the four direction-keyed
style caches and their selection in updateWithValue, and the
`directionality` field on LayoutGapParent.

Tests updated to assert the logical properties; full library suite passes
(478/478).

Closes #95

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* update to 22.0.1

* refactor!: remove deprecated Default* directives (#112)

The Default<X>Directive classes were deprecated in v21 ("will be removed
in version 21") but were never removed. They are redundant subclasses —
the base directives already carry the same selector and inputs — so they
add nothing but a duplicate public symbol.

Remove all 22 Default* directives and repoint the Flex/Extended/Grid
modules (and specs/demo) to the base directives. Grid directive
descriptions that were attached to the Default* classes are moved onto
the corresponding base directives so no docs are lost.

The NgModules are intentionally kept for now.

BREAKING CHANGE: The deprecated Default* directives have been removed.
Replace each `Default<X>Directive` with `<X>Directive` (e.g.
`DefaultFlexDirective` -> `FlexDirective`). The base directives have
identical selectors and behavior, so only the import/symbol name changes.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(core): guard against undefined element.style during style flush (#57) (#108)

On a media/breakpoint change (e.g. window resize), MediaMarshaller.updateStyles()
iterates elementMap (a strong Map) and flushes styles for every tracked element.
An element can be detached/destroyed between the resize event firing and the flush,
leaving element.style undefined and causing
"TypeError: Cannot read properties of undefined (reading 'setProperty')".

Because the throw aborted the whole forEach pass, a single stale element broke
styling for all remaining elements too.

Guard the browser setProperty call with optional chaining so a non-styleable
element is skipped instead of throwing. The server path is unaffected.

Closes #57

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(layout-gap): use CSS `gap` instead of child margins (#43) (#110)

* feat(layout-gap): use CSS `gap` instead of child margins (#43)

fxLayoutGap previously applied `margin` to each child except the last,
which left two rendering bugs with `fxLayout="row wrap"`:
  - no spacing between wrapped rows, and
  - an extra leading margin on the last/short row.

Switch fxLayoutGap to set the native CSS `gap` property on the flex
container itself. `gap` spaces items within a row/column AND between
wrapped rows, fixing both issues, and is direction-independent so RTL
and reversed flows need no special handling.

This removes the MutationObserver, child sorting, layout tracking, and
the grid negative-margin/padding machinery; BaseDirective2's default
update path now applies the gap to the host.

BREAKING CHANGE: fxLayoutGap now emits CSS `gap` on the container rather
than `margin` on children, so the element must be a flex container
(apply fxLayout) for the gap to take effect. The legacy " grid" suffix
is still accepted but is now equivalent to a plain gap.

Closes #43

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(demo): update layout-gap demo for CSS gap behavior

The "Layout Gap" demo used the legacy `fxLayoutGap="10px 5px grid"` with
`fxFlex="25"` children, relying on the old negative-margin grid hack so
four 25%-basis items fit per row with gutters.

With CSS `gap`, the gap is added on top of the flex-basis, so size the
children with `calc(25% - 7.5px)` to keep four per row, and use a plain
`fxLayoutGap="10px"` (the ` grid` suffix is now a no-op).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(breakpoints): add opt-in MATERIAL_BREAKPOINTS preset (#26) (#111)

The default breakpoints retain the original @angular/flex-layout ranges
where `md` starts at 960px, which is dated. Rather than change the
defaults (a breaking change for migrants), add an importable
MATERIAL_BREAKPOINTS preset with modern Material 3 values:

  xs <600 | sm 600 | md 900 | lg 1200 | xl 1536

It uses the same xs/sm/md/lg/xl (+ lt-*/gt-*) alias set as the defaults,
so responsive selectors are unchanged — only the activation ranges. Opt
in by disabling the built-ins and providing the preset:

  FlexLayoutModule.withConfig({ disableDefaultBps: true }, MATERIAL_BREAKPOINTS)

Also fixes the breakpoints doc, which referenced a non-existent
DISABLE_DEFAULT_BREAKPOINTS token (the real option is `disableDefaultBps`).

Closes #26

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@DuncanFaulkner DuncanFaulkner mentioned this pull request Jun 15, 2026
DuncanFaulkner added a commit that referenced this pull request Jun 15, 2026
* feat: update universal-demo-app to support BootstrapContext in main.server.ts

chore: update peer dependency for @angular/cdk to version 21.0.0 in flex-layout package.json

fix: correct schema path in flex-layout project.json and add tsConfig option for packaging

* chore: update version to 21.0.0-rc.1 in package.json

* Refactor code structure for improved readability and maintainability

* feat: add markdown page and sitemap, implement section page component

- Created a new markdown page (index.html) for standalone content.
- Added a sitemap.xml for better SEO and navigation.
- Implemented SectionPageComponent with dynamic content rendering and layout using Angular Material and ngx-layout directives.

* feat: enhance components with ChangeDetectionStrategy.Eager for improved performance

- Updated multiple components in the responsive and stackoverflow pages to use ChangeDetectionStrategy.Eager.
- Modified tsconfig.app.json to include angularCompilerOptions for nullish coalescing and optional chaining checks.
- Adjusted various test files to import necessary components instead of declaring them.
- Added .npmrc file to set legacy-peer-deps to true for compatibility.
- Changed module resolution strategy in tsconfig.json from node to bundler.

* refactor: migrate tests from Jasmine to Vitest and remove custom matchers

- Replaced Jasmine spies with Vitest spies in media-marshaller tests.
- Removed custom matchers from various spec files and set up Vitest environment.
- Updated TypeScript configuration to use Vitest types instead of Jasmine.
- Deleted obsolete Karma configuration files and test setup.

* fix: update output paths for updated-demo and universal-demo-app builds

* chore: remove Netlify configuration file

* fix: update output paths for updated-demo and universal-demo-app builds

* fix: remove duplicate dependencies from package.json

* fix: update version to 22.0.0-rc in package.json

* fix: add ignoreDeprecations option to tsconfig.json

* chore: update dependencies and refactor tests

- Updated @docusaurus/preset-classic to ^3.10.1 and related Docusaurus packages.
- Replaced conventional-changelog-cli with conventional-changelog.
- Added jsdom as a dependency.
- Removed unused expect imports from various test files.
- Refactored test imports to use Default directives from ngx-layout.
- Updated test setup for Vitest compatibility, including ProxyZone handling.
- Added vitest-base.config.ts for base Vitest configuration.
- Adjusted tsconfig.spec.json to include test-setup.ts.

* feat: add SSR testing support for flex-layout library

- Introduced a new test target for server-side rendering (SSR) in angular.json.
- Updated package.json to use the new SSR test command.
- Removed deprecated SSR test files and configurations.
- Added a new provider file for SSR tests to set PLATFORM_ID.
- Adjusted flex directive tests to handle null styles during SSR.
- Updated TypeScript configuration to include the new SSR provider file.

* fix: update CI badge and remove deprecated dependencies from package.json and package-lock.json

* fix(layout-gap): use direction-independent CSS properties for fxLayoutGap (#95) (#106)

fxLayoutGap emitted physical margin properties (margin-right/left) and
selected between them by reading the CDK Directionality (the `dir`
attribute). That broke layouts when the writing direction was set via CSS
`direction: rtl`, and required LTR/RTL branching throughout the directive.

Switch to logical properties, which the browser resolves against the
writing direction automatically:

  row          -> margin-inline-end
  row-reverse  -> margin-inline-start
  column       -> margin-block-end
  column-reverse -> margin-block-start
  grid margin  -> margin-inline / margin-block
  grid padding -> padding-inline / padding-block

This both fixes the RTL bug and removes now-dead direction handling: the
Directionality injection and its change trigger, the four direction-keyed
style caches and their selection in updateWithValue, and the
`directionality` field on LayoutGapParent.

Tests updated to assert the logical properties; full library suite passes
(478/478).

Closes #95

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* update to 22.0.1

* refactor!: remove deprecated Default* directives (#112)

The Default<X>Directive classes were deprecated in v21 ("will be removed
in version 21") but were never removed. They are redundant subclasses —
the base directives already carry the same selector and inputs — so they
add nothing but a duplicate public symbol.

Remove all 22 Default* directives and repoint the Flex/Extended/Grid
modules (and specs/demo) to the base directives. Grid directive
descriptions that were attached to the Default* classes are moved onto
the corresponding base directives so no docs are lost.

The NgModules are intentionally kept for now.

BREAKING CHANGE: The deprecated Default* directives have been removed.
Replace each `Default<X>Directive` with `<X>Directive` (e.g.
`DefaultFlexDirective` -> `FlexDirective`). The base directives have
identical selectors and behavior, so only the import/symbol name changes.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(core): guard against undefined element.style during style flush (#57) (#108)

On a media/breakpoint change (e.g. window resize), MediaMarshaller.updateStyles()
iterates elementMap (a strong Map) and flushes styles for every tracked element.
An element can be detached/destroyed between the resize event firing and the flush,
leaving element.style undefined and causing
"TypeError: Cannot read properties of undefined (reading 'setProperty')".

Because the throw aborted the whole forEach pass, a single stale element broke
styling for all remaining elements too.

Guard the browser setProperty call with optional chaining so a non-styleable
element is skipped instead of throwing. The server path is unaffected.

Closes #57

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(layout-gap): use CSS `gap` instead of child margins (#43) (#110)

* feat(layout-gap): use CSS `gap` instead of child margins (#43)

fxLayoutGap previously applied `margin` to each child except the last,
which left two rendering bugs with `fxLayout="row wrap"`:
  - no spacing between wrapped rows, and
  - an extra leading margin on the last/short row.

Switch fxLayoutGap to set the native CSS `gap` property on the flex
container itself. `gap` spaces items within a row/column AND between
wrapped rows, fixing both issues, and is direction-independent so RTL
and reversed flows need no special handling.

This removes the MutationObserver, child sorting, layout tracking, and
the grid negative-margin/padding machinery; BaseDirective2's default
update path now applies the gap to the host.

BREAKING CHANGE: fxLayoutGap now emits CSS `gap` on the container rather
than `margin` on children, so the element must be a flex container
(apply fxLayout) for the gap to take effect. The legacy " grid" suffix
is still accepted but is now equivalent to a plain gap.

Closes #43

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(demo): update layout-gap demo for CSS gap behavior

The "Layout Gap" demo used the legacy `fxLayoutGap="10px 5px grid"` with
`fxFlex="25"` children, relying on the old negative-margin grid hack so
four 25%-basis items fit per row with gutters.

With CSS `gap`, the gap is added on top of the flex-basis, so size the
children with `calc(25% - 7.5px)` to keep four per row, and use a plain
`fxLayoutGap="10px"` (the ` grid` suffix is now a no-op).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(breakpoints): add opt-in MATERIAL_BREAKPOINTS preset (#26) (#111)

The default breakpoints retain the original @angular/flex-layout ranges
where `md` starts at 960px, which is dated. Rather than change the
defaults (a breaking change for migrants), add an importable
MATERIAL_BREAKPOINTS preset with modern Material 3 values:

  xs <600 | sm 600 | md 900 | lg 1200 | xl 1536

It uses the same xs/sm/md/lg/xl (+ lt-*/gt-*) alias set as the defaults,
so responsive selectors are unchanged — only the activation ranges. Opt
in by disabling the built-ins and providing the preset:

  FlexLayoutModule.withConfig({ disableDefaultBps: true }, MATERIAL_BREAKPOINTS)

Also fixes the breakpoints doc, which referenced a non-existent
DISABLE_DEFAULT_BREAKPOINTS token (the real option is `disableDefaultBps`).

Closes #26

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* chore: update version to 22.0.0 and add conventional-changelog-angular dependency

* docs(changelog): remove duplicate 22.0.0-rc sections

The changelog regeneration emitted two identical 22.0.0-rc (2026-06-15)
blocks that duplicate the new 22.0.0 release section. Remove them, keeping
the original historical 22.0.0-rc (2026-06-03) entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(readme): document standalone usage and Default* removal

Update the library README to reflect that ngx-layout is now fully
standalone: add a standalone component / provideFlexLayout example and
call out the removal of the deprecated Default* directives. The NgModule
usage is retained as still supported.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
DuncanFaulkner added a commit that referenced this pull request Jul 20, 2026
* feat: update universal-demo-app to support BootstrapContext in main.server.ts

chore: update peer dependency for @angular/cdk to version 21.0.0 in flex-layout package.json

fix: correct schema path in flex-layout project.json and add tsConfig option for packaging

* chore: update version to 21.0.0-rc.1 in package.json

* Refactor code structure for improved readability and maintainability

* feat: add markdown page and sitemap, implement section page component

- Created a new markdown page (index.html) for standalone content.
- Added a sitemap.xml for better SEO and navigation.
- Implemented SectionPageComponent with dynamic content rendering and layout using Angular Material and ngx-layout directives.

* feat: enhance components with ChangeDetectionStrategy.Eager for improved performance

- Updated multiple components in the responsive and stackoverflow pages to use ChangeDetectionStrategy.Eager.
- Modified tsconfig.app.json to include angularCompilerOptions for nullish coalescing and optional chaining checks.
- Adjusted various test files to import necessary components instead of declaring them.
- Added .npmrc file to set legacy-peer-deps to true for compatibility.
- Changed module resolution strategy in tsconfig.json from node to bundler.

* refactor: migrate tests from Jasmine to Vitest and remove custom matchers

- Replaced Jasmine spies with Vitest spies in media-marshaller tests.
- Removed custom matchers from various spec files and set up Vitest environment.
- Updated TypeScript configuration to use Vitest types instead of Jasmine.
- Deleted obsolete Karma configuration files and test setup.

* fix: update output paths for updated-demo and universal-demo-app builds

* chore: remove Netlify configuration file

* fix: update output paths for updated-demo and universal-demo-app builds

* fix: remove duplicate dependencies from package.json

* fix: update version to 22.0.0-rc in package.json

* fix: add ignoreDeprecations option to tsconfig.json

* chore: update dependencies and refactor tests

- Updated @docusaurus/preset-classic to ^3.10.1 and related Docusaurus packages.
- Replaced conventional-changelog-cli with conventional-changelog.
- Added jsdom as a dependency.
- Removed unused expect imports from various test files.
- Refactored test imports to use Default directives from ngx-layout.
- Updated test setup for Vitest compatibility, including ProxyZone handling.
- Added vitest-base.config.ts for base Vitest configuration.
- Adjusted tsconfig.spec.json to include test-setup.ts.

* feat: add SSR testing support for flex-layout library

- Introduced a new test target for server-side rendering (SSR) in angular.json.
- Updated package.json to use the new SSR test command.
- Removed deprecated SSR test files and configurations.
- Added a new provider file for SSR tests to set PLATFORM_ID.
- Adjusted flex directive tests to handle null styles during SSR.
- Updated TypeScript configuration to include the new SSR provider file.

* fix: update CI badge and remove deprecated dependencies from package.json and package-lock.json

* fix(layout-gap): use direction-independent CSS properties for fxLayoutGap (#95) (#106)

fxLayoutGap emitted physical margin properties (margin-right/left) and
selected between them by reading the CDK Directionality (the `dir`
attribute). That broke layouts when the writing direction was set via CSS
`direction: rtl`, and required LTR/RTL branching throughout the directive.

Switch to logical properties, which the browser resolves against the
writing direction automatically:

  row          -> margin-inline-end
  row-reverse  -> margin-inline-start
  column       -> margin-block-end
  column-reverse -> margin-block-start
  grid margin  -> margin-inline / margin-block
  grid padding -> padding-inline / padding-block

This both fixes the RTL bug and removes now-dead direction handling: the
Directionality injection and its change trigger, the four direction-keyed
style caches and their selection in updateWithValue, and the
`directionality` field on LayoutGapParent.

Tests updated to assert the logical properties; full library suite passes
(478/478).

Closes #95

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* update to 22.0.1

* refactor!: remove deprecated Default* directives (#112)

The Default<X>Directive classes were deprecated in v21 ("will be removed
in version 21") but were never removed. They are redundant subclasses —
the base directives already carry the same selector and inputs — so they
add nothing but a duplicate public symbol.

Remove all 22 Default* directives and repoint the Flex/Extended/Grid
modules (and specs/demo) to the base directives. Grid directive
descriptions that were attached to the Default* classes are moved onto
the corresponding base directives so no docs are lost.

The NgModules are intentionally kept for now.

BREAKING CHANGE: The deprecated Default* directives have been removed.
Replace each `Default<X>Directive` with `<X>Directive` (e.g.
`DefaultFlexDirective` -> `FlexDirective`). The base directives have
identical selectors and behavior, so only the import/symbol name changes.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(core): guard against undefined element.style during style flush (#57) (#108)

On a media/breakpoint change (e.g. window resize), MediaMarshaller.updateStyles()
iterates elementMap (a strong Map) and flushes styles for every tracked element.
An element can be detached/destroyed between the resize event firing and the flush,
leaving element.style undefined and causing
"TypeError: Cannot read properties of undefined (reading 'setProperty')".

Because the throw aborted the whole forEach pass, a single stale element broke
styling for all remaining elements too.

Guard the browser setProperty call with optional chaining so a non-styleable
element is skipped instead of throwing. The server path is unaffected.

Closes #57

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(layout-gap): use CSS `gap` instead of child margins (#43) (#110)

* feat(layout-gap): use CSS `gap` instead of child margins (#43)

fxLayoutGap previously applied `margin` to each child except the last,
which left two rendering bugs with `fxLayout="row wrap"`:
  - no spacing between wrapped rows, and
  - an extra leading margin on the last/short row.

Switch fxLayoutGap to set the native CSS `gap` property on the flex
container itself. `gap` spaces items within a row/column AND between
wrapped rows, fixing both issues, and is direction-independent so RTL
and reversed flows need no special handling.

This removes the MutationObserver, child sorting, layout tracking, and
the grid negative-margin/padding machinery; BaseDirective2's default
update path now applies the gap to the host.

BREAKING CHANGE: fxLayoutGap now emits CSS `gap` on the container rather
than `margin` on children, so the element must be a flex container
(apply fxLayout) for the gap to take effect. The legacy " grid" suffix
is still accepted but is now equivalent to a plain gap.

Closes #43

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(demo): update layout-gap demo for CSS gap behavior

The "Layout Gap" demo used the legacy `fxLayoutGap="10px 5px grid"` with
`fxFlex="25"` children, relying on the old negative-margin grid hack so
four 25%-basis items fit per row with gutters.

With CSS `gap`, the gap is added on top of the flex-basis, so size the
children with `calc(25% - 7.5px)` to keep four per row, and use a plain
`fxLayoutGap="10px"` (the ` grid` suffix is now a no-op).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* feat(breakpoints): add opt-in MATERIAL_BREAKPOINTS preset (#26) (#111)

The default breakpoints retain the original @angular/flex-layout ranges
where `md` starts at 960px, which is dated. Rather than change the
defaults (a breaking change for migrants), add an importable
MATERIAL_BREAKPOINTS preset with modern Material 3 values:

  xs <600 | sm 600 | md 900 | lg 1200 | xl 1536

It uses the same xs/sm/md/lg/xl (+ lt-*/gt-*) alias set as the defaults,
so responsive selectors are unchanged — only the activation ranges. Opt
in by disabling the built-ins and providing the preset:

  FlexLayoutModule.withConfig({ disableDefaultBps: true }, MATERIAL_BREAKPOINTS)

Also fixes the breakpoints doc, which referenced a non-existent
DISABLE_DEFAULT_BREAKPOINTS token (the real option is `disableDefaultBps`).

Closes #26

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* chore: update version to 22.0.0 and add conventional-changelog-angular dependency

* docs(changelog): remove duplicate 22.0.0-rc sections

The changelog regeneration emitted two identical 22.0.0-rc (2026-06-15)
blocks that duplicate the new 22.0.0 release section. Remove them, keeping
the original historical 22.0.0-rc (2026-06-03) entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(readme): document standalone usage and Default* removal

Update the library README to reflect that ngx-layout is now fully
standalone: add a standalone component / provideFlexLayout example and
call out the removal of the deprecated Default* directives. The NgModule
usage is retained as still supported.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(layout-gap): use native CSS gap instead of child margins (#117)

Replace the margin-based gap implementation (onLayoutChange,
updateWithValue, clearStyles override, willDisplay, MutationObserver
child observation) with the container's native CSS `gap` property.

Update the spec to assert `gap` on the container rather than
margin-inline-end / margin-block-end on individual children.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(deps): patch known security vulnerabilities in build toolchain (#116)

Resolve all 12 npm audit findings (5 high, 4 moderate, 3 low) in
transitive dev/build dependencies by pinning each to its minimum patched
version within the compatible major via package.json overrides:

- @babel/core 7.29.6, esbuild 0.28.1, hono 4.12.31,
  http-proxy-middleware 2.0.10, joi 17.13.4, js-yaml 4.2.0,
  piscina 5.2.0, undici 7.28.0, vite 7.3.5, webpack-dev-server 5.2.5
- gray-matter scoped to js-yaml 3.15.0 (needs 3.x API)
- node-gyp scoped to undici 6.27.0 (needs ^6); jsdom uses undici 7.28.0

npm audit now reports 0 vulnerabilities; library build and all 468
tests pass.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant