selectActiveItem() guarded the commit with a truthiness check (if (value)), so an
option whose value is falsy (false, 0, '') was dropped: pressing Enter just closed
the panel without selecting. Check value !== null && value !== undefined instead, so
a defined falsy value is committed while a missing active item still closes. Applies
to both BrnSelect and BrnSelectMultiple.
Co-authored-by: Benjamin <benjamin@amcbanking.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Destroying a host while its overlay/dialog is open ran the onDestroy
forceClose(), which emits on closed$/stateChanged$. The subscriptions
forwarded those to the component outputs after the directive was destroyed,
logging NG0953 in dev mode. Set a destroyed flag (registered before the
forceClose teardown) and skip the emits once it is set.
Moving popover-backed controls off CDK Dialog dropped the dialog's
autoFocus: 'first-tabbable', so popovers no longer moved focus to their
content on open (form fields were left unfocused, comboboxes could not be
typed into immediately).
Add an opt-in autoFocus option to BrnOverlay that focuses the first tabbable
element once the content renders, and enable it in the popover defaults. The
select keeps focus on its trigger because its listbox has no tabbable element,
preserving the aria-activedescendant keyboard model. Re-expose the autoFocus
input on the helm popover wrapper and its CLI template, and refresh the docs
API snapshot.
Address review feedback on the overlay lifecycle:
- the select trigger re-read its expanded state after selectActiveItem() had
already closed the panel, so Enter/Tab fell through and re-opened the
overlay via reopen(); capture the expansion state up-front so a commit no
longer re-opens on the same keypress
- make the overlay id atomic (++overlaySequence) so the duplicate-id guard and
the ref share the same counter, matching brn-dialog.service.ts
The drawer landed on main against the old BrnDialog API. Reconcile it with
this branch's refactor:
- override getPositionStrategy() for the directional position instead of the
removed mutablePositionStrategy/positionBuilder members (matches BrnSheet)
- constrain BrnDrawerContent<T extends Record<string, unknown>> to satisfy
BrnDialogContent's tightened generic
- drop the removed closeDelay-era 'delay' input from the drawer close wrapper
and its CLI template
Resolve brn-dialog.service.ts conflict: drop the deprecated injectBrnDialogCtx
alias (removed on main during the pre-1.0 audit) and keep the typed
injectBrnDialogContext with the unknown default.
* feat(command): add style and rtl support #1399
* fix: comments
* feat(command): remove custom data-hidden is built in
* remove important from rounded
* docs(command): fix typo
* fix(command): check has search input for separator
* otherwise shows multiple separator while searching in the scrollable example
---------
Co-authored-by: Merlin <merlin.moos@oldendorff.com>
Co-authored-by: Marc Stammerjohann <8985933+marcjulian@users.noreply.github.com>
* feat(date-picker): add Jalali (Persian) calendar support
- Add pure Jalali calendar math algorithms (no npm packages)
- Add JalaliDate class with valueOf() for reliable calendar comparison
- Add BrnJalaliDateAdapter implementing BrnDateAdapter<JalaliDate>
- Add comprehensive unit tests for all adapter methods
- Add Persian (Farsi) calendar i18n with RTL layout
- Add Jalali date picker example to docs page
- Add Storybook story with Persian locale
- Extend TranslateService to support 'fa' language
* feat(date-picker): add Jalali (Persian) calendar support
Add pure Jalali calendar math, JalaliDate class, and BrnJalaliDateAdapter with tests.
Move example from component docs to blocks/calendar page per PR review.
* refactor: clean up jalali date-time code
- Deduplicate JALALI_MONTHS array in calendar i18n files
- snake_case to camelCase in PersianDate (gregorianDaysInMonth, jalaliDaysInMonth)
- Expand cryptic param names (j_y to jalaliYear, g_y to gregorianYear, etc.)
- Remove parseInt() no-ops on typed number params
- Remove getMonthName (i18n concern, not calendrical)
- Destructure tuples with descriptive names across adapter/date/persian-date
- Fix month+day add drift: clamp day after month step before day arithmetic
* style: fix long line formatting to pass nx format check
* fix(date-picker): resolve merge conflicts from upstream and stashed changes
Merge descriptive variable names (_constrain, parseInt, getMonthName,
full descriptive names) with upstream descriptive names. All tests pass.
* refactor: remove parseInt unwrapping and getMonthName, add PersianDate tests
A touch drag interrupted by the system fires touchcancel instead of touchend, so the drag teardown
(listeners + cursor) never ran until the next drag or destroy. Listen for touchcancel alongside
touchend and remove it in cleanup. Adds a regression test.
Drop the deprecated `injectBrnDialogCtx` in favour of `injectBrnDialogContext`, which the helm
dialog, docs app and storybook already use exclusively. Removing it before 1.0 avoids carrying the
alias on the stable line. The `BrnDialogContext` type it returned stays - it is still used inside
the service.
_cleanup removed only the pointermove listener; the pointerup/pointercancel listeners (registered
with { once: true }) were left attached to document, so a destroy-during-drag - or a normal end
where pointercancel never fired - kept this directive pinned to document until the next global
pointer release. The 500ms _resetDrawer timeout was also fire-and-forget, leaving a stale style
write on detached elements when destroyed within that window.
Remove both listeners in _cleanup, and track the reset timeout so it can be cancelled on destroy.
The timeout is intentionally only cleared on destroy, not in _cleanup, so the normal snap-back
path (which schedules the timeout immediately before _cleanup runs) still clears the transition.
startResize attaches mousemove/touchmove/mouseup/touchend listeners to document, removed only
in the drag-end handler. If the group is destroyed mid-drag (navigation, conditional removal),
those listeners stay attached to document - which is never garbage-collected - pinning the
directive and firing against a torn-down view, and the body cursor is left stuck on the resize
cursor.
Hoist the cleanup into a single teardown stored on the instance: it detaches the four document
listeners, cancels any queued animation frame and restores the cursor. It runs on normal
drag-end and from a DestroyRef.onDestroy hook, and also clears a stale in-flight drag before a
new one starts.
BrnAvatarImage/BrnAvatarFallback and their helm wrappers all used the same
unprefixed exportAs ('avatarImage'/'avatarFallback'). Since the helm directive
host-applies the brain one on the same <img>, a template ref was ambiguous, and
the names broke the brn*/hlm* convention every other exportAs follows. Rename to
brnAvatarImage/brnAvatarFallback and hlmAvatarImage/hlmAvatarFallback before the
exportAs surface is locked at 1.0. Regenerated the CLI avatar templates to match.
- Remove unused HlmDrawerHandle directive
- Add static touch-action: none to .spartan-drawer-content in all
style files so iOS Safari does not hijack the gesture before the
pointerdown handler fires
- Set _openTime via effect on dialog state open instead of on first
pointerdown, so the 500ms cooldown starts when the drawer opens
rather than eating the user's first drag
- Replace preventDefault() on pointerdown with conditional touch-action:
none to avoid suppressing click events per Pointer Events spec
- Defer setPointerCapture to only when drag starts, not on every
pointerdown — capture redirects synthetic clicks to the captured
element instead of the button
- Add releasePointerCapture and touch-action restoration in cleanup
so capture is released before the browser fires click
- Fix _animateAndClose target formula: use ±dimension instead of
dimension + getTranslate() which produced wrong position for
left/top drawers (negative transforms), causing drawer to stay
half-closed
- Add animation: none inline style before close() to prevent
Tailwind data-closed:slide-out-* keyframe animation from
overriding inline transform (was visually reopening drawer)
- Add setPointerCapture for reliable pointercancel delivery
- Add DestroyRef.onDestroy cleanup to prevent pointermove listener
leak if directive is destroyed mid-gesture
- Remove unused _scrollEl field
Detect scrollable ancestors on pointerdown and skip drawer drag when
the scrollable content has remaining scroll in the drag direction.
Only prevent default on pointerdown when no scrollable element is
found, so browser scrolling is not blocked. Move preventDefault in
pointermove after the scroll check.
Improve drag smoothness and reliability by implementing position
tracking in BrnDrawerHandle. Simplify the component structure by moving
the handle logic to a host directive on HlmDrawerContent and removing
manual pointer-events management.
Replace fixed close delays with lifecycle-driven disposal so overlays remain responsive.
Wait only for exit animations that actually start and centralize Escape, backdrop, and
outside-pointer dismissal.
BREAKING CHANGE: remove the closeDelay input and configuration option. Remove explicit delay
values; closed-state exit animations are now detected automatically.
The mcp vite config lacked `root: __dirname`, so the @nx/vitest executor (which
runs from the workspace root) resolved `src/**/*.spec.ts` against the workspace
root, found nothing, and `passWithNoTests` passed it silently - mcp's 19 tests
never ran.
Pin mcp's root, and drop `passWithNoTests` from every project that has specs
(mcp, cli, tools, brain, helm) so a future test-discovery break fails loudly
instead of going green. registry keeps the flag - it genuinely has no specs.
Replace the cross-entry-point relative import (../../../src/testing/axe) in the
checkbox, collapsible and switch specs with a @spartan-ng/brain/testing path
alias, mirroring the @angular/core/testing convention. The helper stays test
only: no ng-package.json, excluded from the lib build and dependency checks, so
it is never published.
Replace Jest with Vitest across the workspace. The brain and helm Angular suites
now run in a real browser (Playwright/Chromium via Vitest browser mode) so they
exercise real layout, focus, pointer events and computed styles that jsdom could
not. The cli, tools, mcp, registry and cli-smoke suites run on Vitest's node
environment.
- Add per-project vite configs and switch test targets to @nx/vitest:test
- Convert jest APIs to vi.* and jest module mocks to vi.mock with importOriginal
- Replace jest-axe with a browser-native axe-core helper and toHaveNoViolations
- Regenerate snapshots in vitest format (content unchanged, format-only)
- Remove all jest packages, configs, the preset and the @nx/jest target default
- Install Playwright Chromium in the CI unit job for the browser suites
Also fixes a brain avatar test that asserted inside a fire-and-forget setTimeout,
which passed vacuously under jsdom and only surfaced once a real browser loaded
the image.
spartan/ui is now Tailwind CSS v4 only. The v3 compatibility layer added
complexity across the brain preset, the CLI generators and the docs, all of
which is removed here.
Brain:
- Delete the v3 JavaScript preset (hlm-tailwind-preset.js) and its package
export; the v4 CSS preset (hlm-tailwind-preset.css) is now the only preset.
- Bump the tailwindcss peer dependency to >=4.0.0 and declare tw-animate-css
as a peer, since it is imported by the CSS preset.
CLI:
- Remove Tailwind version detection and the v3 confirmation prompt; the theme
generator always emits the v4 stylesheet (preset import plus OKLCH theme).
- Drop the legacy HSL theme table, the v3-only CDK overlay import branch, the
vestigial addCdkStyles option and the unused registryItemTailwindSchema.
Docs and tooling:
- Remove the Tailwind v3 installation and theming sections and snippets.
- Drop autoprefixer; Tailwind v4 handles vendor prefixing via @tailwindcss/postcss.
BREAKING CHANGE: Tailwind CSS v3 is no longer supported. The
@spartan-ng/brain/hlm-tailwind-preset (JS) package export has been removed; use
the @spartan-ng/brain/hlm-tailwind-preset.css preset with Tailwind CSS v4. The
tailwindcss peer dependency now requires >=4.0.0.