Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions angular/bootstrap/src/components/drawer/drawer.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {SlotContent, TransitionFn} from '@agnos-ui/angular-headless';
import {
auBooleanAttribute,
auNumberAttribute,
auNumberOrNullAttribute,
BaseWidgetDirective,
callWidgetFactory,
ComponentTemplate,
Expand Down Expand Up @@ -62,19 +62,18 @@ export class DrawerBodyDirective {

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [SlotDirective, DrawerStructureDirective, UseDirective],
imports: [SlotDirective, DrawerStructureDirective],
template: ` <ng-template auDrawerStructure #structure let-state="state" let-api="api" let-directives="directives">
@if (state.header()) {
<div class="au-drawer-header">
<ng-template [auSlot]="state.header()" [auSlotProps]="{state, api, directives}" />
<div class="au-drawer-content">
@if (state.header()) {
<div class="au-drawer-header">
<ng-template [auSlot]="state.header()" [auSlotProps]="{state, api, directives}" />
</div>
}
<div class="au-drawer-body">
<ng-template [auSlot]="state.children()" [auSlotProps]="{state, api, directives}" />
</div>
}
<div class="au-drawer-body">
<ng-template [auSlot]="state.children()" [auSlotProps]="{state, api, directives}" />
</div>
@if (state.resizable()) {
<div [auUse]="directives.splitterDirective"></div>
}
</ng-template>`,
})
class DrawerDefaultSlotsComponent {
Expand Down Expand Up @@ -102,6 +101,9 @@ export const drawerDefaultSlotStructure: SlotContent<DrawerContext> = new Compon
<div [auUse]="directives.containerDirective">
<ng-template [auSlot]="state.structure()" [auSlotProps]="{state, api, directives}" />
</div>
@if (state.resizable()) {
<div [auUse]="directives.splitterDirective" [tabIndex]="0"></div>
}
</div>
}
@if (!state.backdropHidden()) {
Expand Down Expand Up @@ -192,6 +194,14 @@ export class DrawerComponent extends BaseWidgetDirective<DrawerWidget> {
*/
readonly bodyScroll = input(undefined, {alias: 'auBodyScroll', transform: auBooleanAttribute});

/**
* Size of the drawer in pixel once the user start interacting.
* It corresponds to the height or the width depending on the drawer orientation
*
* @defaultValue `null`
*/
readonly size = input(undefined, {alias: 'auSize', transform: auNumberOrNullAttribute});

/**
* Classes to add on the backdrop DOM element.
*
Expand All @@ -214,54 +224,58 @@ export class DrawerComponent extends BaseWidgetDirective<DrawerWidget> {
readonly resizable = input(undefined, {alias: 'auResizable', transform: auBooleanAttribute});

/**
* The width of the drawer in pixels.
* An event emitted when the drawer size (width or height depending on the orientation).
*
* @defaultValue `200`
* Event payload is equal to the newly selected width or height.
*
* @defaultValue
* ```ts
* () => {}
* ```
*/
readonly width = input(undefined, {alias: 'auWidth', transform: auNumberAttribute});
readonly sizeChange = output<number | null>({alias: 'auSizeChange'});

/**
* The height of the drawer in pixels.
* Event to be triggered when the visible property changes.
*
* @defaultValue `200`
* @param visible - new value of the visible propery
*
* @defaultValue
* ```ts
* () => {}
* ```
*/
readonly height = input(undefined, {alias: 'auHeight', transform: auNumberAttribute});
readonly visibleChange = output<boolean>({alias: 'auVisibleChange'});

/**
* An event emitted when the width is changed.
*
* Event payload is equal to the newly selected width.
* Event to be triggered when the minimized state changes.
*
* @defaultValue
* ```ts
* () => {}
* ```
*/
readonly widthChange = output<number>({alias: 'auWidthChange'});
readonly minimizedChange = output<boolean>({alias: 'auMinimizedChange'});

/**
* An event emitted when the height is changed.
*
* Event payload is equal to the newly selected height.
* Event to be triggered when the maximized state changes.
*
* @defaultValue
* ```ts
* () => {}
* ```
*/
readonly heightChange = output<number>({alias: 'auHeightChange'});
readonly maximizedChange = output<boolean>({alias: 'auMaximizedChange'});

/**
* Event to be triggered when the visible property changes.
*
* @param visible - new value of the visible propery
* Event to be triggered when the user start or stop resizing the drawer.
*
* @defaultValue
* ```ts
* () => {}
* ```
*/
readonly visibleChange = output<boolean>({alias: 'auVisibleChange'});
readonly resizingChange = output<boolean>({alias: 'auResizingChange'});

/**
* Event to be triggered when the transition is completed and the drawer is not visible.
Expand Down Expand Up @@ -295,14 +309,14 @@ export class DrawerComponent extends BaseWidgetDirective<DrawerWidget> {
children: this.slotBodyFromContent()?.templateRef,
}),
events: {
onResizingChange: (event) => this.resizingChange.emit(event),
onHidden: () => this.hidden.emit(),
onShown: () => this.shown.emit(),
onWidthChange: (width: number) => {
this.widthChange.emit(width);
},
onHeightChange: (height: number) => {
this.heightChange.emit(height);
onSizeChange: (size: number | null) => {
this.sizeChange.emit(size);
},
onMaximizedChange: (event) => this.maximizedChange.emit(event),
onMinimizedChange: (event) => this.minimizedChange.emit(event),
onVisibleChange: (event) => this.visibleChange.emit(event),
},
slotChildren: () => this.slotChildren(),
Expand Down
15 changes: 11 additions & 4 deletions angular/demo/bootstrap/src/app/samples/drawer/position.route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {DrawerPositions} from '@agnos-ui/angular-bootstrap';
import {DrawerComponent, DrawerHeaderDirective} from '@agnos-ui/angular-bootstrap';
import {Component, signal} from '@angular/core';
import {Component, signal, ViewEncapsulation} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
Expand All @@ -15,7 +15,7 @@ import {FormsModule} from '@angular/forms';
<option value="block-end">Bottom</option>
</select>
</div>
<au-component #drawer auDrawer [auClassName]="drawerPlacement()" [(auWidth)]="width" [(auHeight)]="height" auResizable>
<au-component #drawer auDrawer [auClassName]="drawerPlacement() + ' drawer-position-size'" auResizable>
<ng-template auDrawerHeader> Hi, I am drawer! </ng-template>
<ul>
<li>First item</li>
Expand All @@ -24,10 +24,17 @@ import {FormsModule} from '@angular/forms';
</ul>
</au-component>
`,
styles: [
`
.drawer-position-size {
--bs-drawer-size: 200px;
--bs-drawer-max-size: calc(100% - 2rem);
}
`,
],
imports: [DrawerComponent, FormsModule, DrawerHeaderDirective],
encapsulation: ViewEncapsulation.None,
})
export default class BasicDrawerComponent {
readonly drawerPlacement = signal<DrawerPositions>('inline-start');
readonly width = signal(200);
readonly height = signal(150);
}
29 changes: 29 additions & 0 deletions angular/demo/bootstrap/src/app/samples/drawer/sizes.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {DrawerComponent, DrawerHeaderDirective} from '@agnos-ui/angular-bootstrap';
import {Component, ViewEncapsulation} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
template: `
<button class="btn btn-primary mb-3" (click)="drawer.api.open()">Open Drawer</button>
<au-component #drawer auDrawer auClassName="inline-start drawer-custom-size" auResizable>
<ng-template auDrawerHeader> Hi, I am drawer! </ng-template>
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
</au-component>
`,
styles: [
`
.drawer-custom-size {
--bs-drawer-size: 20rem;
--bs-drawer-min-size: max-content;
--bs-drawer-max-size: calc(100vw - 2rem);
}
`,
],
imports: [DrawerComponent, FormsModule, DrawerHeaderDirective],
encapsulation: ViewEncapsulation.None,
})
export default class SizesDrawerComponent {}
16 changes: 16 additions & 0 deletions angular/headless/src/utils/coercion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,19 @@ export function auNumberAttribute(value: unknown): number | undefined {
}
return numberAttribute(value);
}

/**
* Transforms a value (typically a string) to a number.
* Intended to be used as a transform function of an input.
* @param value - Value to be transformed. `null` or `undefined`, returns the value unchanged.
*
* @example
* ```readonly id = input({ transform: auNumberOrNullAttribute });```
* @returns the value transformed
*/
export function auNumberOrNullAttribute(value: unknown): number | null | undefined {
if (value == null) {
return value;
}
return numberAttribute(value);
}
29 changes: 17 additions & 12 deletions core-bootstrap/src/components/drawer/drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import type {
} from '@agnos-ui/core/components/drawer';
import {createDrawer as createCoreDrawer, getDrawerDefaultConfig as getCoreDefaultConfig} from '@agnos-ui/core/components/drawer';
import {extendWidgetProps} from '@agnos-ui/core/services/extendWidget';
import type {TransitionFn} from '@agnos-ui/core/services/transitions/baseTransitions';
import type {ConfigValidator, Directive, PropsConfig, SlotContent, Widget, WidgetFactory, WidgetSlotContext} from '@agnos-ui/core/types';
import {collapseHorizontalTransition, collapseVerticalTransition, fadeTransition} from '../../services/transitions';
import {createWidgetFactory} from '@agnos-ui/core/utils/widget';
import {createAttributesDirective, mergeDirectives} from '@agnos-ui/core/utils/directive';
import {true$} from '@agnos-ui/core/utils/stores';
import type {TransitionFn} from '@agnos-ui/core/services/transitions/baseTransitions';
import {createWidgetFactory} from '@agnos-ui/core/utils/widget';
import {collapseHorizontalTransition, collapseVerticalTransition, fadeTransition} from '../../services/transitions';
import {computed} from '@amadeus-it-group/tansu';

export * from '@agnos-ui/core/components/drawer';

Expand Down Expand Up @@ -132,18 +133,25 @@ export function getDrawerDefaultConfig(): DrawerProps {
*/
export const createDrawer: WidgetFactory<DrawerWidget> = createWidgetFactory('drawer', (config?: PropsConfig<DrawerProps>): DrawerWidget => {
const widget = extendWidgetProps(createCoreDrawer, defaultConfigExtraProps, configValidator, coreOverride)(config);

return {
...widget,
directives: {
...widget.directives,
drawerDirective: mergeDirectives(
widget.directives.drawerDirective,
createAttributesDirective(() => ({
classNames: {
'au-drawer': true$,
show: widget.stores.visible$,
},
styles: {
'--bs-drawer-size': computed(() => {
const size = widget.stores.size$();
return size == null ? undefined : size + 'px';
}),
},
})),
widget.directives.drawerDirective, // need to be last, to ensure that all the classes and attributes are applied for the transition
),
backdropDirective: mergeDirectives(
widget.directives.backdropDirective,
Expand All @@ -162,14 +170,11 @@ export const createDrawer: WidgetFactory<DrawerWidget> = createWidgetFactory('dr
},
})),
),
containerDirective: mergeDirectives(
widget.directives.containerDirective,
createAttributesDirective(() => ({
classNames: {
'au-drawer-container': true$,
},
})),
),
containerDirective: createAttributesDirective(() => ({
classNames: {
'au-drawer-container': true$,
},
})),
},
};
});
Loading
Loading