@db-ux/ngx-core-components 4.11.1 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @db-ux/ngx-core-components
|
|
2
2
|
|
|
3
|
+
## 4.12.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fix(custom-select): dropdown with `dropdownWidth="auto"` now correctly sizes to content width and respects the trigger minimum width. Long option labels no longer get truncated: `auto` keeps them on a single line (dropdown grows to the longest option), while `fixed` and `full` wrap long labels onto new lines. - [see commit 68dedc3](https://github.com/db-ux-design-system/core-web/commit/68dedc33c324b48339d5bb73a85fdff3045ed059)
|
|
8
|
+
|
|
9
|
+
- fix(drawer): prevent backdrop drag-close when selection starts inside content - [see commit b53ff8a](https://github.com/db-ux-design-system/core-web/commit/b53ff8a4f0a5350c5be41fad072e14797676bba7)
|
|
10
|
+
|
|
11
|
+
## 4.12.0
|
|
12
|
+
|
|
13
|
+
_version bump_
|
|
14
|
+
|
|
3
15
|
## 4.11.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -2074,24 +2074,69 @@ const handleDataOutside = (el) => {
|
|
|
2074
2074
|
const handleFixedDropdown = (element, parent, placement) => {
|
|
2075
2075
|
if (!element || !parent)
|
|
2076
2076
|
return;
|
|
2077
|
-
|
|
2077
|
+
const fullWidth = element.dataset['width'] === 'full';
|
|
2078
|
+
const autoWidth = element.dataset['width'] === 'auto';
|
|
2079
|
+
// Reset width-specific inline styles first so a previous mode (e.g. "auto")
|
|
2080
|
+
// doesn't leave a stale minInlineSize/inlineSize behind when the dropdown
|
|
2081
|
+
// width changes at runtime. This must happen before getFloatingProps
|
|
2082
|
+
// measures the element, otherwise the dropdown would be measured with a
|
|
2083
|
+
// width it no longer has and positioned incorrectly. It also has to run
|
|
2084
|
+
// before the mobile bailout below: otherwise a desktop minInlineSize would
|
|
2085
|
+
// survive into the mobile sheet, where CSS min-inline-size beats the
|
|
2086
|
+
// mobile max-inline-size guard and overflows the viewport.
|
|
2087
|
+
element.style.inlineSize = '';
|
|
2088
|
+
element.style.minInlineSize = '';
|
|
2089
|
+
// We skip the rest if we are in mobile, it's already fixed via CSS.
|
|
2078
2090
|
if (getComputedStyle(element).zIndex === '9999')
|
|
2079
2091
|
return;
|
|
2080
|
-
const { top, bottom, childHeight, childWidth, width, right, left, correctedPlacement } = getFloatingProps(element, parent, placement);
|
|
2081
|
-
|
|
2092
|
+
const { top, bottom, childHeight, childWidth, width, right, left, correctedPlacement, innerWidth } = getFloatingProps(element, parent, placement);
|
|
2093
|
+
// For auto width the dropdown is forced to be at least as wide as the trigger,
|
|
2094
|
+
// but clamped to its own max-inline-size: CSS lets a min-inline-size override
|
|
2095
|
+
// the max when the minimum is larger, so a trigger wider than the viewport
|
|
2096
|
+
// limit would otherwise drop the side margins or overflow horizontally.
|
|
2097
|
+
let autoMinWidth = width;
|
|
2098
|
+
if (autoWidth) {
|
|
2099
|
+
const maxInlineSize = parseFloat(getComputedStyle(element).maxInlineSize);
|
|
2100
|
+
if (!isNaN(maxInlineSize) && maxInlineSize > 0) {
|
|
2101
|
+
autoMinWidth = Math.min(width, maxInlineSize);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2082
2104
|
if (fullWidth) {
|
|
2083
2105
|
element.style.inlineSize = `${width}px`;
|
|
2084
2106
|
}
|
|
2085
|
-
if (
|
|
2107
|
+
else if (autoWidth) {
|
|
2108
|
+
element.style.minInlineSize = `${autoMinWidth}px`;
|
|
2109
|
+
}
|
|
2110
|
+
// getFloatingProps measured childWidth before the inline styles were
|
|
2111
|
+
// (re)applied, so use the width the dropdown will actually have:
|
|
2112
|
+
// - auto: the clamped minimum, so end-aligned dropdowns don't extend past
|
|
2113
|
+
// the trigger's right edge.
|
|
2114
|
+
// - full: the trigger width (the reset above drops it to content width).
|
|
2115
|
+
let effectiveChildWidth = childWidth;
|
|
2116
|
+
if (autoWidth) {
|
|
2117
|
+
effectiveChildWidth = Math.max(childWidth, autoMinWidth);
|
|
2118
|
+
}
|
|
2119
|
+
else if (fullWidth) {
|
|
2120
|
+
effectiveChildWidth = width;
|
|
2121
|
+
}
|
|
2122
|
+
// getFloatingProps detects horizontal overflow assuming a centered element
|
|
2123
|
+
// (it halves childWidth). The dropdown is actually start-aligned (inset =
|
|
2124
|
+
// left), so for the wider auto dropdown re-check overflow against its full
|
|
2125
|
+
// width and flip to end-alignment when it would extend past the viewport.
|
|
2126
|
+
let dropdownPlacement = correctedPlacement;
|
|
2127
|
+
if (autoWidth && (dropdownPlacement === 'top' || dropdownPlacement === 'bottom' || dropdownPlacement === 'top-start' || dropdownPlacement === 'bottom-start') && left + effectiveChildWidth > innerWidth) {
|
|
2128
|
+
dropdownPlacement = dropdownPlacement.startsWith('top') ? 'top-end' : 'bottom-end';
|
|
2129
|
+
}
|
|
2130
|
+
if (dropdownPlacement === 'top' || dropdownPlacement === 'bottom' || dropdownPlacement === 'top-start' || dropdownPlacement === 'bottom-start') {
|
|
2086
2131
|
element.style.insetInlineStart = `${left}px`;
|
|
2087
2132
|
}
|
|
2088
|
-
else if (
|
|
2089
|
-
element.style.insetInlineStart = `${right -
|
|
2133
|
+
else if (dropdownPlacement === 'top-end' || dropdownPlacement === 'bottom-end') {
|
|
2134
|
+
element.style.insetInlineStart = `${Math.max(right - effectiveChildWidth, 0)}px`;
|
|
2090
2135
|
}
|
|
2091
|
-
if (
|
|
2136
|
+
if (dropdownPlacement?.startsWith('top')) {
|
|
2092
2137
|
element.style.insetBlockStart = `${top - childHeight}px`;
|
|
2093
2138
|
}
|
|
2094
|
-
else if (
|
|
2139
|
+
else if (dropdownPlacement?.startsWith('bottom')) {
|
|
2095
2140
|
element.style.insetBlockStart = `${bottom}px`;
|
|
2096
2141
|
}
|
|
2097
2142
|
element.style.position = 'fixed';
|
|
@@ -2638,7 +2683,8 @@ class DBCustomSelectListItem {
|
|
|
2638
2683
|
[disabled]="getBoolean(disabled(), 'disabled')"
|
|
2639
2684
|
[value]="value()"
|
|
2640
2685
|
(change)="handleChange($event)" />
|
|
2641
|
-
|
|
2686
|
+
<span class="db-custom-select-list-item-label"
|
|
2687
|
+
>@if(label()){{{label()}}} <ng-content></ng-content></span
|
|
2642
2688
|
></label>
|
|
2643
2689
|
}@else{
|
|
2644
2690
|
<span>{{groupTitle()}}</span>
|
|
@@ -2675,7 +2721,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
2675
2721
|
[disabled]="getBoolean(disabled(), 'disabled')"
|
|
2676
2722
|
[value]="value()"
|
|
2677
2723
|
(change)="handleChange($event)" />
|
|
2678
|
-
|
|
2724
|
+
<span class="db-custom-select-list-item-label"
|
|
2725
|
+
>@if(label()){{{label()}}} <ng-content></ng-content></span
|
|
2679
2726
|
></label>
|
|
2680
2727
|
}@else{
|
|
2681
2728
|
<span>{{groupTitle()}}</span>
|
|
@@ -4196,6 +4243,9 @@ class DBCustomSelect {
|
|
|
4196
4243
|
.toLowerCase()
|
|
4197
4244
|
.includes(filterText.toLowerCase())));
|
|
4198
4245
|
}
|
|
4246
|
+
if (this.detailsRef()?.nativeElement?.open) {
|
|
4247
|
+
this.handleAutoPlacement();
|
|
4248
|
+
}
|
|
4199
4249
|
}
|
|
4200
4250
|
handleClearAll(event) {
|
|
4201
4251
|
event.stopPropagation();
|
|
@@ -4531,6 +4581,10 @@ class DBCustomSelect {
|
|
|
4531
4581
|
// ---
|
|
4532
4582
|
this._options.set(this.options());
|
|
4533
4583
|
this.amountOptions.set(this.options()?.filter((option) => !option.isGroupTitle).length ?? 0);
|
|
4584
|
+
// Reposition open auto-width dropdowns: replacing options can change their content width.
|
|
4585
|
+
if (this.detailsRef()?.nativeElement?.open) {
|
|
4586
|
+
this.handleAutoPlacement();
|
|
4587
|
+
}
|
|
4534
4588
|
}, Number(VERSION.major) < 19
|
|
4535
4589
|
? { allowSignalWrites: true }
|
|
4536
4590
|
: undefined);
|
|
@@ -5375,6 +5429,18 @@ const DividerVariantList = ['horizontal', 'vertical'];
|
|
|
5375
5429
|
|
|
5376
5430
|
const defaultProps$q = {};
|
|
5377
5431
|
class DBDrawer {
|
|
5432
|
+
isNotModal() {
|
|
5433
|
+
return (this.position() === "absolute" ||
|
|
5434
|
+
this.backdrop() === "none" ||
|
|
5435
|
+
this.variant() === "inside");
|
|
5436
|
+
}
|
|
5437
|
+
handleBackdropPointerDown(event) {
|
|
5438
|
+
// Remember whether the pointer interaction started on the backdrop
|
|
5439
|
+
// (the DIALOG element itself) so we only close on a real backdrop
|
|
5440
|
+
// click and not when a drag started inside the content and ended
|
|
5441
|
+
// on the backdrop.
|
|
5442
|
+
this.backdropPointerDown.set(event?.target?.nodeName === "DIALOG");
|
|
5443
|
+
}
|
|
5378
5444
|
handleClose(event, forceClose) {
|
|
5379
5445
|
if (!event)
|
|
5380
5446
|
return;
|
|
@@ -5395,11 +5461,15 @@ class DBDrawer {
|
|
|
5395
5461
|
}
|
|
5396
5462
|
if (event.target?.nodeName === "DIALOG" &&
|
|
5397
5463
|
event.type === "click" &&
|
|
5398
|
-
this.backdrop() !== "none"
|
|
5464
|
+
this.backdrop() !== "none" &&
|
|
5465
|
+
this.backdropPointerDown()) {
|
|
5399
5466
|
if (this.close) {
|
|
5400
5467
|
this.close.emit(event);
|
|
5401
5468
|
}
|
|
5402
5469
|
}
|
|
5470
|
+
// Reset after handling the click so the next interaction
|
|
5471
|
+
// starts from a clean state.
|
|
5472
|
+
this.backdropPointerDown.set(false);
|
|
5403
5473
|
}
|
|
5404
5474
|
}
|
|
5405
5475
|
handleDialogOpen() {
|
|
@@ -5409,12 +5479,15 @@ class DBDrawer {
|
|
|
5409
5479
|
if (this.dialogContainerRef()?.nativeElement) {
|
|
5410
5480
|
(this.dialogContainerRef()?.nativeElement).removeAttribute("data-transition");
|
|
5411
5481
|
}
|
|
5412
|
-
if (this.
|
|
5413
|
-
this.backdrop() === "none" ||
|
|
5414
|
-
this.variant() === "inside") {
|
|
5482
|
+
if (this.isNotModal()) {
|
|
5415
5483
|
this._ref()?.nativeElement.show();
|
|
5416
5484
|
}
|
|
5417
5485
|
else {
|
|
5486
|
+
// Set the closedby attribute imperatively: the JSX
|
|
5487
|
+
// dialog type does not know this attribute yet, and it
|
|
5488
|
+
// only applies to modal dialogs. "any" enables native
|
|
5489
|
+
// light dismiss (backdrop click / Esc).
|
|
5490
|
+
this._ref()?.nativeElement.setAttribute("closedby", "any");
|
|
5418
5491
|
this._ref()?.nativeElement.showModal();
|
|
5419
5492
|
}
|
|
5420
5493
|
void delay(() => {
|
|
@@ -5473,6 +5546,7 @@ class DBDrawer {
|
|
|
5473
5546
|
this._ref = viewChild("_ref", ...(ngDevMode ? [{ debugName: "_ref" }] : /* istanbul ignore next */ []));
|
|
5474
5547
|
this.dialogContainerRef = viewChild("dialogContainerRef", ...(ngDevMode ? [{ debugName: "dialogContainerRef" }] : /* istanbul ignore next */ []));
|
|
5475
5548
|
this.initialized = signal(false, ...(ngDevMode ? [{ debugName: "initialized" }] : /* istanbul ignore next */ []));
|
|
5549
|
+
this.backdropPointerDown = signal(false, ...(ngDevMode ? [{ debugName: "backdropPointerDown" }] : /* istanbul ignore next */ []));
|
|
5476
5550
|
this.observer = signal(undefined, ...(ngDevMode ? [{ debugName: "observer" }] : /* istanbul ignore next */ []));
|
|
5477
5551
|
if (typeof window !== "undefined") {
|
|
5478
5552
|
effect(() => {
|
|
@@ -5562,6 +5636,7 @@ class DBDrawer {
|
|
|
5562
5636
|
[attr.id]="id() ?? propOverrides()?.id"
|
|
5563
5637
|
#_ref
|
|
5564
5638
|
(click)="handleClose($event)"
|
|
5639
|
+
(mousedown)="handleBackdropPointerDown($event)"
|
|
5565
5640
|
(keydown)="handleClose($event)"
|
|
5566
5641
|
[attr.data-position]="position()"
|
|
5567
5642
|
[attr.data-backdrop]="backdrop()"
|
|
@@ -5601,6 +5676,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
5601
5676
|
[attr.id]="id() ?? propOverrides()?.id"
|
|
5602
5677
|
#_ref
|
|
5603
5678
|
(click)="handleClose($event)"
|
|
5679
|
+
(mousedown)="handleBackdropPointerDown($event)"
|
|
5604
5680
|
(keydown)="handleClose($event)"
|
|
5605
5681
|
[attr.data-position]="position()"
|
|
5606
5682
|
[attr.data-backdrop]="backdrop()"
|