Drop node-sass support - #41112
Conversation
1816145 to
f232df5
Compare
| } | ||
| } | ||
| @include color-mode(dark, true) { | ||
| --custom-color: #{mix($indigo, $blue, 50%)}; |
There was a problem hiding this comment.
I removed the mix($indigo, $blue, 50%) because color.mix($indigo, $blue, 50%) doesn't return a value as an hex string, but a color that should be transformed to a hex string. I don't think it's necessary to add such complexity for now. Let's see at the end of the PR if we reintroduce something like that.
There was a problem hiding this comment.
I think that the mix($indigo, $blue, 50%) is not appropriate in this as css and bootstrap has feature of colour-mix which include the change or mix in colour using hex string but you can't use colour.mix to change colour into hex string
8ec183e to
343bc9e
Compare
| { | ||
| "path": "./dist/css/bootstrap-reboot.min.css", | ||
| "maxSize": "3.25 kB" | ||
| "maxSize": "3.5 kB" |
There was a problem hiding this comment.
Changes here are for now due to the introduction of color.* functions that return the color as a rgb values instead of an hex value. The strings for the fill values of SVGs are a bit longer, so the bundle is a little bit bigger. For instance:
4698,4699c4698,4699
< --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");
< --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");
---
> --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='rgb%28109.8, 168, 253.8%29'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");
> --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='rgb%28109.8, 168, 253.8%29'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");However, it doesn't seem to be a regression so far.
|
@julien-deramond Sass color functions don't round particular values in RGB colors (Please check this - sass/dart-sass#2456). You chose to round values in the following functions: I recommend avoiding math rounds in those functions, as suggested by the Sass creators. It requires modifying the luminance function like this. The |
This comment was marked as outdated.
This comment was marked as outdated.
|
@julien-deramond @XhmikosR I just finished the migration in my Bootstrap-based project, and I'm able to generate almost the same CSS as is available now. The only differences are in color: hex colors are now rgb, and the precision of the rgb colors is different than before. Almost everything works like before. I can use whole library and override variables: @use "bootstrap" with (
$accordion-padding-x: 20rem
);I can use particular components: @use "alert";But the problem occurs when I want to import a particular component and override its variables: @use "alert" with (
$accordion-padding-x: 20rem
);Then I have the following error: Error: This variable was not declared with !default in the @used module.
2 │ $accordion-padding-x: 20remWhat will you do with specific component variables? It's not possible to use certain components with custom variables. The only solution I came up with is to define component-related variables directly within the components. What do you think about it? |
ca1d0c2 to
30220cd
Compare
|
Closing this one. Superseded by #41512. |
Bootstrap 5.3.x's SCSS triggers Sass 3.0 deprecations (`@import`, `mix()` and other global builtins, `red()`/`green()`/`blue()`, `if()`, the legacy JS API). Upstream's fix PR (twbs/bootstrap#41112) was closed without merging; the cleanup landed only on v6-dev as part of a full rewrite (twbs/bootstrap#40849, twbs/bootstrap#40962). Until we can move the peer dep to Bootstrap v6, these IDs are silenced at each Sass compiler invocation: - apps/ng-bootstrap-demo/project.json — extends the existing demo `stylePreprocessorOptions.sass.silenceDeprecations` array (was just `["import"]`). Comment on `targets.build` explains the rationale and the upstream issue/PR numbers for future readers. - tools/scripts/build-web-components.mjs — passes the same list to `sass.compile()` calls in the codegen-wc pipeline (which compiles `*.element.scss` and `*.styles.scss` files, some of which @import bootstrap partials). - libs/mintplayer-ng-bootstrap/ng-package.json — adds `lib.sass.silenceDeprecations` for the umbrella primary entry point. - libs/mintplayer-ng-bootstrap/ng-package.secondary.cjs — new shared module exporting `secondaryEntry(extra)` that builds each secondary entry point's ng-package config with the same silenced-deprecations list. Single source of truth for the 94 secondaries. - 94 secondary entry-point configs renamed via `git mv` from `ng-package.json` to `ng-package.js` (the only ng-package extension ng-packagr supports for JS modules — `.cjs` is not scanned). Each is a two-liner: `module.exports = require('<rel>/ng-package.secondary.cjs').secondaryEntry();` with optional override for `styleIncludePaths` on `datatable` and `virtual-datatable`. Net effect: `nx build ng-bootstrap-demo` goes from 1538 deprecation warnings (incl. the per-entry-point "N repetitive deprecation warnings omitted" rollups) to 0. Removal trigger: when the bootstrap peer dep bumps to v6, drop the `silenceDeprecations` arrays from all four locations above (the shared `.cjs` comment lists them). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…335) * fix(dock): isolate intersection glyphs to splitters in the same layer (#326) renderIntersectionHandles() paired every horizontal divider with every vertical divider across the entire shadow root, including dividers from floating panes. Coincidental on-screen alignment between a docked splitter and a floating pane's splitter (or between two floating panes) produced a phantom grip between two splitters that don't actually cross. Reject cross-layer (h, v) pairs by tagging each DividerInfo with the layer of its containing element. Layer key is read via splitter.closest('.dock-floating')?.dataset.path — splitters carry no layer prefix on their own data-path, but the floating wrapper does (formatted as f:N by renderFloatingPanes). The corner-resize fast path is untouched: it acts on an already- validated pair from a previous render. Covered by mint-dock-manager.intersections.spec.ts (4 specs, regression- positive — confirmed to fail when the guard is disabled) and dock-intersections.spec.ts e2e (Chromium + Firefox). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(release): bump @mintplayer/ng-bootstrap to 21.33.0 Carries the dock cross-layer intersection-glyph fix (#326). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(build): silence Bootstrap 5.3 SCSS deprecation warnings Bootstrap 5.3.x's SCSS triggers Sass 3.0 deprecations (`@import`, `mix()` and other global builtins, `red()`/`green()`/`blue()`, `if()`, the legacy JS API). Upstream's fix PR (twbs/bootstrap#41112) was closed without merging; the cleanup landed only on v6-dev as part of a full rewrite (twbs/bootstrap#40849, twbs/bootstrap#40962). Until we can move the peer dep to Bootstrap v6, these IDs are silenced at each Sass compiler invocation: - apps/ng-bootstrap-demo/project.json — extends the existing demo `stylePreprocessorOptions.sass.silenceDeprecations` array (was just `["import"]`). Comment on `targets.build` explains the rationale and the upstream issue/PR numbers for future readers. - tools/scripts/build-web-components.mjs — passes the same list to `sass.compile()` calls in the codegen-wc pipeline (which compiles `*.element.scss` and `*.styles.scss` files, some of which @import bootstrap partials). - libs/mintplayer-ng-bootstrap/ng-package.json — adds `lib.sass.silenceDeprecations` for the umbrella primary entry point. - libs/mintplayer-ng-bootstrap/ng-package.secondary.cjs — new shared module exporting `secondaryEntry(extra)` that builds each secondary entry point's ng-package config with the same silenced-deprecations list. Single source of truth for the 94 secondaries. - 94 secondary entry-point configs renamed via `git mv` from `ng-package.json` to `ng-package.js` (the only ng-package extension ng-packagr supports for JS modules — `.cjs` is not scanned). Each is a two-liner: `module.exports = require('<rel>/ng-package.secondary.cjs').secondaryEntry();` with optional override for `styleIncludePaths` on `datatable` and `virtual-datatable`. Net effect: `nx build ng-bootstrap-demo` goes from 1538 deprecation warnings (incl. the per-entry-point "N repetitive deprecation warnings omitted" rollups) to 0. Removal trigger: when the bootstrap peer dep bumps to v6, drop the `silenceDeprecations` arrays from all four locations above (the shared `.cjs` comment lists them). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scss): use node_modules/ form for bootstrap @imports + Some Sass setup Component SCSS files used a fragile, depth-dependent relative path to pull in Bootstrap partials (e.g. `@import "../../../../../node_modules/bootstrap/scss/functions"`). Replace those with the workspace-relative `@import "node_modules/bootstrap/scss/..."` form so the path is stable regardless of where the file lives. To keep both this new form AND the existing `bootstrap/scss/...` form (used by the published `_bootstrap.scss`, which consumers import as a public API) resolving at Sass-compile time: - libs/mintplayer-ng-bootstrap/ng-package.secondary.cjs — `secondaryEntry()` now seeds `styleIncludePaths` with both `REPO_ROOT` (workspace root) and `NODE_MODULES`. Per-entry-point overrides still merge on top. - tools/scripts/build-web-components.mjs — sass.compile() `loadPaths` extended with `repoRoot` for the codegen-wc pipeline. - apps/ng-bootstrap-demo/project.json — `stylePreprocessorOptions.includePaths` now lists `.` and `node_modules` (the `libs/mintplayer-ng-bootstrap/datatable/src` entry was no longer needed once node_modules resolution is in place). 34 library `*.component.scss` files (190 @imports) and the demo's `swiper.component.scss` (5 @imports) were rewritten by a one-shot regex. `has-overlay.component.scss` got the same treatment for its `@angular/cdk` import. The published `_bootstrap.scss` was deliberately reverted — it ships to consumers, who expect the Node-style `bootstrap/scss/...` form. VS Code Ctrl+click on the new imports requires the Some Sass extension — its link provider is what understands workspace load paths: - .vscode/extensions.json — recommends `somewhatstationery.some-sass`. - .vscode/settings.json — sets `somesass.workspace.loadPaths` to `[".", "node_modules"]` and turns on `somesass.scss.links.enabled` + `somesass.sass.links.enabled` (both default to `false`; without them, Ctrl+click falls back to VS Code's built-in relative-from-current-file resolution which breaks for our form). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(datatable): @use 'datatable-sort' breaks when demo build resolves library SCSS from source The demo's Angular sass plugin compiles `datatable.component.scss` and `virtual-datatable.component.scss` directly from source (TypeScript path mapping points the library imports at the workspace tree, not the ng-packagr dist). It uses `apps/ng-bootstrap-demo/project.json`'s `stylePreprocessorOptions.includePaths` for load paths, which no longer lists `libs/mintplayer-ng-bootstrap/datatable/src` — so the bare `@use 'datatable-sort'` couldn't resolve. Switch the two import sites to the workspace-relative form `@use 'libs/mintplayer-ng-bootstrap/datatable/src/datatable-sort'`, which resolves the same way in both build paths: - Demo build: via the demo's `.` include path (workspace root). - ng-packagr (datatable + virtual-datatable entry points): via the `REPO_ROOT` load path seeded by `secondaryEntry()` in `ng-package.secondary.cjs`. That makes the per-entry-point `styleIncludePaths` overrides in datatable/ng-package.js (`'src'`) and virtual-datatable/ng-package.js (`'../datatable/src'`) redundant — both files now use the bare `secondaryEntry()` like the other 92 secondaries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(scss): use plain relative paths for the datatable-sort @use Simpler than the workspace-relative form: `../datatable-sort` from the datatable component, `../../../datatable/src/datatable-sort` from the virtual-datatable component. Both Ctrl+click natively in any editor without needing a load-path-aware language server. The cross-entry-point dependency in virtual-datatable is unchanged — it already shared this partial via the old `styleIncludePaths: ['../datatable/src']` override that was dropped in the previous commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(navbar): noscript SSR menu empty since #280 signal migration The RxJS → signals refactor in #280 changed showNavs() to return false eagerly when windowWidth is null (SSR), which makes [@slideUpDown] bake style="height: 0" into the SSR HTML. The existing noscript CSS reveal rules only fire on :has(:checked) / :focus-within, so noscript users saw an empty menu at every viewport on first paint. Restore wide-mode visibility with a new @each $grid-breakpoints loop in the .navbar.noscript SCSS block that overrides height + overflow at @media (min-width: navbar-expand-bp). Small-mode click reveal (:has(:checked)) and small-mode keyboard reveal (:focus-within) paths are unchanged. Spec captured at docs/prd/navbar-noscript.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(datatable): PRD — virtual+regular merge and Vidyano-style selection Trims the broader draft on PR #314 (origin/docs/prd-datatable-tree-and-selection, commit 71ae51c) to the scope agreed for issues/#326: merge virtual-datatable into datatable behind a [virtualScroll] switch, unify [(data)] and [dataSource] under a single [fetch] callback input, and add Vidyano-style checkbox selection (no select-all; deselect-all header checkbox appears checked when >=1 row is selected; selection stores objects compared via consumer-supplied compareWith to survive pagination/virtual-scroll re-fetches). Tree mode and the apps/api ASP.NET Core demo backend from the broader draft are deferred to follow-up PRDs. Tracks #306 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(tooltip): import BsTooltipDirective in test harness component Silence NG0303 ("Can't bind to 'bsTooltip' since it isn't a known property of 'div'") that fired in stderr on every test run. The harness component is standalone, so the directive must appear in its own `imports`, not just in the TestBed module's `imports`. The test itself was already passing; only the stderr noise is removed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(datatable): merge virtual-datatable in + Vidyano-style selection (#306) Folds <bs-virtual-datatable> into <bs-datatable> behind a [virtualScroll] input switch. Both modes now share the same data contract: fetch = input.required<(req: PaginationRequest) => Promise<PaginationResponse<T>>>(); replacing both [(data)] (paginated) and [dataSource] (virtual). The component owns the call cadence: one fetch per settings change in paginated mode; viewport-driven page fetches in virtual mode. Adds checkbox selection per the Vidyano UX: - [selectable]='none' | 'single' | 'multiple' - [(selection)]: TData[] — stores object refs as fetched - [compareWith]: (a, b) => boolean — required for non-'none' modes so selection survives cross-page / cross-scroll re-fetches where object identity isn't preserved - Header cell is empty when selection is empty. When >=1 row is selected a single deselect-all toggle appears, rendered already-checked. Any toggle event on it clears the entire selection. - Per-row + header cells use <bs-toggle-button type="checkbox"> for the Bootstrap-themed check style. Row template normalises to <ng-template bsRowTemplate let-row> with <td> cells inside (the old [bsRowTemplateOf]="data()" feed-from-input shape is gone; the component owns the data). The virtual-only bsVirtualRowTemplate directive is dropped. The @mintplayer/ng-bootstrap/virtual-datatable secondary entry-point is deleted entirely. The aria-rowcount/-rowindex coverage from its spec is ported under datatable/. Demo migrated: apps/ng-bootstrap-demo/.../datatables.component is now a single <bs-datatable> driven by a 'pagination' | 'virtualScroll' <bs-select> toggle, with multi-select wired to ArtistService and compareWith on id. Tree mode and the apps/api ASP.NET Core backend from the broader draft on PR #314 are intentionally deferred — see PRD. Tracks #306. PRD: docs/prd/datatable-virtual-merge-and-selection.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(datatable): center select toggles + reserve header space (no layout shift) Two regressions from the bs-toggle-button swap: 1. The toggle's <label class="form-check"> was top-aligned inside its inline-block host because Bootstrap's form-check has a 2px margin-bottom that left an unused band below the label, and the row's <td> had Bootstrap's default `vertical-align: top`. Fix: vertical-align: middle on .select-col cells, inline-flex + align-items:center on the host bs-toggle-button, and ::ng-deep strip the label's margin-bottom. 2. The header's deselect-all toggle was conditionally rendered via @if (hasAnySelection()), so the <th> grew the moment the first row was checked — every column below jumped down by a row. Fix: render the toggle unconditionally and gate visibility with [class.invisible]="!hasAnySelection()". The toggle still participates in layout when hidden, so no shift. The deselect-all handler now guards on `value === false` because the two-way [isToggled]="hasAnySelection()" binding triggers the change event on parent-side writes too — only the user-driven false transition should clear the selection. Verified at /advanced/datatables: - Header th height before vs after first-row selection: 42.5px → 42.5px (heightDelta 0). - Input vertical center vs td vertical center: 258 vs 257. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(datatable): re-wire scroll + column-width sync when virtual mode activates The ported `setupScrollSync` and `setupColumnWidthSync` ran in `ngAfterViewInit` — once, at component init. With the unified component the user can flip into virtual mode *later*, by which point ngAfterViewInit has already run. The `cdk-virtual-scroll-viewport` that the setup methods need to query doesn't exist in the DOM until the `@if (virtualScroll())` branch renders, so the queries returned null and the sync never wired. Result: header columns sized to header text, body columns sized to row content, no alignment. Move the setup into an `effect()` that fires whenever `virtualScroll()` is true, defers one frame for the viewport DOM to mount, and cleans up its own listeners + MutationObserver on the next toggle-off (or on component destroy). `setupScrollSync` / `setupColumnWidthSync` now take a `teardowns` array parameter instead of pushing to a single component-lifetime cleanup list, so a pagination→virtual→pagination cycle doesn't leak listeners. The width-sync logic itself is unchanged (still matches master's `6a537c19` reference: maxWidths[] accumulator, save/restore scrollLeft, width:max-content override during measurement, apply min-widths to all visible rows). Verified at /advanced/datatables: - First switch into virtual mode: header/body column-left offsets match pixel-perfect (max misalignment 0px across 4 columns). - After scroll into a not-yet-fetched page: still 0px. - After pagination→virtual→pagination→virtual cycle: still 0px. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(e2e): regression test for datatable virtual-mode column alignment Three Playwright scenarios that lock in the fix from fd2d1f1: 1. Paginated → virtual: after flipping the mode select, header column left positions must match the first body row's column lefts within 1px (sub-pixel rounding tolerance). 2. Pagination ↔ virtual cycle: the second entry into virtual mode used to miss the sync wiring because ngAfterViewInit had already fired. 3. Vertical scroll into a not-yet-fetched page: alignment must hold after the MutationObserver-driven re-measure runs. The mintplayer.com artist API is mocked via page.route() so the spec is deterministic and offline-friendly. Names are deliberately wider than the "Artist" column header to force the alignment problem. Verified by reverting the fix locally: the first scenario fails with "Received: 216" (216px misalignment) on both Chromium and Firefox. With the fix in place, all 6 cases (3 scenarios × 2 browsers) pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(datatable): PRD — measure-once resizable columns Adds a "Resizable columns — measure-once" section to the existing datatable PRD and updates the overview + component API table. Spec at a glance: - Each column has three runtime states: Pristine → Auto-sized → User-locked. Transitions are one-way; content-driven re-measure never fires again once a column is sized. - Source of truth: `columnWidths = signal<Map<string, number>>(new Map())` keyed by `BsDatatableColumnDirective.name()`. Position- indexed accumulators are gone. - Initial sizing runs once on the first row-mount mutation (virtual mode) or first response render (paginated mode): measures each column as `max(header, …visible td)` using the existing `width: max-content !important` trick, then disconnects the MutationObserver. - Resize handle: 6px vertical bar absolutely positioned at the right edge of each <th>, role="separator", touch-action: none, focusable, pointerdown + setPointerCapture + pointermove + pointerup with stopPropagation() so the sort click underneath doesn't fire. - Keyboard: Arrow ±10px, Shift+Arrow ±1px. aria-valuenow tracks live pixel width. - No adjacent-column compensation — only the dragged column moves. New component inputs: `resizableColumns: boolean = true`, `minColumnWidth: number = 40`. Replaces the current `setupColumnWidthSync` maxWidths "only grows, never shrinks" loop, which had to be removed to honour the "widths remain the same across page/scroll" promise. Out of scope for v1: persistence beyond component lifetime, double-click-to-fit, drag-to-reorder, per-column min-width override. Tracks #306. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(datatable): PRD — promote double-click-to-fit into scope Adds a "Double-click to fit" subsection inside the resizable-columns section. `dblclick` on the resize handle re-measures the column from its currently visible content (the same `measureColumnWidth(name)` helper used for initial auto-sizing) and pins the result, transitioning the column to (or keeping it in) User-locked. In virtual mode the measurement only sees the rendered range, so fitting after scrolling can yield a different width if the new viewport's content varies; that's the intended behaviour. Keyboard equivalent: `Home` on the focused handle triggers the same fit path so the gesture isn't pointer-only. Markup snippet updated to include the `(dblclick)` binding for internal consistency. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(datatable): measure-once resizable columns Implements the resizable-columns spec from docs/prd/datatable-virtual-merge-and-selection.md § Resizable columns. Behaviour - Three-state lifecycle per column (Pristine → Auto-sized → User-locked); transitions are one-way and content-driven re-measurement never fires again once a column is sized. The previous "only grows, never shrinks" maxWidths[] accumulator in setupColumnWidthSync is gone. - Source of truth: `columnWidths = signal<ReadonlyMap<string, number>>` keyed by `BsDatatableColumnDirective.name()`. Position-based indexing was fragile under column reorder. - Initial auto-size runs on the first row-mount mutation (virtual) or first non-empty response (paginated) via an effect that short-circuits once every column has a width. Disconnects the MutationObserver after the first apply; subsequent re-applies on virtual recycle just write the locked pixel value to the new cell. - table-layout: fixed + width / min-width / max-width all set to the locked value pins the column exactly. With table-layout: auto, body content wider than the pinned width would have stretched the column past the user's chosen value; fixed honours the width and clips via overflow:hidden + text-overflow:ellipsis on the cell. Gestures (handle is a 6px vertical `<div role="separator">` at the right edge of each <th>): - Pointer: pointerdown + setPointerCapture + pointermove (rAF-coalesced) + pointerup/cancel. Drag updates only the dragged column; no adjacent-column compensation. - Double-click: re-measures the column from currently visible content and pins the result. In virtual mode this is the rendered range only. - Keyboard: ArrowLeft/Right ±10px (Shift = ±1px), Home = fit. Enter/Space are consumed no-ops so the handle is a real focus stop instead of bubbling to the <th>'s sort handler. - Click on the handle is consumed (stopPropagation) so it can't trigger a sort. pointerdown also stopPropagation's. Component API: - New input `resizableColumns: boolean = true` - New input `minColumnWidth: number = 40` (drag clamp + keyboard floor) Demo + aria spec fix The `name` input on `BsDatatableColumnDirective` is aliased to `bsDatatableColumn`, so `<ng-template bsDatatableColumn name="Name">` silently doesn't bind (all columns ended up with name() === '' and a single Map entry). Switched the demo + datatable.aria.spec harness to the working `<ng-template bsDatatableColumn="Name">` syntax. Verified live at /advanced/datatables in virtual mode: - Drag −50px: header & body both shrink 50px, alignment preserved. - Keyboard ArrowRight ×5: column grows 50px (5 × 10px step). - Home: column re-measures to natural max(header, body) width. - Click on the handle does NOT toggle sort (aria-sort stays 'none'). - All 6 alignment regression tests (datatable-virtual.spec.ts) still pass on Chromium + Firefox. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(datatable): rename column.templateRef → headerTemplateRef Clarifies that the `<ng-template bsDatatableColumn="Name">…</ng-template>` content is the *header* template (rendered inside the <th>) and that the directive's value is the data-property name (matched against the row JSON, used as `SortColumn.property`). Internal rename only — the public directive surface (selector, inputs) is unchanged. Updates the two template outlet sites and the matching PRD snippet. Adds a JSDoc on the directive explaining the two roles. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * revert(datatable): keep templateRef name (rename broke header rendering) The previous rename (templateRef → headerTemplateRef) was meant to be internal-only, but it left the headers empty in both modes — direct inspection via window.ng shows the directive instance does have a TemplateRef under headerTemplateRef, yet `<ng-container *ngTemplateOutlet="column.headerTemplateRef">` rendered nothing. Stale Vite cache vs. directive-side rename, most likely. Revert to the working `templateRef` property name; keep the JSDoc on the directive that documents the contract (value = data property name; template content = header template). The "column header template" naming lives in the doc comment now. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(datatable): pad measured column widths by 0.25rem to prevent clipping Sub-pixel rendering rounds offsetWidth down by 1-3px. With table-layout: fixed + overflow: hidden, pinning the column to that exact value squeezed the content and clipped the last few px of long labels — most noticeable on double-click-to-fit. Add a 4px (0.25rem at default 16px base) safety pad to every measured width in `measureWidths`. Applies to both initial auto-sizing and the dblclick / Home fit gestures. Verified at /advanced/datatables, virtual mode: - Auto-sized Artist column: 196 → 200, content uncropped (scrollWidth equals clientWidth across visible rows). - Shrink → dblclick fit roundtrip: returns 40 → 200, matches initial. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(scheduler): never pan from mouse; mouse-on-event always drags Mouse on an event tile incorrectly entered pan mode after a >10px move within 600ms, hijacking the gesture from the drag state machine. Panning is a touch-only affordance; with a mouse the user should be able to drag events immediately and navigate via scroll / wheel / nav controls. Strips the mouse pan-candidate state and branches from InputHandler so mouse handlers forward straight to the drag manager. Touch path (short-move → pan, hold 600ms → drag/create) is unchanged. PRD: docs/prd/scheduler-improve-pan.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(datatable): extract shared header + row-cell templates Pulls the duplicated <thead> subtree and per-row <td> rendering out of the virtual and paginated branches into #headerCells / #rowCells ng-templates referenced via *ngTemplateOutlet. Trims the compiled component by ~1.8 KB raw (gzip is unchanged) and removes the maintenance hazard of keeping the two header copies in sync. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(demo): drop unused BsFormControlDirective import in tile-manager Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(toggle-button): mirror aria-* attributes from host onto inner <input> The host element is a plain Angular tag (no shadow DOM), so screen readers compute the focused control's accessible name from the inner <input> only. `aria-label` / `aria-labelledby` / `aria-describedby` set on <bs-toggle-button> never reached AT. ngAfterViewInit now copies every aria-* attribute to the input and a MutationObserver keeps the mirror in sync with [attr.aria-…] bindings that change at runtime. SSR-safe; observer torn down via DestroyRef. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(datatable): SSR-safe rAF + row checkbox aria-label + resize aria-valuemax - Guard the virtual-mode wire-up effect and the initial-auto-size effect with isPlatformServer(PLATFORM_ID) so requestAnimationFrame doesn't run during SSR. - Add [selectionLabel]="(row) => …" input; row checkbox aria-label is "Select <label>" or "Select row N" fallback when omitted. Row index is now threaded through the #rowCells template context. - Observe host width via ResizeObserver to drive aria-valuemax on the resize-handle separators; previously only valuenow/valuemin were set, leaving role=separator's value scale ambiguous. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(prd): split <bs-toggle-button> into <bs-checkbox> and <bs-radio> PRD for the multi-step refactor that introduces two narrowed per-family public components and reduces <bs-toggle-button> to a styling-only wrapper. Covers component hierarchy, type unions, input contracts, value accessor split, group directives with exportAs + explicit-vs-ancestor [group] resolution, ARIA mirroring relocation, ReactiveFormsModule integration, demo page scenarios with <bs-code-snippet> blocks, and the migration table for all 15 current consumers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(toggle-button): reduce to styling wrapper Per PRD docs/prd/toggle-button-split.md § 5 commit 1. <bs-toggle-button> becomes a passive component whose only job is to scope the Bootstrap form-check SCSS via :host ::ng-deep onto descendant content. No template file, no inputs, no value accessor, no group directive, no type union. This commit alone breaks every consumer of the old API (datatable, multiselect, color-picker, 12 demo pages). The subsequent commits in the chain add <bs-checkbox> and <bs-radio> and migrate every consumer. Removed exports (breaking): - BsToggleButtonValueAccessor - BsToggleButtonGroupDirective - BsCheckStyle Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(checkbox): add BsCheckboxComponent + group + accessor Per PRD docs/prd/toggle-button-split.md § 5 commit 2. New package at @mintplayer/ng-bootstrap/checkbox exporting: - BsCheckboxComponent — type 'checkbox' | 'switch' | 'toggle_button'. Template wraps <bs-toggle-button> (from @mintplayer/ng-bootstrap/toggle-button) around the <label>/<input>; the wrapper's :host ::ng-deep SCSS provides Bootstrap form-check styling. ARIA host→input mirroring preserved (MutationObserver, SSR-guarded). Resolves [group] as explicit input ?? DI-injected ancestor (skipSelf:true). - BsCheckboxGroupDirective — [bsCheckboxGroup] with exportAs and a [name] input. Implements ControlValueAccessor for multi-mode: emits string[] of checked values; writeValue sets isToggled on matching children. - BsCheckboxValueAccessor — host directive on bs-checkbox for single-mode (standalone) boolean form binding. - BsCheckboxType — narrowed type union. Name resolution is mode-aware: standalone uses the component's own [name]; in-group uses the group's [name] with [] suffix auto-applied. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(radio): add BsRadioComponent + group + accessor Per PRD docs/prd/toggle-button-split.md § 5 commit 3. New package at @mintplayer/ng-bootstrap/radio exporting: - BsRadioComponent — type 'radio' | 'toggle_button'. Mirrors bs-checkbox's structure (template wraps <bs-toggle-button>, ARIA host→input mirroring, resolvedGroup = explicit input ?? DI ancestor). NO [name] input — name comes from the resolved group. NO per-instance value accessor — radios always bind through the group. - BsRadioGroupDirective — [bsRadioGroup] with exportAs, [name] input, and ControlValueAccessor implementation. Emits the selected radio's value on change; writeValue walks children and sets isToggled on the match. - BsRadioType — narrowed type union. Standalone <bs-radio> (no group resolved) renders but doesn't bind — by design; radios exist to express one-of-N within a group. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(datatable): migrate from <bs-toggle-button> to <bs-checkbox> Per PRD docs/prd/toggle-button-split.md § 5 commit 4. Two sites: - datatable.component.html: <bs-toggle-button type="checkbox"> Deselect-all header toggle + per-row select checkbox both swap to <bs-checkbox>. The [isToggled] / (isToggledChange) bindings carry over unchanged; aria-label and [attr.aria-label] still mirror to the inner <input> via the new component's ARIA mirroring. - datatable.component.ts: imports flip from BsToggleButtonComponent to BsCheckboxComponent. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(multiselect, color-picker): migrate to <bs-checkbox> Per PRD docs/prd/toggle-button-split.md § 5 commit 5. - multiselect: in-dropdown item toggle (was the implicit-default checkbox) swaps to <bs-checkbox>; [ngModel] binding carries over. - color-picker: accessibility-toggle for the hue/saturation sliders (type="switch") swaps to <bs-checkbox type="switch">. Both component .ts files update their imports. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(demo): migrate non-showcase toggle-button consumers Per PRD docs/prd/toggle-button-split.md § 5 commit 6. Swaps every demo page that uses <bs-toggle-button> (except the dedicated showcase, which is deleted in the next commit). 13 .html files + matching .ts imports + spec mocks updated. Checkbox-style migrations (12 pages): basic/color-picker, basic/forms/range, basic/forms/multi-range, basic/forms/select, basic/placeholder, basic/table, animations/color-transition, advanced/navigation-lock, advanced/pipes/has-property, advanced/pipes/linify, advanced/tile-manager, advanced/splitter, overlay/offcanvas — all swap to <bs-checkbox>. Radio-style migration (1 page): overlay/shell's three radio_toggle_button toggles become <bs-radio type="toggle_button"> inside a <bs-button-group bsRadioGroup name="shellState" [(ngModel)]="shellState">. Drive-by cleanup: tab-control.component.spec.ts had dead MockComponent / MockDirective imports for BsToggleButtonComponent + BsToggleButtonGroupDirective that its template never used; both removed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(demo): add /forms/checkbox + /forms/radio, delete /forms/toggle-button Per PRD docs/prd/toggle-button-split.md § 5 commit 7 and § 6 demo scenarios. Two new routes replace the old `/forms/toggle-button` showcase; no redirect. /forms/checkbox covers 7 scenarios per § 6.1: 1. Single template-driven ([(ngModel)] + boolean) 2. Single reactive ([formControl] + boolean) 3. type="switch" 4. type="toggle_button" (standalone) 5. Group adjacent template-driven (string[] via [bsCheckboxGroup]) 6. Group adjacent reactive 7. Group non-adjacent (table row pattern with exportAs + [group]) /forms/radio covers 4 scenarios per § 6.2: 1. Group adjacent template-driven (string via [bsRadioGroup]) 2. Group adjacent reactive 3. Group with type="toggle_button" (radio-style button toggle) 4. Group non-adjacent (table row pattern) Each scenario is rendered as a live demo paired with <bs-code-snippet> blocks (TS + HTML) per the existing /forms/multi-range pattern. forms.routes.ts and the nav menu add `/forms/checkbox` + `/forms/radio` in place of `/forms/toggle-button`. The /forms/toggle-button folder is deleted; the demo app's bundle restores cleanly after this commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(changelog): document <bs-toggle-button> split Per PRD docs/prd/toggle-button-split.md § 5 commit 8. Records the new @mintplayer/ng-bootstrap/checkbox and /radio packages under Added, plus the public-API removals (BsToggleButtonValueAccessor, BsToggleButtonGroupDirective, BsCheckStyle) and the full migration table under Breaking. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(checkbox, radio): sync writeValue + radio auto-uncheck via DOM truth Two bugs caught via playwright smoke test of the new demo pages and the migrated shell route: 1. BsRadioGroupDirective.onChildChange read stale isToggled() signals. The browser auto-unchecks sibling radios in the same `name` group without firing change events on them, so a previously-checked sibling's isToggled cache stayed true. The group's `find(r => r.isToggled())` returned the stale match instead of the freshly-clicked one, so ngModel/formControl kept emitting the old value. Re-sync every child's isToggled signal from `input.checked` on every change, then emit. 2. Both group directives' writeValue fired before contentChildren had populated, so an initial ngModel/formControl value (e.g. shell's shellState='auto') never reflected on the DOM — none of the children were visible to writeValue at call time. Introduce a currentValue signal plus an effect that syncs each child's isToggled whenever currentValue OR the children set changes. The effect runs after view init regardless of timing. Demo: rename the two `name="fruit"` radio groups in /forms/radio to "fruitTemplate" / "fruitReactive" so they're separate HTML radio groups (the browser was treating them as one and cross-unchecking). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(datatable): rows span host width via trailing spacer cell When pinned column widths sum to less than the host width, the rows visually under-filled their container. The naive fix (min-width: 100% on the table) either stretches all pinned columns proportionally — which breaks the resize-freeze semantics — or, at narrow viewports, blows the body past the viewport because isResponsive=false leaves no scroll wrapper. This change appends an aria-hidden .bs-datatable-spacer cell to every header and body row. Under table-layout: fixed the unsized spacer absorbs leftover width without redistributing it, so pinned widths stay frozen. The CSS pairs width: max-content with min-width: 100% (the inverse order triggers a CDK virtual-scroll wrapper layout loop). The bs-table wrapper also gets unconditional overflow-x: auto in resizable mode so wider-than-host pinned widths scroll inside the table region instead of expanding the page body. Footer colspan bumped by 1. Closes the deferred column-fill PRD via a CSS+DOM hybrid rather than the originally-recommended JS proportional-distribution algorithm — browser table-layout does the math, no scrollLeft preservation needed, no ResizeObserver for stretch math. Regression: datatable.spacer.spec.ts asserts the spacer DOM contract (tag, class, position, aria-hidden) and footer colspan across paginated (with and without selection) and virtual modes. * fix(checkbox, radio): wrap contentChildren predicate in forwardRef `BsRadioGroupDirective` queries `BsRadioComponent` children via `contentChildren(BsRadioComponent, ...)`; the component imports back to inject the directive, so the two modules form an ES-module cycle. Whichever side webpack/vite evaluates first sees `undefined` for the other's exports at field-initialiser time, the recorded query predicate becomes null, and ng-mocks' `MockDirective` fails directive-metadata extraction with "Can't construct a query for the property 'radios' of 'BsRadioGroupDirective2' since the query selector wasn't defined" — breaking the shell.component.spec.ts in the demo app, which is what the CI workflow tripped on. Same latent bug exists between `BsCheckboxGroupDirective` and `BsCheckboxComponent`; nothing was mocking that pair yet but the trap was set. Fixed both: predicate wrapped in `forwardRef(...)` with the generic type argument supplied at the `contentChildren<T>(...)` call site so we keep the typed signal without an `as Type<T>` cast. * chore(ng-package): drop dead lib.sass block from umbrella ng-package.json The umbrella `ng-package.json` only configures the primary entry, whose `src/index.ts` exports enums/providers/types and pulls in no Bootstrap SCSS (the only `*.scss` files under `src/` are utilities that just `@use "sass:map"`). The `lib.sass.silenceDeprecations` block therefore had nothing to silence. The same list lives in `ng-package.secondary.cjs` for the secondary entries, where it does the actual work — that is where the Bootstrap 5.3 deprecation noise is actually generated. Updated its cleanup-on-v6 comment to drop the now-stale "AND in the umbrella ng-package.json" breadcrumb so a future bootstrap-v6 bump doesn't go hunting for a place that no longer exists. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Warning
This PR is heavily draft. It prepares the modifications for when we'll drop
node-sasssupportContext
Sass has begun deprecating certain features (see #40962 and #40849), which currently generate warning messages during compilation with Sass.
The first major breaking change for us came in Sass 1.79.x, where the
blue(),red(), andgreen()functions were deprecated. These functions were replaced by thecolor.channel()function, which is not supported bynode-sass, and where we can't use any workaround on our side.As a result, addressing these warnings requires us to drop
node-sasssupport.Bootstrap v6 development has not yet started, which raises an important consideration: Dart Sass v2 might be released before Bootstrap v6. If that happens, it may become necessary to remove
node-sasssupport in the final Bootstrap v5.x release rather than waiting for v6. This is because Bootstrap v6 will likely introduce a significant set of new features, whereas a v5.x release can focus on resolving compatibility issues. Droppingnode-sassin the last v5.x release ensures that all v5 content remains accessible tonode-sassusers while aligning with Dart Sass's latest updates.Even if it is clearly a breaking change, for
node-sassusers, with the right communication, I think this could be an acceptable solution for everyone.It'll also mean that for everyone, cascading the
node-sassremoval, Bootstrap users will need to use@useinstead of@import, but also@use <color;math;map;etc.>.Description
This PR drops
node-sasssupport and bump thesassdependency to the latest release.Following these changes, users will need to use
@useinstead of@import, and use@useSass color, math, map, etc. modules.Sub-tasks
node-sassGitHub workflowRGBAwithrgbasass→ 1.79.6Deprecation Warning: blue() is deprecated. Suggestion: color.channel($color, "blue", $space: rgb) More info: https://sass-lang.com/d/color-functions(same thing forred()andgreen()) → 1816145sass→ 1.80.xsass→ 1.81.xsass→ 1.82.xsass→ 1.83.xdist/css/bootstrap.cssfrom the main branch and this onerfsrepository (andvendordir here)docsjob, and with Netlifynode-sassspecific content (if any)node-sassanymoreType of changes
Checklist
npm run lint)Live previews
Related issues
Closes #40962
Closes #40849