Skip to content

Commit

Permalink
feat: add modal sample using already defined component
Browse files Browse the repository at this point in the history
  • Loading branch information
quentinderoubaix committed Nov 10, 2023
1 parent a545430 commit 000c2e5
Show file tree
Hide file tree
Showing 39 changed files with 342 additions and 204 deletions.
3 changes: 1 addition & 2 deletions angular/demo/src/app/samples/accordion/togglePanels.route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {AgnosUIAngularModule} from '@agnos-ui/angular';
import {Component} from '@angular/core';
import {NgFor} from '@angular/common';
import BODY from '!raw-loader!@agnos-ui/common/samples/accordion/body.txt';

@Component({
standalone: true,
imports: [AgnosUIAngularModule, NgFor],
imports: [AgnosUIAngularModule],
template: `
<div auAccordion #accordion="auAccordion">
<div auAccordionItem auItemId="first">
Expand Down
44 changes: 44 additions & 0 deletions angular/demo/src/app/samples/alert/alert-icon.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import biCheckCircleFill from '!raw-loader!bootstrap-icons/icons/check-circle-fill.svg';
import biDashCircleFill from '!raw-loader!bootstrap-icons/icons/dash-circle-fill.svg';
import biExclamationTriangleFill from '!raw-loader!bootstrap-icons/icons/exclamation-triangle-fill.svg';
import biInfoCircleFill from '!raw-loader!bootstrap-icons/icons/info-circle-fill.svg';
import biLightbulb from '!raw-loader!bootstrap-icons/icons/lightbulb.svg';
import type {AlertWidget} from '@agnos-ui/angular';
import {SlotComponent, SlotDirective} from '@agnos-ui/angular';
import {NgIf} from '@angular/common';
import {Component, inject} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';

@Component({
standalone: true,
imports: [NgIf, SlotDirective],
providers: [SlotDirective],
selector: 'app-icon-structure',
host: {
style: 'display: contents;',
},
template: `
<span class="d-flex align-items-center me-2" [innerHTML]="sanitizer.bypassSecurityTrustHtml(typeIcon[state.type])"></span>
<div class="d-flex w-100 alert-body">
<ng-template [auSlot]="state.slotDefault" [auSlotProps]="{widget, state}"></ng-template>
<button
*ngIf="state.dismissible"
type="button"
class="btn-close ms-auto"
(click)="widget.api.close()"
[attr.aria-label]="state.ariaCloseButtonLabel"
></button>
</div>
`,
})
export default class AlertIconComponent extends SlotComponent<AlertWidget> {
sanitizer = inject(DomSanitizer);

typeIcon: Record<string, string> = {
success: biCheckCircleFill,
info: biInfoCircleFill,
warning: biExclamationTriangleFill,
danger: biDashCircleFill,
light: biLightbulb,
};
}
17 changes: 5 additions & 12 deletions angular/demo/src/app/samples/alert/generic.route.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
import {AgnosUIAngularModule, provideWidgetsConfig} from '@agnos-ui/angular';
import {NgFor} from '@angular/common';
import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
standalone: true,
imports: [AgnosUIAngularModule, NgFor, FormsModule],
imports: [AgnosUIAngularModule],
providers: [
provideWidgetsConfig((config) => {
config.alert = {...config.alert, dismissible: false};
return config;
}),
],
template: ` <au-component auAlert auType="primary">Simple primary alert</au-component>
template: `
<au-component auAlert auType="primary">Simple primary alert</au-component>
<au-component auAlert auType="secondary">Simple secondary alert</au-component>
<au-component auAlert auType="success">Simple success alert</au-component>
<au-component auAlert auType="danger">Simple danger alert</au-component>
<au-component auAlert auType="warning">Simple warning alert</au-component>
<au-component auAlert auType="info">Simple info alert</au-component>
<au-component auAlert auType="light">Simple light alert</au-component>
<au-component auAlert auType="dark">Simple dark alert</au-component>`,
<au-component auAlert auType="dark">Simple dark alert</au-component>
`,
})
export default class GenericAlertComponent {}
59 changes: 7 additions & 52 deletions angular/demo/src/app/samples/alert/icon.route.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import biCheckCircleFill from '!raw-loader!bootstrap-icons/icons/check-circle-fill.svg';
import biDashCircleFill from '!raw-loader!bootstrap-icons/icons/dash-circle-fill.svg';
import biExclamationTriangleFill from '!raw-loader!bootstrap-icons/icons/exclamation-triangle-fill.svg';
import biInfoCircleFill from '!raw-loader!bootstrap-icons/icons/info-circle-fill.svg';
import biLightbulb from '!raw-loader!bootstrap-icons/icons/lightbulb.svg';
import type {AlertComponent} from '@agnos-ui/angular';
import {AgnosUIAngularModule, ComponentTemplate, provideWidgetsConfig, SlotDirective} from '@agnos-ui/angular';
import {AsyncPipe, NgFor, NgIf} from '@angular/common';
import {Component, inject, ViewChild} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {DomSanitizer} from '@angular/platform-browser';
import {AlertComponent} from '@agnos-ui/angular';
import {provideWidgetsConfig} from '@agnos-ui/angular';
import {Component} from '@angular/core';
import AlertIconComponent from './alert-icon.component';

@Component({
standalone: true,
imports: [AgnosUIAngularModule, NgFor, FormsModule],
imports: [AlertComponent],
providers: [
provideWidgetsConfig((config) => {
config.alert = {...config.alert, dismissible: false, slotStructure: new ComponentTemplate(AlertIconComponent, 'iconDemo')};
config.alert = {...config.alert, dismissible: false, slotStructure: AlertIconComponent};
return config;
}),
],
Expand All @@ -27,42 +20,4 @@ import {DomSanitizer} from '@angular/platform-browser';
<au-component auAlert auType="light">Alert light with a customisable icon</au-component>
`,
})
export default class IconAlertComponent {
async showAlert(alert: AlertComponent) {
alert.api.open();
}
}

@Component({
standalone: true,
imports: [AgnosUIAngularModule, NgIf, AsyncPipe, NgFor, SlotDirective],
providers: [SlotDirective],
template: `
<ng-template #iconDemo let-state="state" let-widget="widget">
<span class="d-flex align-items-center me-2" [innerHTML]="sanitizer.bypassSecurityTrustHtml(typeIcon[state.type])"> </span>
<div class="d-flex w-100 alert-body">
<ng-template [auSlot]="state.slotDefault" [auSlotProps]="{widget, state}"></ng-template>
<button
*ngIf="state.dismissible"
type="button"
class="btn-close ms-auto"
(click)="widget.api.close()"
[attr.aria-label]="state.ariaCloseButtonLabel"
></button>
</div>
</ng-template>
`,
})
export class AlertIconComponent {
sanitizer = inject(DomSanitizer);

@ViewChild('iconDemo', {static: true}) iconDemo: any;

typeIcon: Record<string, string> = {
success: biCheckCircleFill,
info: biInfoCircleFill,
warning: biExclamationTriangleFill,
danger: biDashCircleFill,
light: biLightbulb,
};
}
export default class IconAlertComponent {}
17 changes: 17 additions & 0 deletions angular/demo/src/app/samples/modal/component.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {AgnosUIAngularModule, ModalService} from '@agnos-ui/angular';
import {Component, inject} from '@angular/core';
import type {ModalData} from './custom-content.component';
import CustomContentComponent from './custom-content.component';

@Component({
standalone: true,
imports: [AgnosUIAngularModule],
template: ` <button class="btn btn-primary" type="button" (click)="openModal()">Launch demo modal</button> `,
})
export default class DemoContentModalComponent {
readonly modalService = inject(ModalService);

openModal() {
this.modalService.open<ModalData>({slotTitle: 'Hi there!', slotDefault: CustomContentComponent, contentData: {name: 'World'}});
}
}
14 changes: 14 additions & 0 deletions angular/demo/src/app/samples/modal/custom-content.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type {ModalWidget} from '@agnos-ui/angular';
import {SlotComponent} from '@agnos-ui/angular';
import {Component} from '@angular/core';

export type ModalData = {name: string};

@Component({
standalone: true,
template: `
<p>Hello, {{ state.contentData.name }}!</p>
<button type="button" class="btn btn-outline-primary" (click)="widget.api.close(true)">Close</button>
`,
})
export default class CustomContentComponent extends SlotComponent<ModalWidget<ModalData>> {}
12 changes: 5 additions & 7 deletions angular/demo/src/app/samples/modal/default.route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import type {ModalComponent} from '@agnos-ui/angular';
import {AgnosUIAngularModule} from '@agnos-ui/angular';
import {modalCloseButtonClick, modalOutsideClick} from '@agnos-ui/core';
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
standalone: true,
imports: [AgnosUIAngularModule, CommonModule, FormsModule],
imports: [AgnosUIAngularModule],
template: `
<button class="btn btn-primary" type="button" (click)="show(modal)">Launch demo modal</button>
<div class="mt-3" data-testid="message">{{ message }}</div>
Expand All @@ -23,15 +21,15 @@ import {FormsModule} from '@angular/forms';
export default class DefaultModalComponent {
message = '';

async show(modal: ModalComponent) {
async show(modal: ModalComponent<void>) {
this.message = '';
const result = await modal.api.open();
if (result === modalCloseButtonClick) {
this.message = 'You clicked on the close button';
this.message = 'You clicked on the close button.';
} else if (result === modalOutsideClick) {
this.message = 'You clicked outside the modal';
this.message = 'You clicked outside the modal.';
} else {
this.message = `You answered the question with "${result ? 'Yes' : 'No'}"`;
this.message = `You answered the question with "${result ? 'Yes' : 'No'}".`;
}
}
}
2 changes: 1 addition & 1 deletion angular/demo/src/app/samples/modal/playground.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const undefinedConfig = getUndefinedValues(getModalDefaultConfig());
template: `<au-component auModal #widget></au-component>`,
})
export default class PlaygroundComponent {
@ViewChild('widget') widget: ModalComponent;
@ViewChild('widget') widget: ModalComponent<any>;

constructor() {
hashChangeHook((props) => {
Expand Down
4 changes: 1 addition & 3 deletions angular/demo/src/app/samples/modal/stack.route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import {AgnosUIAngularModule, ModalService} from '@agnos-ui/angular';
import {CommonModule} from '@angular/common';
import {Component, inject} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
standalone: true,
imports: [AgnosUIAngularModule, CommonModule, FormsModule],
imports: [AgnosUIAngularModule],
template: `
<button class="btn btn-primary" (click)="modalService.open({slotTitle: 'First modal', className: 'modal-sm', slotDefault: recursiveModal})">
Launch demo modal
Expand Down
2 changes: 1 addition & 1 deletion angular/demo/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"types": ["@types/webpack-env"]
},
"files": ["src/main.ts", "src/polyfills.ts", "src/environments/environment.prod.ts"],
"include": ["src/**/*.d.ts", "src/app/samples/**/*.route.ts"]
"include": ["src/**/*.d.ts", "src/app/samples/**/*.route.ts", "src/app/samples/**/*.component.ts"]
}
12 changes: 10 additions & 2 deletions angular/headless/src/lib/slotTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {SlotContent as CoreSlotContent, Widget, WidgetFactory, WidgetProps, WidgetSlotContext, WidgetState} from '@agnos-ui/core';
import type {TemplateRef, Type} from '@angular/core';
import type {ContextWidget, SlotContent as CoreSlotContent, Widget, WidgetFactory, WidgetProps, WidgetSlotContext, WidgetState} from '@agnos-ui/core';
import {Directive, Input, type TemplateRef, type Type} from '@angular/core';

export class ComponentTemplate<Props, K extends string, T extends {[key in K]: TemplateRef<Props>}> {
constructor(public readonly component: Type<T>, public readonly templateProp: K) {}
Expand Down Expand Up @@ -30,3 +30,11 @@ export type AdaptWidgetSlots<W extends Widget> = Widget<
W['actions'],
W['directives']
>;

@Directive()
export abstract class SlotComponent<W extends Widget> {
@Input()
state!: WidgetState<W>;
@Input()
widget!: ContextWidget<W>;
}
10 changes: 5 additions & 5 deletions angular/headless/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ export type AlertState = WidgetState<AlertWidget>;
export type AlertContext = AdaptSlotContentProps<import('@agnos-ui/core').AlertContext>;
export const createAlert: WidgetFactory<AlertWidget> = coreCreateAlert as any;

export type ModalWidget = AdaptWidgetSlots<import('@agnos-ui/core').ModalWidget>;
export type ModalProps = WidgetProps<ModalWidget>;
export type ModalState = WidgetState<ModalWidget>;
export type ModalContext = AdaptSlotContentProps<import('@agnos-ui/core').ModalContext>;
export const createModal: WidgetFactory<ModalWidget> = coreCreateModal as any;
export type ModalWidget<Data> = AdaptWidgetSlots<import('@agnos-ui/core').ModalWidget<Data>>;
export type ModalProps<Data> = WidgetProps<ModalWidget<Data>>;
export type ModalState<Data> = WidgetState<ModalWidget<Data>>;
export type ModalContext<Data> = AdaptSlotContentProps<import('@agnos-ui/core').ModalContext<Data>>;
export const createModal: <Data>(props?: PropsConfig<ModalProps<Data>>) => ModalWidget<Data> = coreCreateModal as any;

export type PaginationWidget = AdaptWidgetSlots<import('@agnos-ui/core').PaginationWidget>;
export type PaginationProps = WidgetProps<PaginationWidget>;
Expand Down
7 changes: 6 additions & 1 deletion angular/lib/src/lib/agnos-ui-angular.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from './pagination/pagination.component';
import {RatingComponent, RatingStarDirective} from './rating/rating.component';
import {SelectComponent} from './select/select.component';
import {AlertComponent} from './alert/alert.component';
import {AlertBodyDirective, AlertComponent, AlertStructureDirective} from './alert/alert.component';
import {
AccordionDirective,
AccordionItemComponent,
Expand All @@ -29,6 +29,7 @@ import {
AccordionItemStructureDirective,
} from './accordion/accordion.component';
import {SliderComponent} from './slider/slider.component';
import {ProgressbarComponent, ProgressbarContentDirective} from './progressbar/progressbar.component';

/* istanbul ignore next */
const components = [
Expand All @@ -52,12 +53,16 @@ const components = [
ModalBodyDirective,
ModalFooterDirective,
AlertComponent,
AlertStructureDirective,
AlertBodyDirective,
AccordionDirective,
AccordionItemComponent,
AccordionHeaderDirective,
AccordionBodyDirective,
AccordionItemStructureDirective,
SliderComponent,
ProgressbarComponent,
ProgressbarContentDirective,
];

@NgModule({
Expand Down
Loading

0 comments on commit 000c2e5

Please sign in to comment.