-
Notifications
You must be signed in to change notification settings - Fork 168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic component support for Sheet #538
Comments
I've got a working implementation. Since I suppose there should be some magnitude of rework/refactor to the sheet component to be done in order to get this properly implemented, I'm posting the code here rather than creating a PR for now. Please feel free to grab my code, and let me know if I can help to put a PR together. hlm-sheet-dynamic-content.component.ts
import { Component, ElementRef, Renderer2, computed, effect, inject, input } from '@angular/core';
import { lucideX } from '@ng-icons/lucide';
import { BrnSheetCloseDirective } from '@spartan-ng/brain/sheet';
import { hlm } from '@spartan-ng/ui-core';
import { HlmIconComponent, provideIcons } from '@spartan-ng/ui-icon-helm';
import type { ClassValue } from 'clsx';
import { HlmSheetCloseDirective } from './hlm-sheet-close.directive';
import { BrnDialogRef, injectBrnDialogContext } from '@spartan-ng/brain/dialog';
import { sheetVariants } from './hlm-sheet-content.component';
import { NgComponentOutlet } from '@angular/common';
@Component({
selector: 'hlm-sheet-dynamic-content',
standalone: true,
imports: [NgComponentOutlet, HlmSheetCloseDirective, BrnSheetCloseDirective, HlmIconComponent],
providers: [provideIcons({ lucideX })],
host: {
'[class]': '_computedClass()',
'[attr.data-state]': 'state()',
},
template: `
@if (component) {
<ng-container [ngComponentOutlet]="component" />
} @else {
<ng-content />
}
<button brnSheetClose hlm>
<span class="sr-only">Close</span>
<hlm-icon class="flex h-4 w-4" size="100%" name="lucideX" />
</button>
`,
})
export class HlmSheetDynamicContentComponent {
private readonly _dialogRef = inject(BrnDialogRef);
private readonly _dialogContext = injectBrnDialogContext({ optional: true });
public readonly component = this._dialogContext?.$component;
public readonly state = computed(() => this._dialogRef?.state() ?? 'closed');
private readonly _renderer = inject(Renderer2);
private readonly _element = inject(ElementRef);
constructor() {
effect(() => {
this._renderer.setAttribute(this._element.nativeElement, 'data-state', this.state());
});
}
public readonly userClass = input<ClassValue>('', { alias: 'class' });
// dirty: need to figure out how to pass the side option here
protected _computedClass = computed(() => hlm(sheetVariants({ side: (this._dialogRef as any)._options.side }), this.userClass()));
} hlm-sheet.service.tsimport type { ComponentType } from '@angular/cdk/portal';
import { Injectable, type TemplateRef, inject } from '@angular/core';
import {
type BrnDialogOptions,
BrnDialogService,
DEFAULT_BRN_DIALOG_OPTIONS,
cssClassesToArray,
} from '@spartan-ng/brain/dialog';
import { HlmSheetContentComponent } from './hlm-sheet-content.component';
import { hlmSheetOverlayClass } from './hlm-sheet-overlay.directive';
import { HlmDialogOptions } from '@spartan-ng/ui-dialog-helm';
import { OverlayPositionBuilder } from '@angular/cdk/overlay';
import { HlmSheetDynamicContentComponent } from './hlm-sheet-dynamic-content.component';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type HlmSheetOptions<DialogContext = any> = HlmDialogOptions<DialogContext> & {
side: 'top' | 'bottom' | 'left' | 'right';
};
@Injectable({
providedIn: 'root',
})
export class HlmSheetService {
private readonly _brnDialogService = inject(BrnDialogService);
private readonly _positionBuilder = inject(OverlayPositionBuilder);
private getPositionStrategy(side?: 'top' | 'bottom' | 'left' | 'right') {
switch (side) {
case 'top':
return this._positionBuilder.global().top();
case 'bottom':
return this._positionBuilder.global().bottom();
case 'left':
return this._positionBuilder.global().left();
case 'right':
default:
return this._positionBuilder.global().right();
}
}
public open(component: ComponentType<unknown> | TemplateRef<unknown>, options?: Partial<HlmSheetOptions>) {
const mergedOptions = {
...DEFAULT_BRN_DIALOG_OPTIONS,
closeDelay: 100,
positionStrategy: this.getPositionStrategy(options?.side),
...(options ?? {}),
backdropClass: cssClassesToArray(`${hlmSheetOverlayClass} ${options?.backdropClass ?? ''}`),
context: { ...options?.context, $component: component, $dynamicComponentClass: options?.contentClass },
};
return this._brnDialogService.open(HlmSheetDynamicContentComponent, undefined, mergedOptions.context, mergedOptions);
}
} Usage: private readonly _sheets = inject(HlmSheetService);
const sheetRef = this._sheets.open(MySheetComponent, {
contentClass: "!w-[540px]",
side: 'left'
});
sheetRef.closed$.subscribe((result) => {
// ...
}); |
Which scope/s are relevant/related to the feature request?
sheet
Information
It would be great if we can use dynamic component in the sheet component, just like we can do in the dialog component - after all, sheet is just another form of dialog!
Describe any alternatives/workarounds you're currently using
No response
I would be willing to submit a PR to fix this issue
The text was updated successfully, but these errors were encountered: