@ethlete/components 0.1.0-next.6 → 0.1.0-next.7
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,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-next.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`256b1b0`](https://github.com/ethlete-io/ethdk/commit/256b1b02d7598a0a6540af55447799a8ced469c4) Thanks [@TomTomB](https://github.com/TomTomB)! - Fix grid overlapping items with invalid base config
|
|
8
|
+
|
|
3
9
|
## 0.1.0-next.6
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { ChangeDetectionStrategy, ViewEncapsulation, Component, Directive, input, booleanAttribute, signal, effect, numberAttribute, computed, inject, ElementRef, InjectionToken, afterNextRender, untracked, DestroyRef, model, viewChild, Injector, output, ApplicationRef, EnvironmentInjector, DOCUMENT, createEnvironmentInjector, createComponent, TemplateRef, linkedSignal, afterRenderEffect, contentChild, inputBinding, PLATFORM_ID, isDevMode, contentChildren } from '@angular/core';
|
|
3
3
|
import * as i2 from '@ethlete/core';
|
|
4
|
-
import { injectStyleManager, createCanAnimateSignal, ColorInteractiveDirective, ProvideColorDirective, RuntimeError, injectErrorTheme, signalElementDimensions, createProvider, AnimatableDirective, ColorInteractiveContainerDirective, ColorInteractiveExcludeDirective, ProvideSurfaceDirective, SURFACE_PROVIDER, injectSurfaceThemes, resolveSurfaceByElevation, ColorInteractiveHasFocusDirective, createStaticRootProvider, signalHostElementDimensions,
|
|
4
|
+
import { injectStyleManager, createCanAnimateSignal, ColorInteractiveDirective, ProvideColorDirective, RuntimeError, injectErrorTheme, signalElementDimensions, createProvider, AnimatableDirective, ColorInteractiveContainerDirective, ColorInteractiveExcludeDirective, ProvideSurfaceDirective, SURFACE_PROVIDER, injectSurfaceThemes, resolveSurfaceByElevation, ColorInteractiveHasFocusDirective, createStaticRootProvider, signalHostElementDimensions, DragHandleDirective, injectRenderer, injectLocale, ResizeHandlesComponent, ANIMATED_LIFECYCLE_TOKEN, injectSurfaceContextTracker, AnimatedLifecycleDirective, createRootProvider, injectOverlayRuntime, typedBreakpointTransform, signalElementChildren, signalElementScrollState, signalElementIntersection, signalHostAttributes, signalHostClasses, signalHostStyles, getScrollContainerTarget, getScrollItemTarget, getElementScrollCoordinates, provideBreakpointInstance, signalClasses, useCursorDragScroll, signalStyles, getScrollSnapTarget, ScrollObserverDirective, ScrollObserverStartDirective, ScrollObserverEndDirective, injectViewportSize, createFlipAnimation, signalHostElementIntersection, injectHostElement, setInputSignal, SurfaceInteractiveDirective, injectRouterEvent, canUseSessionMemory, createAutoSessionMemoryKey, createSessionMemory, COLOR_PROVIDER, injectFocusVisibleTracker } from '@ethlete/core';
|
|
5
5
|
import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
|
|
6
6
|
import { toObservable, takeUntilDestroyed, outputToObservable, outputFromObservable, rxResource } from '@angular/core/rxjs-interop';
|
|
7
7
|
import { switchMap, EMPTY, filter, tap, timer, take, of, animationFrameScheduler, Subject, fromEvent, debounceTime, map, exhaustMap, takeUntil, finalize, distinctUntilChanged, Observable, shareReplay, catchError, interval } from 'rxjs';
|
|
@@ -2385,14 +2385,38 @@ const itemsCollide = (a, b) => {
|
|
|
2385
2385
|
* Returns the first item that collides with the given position, or undefined if none.
|
|
2386
2386
|
*/
|
|
2387
2387
|
const findCollision = (options) => options.entries.find((entry) => entry.id !== options.excludeId && itemsCollide(entry.position, options.position));
|
|
2388
|
+
/**
|
|
2389
|
+
* Clamps a position so it fits horizontally within the given column count.
|
|
2390
|
+
* Reduces an over-wide span and slides an out-of-bounds column back in range.
|
|
2391
|
+
*/
|
|
2392
|
+
const clampToColumns = (position, columns) => {
|
|
2393
|
+
const colSpan = Math.max(1, Math.min(position.colSpan, columns));
|
|
2394
|
+
const col = Math.max(0, Math.min(position.col, columns - colSpan));
|
|
2395
|
+
return { ...position, col, colSpan };
|
|
2396
|
+
};
|
|
2388
2397
|
/**
|
|
2389
2398
|
* Compacts the layout vertically (moves items up as far as possible without collision).
|
|
2399
|
+
*
|
|
2400
|
+
* Also acts as a self-healing normaliser: positions that overflow the grid horizontally
|
|
2401
|
+
* (e.g. stale data from a wider breakpoint clamped into a narrower one — a colSpan of 12
|
|
2402
|
+
* or a col of 8 in a 6-column grid) are first clamped back into bounds, then any items
|
|
2403
|
+
* left overlapping are pushed down before the upward compaction runs. This guarantees the
|
|
2404
|
+
* returned layout is always in-bounds and overlap-free regardless of the input — clamping
|
|
2405
|
+
* a column alone is not enough, because it can drop an item on top of an existing one.
|
|
2390
2406
|
*/
|
|
2391
|
-
const compactLayout = (entries,
|
|
2392
|
-
const sorted = [...entries]
|
|
2407
|
+
const compactLayout = (entries, columns) => {
|
|
2408
|
+
const sorted = [...entries]
|
|
2409
|
+
.map((entry) => ({ ...entry, position: clampToColumns(entry.position, columns) }))
|
|
2410
|
+
.sort((a, b) => a.position.row - b.position.row || a.position.col - b.position.col);
|
|
2393
2411
|
const compacted = [];
|
|
2394
2412
|
for (const entry of sorted) {
|
|
2395
2413
|
const candidate = { ...entry, position: { ...entry.position } };
|
|
2414
|
+
// Push down past any already-placed item it overlaps. This resolves overlaps introduced
|
|
2415
|
+
// by clamping out-of-bounds columns (which the upward pass below cannot fix on its own).
|
|
2416
|
+
while (findCollision({ entries: compacted, position: candidate.position, excludeId: candidate.id })) {
|
|
2417
|
+
candidate.position.row += 1;
|
|
2418
|
+
}
|
|
2419
|
+
// Then pull up as far as possible without colliding.
|
|
2396
2420
|
while (candidate.position.row > 0) {
|
|
2397
2421
|
const moved = { ...candidate, position: { ...candidate.position, row: candidate.position.row - 1 } };
|
|
2398
2422
|
if (findCollision({ entries: compacted, position: moved.position, excludeId: candidate.id })) {
|
|
@@ -3302,10 +3326,8 @@ class GridItemDirective {
|
|
|
3302
3326
|
this.currentRow = computed(() => this.renderPosition()?.row ?? 0, ...(ngDevMode ? [{ debugName: "currentRow" }] : /* istanbul ignore next */ []));
|
|
3303
3327
|
this.currentColSpan = computed(() => this.renderPosition()?.colSpan ?? 1, ...(ngDevMode ? [{ debugName: "currentColSpan" }] : /* istanbul ignore next */ []));
|
|
3304
3328
|
this.currentRowSpan = computed(() => this.renderPosition()?.rowSpan ?? 1, ...(ngDevMode ? [{ debugName: "currentRowSpan" }] : /* istanbul ignore next */ []));
|
|
3305
|
-
this.
|
|
3306
|
-
|
|
3307
|
-
'grid-row': computed(() => `${this.currentRow() + 1} / span ${this.currentRowSpan()}`),
|
|
3308
|
-
});
|
|
3329
|
+
this.gridColumn = computed(() => `${this.currentCol() + 1} / span ${this.currentColSpan()}`, ...(ngDevMode ? [{ debugName: "gridColumn" }] : /* istanbul ignore next */ []));
|
|
3330
|
+
this.gridRow = computed(() => `${this.currentRow() + 1} / span ${this.currentRowSpan()}`, ...(ngDevMode ? [{ debugName: "gridRow" }] : /* istanbul ignore next */ []));
|
|
3309
3331
|
effect((onCleanup) => {
|
|
3310
3332
|
const id = this.itemId();
|
|
3311
3333
|
const el = this.hostElement.nativeElement;
|
|
@@ -3336,7 +3358,7 @@ class GridItemDirective {
|
|
|
3336
3358
|
});
|
|
3337
3359
|
}
|
|
3338
3360
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: GridItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3339
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.13", type: GridItemDirective, isStandalone: true, selector: "[etGridItem]", inputs: { itemId: { classPropertyName: "itemId", publicName: "itemId", isSignal: true, isRequired: true, transformFunction: null }, minColSpan: { classPropertyName: "minColSpan", publicName: "minColSpan", isSignal: true, isRequired: false, transformFunction: null }, maxColSpan: { classPropertyName: "maxColSpan", publicName: "maxColSpan", isSignal: true, isRequired: false, transformFunction: null }, minRowSpan: { classPropertyName: "minRowSpan", publicName: "minRowSpan", isSignal: true, isRequired: false, transformFunction: null }, maxRowSpan: { classPropertyName: "maxRowSpan", publicName: "maxRowSpan", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "et-grid-item" }, exportAs: ["etGridItem"], ngImport: i0 }); }
|
|
3361
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.13", type: GridItemDirective, isStandalone: true, selector: "[etGridItem]", inputs: { itemId: { classPropertyName: "itemId", publicName: "itemId", isSignal: true, isRequired: true, transformFunction: null }, minColSpan: { classPropertyName: "minColSpan", publicName: "minColSpan", isSignal: true, isRequired: false, transformFunction: null }, maxColSpan: { classPropertyName: "maxColSpan", publicName: "maxColSpan", isSignal: true, isRequired: false, transformFunction: null }, minRowSpan: { classPropertyName: "minRowSpan", publicName: "minRowSpan", isSignal: true, isRequired: false, transformFunction: null }, maxRowSpan: { classPropertyName: "maxRowSpan", publicName: "maxRowSpan", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.grid-column": "gridColumn()", "style.grid-row": "gridRow()" }, classAttribute: "et-grid-item" }, exportAs: ["etGridItem"], ngImport: i0 }); }
|
|
3340
3362
|
}
|
|
3341
3363
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: GridItemDirective, decorators: [{
|
|
3342
3364
|
type: Directive,
|
|
@@ -3345,6 +3367,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
|
|
|
3345
3367
|
exportAs: 'etGridItem',
|
|
3346
3368
|
host: {
|
|
3347
3369
|
class: 'et-grid-item',
|
|
3370
|
+
'[style.grid-column]': 'gridColumn()',
|
|
3371
|
+
'[style.grid-row]': 'gridRow()',
|
|
3348
3372
|
},
|
|
3349
3373
|
}]
|
|
3350
3374
|
}], ctorParameters: () => [], propDecorators: { itemId: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemId", required: true }] }], minColSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "minColSpan", required: false }] }], maxColSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxColSpan", required: false }] }], minRowSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRowSpan", required: false }] }], maxRowSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRowSpan", required: false }] }] } });
|
|
@@ -3359,12 +3383,10 @@ class GridDragDirective {
|
|
|
3359
3383
|
this.renderer = injectRenderer();
|
|
3360
3384
|
this.dragStartClient = signal(null, ...(ngDevMode ? [{ debugName: "dragStartClient" }] : /* istanbul ignore next */ []));
|
|
3361
3385
|
this.dragPixelOffset = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "dragPixelOffset" }] : /* istanbul ignore next */ []));
|
|
3362
|
-
this.
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
}),
|
|
3367
|
-
});
|
|
3386
|
+
this.dragTransform = computed(() => {
|
|
3387
|
+
const offset = this.dragPixelOffset();
|
|
3388
|
+
return `translate(${offset.x}px, ${offset.y}px)`;
|
|
3389
|
+
}, ...(ngDevMode ? [{ debugName: "dragTransform" }] : /* istanbul ignore next */ []));
|
|
3368
3390
|
outputToObservable(this.dragHandle.dragStarted)
|
|
3369
3391
|
.pipe(filter(() => !this.grid.readOnly()), tap(() => {
|
|
3370
3392
|
this.dragPixelOffset.set({ x: 0, y: 0 });
|
|
@@ -3478,7 +3500,7 @@ class GridDragDirective {
|
|
|
3478
3500
|
this.renderer.removeStyles(el, 'position', 'left', 'top', 'width', 'height');
|
|
3479
3501
|
}
|
|
3480
3502
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: GridDragDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3481
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.13", type: GridDragDirective, isStandalone: true, selector: "[etGridDrag]", host: { properties: { "class.et-grid-drag--active": "!grid.readOnly() && dragHandle.isDragging()", "attr.aria-grabbed": "!grid.readOnly() && dragHandle.isDragging()" }, classAttribute: "et-grid-drag" }, hostDirectives: [{ directive: i2.DragHandleDirective, outputs: ["dragStarted", "dragStarted", "dragMoved", "dragMoved", "dragEnded", "dragEnded"] }], ngImport: i0 }); }
|
|
3503
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.13", type: GridDragDirective, isStandalone: true, selector: "[etGridDrag]", host: { properties: { "class.et-grid-drag--active": "!grid.readOnly() && dragHandle.isDragging()", "attr.aria-grabbed": "!grid.readOnly() && dragHandle.isDragging()", "style.transform": "dragTransform()" }, classAttribute: "et-grid-drag" }, hostDirectives: [{ directive: i2.DragHandleDirective, outputs: ["dragStarted", "dragStarted", "dragMoved", "dragMoved", "dragEnded", "dragEnded"] }], ngImport: i0 }); }
|
|
3482
3504
|
}
|
|
3483
3505
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: GridDragDirective, decorators: [{
|
|
3484
3506
|
type: Directive,
|
|
@@ -3494,6 +3516,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
|
|
|
3494
3516
|
class: 'et-grid-drag',
|
|
3495
3517
|
'[class.et-grid-drag--active]': '!grid.readOnly() && dragHandle.isDragging()',
|
|
3496
3518
|
'[attr.aria-grabbed]': '!grid.readOnly() && dragHandle.isDragging()',
|
|
3519
|
+
'[style.transform]': 'dragTransform()',
|
|
3497
3520
|
},
|
|
3498
3521
|
}]
|
|
3499
3522
|
}], ctorParameters: () => [] });
|
|
@@ -3623,17 +3646,15 @@ class GridComponent {
|
|
|
3623
3646
|
const label = this.grid.readOnly() ? this.gridConfig.readonlyAriaLabel : this.gridConfig.interactiveAriaLabel;
|
|
3624
3647
|
return this.gridConfig.transformer(label, this.locale.currentLocale());
|
|
3625
3648
|
}, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
3626
|
-
this.
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
'--et-grid-row-height': computed(() => `${this.grid.rowHeight()}px`),
|
|
3630
|
-
});
|
|
3649
|
+
this.gridColumns = computed(() => this.grid.activeColumns(), ...(ngDevMode ? [{ debugName: "gridColumns" }] : /* istanbul ignore next */ []));
|
|
3650
|
+
this.gridGap = computed(() => `${this.grid.gap()}px`, ...(ngDevMode ? [{ debugName: "gridGap" }] : /* istanbul ignore next */ []));
|
|
3651
|
+
this.gridRowHeight = computed(() => `${this.grid.rowHeight()}px`, ...(ngDevMode ? [{ debugName: "gridRowHeight" }] : /* istanbul ignore next */ []));
|
|
3631
3652
|
effect(() => {
|
|
3632
3653
|
this.grid.setGhostElement(this.ghostRef()?.nativeElement ?? null);
|
|
3633
3654
|
});
|
|
3634
3655
|
}
|
|
3635
3656
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: GridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3636
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: GridComponent, isStandalone: true, selector: "et-grid, [et-grid]", host: { attributes: { "role": "region" }, properties: { "attr.aria-label": "ariaLabel()" }, classAttribute: "et-grid" }, viewQueries: [{ propertyName: "ghostRef", first: true, predicate: ["ghostRef"], descendants: true, isSignal: true }], hostDirectives: [{ directive: GridDirective, inputs: ["breakpoints", "breakpoints", "rowHeight", "rowHeight", "gap", "gap", "initialItems", "initialItems", "readOnly", "readOnly"], outputs: ["layoutChange", "layoutChange"] }], ngImport: i0, template: `
|
|
3657
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: GridComponent, isStandalone: true, selector: "et-grid, [et-grid]", host: { attributes: { "role": "region" }, properties: { "attr.aria-label": "ariaLabel()", "style.--_et-grid-columns": "gridColumns()", "style.--et-grid-gap": "gridGap()", "style.--et-grid-row-height": "gridRowHeight()" }, classAttribute: "et-grid" }, viewQueries: [{ propertyName: "ghostRef", first: true, predicate: ["ghostRef"], descendants: true, isSignal: true }], hostDirectives: [{ directive: GridDirective, inputs: ["breakpoints", "breakpoints", "rowHeight", "rowHeight", "gap", "gap", "initialItems", "initialItems", "readOnly", "readOnly"], outputs: ["layoutChange", "layoutChange"] }], ngImport: i0, template: `
|
|
3637
3658
|
<ng-content />
|
|
3638
3659
|
@if (grid.ghostPosition(); as ghost) {
|
|
3639
3660
|
<div
|
|
@@ -3667,6 +3688,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
|
|
|
3667
3688
|
class: 'et-grid',
|
|
3668
3689
|
role: 'region',
|
|
3669
3690
|
'[attr.aria-label]': 'ariaLabel()',
|
|
3691
|
+
'[style.--_et-grid-columns]': 'gridColumns()',
|
|
3692
|
+
'[style.--et-grid-gap]': 'gridGap()',
|
|
3693
|
+
'[style.--et-grid-row-height]': 'gridRowHeight()',
|
|
3670
3694
|
}, styles: ["@property --et-grid-gap{syntax: \"<length>\"; inherits: false; initial-value: 16px;}@property --et-grid-row-height{syntax: \"<length>\"; inherits: false; initial-value: 100px;}@property --et-grid-padding{syntax: \"<length>\"; inherits: false; initial-value: 0px;}.et-grid{display:grid;grid-template-columns:repeat(var(--_et-grid-columns, 12),1fr);grid-auto-rows:var(--et-grid-row-height);gap:var(--et-grid-gap);padding:var(--et-grid-padding);position:relative;min-height:0}.et-grid:has(.et-grid-item--dragging,.et-grid-item--resizing){-webkit-user-select:none;user-select:none;cursor:grabbing}.et-grid:has(.et-grid-item--resizing){cursor:nwse-resize}.et-grid-ghost{border-radius:8px;background:rgb(var(--et-surface-color, 23 23 23) / .08);border:2px dashed rgb(var(--et-surface-color, 23 23 23) / .2);pointer-events:none;transition:grid-column 0s,grid-row 0s}.et-grid--readonly .et-grid-item__drag-handle{cursor:default;pointer-events:none}.et-grid--readonly .et-grid-item__actions{display:none}.et-grid--readonly .et-grid-item:hover .et-resize-handle--e:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--s:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--w:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--n:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--se:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--sw:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--ne:after,.et-grid--readonly .et-grid-item:hover .et-resize-handle--nw:after{content:unset}\n"] }]
|
|
3671
3695
|
}], ctorParameters: () => [], propDecorators: { ghostRef: [{ type: i0.ViewChild, args: ['ghostRef', { isSignal: true }] }] } });
|
|
3672
3696
|
|