683 Commits

Author SHA1 Message Date
Marc Stammerjohann d0081b77a8 fix(command): check has search input for separator
* otherwise shows multiple separator while searching in the scrollable example
2026-06-16 18:23:57 +02:00
Marc Stammerjohann 63b1ec6b64 feat(command): remove custom data-hidden is built in
* remove important from rounded
2026-06-16 18:14:41 +02:00
Merlin 4812cc620b fix: comments 2026-06-16 17:52:47 +02:00
moosme 61a546f24a Merge pull request #1476 from spartan-ng/pre-1.0-audit
chore(repo): pre-1.0 audit - overlay leak fixes, data-slot, CLI drawer, naming
2026-06-16 11:43:11 +02:00
Leonidas bd321c9ab0 chore: nightly release 2026-06-16 ⚡ 2026-06-16 04:12:33 +00:00
Ahmad Mehrabi 715499fe4e feat(date-picker): add Jalali (Persian) calendar support (#1429)
* 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
2026-06-15 15:05:30 +02:00
copilot-swe-agent[bot] aaf29b4e05 chore(repo): merge main into pre-1.0-audit (resolve drawer brain version conflict) 2026-06-15 11:47:09 +00:00
Ashley Hunter 603d213799 fix(resizable): end drag on touchcancel
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.
2026-06-15 08:04:00 +01:00
Leonidas 7da9d3e4c3 chore: nightly release 2026-06-15 ⚡ 2026-06-15 04:22:11 +00:00
Ashley Hunter f533fa426c refactor(dialog): remove deprecated injectBrnDialogCtx
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.
2026-06-14 23:43:51 +01:00
Ashley Hunter 4ee05f9c50 fix(drawer): release drag listeners and reset timeout on destroy
_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.
2026-06-14 23:09:41 +01:00
Ashley Hunter a16b251a26 fix(resizable): tear down drag listeners when destroyed mid-drag
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.
2026-06-14 23:02:40 +01:00
Ashley Hunter 935e4c2404 fix(avatar): give image/fallback distinct prefixed exportAs names
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.
2026-06-14 22:26:34 +01:00
mehrabix 94a57f4eee Merge remote-tracking branch 'origin/main' into feat/drawer-component 2026-06-14 18:49:54 +03:30
mehrabix 4188133dd8 fix(drawer): remove dead HlmDrawerHandle, fix iOS touch-action, fix _openTime cooldown
- 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
2026-06-14 18:40:26 +03:30
mehrabix 112f9d12b2 fix(drawer): prevent pointer capture on tap to fix button clicks on touch
- 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
2026-06-14 15:26:00 +03:30
mehrabix 284c1f04db fix(drawer): correct close animation for left/top and prevent double-close
- 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
2026-06-14 14:50:21 +03:30
mehrabix e3271391f8 style(drawer): fix formatting for long line 2026-06-14 13:54:10 +03:30
mehrabix 224c3d268a fix(drawer): handle null event target in shouldDrag call 2026-06-14 13:50:18 +03:30
mehrabix 78399c9aec feat(drawer): rewrite drag handling to match vaul/shadcn implementation
Rewrite BrnDrawerHandle to match vaul v1.1.2 touch/swipe logic:
- Direction detection: 10px touch / 2px mouse swipe threshold, axis
  alignment check prevents horizontal drags on vertical drawers
- shouldDrag with scroll lock: 100ms cooldown after scroll detection,
  DOM walk for scrollable ancestors, 500ms cooldown after open
- Simple velocity: totalDistance / totalTime on release (threshold 0.4)
- Logarithmic rubber-band: dampenValue uses 8 * ln(v+1) - 16
- isAllowedToDrag latch: once drag starts, no re-check on subsequent moves
- Close threshold: 0.25 (25% of drawer dimension, matching vaul)
2026-06-14 13:38:10 +03:30
mehrabix 5d885f3931 feat(drawer): add scrollable content detection to prevent drag on scroll
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.
2026-06-14 13:15:53 +03:30
mehrabix fac0ed879b refactor(drawer): refactor drag handling and component structure
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.
2026-06-14 13:14:40 +03:30
Leonidas f377723a53 chore: nightly release 2026-06-14 ⚡ 2026-06-14 04:09:37 +00:00
mehrabix 2e3059c8a2 feat(drawer): add drawer component with brain primitive and helm styling 2026-06-13 21:31:07 +03:30
Ashley Hunter e0f236ba47 fix: run mcp tests and stop passWithNoTests masking missing test discovery
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.
2026-06-13 17:16:45 +01:00
Ashley Hunter 7182249f21 test: expose brain axe helper via @spartan-ng/brain/testing alias
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.
2026-06-13 15:33:21 +01:00
Ashley Hunter 1b743f8019 test: migrate from jest to vitest with browser testing for brain/helm
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.
2026-06-13 15:16:45 +01:00
Leonidas f29b10bb68 chore: nightly release 2026-06-13 ⚡ 2026-06-13 03:47:26 +00:00
Leonidas 1d4b389e6e chore: nightly release 2026-06-12 ⚡ 2026-06-12 04:02:56 +00:00
Ashley Hunter 859676a41a feat!: drop Tailwind CSS v3 support (#1460)
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.
2026-06-12 00:39:14 +02:00
Leonidas a3607e71d7 chore: nightly release 2026-06-11 ⚡ 2026-06-11 04:03:48 +00:00
Ashley Hunter 66f4d9f454 feat(resizable): add style support and rtl example
Closes #1412
2026-06-10 18:09:51 +01:00
Marc Stammerjohann dc2d2c112d feat(collapsible): add rtl and settings panel example (#1453)
* feat(collapsible): add rtl and settings panel example

* docs(collapsible): update feedback
2026-06-10 15:59:40 +02:00
moosme 3dd146e20f Merge pull request #1443 from spartan-ng/wt/popover-1409
feat(popover): add style support and rtl example
2026-06-10 08:57:08 +02:00
moosme 164ca5d620 Merge pull request #1444 from spartan-ng/wt/hover-card-1405
feat(hover-card): add style support and rtl example
2026-06-10 08:41:49 +02:00
Leonidas f25559dfca chore: nightly release 2026-06-10 ⚡ 2026-06-10 03:47:12 +00:00
Ashley Hunter 6e63ba9d74 feat(hover-card): add style support and rtl example
Make the hover card overlay inherit the ambient direction so it renders RTL.

Closes #1405
2026-06-09 20:16:36 +01:00
Ashley Hunter bbf5dfadc7 feat(popover): add style support and rtl example
Make BrnDialog overlays inherit the trigger's ambient direction so popover renders RTL.

Closes #1409
2026-06-09 20:13:40 +01:00
moosme cc0605d592 fix(accordion): prevent openChange emit on init (#1437)
* fix(accordion): prevent openChange emit on init

* fix: review

* fix: review

* fix: review

---------

Co-authored-by: Merlin <merlin.moos@oldendorff.com>
2026-06-09 19:11:13 +02:00
Marc Stammerjohann 34e835e843 feat: update duplicate outputs, replace ngModel (#1428)
* feat: migrate duplicate output from model + output to input + linkedSignal + output, closes #1385

# Conflicts:
#	libs/helm/switch/src/lib/hlm-switch.ts

* feat(pagination): replace ngModel

* fix(toggle-group): select fires twice onChange and change.emit

* feat: rename value/checked input and linkedSignal
2026-06-09 18:51:22 +02:00
Ashley Hunter d872c74580 feat(switch): add size input
Expose a size input ('default' | 'sm') on the brain and helm switch, bound to the
button's data-size attribute, so the size-keyed registry style rules resolve and
a smaller switch can be rendered. Replaces the previously hardcoded
data-size="default". Add brn-switch specs covering the data-size reflection and
the thumb data-state, and update the ui-docs snapshot for the new input.
2026-06-09 08:35:41 +01:00
Ashley Hunter af74a9941b feat(switch): support styles per registry style
Emit the spartan-switch and spartan-switch-thumb markers so the per-style track
size, border width, corner radius and thumb size/offset defined in the registry
styles apply at runtime across vega, nova, maia, lyra, mira and luma, matching
shadcn. Add a data-size attribute to the brn-switch button and a data-state
attribute to the thumb so the registry's size- and state-keyed rules resolve, and
make the checked thumb translate direction-aware (ltr/rtl) so it animates
correctly under RTL. Enable the docs theme switcher and show all styles.
2026-06-09 08:35:41 +01:00
Leonidas 84b9220ed2 chore: nightly release 2026-06-09 ⚡ 2026-06-09 03:35:49 +00:00
Ashley Hunter d4da582d40 feat(repo)!: upgrade nx to 22 and angular to 21 (#1430)
* feat(repo)!: upgrade nx to 22 and angular to 21

Upgrade the workspace to Nx 22.7.5 and Angular 21.2.16, and widen the
published peer dependency ranges to support Angular 21 and 22 while
dropping Angular 20.

- nx and @nx/* to 22.7.5; @angular/* to 21.2.16 (framework) and 21.2.14
  (cli, build, devkit, cdk, ssr)
- brain, helm and trpc @angular/* peers to ">=21.0.0 <23.0.0"; the helm
  embla-carousel-angular peer uses the same range
- @spartan-ng/cli minimums raised to >=21 and @schematics/angular to
  21.2.14; the private tools lib aligned to nx 22 / angular 21
- analog 2.6.0, ng-packagr 21, angular-eslint 21, storybook 10, jest 30,
  @testing-library/angular 19 and related tooling
- replace the calendar select host-listener $event with a typed output
  subscription; constrain the ai-chat-link directive to anchors
- drive the menubar e2e arrow keys via native keydown and disable
  Chromium throttling; hold cypress at 14 (15 breaks realPress specs)

BREAKING CHANGE: Angular 20 is no longer supported. spartan/ui now
supports Angular 21 and 22.

* ci: lower unit test parallelism to fit the runner memory

The docs app's vitest + Analog suite peaks at a few GB; running it
alongside two other test projects under --parallel=3 exhausted the
GitHub runner memory and the worker was OOM-killed with no output.
Run at most two test projects concurrently.

* fix(trpc): drop invalid environment providers from the test env module

The Nx 22 migration wrapped provideZoneChangeDetection() in an @NgModule,
but EnvironmentProviders are not valid in @NgModule providers and Angular
21 throws NG0207 at runtime. Restore the plain initTestEnvironment setup
(matching the docs app); the explicit zone provider is unnecessary.

* ci: run the docs app test suite in isolation

Bundling the docs app's vitest + Analog suite into the parallel test run
made it fail in CI while it passes on its own, and the interleaved output
hid the cause. Run the other projects in parallel, then the docs app as a
separate step.

* fix(nx): point test cache inputs at the real jest preset file

The test targets' cache inputs referenced jest.preset.js, but the
workspace preset is jest.preset.cjs, so the input never matched and a
preset change would not invalidate the cache. Fix both the
@nx/vitest:test target (added by the Nx 22 migration) and the existing
@nx/jest:jest target.

* ci: log exit code and retry the docs app test suite once

The docs app's vitest + Analog suite intermittently crashes in CI right
after loading Angular's testing modules, with no error output. Log the
exit code to classify the crash (128+signal indicates a native crash)
and retry once so a flaky crash does not fail the run while the root
cause is investigated.

* ci: clarify docs app test retry after pinpointing the flaky crash

The crash is in app.spec.ts: booting the full app shell (Header/Footer
and their deep import graph) intermittently crashes the vitest worker
during the Analog transform in CI (exit 1, no output, not reproducible
locally). It happens before any test runs, so a vitest per-test retry
cannot help; re-running the suite once does.

* chore(repo): hold analog at 2.1.3 and drop unit-test ci workarounds

Analog 2.6.0 intermittently crashed the docs app's vitest worker in CI
(a flaky transform of the app shell), so keep analog at the nx-migration's
tested 2.1.3, revert the marked bumps that 2.6.0 required, and restore the
ai-chat-link directive selector. Also restore the original unit test ci
step, dropping the parallel, isolation and retry workarounds added while
chasing that crash.
2026-06-08 22:23:45 +02:00
Youssef Chaygani c5afdcdabf fix: allow undefined in value model signals for autocomplete, combobox, and select (#1423)
* fix: allow undefined in value model signals for autocomplete, combobox, and select

Closes: #1387

* fix(select): guard undefined in select/select-multiple key-manager effect values

* feat: explicit check for undefined value

---------

Co-authored-by: Marc Stammerjohann <8985933+marcjulian@users.noreply.github.com>
2026-06-08 12:17:50 +02:00
moosme 9ee58f8795 Merge pull request #1420 from marcjulian/feat/button-styles-and-radius
feat: update radius calculation from pixel to percentage, add button …
2026-06-08 12:07:52 +02:00
Leonidas cd1a7e0583 chore: nightly release 2026-06-08 ⚡ 2026-06-08 04:05:48 +00:00
Leonidas 9ed64a10a1 chore: nightly release 2026-06-06 ⚡ 2026-06-06 03:34:02 +00:00
Marc Stammerjohann d71d6c0036 feat: update radius calculation from pixel to percentage, add button active style 2026-06-05 12:24:12 +02:00
Leonidas 5b85c56c51 chore: nightly release 2026-06-05 ⚡ 2026-06-05 03:48:53 +00:00