@frame-kit/ui-ng 0.1.16 → 0.1.17

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.
@@ -21,6 +21,14 @@ class WizardDialogRef {
21
21
  headerComponent;
22
22
  /** Inputs applied to `headerComponent` when it is created. */
23
23
  headerInputs;
24
+ /** Optional consumer-supplied footer, replacing the built-in buttons. */
25
+ footerComponent;
26
+ /** Inputs applied to `footerComponent` when it is created. */
27
+ footerInputs;
28
+ /** Optional consumer-supplied stepper, replacing the built-in pill row. */
29
+ stepperComponent;
30
+ /** Inputs applied to `stepperComponent` when it is created. */
31
+ stepperInputs;
24
32
  // ===== NAVIGATION STATE =====
25
33
  currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
26
34
  highestVisitedIndex = signal(0, ...(ngDevMode ? [{ debugName: "highestVisitedIndex" }] : /* istanbul ignore next */ []));
@@ -52,6 +60,10 @@ class WizardDialogRef {
52
60
  this.backLabel = config.backLabel ?? 'Back';
53
61
  this.headerComponent = config.headerComponent ?? null;
54
62
  this.headerInputs = config.headerInputs ?? {};
63
+ this.footerComponent = config.footerComponent ?? null;
64
+ this.footerInputs = config.footerInputs ?? {};
65
+ this.stepperComponent = config.stepperComponent ?? null;
66
+ this.stepperInputs = config.stepperInputs ?? {};
55
67
  this.consumerData = config.data;
56
68
  }
57
69
  get data() {
@@ -119,6 +131,8 @@ class WizardDialogContainerComponent {
119
131
  injector = inject(Injector);
120
132
  bodyOutlet = viewChild('bodyOutlet', { ...(ngDevMode ? { debugName: "bodyOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
121
133
  headerOutlet = viewChild('headerOutlet', { ...(ngDevMode ? { debugName: "headerOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
134
+ footerOutlet = viewChild('footerOutlet', { ...(ngDevMode ? { debugName: "footerOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
135
+ stepperOutlet = viewChild('stepperOutlet', { ...(ngDevMode ? { debugName: "stepperOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
122
136
  ref = this.data.wizardRef;
123
137
  classes = computed(() => 'fk-wizard-dialog', ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
124
138
  get hostClass() {
@@ -131,7 +145,9 @@ class WizardDialogContainerComponent {
131
145
  parent: this.injector,
132
146
  providers: [{ provide: WIZARD_DIALOG_REF, useValue: this.ref }],
133
147
  });
134
- this.mountHeader(childInjector);
148
+ this.mountRegion(this.headerOutlet(), this.ref.headerComponent, this.ref.headerInputs, childInjector);
149
+ this.mountRegion(this.footerOutlet(), this.ref.footerComponent, this.ref.footerInputs, childInjector);
150
+ this.mountRegion(this.stepperOutlet(), this.ref.stepperComponent, this.ref.stepperInputs, childInjector);
135
151
  const outlet = this.bodyOutlet();
136
152
  if (!outlet) {
137
153
  return;
@@ -141,18 +157,17 @@ class WizardDialogContainerComponent {
141
157
  });
142
158
  }
143
159
  /**
144
- * Render the optional consumer-supplied header into the fixed region above
145
- * the stepper, applying `headerInputs` so the consumer configures it
146
- * declaratively from the wizard config.
160
+ * Render an optional consumer-supplied region (header or footer) into its
161
+ * fixed outlet, applying the configured inputs so consumers configure it
162
+ * declaratively from the wizard config. Shares the wizard injector, so the
163
+ * component can inject `WIZARD_DIALOG_REF`.
147
164
  */
148
- mountHeader(injector) {
149
- const headerComponent = this.ref.headerComponent;
150
- const outlet = this.headerOutlet();
151
- if (!headerComponent || !outlet) {
165
+ mountRegion(outlet, component, inputs, injector) {
166
+ if (!component || !outlet) {
152
167
  return;
153
168
  }
154
- const componentRef = outlet.createComponent(headerComponent, { injector });
155
- for (const [name, value] of Object.entries(this.ref.headerInputs)) {
169
+ const componentRef = outlet.createComponent(component, { injector });
170
+ for (const [name, value] of Object.entries(inputs)) {
156
171
  componentRef.setInput(name, value);
157
172
  }
158
173
  componentRef.changeDetectorRef.detectChanges();
@@ -164,15 +179,19 @@ class WizardDialogContainerComponent {
164
179
  this.ref.emitSubmit();
165
180
  }
166
181
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WizardDialogContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
167
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: WizardDialogContainerComponent, isStandalone: true, selector: "fk-wizard-dialog-container", host: { properties: { "class": "this.hostClass" } }, viewQueries: [{ propertyName: "bodyOutlet", first: true, predicate: ["bodyOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "headerOutlet", first: true, predicate: ["headerOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<!-- Consumer-supplied header (fixed, above the stepper) -->\n@if (ref.headerComponent) {\n <div class=\"fk-wizard-dialog__header\">\n <ng-template #headerOutlet />\n </div>\n}\n\n<!-- Stepper -->\n<div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n role=\"tablist\"\n>\n @for (step of ref.steps; track $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"fk-wizard-dialog__step\"\n [class.fk-wizard-dialog__step--active]=\"$index === ref.currentIndex()\"\n [class.fk-wizard-dialog__step--completed]=\"$index < ref.currentIndex()\"\n [attr.aria-selected]=\"$index === ref.currentIndex()\"\n [disabled]=\"ref.linear && $index > ref.highestVisitedIndex()\"\n (click)=\"ref.goTo($index)\"\n >\n <span class=\"fk-wizard-dialog__step-number\">{{ $index + 1 }}</span>\n <span class=\"fk-wizard-dialog__step-title\">{{ step.title }}</span>\n @if (step.optional) {\n <span class=\"fk-wizard-dialog__step-optional\">(Optional)</span>\n }\n </button>\n }\n</div>\n\n<!-- Scrollable body -->\n<div class=\"fk-wizard-dialog__body\">\n <ng-template #bodyOutlet />\n</div>\n\n<!-- Footer (moved to dialog footer by DialogService) -->\n<div\n class=\"fk-wizard-dialog__footer\"\n [class.fk-wizard-dialog__footer--hidden]=\"!ref.chromeVisible()\"\n libDialogFooter\n>\n @if (ref.isFirstStep()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"onCancel()\">\n {{ ref.cancelLabel }}\n </fk-button>\n } @else {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"ref.previous()\">\n {{ ref.backLabel }}\n </fk-button>\n }\n\n @if (ref.extraAction(); as action) {\n <fk-button\n [variant]=\"action.variant ?? 'outline'\"\n type=\"button\"\n (click)=\"action.onClick()\"\n >\n {{ action.label }}\n </fk-button>\n }\n\n @if (ref.isLastStep()) {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.submitDisabled()\"\n (click)=\"onSubmit()\"\n >\n {{ ref.submitLabel }}\n </fk-button>\n } @else {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.nextDisabled()\"\n (click)=\"ref.next()\"\n >\n {{ ref.nextLabel }}\n </fk-button>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;margin-top:-1rem}.fk-wizard-dialog__header{padding:var(--fk-wizard-dialog-header-padding, 0);flex-shrink:0}.fk-wizard-dialog__stepper{display:flex;gap:var(--fk-wizard-dialog-stepper-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-wizard-dialog-stepper-padding, 0 0 .75rem);flex-shrink:0}.fk-wizard-dialog__stepper--hidden{display:none}.fk-wizard-dialog__step{flex:1;display:flex;align-items:center;justify-content:center;gap:var(--fk-rhythm-2, .5rem);padding:var(--fk-rhythm-2, .5rem);font-family:var(--fk-input-font-family, inherit);font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .875rem));color:var(--fk-wizard-dialog-step-color, var(--fk-form-hint-color, #6b7280));background:transparent;border:none;cursor:pointer}.fk-wizard-dialog__step:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-radius-sm, .25rem)}.fk-wizard-dialog__step:disabled{opacity:.5;cursor:not-allowed}.fk-wizard-dialog__step--active{color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step--completed{color:var(--fk-wizard-dialog-step-completed-color, var(--fk-form-label-color, #0b1420))}.fk-wizard-dialog__step-number{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;font-size:var(--fk-font-size-xs, .75rem);font-weight:600;border-radius:50%;border:1px solid currentColor}.fk-wizard-dialog__step--active .fk-wizard-dialog__step-number{background:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff));color:var(--fk-wizard-dialog-step-number-active-color, #fff);border-color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step-optional{font-size:var(--fk-font-size-xs, .75rem);opacity:.7}.fk-wizard-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;padding:var(--fk-wizard-dialog-body-padding, var(--fk-rhythm-4, 1rem) 0);--fk-form-field-gap: 0}.fk-wizard-dialog__footer{display:flex;gap:var(--fk-wizard-dialog-footer-gap, var(--fk-rhythm-3, .75rem));padding:var(--fk-wizard-dialog-footer-padding, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-6, 1.5rem));border-top:var(--fk-wizard-dialog-footer-border, 1px solid var(--fk-color-border, #d9e2ee));flex-shrink:0}.fk-wizard-dialog__footer--hidden{display:none}.fk-wizard-dialog__footer>*{flex:1 1 0;min-width:0}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "fk-button", inputs: ["variant", "size", "loading", "disabled", "type", "fullWidth", "id", "className", "ariaLabel", "ariaDescribedBy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
182
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: WizardDialogContainerComponent, isStandalone: true, selector: "fk-wizard-dialog-container", host: { properties: { "class": "this.hostClass" } }, viewQueries: [{ propertyName: "bodyOutlet", first: true, predicate: ["bodyOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "headerOutlet", first: true, predicate: ["headerOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "footerOutlet", first: true, predicate: ["footerOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "stepperOutlet", first: true, predicate: ["stepperOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<!-- Consumer-supplied header (fixed, above the stepper) -->\n@if (ref.headerComponent) {\n <div class=\"fk-wizard-dialog__header\">\n <ng-template #headerOutlet />\n </div>\n}\n\n<!-- Stepper -->\n@if (ref.stepperComponent) {\n <div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n >\n <ng-template #stepperOutlet />\n </div>\n} @else {\n <div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n role=\"tablist\"\n >\n @for (step of ref.steps; track $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"fk-wizard-dialog__step\"\n [class.fk-wizard-dialog__step--active]=\"$index === ref.currentIndex()\"\n [class.fk-wizard-dialog__step--completed]=\"$index < ref.currentIndex()\"\n [attr.aria-selected]=\"$index === ref.currentIndex()\"\n [disabled]=\"ref.linear && $index > ref.highestVisitedIndex()\"\n (click)=\"ref.goTo($index)\"\n >\n <span class=\"fk-wizard-dialog__step-number\">{{ $index + 1 }}</span>\n <span class=\"fk-wizard-dialog__step-title\">{{ step.title }}</span>\n @if (step.optional) {\n <span class=\"fk-wizard-dialog__step-optional\">(Optional)</span>\n }\n </button>\n }\n </div>\n\n <!-- Scrollable body -->\n}\n\n<div class=\"fk-wizard-dialog__body\">\n <ng-template #bodyOutlet />\n</div>\n\n<!-- Footer (moved to dialog footer by DialogService) -->\n<div\n class=\"fk-wizard-dialog__footer\"\n [class.fk-wizard-dialog__footer--hidden]=\"!ref.chromeVisible()\"\n libDialogFooter\n>\n @if (ref.footerComponent) {\n <ng-template #footerOutlet />\n } @else {\n @if (ref.isFirstStep()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"onCancel()\">\n {{ ref.cancelLabel }}\n </fk-button>\n } @else {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"ref.previous()\">\n {{ ref.backLabel }}\n </fk-button>\n }\n\n @if (ref.extraAction(); as action) {\n <fk-button\n [variant]=\"action.variant ?? 'outline'\"\n type=\"button\"\n (click)=\"action.onClick()\"\n >\n {{ action.label }}\n </fk-button>\n }\n\n @if (ref.isLastStep()) {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.submitDisabled()\"\n (click)=\"onSubmit()\"\n >\n {{ ref.submitLabel }}\n </fk-button>\n } @else {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.nextDisabled()\"\n (click)=\"ref.next()\"\n >\n {{ ref.nextLabel }}\n </fk-button>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;margin-top:var(--fk-wizard-dialog-top-offset, -1rem)}.fk-wizard-dialog__header{padding:var(--fk-wizard-dialog-header-padding, 0);flex-shrink:0}.fk-wizard-dialog__stepper{display:flex;gap:var(--fk-wizard-dialog-stepper-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-wizard-dialog-stepper-padding, 0 0 .75rem);flex-shrink:0}.fk-wizard-dialog__stepper--hidden{display:none}.fk-wizard-dialog__step{flex:1;display:flex;align-items:center;justify-content:center;gap:var(--fk-rhythm-2, .5rem);padding:var(--fk-rhythm-2, .5rem);font-family:var(--fk-input-font-family, inherit);font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .875rem));color:var(--fk-wizard-dialog-step-color, var(--fk-form-hint-color, #6b7280));background:transparent;border:none;cursor:pointer}.fk-wizard-dialog__step:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-radius-sm, .25rem)}.fk-wizard-dialog__step:disabled{opacity:.5;cursor:not-allowed}.fk-wizard-dialog__step--active{color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step--completed{color:var(--fk-wizard-dialog-step-completed-color, var(--fk-form-label-color, #0b1420))}.fk-wizard-dialog__step-number{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;font-size:var(--fk-font-size-xs, .75rem);font-weight:600;border-radius:50%;border:1px solid currentColor}.fk-wizard-dialog__step--active .fk-wizard-dialog__step-number{background:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff));color:var(--fk-wizard-dialog-step-number-active-color, #fff);border-color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step-optional{font-size:var(--fk-font-size-xs, .75rem);opacity:.7}.fk-wizard-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;padding:var(--fk-wizard-dialog-body-padding, var(--fk-rhythm-4, 1rem) 0);--fk-form-field-gap: 0}.fk-wizard-dialog__footer{display:flex;gap:var(--fk-wizard-dialog-footer-gap, var(--fk-rhythm-3, .75rem));padding:var(--fk-wizard-dialog-footer-padding, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-6, 1.5rem));border-top:var(--fk-wizard-dialog-footer-border, 1px solid var(--fk-color-border, #d9e2ee));flex-shrink:0}.fk-wizard-dialog__footer--hidden{display:none}.fk-wizard-dialog__footer>*{flex:1 1 0;min-width:0}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "fk-button", inputs: ["variant", "size", "loading", "disabled", "type", "fullWidth", "id", "className", "ariaLabel", "ariaDescribedBy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
168
183
  }
169
184
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WizardDialogContainerComponent, decorators: [{
170
185
  type: Component,
171
- args: [{ selector: 'fk-wizard-dialog-container', standalone: true, imports: [ButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Consumer-supplied header (fixed, above the stepper) -->\n@if (ref.headerComponent) {\n <div class=\"fk-wizard-dialog__header\">\n <ng-template #headerOutlet />\n </div>\n}\n\n<!-- Stepper -->\n<div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n role=\"tablist\"\n>\n @for (step of ref.steps; track $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"fk-wizard-dialog__step\"\n [class.fk-wizard-dialog__step--active]=\"$index === ref.currentIndex()\"\n [class.fk-wizard-dialog__step--completed]=\"$index < ref.currentIndex()\"\n [attr.aria-selected]=\"$index === ref.currentIndex()\"\n [disabled]=\"ref.linear && $index > ref.highestVisitedIndex()\"\n (click)=\"ref.goTo($index)\"\n >\n <span class=\"fk-wizard-dialog__step-number\">{{ $index + 1 }}</span>\n <span class=\"fk-wizard-dialog__step-title\">{{ step.title }}</span>\n @if (step.optional) {\n <span class=\"fk-wizard-dialog__step-optional\">(Optional)</span>\n }\n </button>\n }\n</div>\n\n<!-- Scrollable body -->\n<div class=\"fk-wizard-dialog__body\">\n <ng-template #bodyOutlet />\n</div>\n\n<!-- Footer (moved to dialog footer by DialogService) -->\n<div\n class=\"fk-wizard-dialog__footer\"\n [class.fk-wizard-dialog__footer--hidden]=\"!ref.chromeVisible()\"\n libDialogFooter\n>\n @if (ref.isFirstStep()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"onCancel()\">\n {{ ref.cancelLabel }}\n </fk-button>\n } @else {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"ref.previous()\">\n {{ ref.backLabel }}\n </fk-button>\n }\n\n @if (ref.extraAction(); as action) {\n <fk-button\n [variant]=\"action.variant ?? 'outline'\"\n type=\"button\"\n (click)=\"action.onClick()\"\n >\n {{ action.label }}\n </fk-button>\n }\n\n @if (ref.isLastStep()) {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.submitDisabled()\"\n (click)=\"onSubmit()\"\n >\n {{ ref.submitLabel }}\n </fk-button>\n } @else {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.nextDisabled()\"\n (click)=\"ref.next()\"\n >\n {{ ref.nextLabel }}\n </fk-button>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;margin-top:-1rem}.fk-wizard-dialog__header{padding:var(--fk-wizard-dialog-header-padding, 0);flex-shrink:0}.fk-wizard-dialog__stepper{display:flex;gap:var(--fk-wizard-dialog-stepper-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-wizard-dialog-stepper-padding, 0 0 .75rem);flex-shrink:0}.fk-wizard-dialog__stepper--hidden{display:none}.fk-wizard-dialog__step{flex:1;display:flex;align-items:center;justify-content:center;gap:var(--fk-rhythm-2, .5rem);padding:var(--fk-rhythm-2, .5rem);font-family:var(--fk-input-font-family, inherit);font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .875rem));color:var(--fk-wizard-dialog-step-color, var(--fk-form-hint-color, #6b7280));background:transparent;border:none;cursor:pointer}.fk-wizard-dialog__step:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-radius-sm, .25rem)}.fk-wizard-dialog__step:disabled{opacity:.5;cursor:not-allowed}.fk-wizard-dialog__step--active{color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step--completed{color:var(--fk-wizard-dialog-step-completed-color, var(--fk-form-label-color, #0b1420))}.fk-wizard-dialog__step-number{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;font-size:var(--fk-font-size-xs, .75rem);font-weight:600;border-radius:50%;border:1px solid currentColor}.fk-wizard-dialog__step--active .fk-wizard-dialog__step-number{background:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff));color:var(--fk-wizard-dialog-step-number-active-color, #fff);border-color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step-optional{font-size:var(--fk-font-size-xs, .75rem);opacity:.7}.fk-wizard-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;padding:var(--fk-wizard-dialog-body-padding, var(--fk-rhythm-4, 1rem) 0);--fk-form-field-gap: 0}.fk-wizard-dialog__footer{display:flex;gap:var(--fk-wizard-dialog-footer-gap, var(--fk-rhythm-3, .75rem));padding:var(--fk-wizard-dialog-footer-padding, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-6, 1.5rem));border-top:var(--fk-wizard-dialog-footer-border, 1px solid var(--fk-color-border, #d9e2ee));flex-shrink:0}.fk-wizard-dialog__footer--hidden{display:none}.fk-wizard-dialog__footer>*{flex:1 1 0;min-width:0}\n"] }]
186
+ args: [{ selector: 'fk-wizard-dialog-container', standalone: true, imports: [ButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Consumer-supplied header (fixed, above the stepper) -->\n@if (ref.headerComponent) {\n <div class=\"fk-wizard-dialog__header\">\n <ng-template #headerOutlet />\n </div>\n}\n\n<!-- Stepper -->\n@if (ref.stepperComponent) {\n <div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n >\n <ng-template #stepperOutlet />\n </div>\n} @else {\n <div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n role=\"tablist\"\n >\n @for (step of ref.steps; track $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"fk-wizard-dialog__step\"\n [class.fk-wizard-dialog__step--active]=\"$index === ref.currentIndex()\"\n [class.fk-wizard-dialog__step--completed]=\"$index < ref.currentIndex()\"\n [attr.aria-selected]=\"$index === ref.currentIndex()\"\n [disabled]=\"ref.linear && $index > ref.highestVisitedIndex()\"\n (click)=\"ref.goTo($index)\"\n >\n <span class=\"fk-wizard-dialog__step-number\">{{ $index + 1 }}</span>\n <span class=\"fk-wizard-dialog__step-title\">{{ step.title }}</span>\n @if (step.optional) {\n <span class=\"fk-wizard-dialog__step-optional\">(Optional)</span>\n }\n </button>\n }\n </div>\n\n <!-- Scrollable body -->\n}\n\n<div class=\"fk-wizard-dialog__body\">\n <ng-template #bodyOutlet />\n</div>\n\n<!-- Footer (moved to dialog footer by DialogService) -->\n<div\n class=\"fk-wizard-dialog__footer\"\n [class.fk-wizard-dialog__footer--hidden]=\"!ref.chromeVisible()\"\n libDialogFooter\n>\n @if (ref.footerComponent) {\n <ng-template #footerOutlet />\n } @else {\n @if (ref.isFirstStep()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"onCancel()\">\n {{ ref.cancelLabel }}\n </fk-button>\n } @else {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"ref.previous()\">\n {{ ref.backLabel }}\n </fk-button>\n }\n\n @if (ref.extraAction(); as action) {\n <fk-button\n [variant]=\"action.variant ?? 'outline'\"\n type=\"button\"\n (click)=\"action.onClick()\"\n >\n {{ action.label }}\n </fk-button>\n }\n\n @if (ref.isLastStep()) {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.submitDisabled()\"\n (click)=\"onSubmit()\"\n >\n {{ ref.submitLabel }}\n </fk-button>\n } @else {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.nextDisabled()\"\n (click)=\"ref.next()\"\n >\n {{ ref.nextLabel }}\n </fk-button>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;margin-top:var(--fk-wizard-dialog-top-offset, -1rem)}.fk-wizard-dialog__header{padding:var(--fk-wizard-dialog-header-padding, 0);flex-shrink:0}.fk-wizard-dialog__stepper{display:flex;gap:var(--fk-wizard-dialog-stepper-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-wizard-dialog-stepper-padding, 0 0 .75rem);flex-shrink:0}.fk-wizard-dialog__stepper--hidden{display:none}.fk-wizard-dialog__step{flex:1;display:flex;align-items:center;justify-content:center;gap:var(--fk-rhythm-2, .5rem);padding:var(--fk-rhythm-2, .5rem);font-family:var(--fk-input-font-family, inherit);font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .875rem));color:var(--fk-wizard-dialog-step-color, var(--fk-form-hint-color, #6b7280));background:transparent;border:none;cursor:pointer}.fk-wizard-dialog__step:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-radius-sm, .25rem)}.fk-wizard-dialog__step:disabled{opacity:.5;cursor:not-allowed}.fk-wizard-dialog__step--active{color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step--completed{color:var(--fk-wizard-dialog-step-completed-color, var(--fk-form-label-color, #0b1420))}.fk-wizard-dialog__step-number{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;font-size:var(--fk-font-size-xs, .75rem);font-weight:600;border-radius:50%;border:1px solid currentColor}.fk-wizard-dialog__step--active .fk-wizard-dialog__step-number{background:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff));color:var(--fk-wizard-dialog-step-number-active-color, #fff);border-color:var(--fk-wizard-dialog-step-active-color, var(--fk-color-primary, #0a84ff))}.fk-wizard-dialog__step-optional{font-size:var(--fk-font-size-xs, .75rem);opacity:.7}.fk-wizard-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;padding:var(--fk-wizard-dialog-body-padding, var(--fk-rhythm-4, 1rem) 0);--fk-form-field-gap: 0}.fk-wizard-dialog__footer{display:flex;gap:var(--fk-wizard-dialog-footer-gap, var(--fk-rhythm-3, .75rem));padding:var(--fk-wizard-dialog-footer-padding, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-6, 1.5rem));border-top:var(--fk-wizard-dialog-footer-border, 1px solid var(--fk-color-border, #d9e2ee));flex-shrink:0}.fk-wizard-dialog__footer--hidden{display:none}.fk-wizard-dialog__footer>*{flex:1 1 0;min-width:0}\n"] }]
172
187
  }], propDecorators: { bodyOutlet: [{ type: i0.ViewChild, args: ['bodyOutlet', { ...{
173
188
  read: ViewContainerRef,
174
189
  }, isSignal: true }] }], headerOutlet: [{ type: i0.ViewChild, args: ['headerOutlet', { ...{
175
190
  read: ViewContainerRef,
191
+ }, isSignal: true }] }], footerOutlet: [{ type: i0.ViewChild, args: ['footerOutlet', { ...{
192
+ read: ViewContainerRef,
193
+ }, isSignal: true }] }], stepperOutlet: [{ type: i0.ViewChild, args: ['stepperOutlet', { ...{
194
+ read: ViewContainerRef,
176
195
  }, isSignal: true }] }], hostClass: [{
177
196
  type: HostBinding,
178
197
  args: ['class']
@@ -1 +1 @@
1
- {"version":3,"file":"frame-kit-ui-ng-ui-wizard-dialog.mjs","sources":["../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog-ref.ts","../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog-container.component.ts","../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog-container.component.html","../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog.service.ts","../../../../packages/ui-ng/ui/wizard-dialog/frame-kit-ui-ng-ui-wizard-dialog.ts"],"sourcesContent":["import type { Type } from '@angular/core';\nimport { computed, signal } from '@angular/core';\n\nimport { Observable, Subject } from 'rxjs';\n\nimport type { DialogRef } from '@frame-kit/ui-ng/ui/dialog';\nimport type {\n WizardDialogConfig,\n WizardDialogStep,\n} from './wizard-dialog.types';\n\n/**\n * Optional extra footer button the consumer can surface on any step\n * — typically a \"View X\" jump-off on the results/last step. Sits\n * between Back and Submit in the wizard's footer. Set via\n * `WizardDialogRef.extraAction.set(...)` on step entry; clear with\n * `null` on step exit (or just rely on swapping it out).\n */\nexport interface WizardExtraAction {\n /** Button label, e.g. \"View Identities\". */\n label: string;\n /** Visual variant. Defaults to `\"outline\"` so the extra action\n * doesn't compete with the primary Submit button. */\n variant?: 'primary' | 'outline' | 'secondary' | 'danger';\n /** Click handler — typically closes the wizard with a sentinel. */\n onClick: () => void;\n}\n\nexport const WIZARD_DIALOG_REF = Symbol('WIZARD_DIALOG_REF');\n\nexport class WizardDialogRef<TResult = unknown, TData = unknown> {\n private dialogRef!: DialogRef<TResult>;\n private readonly consumerData: TData | undefined;\n private readonly submitSubject = new Subject<void>();\n private readonly stepChangeSubject = new Subject<number>();\n\n // ===== CONFIG =====\n\n readonly steps: WizardDialogStep[];\n readonly linear: boolean;\n readonly submitLabel: string;\n readonly cancelLabel: string;\n readonly nextLabel: string;\n readonly backLabel: string;\n\n /** Optional consumer-supplied header rendered above the stepper. */\n readonly headerComponent: Type<unknown> | null;\n\n /** Inputs applied to `headerComponent` when it is created. */\n readonly headerInputs: Record<string, unknown>;\n\n // ===== NAVIGATION STATE =====\n\n readonly currentIndex = signal(0);\n readonly highestVisitedIndex = signal(0);\n\n readonly totalSteps = computed(() => this.steps.length);\n\n readonly isFirstStep = computed(() => this.currentIndex() === 0);\n\n readonly isLastStep = computed(\n () => this.currentIndex() === this.steps.length - 1,\n );\n\n readonly currentStep = computed(() => this.steps[this.currentIndex()]);\n\n // ===== CONSUMER-CONTROLLED STATE =====\n\n readonly submitDisabled = signal(false);\n readonly nextDisabled = signal(false);\n readonly chromeVisible = signal(true);\n /**\n * Optional third footer button. When non-null, the wizard chrome\n * renders an additional `<fk-button>` between Back and Submit\n * using this metadata. Consumers typically wire this in an\n * `effect` keyed on `currentIndex` so the button only appears on\n * the step(s) that need it.\n */\n readonly extraAction = signal<WizardExtraAction | null>(null);\n\n // ===== OBSERVABLES =====\n\n readonly submitted: Observable<void> = this.submitSubject.asObservable();\n readonly stepChanged: Observable<number> =\n this.stepChangeSubject.asObservable();\n\n constructor(config: WizardDialogConfig<TData>) {\n this.steps = config.steps;\n this.linear = config.linear ?? true;\n this.submitLabel = config.submitLabel ?? 'Submit';\n this.cancelLabel = config.cancelLabel ?? 'Cancel';\n this.nextLabel = config.nextLabel ?? 'Next';\n this.backLabel = config.backLabel ?? 'Back';\n this.headerComponent = config.headerComponent ?? null;\n this.headerInputs = config.headerInputs ?? {};\n this.consumerData = config.data;\n }\n\n get data(): TData | undefined {\n return this.consumerData;\n }\n\n // ===== NAVIGATION =====\n\n next(): boolean {\n if (this.isLastStep()) {\n return false;\n }\n\n this.currentIndex.update((i) => i + 1);\n this.updateHighestVisited();\n this.stepChangeSubject.next(this.currentIndex());\n\n return true;\n }\n\n previous(): boolean {\n if (this.isFirstStep()) {\n return false;\n }\n\n this.currentIndex.update((i) => i - 1);\n this.stepChangeSubject.next(this.currentIndex());\n\n return true;\n }\n\n goTo(index: number): boolean {\n if (index < 0 || index >= this.steps.length) {\n return false;\n }\n\n if (this.linear && index > this.highestVisitedIndex()) {\n return false;\n }\n\n if (index > this.currentIndex() && this.nextDisabled()) {\n return false;\n }\n\n this.currentIndex.set(index);\n this.updateHighestVisited();\n this.stepChangeSubject.next(index);\n\n return true;\n }\n\n private updateHighestVisited(): void {\n const current = this.currentIndex();\n\n if (current > this.highestVisitedIndex()) {\n this.highestVisitedIndex.set(current);\n }\n }\n\n // ===== DIALOG LIFECYCLE =====\n\n close(result?: TResult): void {\n this.dialogRef.close(result);\n }\n\n afterClosed(): Observable<TResult | undefined> {\n return this.dialogRef.afterClosed();\n }\n\n // ===== INTERNAL =====\n\n /** @internal Called by container when submit button is clicked. */\n emitSubmit(): void {\n this.submitSubject.next();\n }\n\n /** @internal Called by WizardDialogService after dialog is opened. */\n setDialogRef(ref: DialogRef<TResult>): void {\n this.dialogRef = ref;\n }\n}\n","import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n inject,\n Injector,\n viewChild,\n ViewContainerRef,\n} from '@angular/core';\n\nimport { ButtonComponent } from '@frame-kit/ui-ng/ui/button';\nimport { DIALOG_DATA } from '@frame-kit/ui-ng/ui/dialog';\nimport type { WizardDialogContainerData } from './wizard-dialog.service';\nimport { WIZARD_DIALOG_REF, WizardDialogRef } from './wizard-dialog-ref';\n\n@Component({\n selector: 'fk-wizard-dialog-container',\n standalone: true,\n imports: [ButtonComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './wizard-dialog-container.component.html',\n styleUrl: './wizard-dialog-container.component.scss',\n})\nexport class WizardDialogContainerComponent implements AfterViewInit {\n private readonly data = inject(\n DIALOG_DATA as never,\n ) as WizardDialogContainerData;\n private readonly injector = inject(Injector);\n private readonly bodyOutlet = viewChild('bodyOutlet', {\n read: ViewContainerRef,\n });\n private readonly headerOutlet = viewChild('headerOutlet', {\n read: ViewContainerRef,\n });\n\n readonly ref: WizardDialogRef = this.data.wizardRef;\n\n readonly classes = computed(() => 'fk-wizard-dialog');\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n ngAfterViewInit(): void {\n // One injector for both regions so a consumer-supplied header can inject\n // WIZARD_DIALOG_REF exactly like the step content does.\n const childInjector = Injector.create({\n parent: this.injector,\n providers: [{ provide: WIZARD_DIALOG_REF as never, useValue: this.ref }],\n });\n\n this.mountHeader(childInjector);\n\n const outlet = this.bodyOutlet();\n\n if (!outlet) {\n return;\n }\n\n outlet.createComponent(this.data.contentComponent, {\n injector: childInjector,\n });\n }\n\n /**\n * Render the optional consumer-supplied header into the fixed region above\n * the stepper, applying `headerInputs` so the consumer configures it\n * declaratively from the wizard config.\n */\n private mountHeader(injector: Injector): void {\n const headerComponent = this.ref.headerComponent;\n const outlet = this.headerOutlet();\n\n if (!headerComponent || !outlet) {\n return;\n }\n\n const componentRef = outlet.createComponent(headerComponent, { injector });\n\n for (const [name, value] of Object.entries(this.ref.headerInputs)) {\n componentRef.setInput(name, value);\n }\n\n componentRef.changeDetectorRef.detectChanges();\n }\n\n onCancel(): void {\n this.ref.close();\n }\n\n onSubmit(): void {\n this.ref.emitSubmit();\n }\n}\n","<!-- Consumer-supplied header (fixed, above the stepper) -->\n@if (ref.headerComponent) {\n <div class=\"fk-wizard-dialog__header\">\n <ng-template #headerOutlet />\n </div>\n}\n\n<!-- Stepper -->\n<div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n role=\"tablist\"\n>\n @for (step of ref.steps; track $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"fk-wizard-dialog__step\"\n [class.fk-wizard-dialog__step--active]=\"$index === ref.currentIndex()\"\n [class.fk-wizard-dialog__step--completed]=\"$index < ref.currentIndex()\"\n [attr.aria-selected]=\"$index === ref.currentIndex()\"\n [disabled]=\"ref.linear && $index > ref.highestVisitedIndex()\"\n (click)=\"ref.goTo($index)\"\n >\n <span class=\"fk-wizard-dialog__step-number\">{{ $index + 1 }}</span>\n <span class=\"fk-wizard-dialog__step-title\">{{ step.title }}</span>\n @if (step.optional) {\n <span class=\"fk-wizard-dialog__step-optional\">(Optional)</span>\n }\n </button>\n }\n</div>\n\n<!-- Scrollable body -->\n<div class=\"fk-wizard-dialog__body\">\n <ng-template #bodyOutlet />\n</div>\n\n<!-- Footer (moved to dialog footer by DialogService) -->\n<div\n class=\"fk-wizard-dialog__footer\"\n [class.fk-wizard-dialog__footer--hidden]=\"!ref.chromeVisible()\"\n libDialogFooter\n>\n @if (ref.isFirstStep()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"onCancel()\">\n {{ ref.cancelLabel }}\n </fk-button>\n } @else {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"ref.previous()\">\n {{ ref.backLabel }}\n </fk-button>\n }\n\n @if (ref.extraAction(); as action) {\n <fk-button\n [variant]=\"action.variant ?? 'outline'\"\n type=\"button\"\n (click)=\"action.onClick()\"\n >\n {{ action.label }}\n </fk-button>\n }\n\n @if (ref.isLastStep()) {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.submitDisabled()\"\n (click)=\"onSubmit()\"\n >\n {{ ref.submitLabel }}\n </fk-button>\n } @else {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.nextDisabled()\"\n (click)=\"ref.next()\"\n >\n {{ ref.nextLabel }}\n </fk-button>\n }\n</div>\n","import { inject, Injectable, Type } from '@angular/core';\n\nimport { DialogService } from '@frame-kit/ui-ng/ui/dialog';\nimport type { DialogRef } from '@frame-kit/ui-ng/ui/dialog';\nimport type { WizardDialogConfig } from './wizard-dialog.types';\nimport { WizardDialogContainerComponent } from './wizard-dialog-container.component';\nimport { WizardDialogRef } from './wizard-dialog-ref';\n\nexport interface WizardDialogContainerData {\n wizardRef: WizardDialogRef;\n contentComponent: Type<unknown>;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class WizardDialogService {\n private readonly dialogService = inject(DialogService);\n\n open<TResult = unknown, TData = unknown>(\n content: Type<unknown>,\n config: WizardDialogConfig<TData>,\n ): WizardDialogRef<TResult, TData> {\n const wizardRef = new WizardDialogRef<TResult, TData>(config);\n\n const dialogRef = this.dialogService.open<\n TResult,\n WizardDialogContainerData\n >(WizardDialogContainerComponent, {\n header: config.header,\n size: config.size ?? 'lg',\n closable: config.closable,\n closeOnEscape: config.closeOnEscape,\n closeOnBackdrop: config.closeOnBackdrop,\n animation: config.animation,\n fullscreenMobile: config.fullscreenMobile,\n className: ['fk-wizard-dialog-host', config.className]\n .filter(Boolean)\n .join(' '),\n ariaLabel: config.ariaLabel,\n ariaDescribedBy: config.ariaDescribedBy,\n flexBody: true,\n data: {\n wizardRef: wizardRef as WizardDialogRef,\n contentComponent: content,\n },\n });\n\n wizardRef.setDialogRef(dialogRef as DialogRef<TResult>);\n\n return wizardRef;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"frame-kit-ui-ng-ui-wizard-dialog.mjs","sources":["../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog-ref.ts","../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog-container.component.ts","../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog-container.component.html","../../../../packages/ui-ng/ui/wizard-dialog/wizard-dialog.service.ts","../../../../packages/ui-ng/ui/wizard-dialog/frame-kit-ui-ng-ui-wizard-dialog.ts"],"sourcesContent":["import type { Type } from '@angular/core';\nimport { computed, signal } from '@angular/core';\n\nimport { Observable, Subject } from 'rxjs';\n\nimport type { DialogRef } from '@frame-kit/ui-ng/ui/dialog';\nimport type {\n WizardDialogConfig,\n WizardDialogStep,\n} from './wizard-dialog.types';\n\n/**\n * Optional extra footer button the consumer can surface on any step\n * — typically a \"View X\" jump-off on the results/last step. Sits\n * between Back and Submit in the wizard's footer. Set via\n * `WizardDialogRef.extraAction.set(...)` on step entry; clear with\n * `null` on step exit (or just rely on swapping it out).\n */\nexport interface WizardExtraAction {\n /** Button label, e.g. \"View Identities\". */\n label: string;\n /** Visual variant. Defaults to `\"outline\"` so the extra action\n * doesn't compete with the primary Submit button. */\n variant?: 'primary' | 'outline' | 'secondary' | 'danger';\n /** Click handler — typically closes the wizard with a sentinel. */\n onClick: () => void;\n}\n\nexport const WIZARD_DIALOG_REF = Symbol('WIZARD_DIALOG_REF');\n\nexport class WizardDialogRef<TResult = unknown, TData = unknown> {\n private dialogRef!: DialogRef<TResult>;\n private readonly consumerData: TData | undefined;\n private readonly submitSubject = new Subject<void>();\n private readonly stepChangeSubject = new Subject<number>();\n\n // ===== CONFIG =====\n\n readonly steps: WizardDialogStep[];\n readonly linear: boolean;\n readonly submitLabel: string;\n readonly cancelLabel: string;\n readonly nextLabel: string;\n readonly backLabel: string;\n\n /** Optional consumer-supplied header rendered above the stepper. */\n readonly headerComponent: Type<unknown> | null;\n\n /** Inputs applied to `headerComponent` when it is created. */\n readonly headerInputs: Record<string, unknown>;\n\n /** Optional consumer-supplied footer, replacing the built-in buttons. */\n readonly footerComponent: Type<unknown> | null;\n\n /** Inputs applied to `footerComponent` when it is created. */\n readonly footerInputs: Record<string, unknown>;\n\n /** Optional consumer-supplied stepper, replacing the built-in pill row. */\n readonly stepperComponent: Type<unknown> | null;\n\n /** Inputs applied to `stepperComponent` when it is created. */\n readonly stepperInputs: Record<string, unknown>;\n\n // ===== NAVIGATION STATE =====\n\n readonly currentIndex = signal(0);\n readonly highestVisitedIndex = signal(0);\n\n readonly totalSteps = computed(() => this.steps.length);\n\n readonly isFirstStep = computed(() => this.currentIndex() === 0);\n\n readonly isLastStep = computed(\n () => this.currentIndex() === this.steps.length - 1,\n );\n\n readonly currentStep = computed(() => this.steps[this.currentIndex()]);\n\n // ===== CONSUMER-CONTROLLED STATE =====\n\n readonly submitDisabled = signal(false);\n readonly nextDisabled = signal(false);\n readonly chromeVisible = signal(true);\n /**\n * Optional third footer button. When non-null, the wizard chrome\n * renders an additional `<fk-button>` between Back and Submit\n * using this metadata. Consumers typically wire this in an\n * `effect` keyed on `currentIndex` so the button only appears on\n * the step(s) that need it.\n */\n readonly extraAction = signal<WizardExtraAction | null>(null);\n\n // ===== OBSERVABLES =====\n\n readonly submitted: Observable<void> = this.submitSubject.asObservable();\n readonly stepChanged: Observable<number> =\n this.stepChangeSubject.asObservable();\n\n constructor(config: WizardDialogConfig<TData>) {\n this.steps = config.steps;\n this.linear = config.linear ?? true;\n this.submitLabel = config.submitLabel ?? 'Submit';\n this.cancelLabel = config.cancelLabel ?? 'Cancel';\n this.nextLabel = config.nextLabel ?? 'Next';\n this.backLabel = config.backLabel ?? 'Back';\n this.headerComponent = config.headerComponent ?? null;\n this.headerInputs = config.headerInputs ?? {};\n this.footerComponent = config.footerComponent ?? null;\n this.footerInputs = config.footerInputs ?? {};\n this.stepperComponent = config.stepperComponent ?? null;\n this.stepperInputs = config.stepperInputs ?? {};\n this.consumerData = config.data;\n }\n\n get data(): TData | undefined {\n return this.consumerData;\n }\n\n // ===== NAVIGATION =====\n\n next(): boolean {\n if (this.isLastStep()) {\n return false;\n }\n\n this.currentIndex.update((i) => i + 1);\n this.updateHighestVisited();\n this.stepChangeSubject.next(this.currentIndex());\n\n return true;\n }\n\n previous(): boolean {\n if (this.isFirstStep()) {\n return false;\n }\n\n this.currentIndex.update((i) => i - 1);\n this.stepChangeSubject.next(this.currentIndex());\n\n return true;\n }\n\n goTo(index: number): boolean {\n if (index < 0 || index >= this.steps.length) {\n return false;\n }\n\n if (this.linear && index > this.highestVisitedIndex()) {\n return false;\n }\n\n if (index > this.currentIndex() && this.nextDisabled()) {\n return false;\n }\n\n this.currentIndex.set(index);\n this.updateHighestVisited();\n this.stepChangeSubject.next(index);\n\n return true;\n }\n\n private updateHighestVisited(): void {\n const current = this.currentIndex();\n\n if (current > this.highestVisitedIndex()) {\n this.highestVisitedIndex.set(current);\n }\n }\n\n // ===== DIALOG LIFECYCLE =====\n\n close(result?: TResult): void {\n this.dialogRef.close(result);\n }\n\n afterClosed(): Observable<TResult | undefined> {\n return this.dialogRef.afterClosed();\n }\n\n // ===== INTERNAL =====\n\n /** @internal Called by container when submit button is clicked. */\n emitSubmit(): void {\n this.submitSubject.next();\n }\n\n /** @internal Called by WizardDialogService after dialog is opened. */\n setDialogRef(ref: DialogRef<TResult>): void {\n this.dialogRef = ref;\n }\n}\n","import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n inject,\n Injector,\n viewChild,\n Type,\n ViewContainerRef,\n} from '@angular/core';\n\nimport { ButtonComponent } from '@frame-kit/ui-ng/ui/button';\nimport { DIALOG_DATA } from '@frame-kit/ui-ng/ui/dialog';\nimport type { WizardDialogContainerData } from './wizard-dialog.service';\nimport { WIZARD_DIALOG_REF, WizardDialogRef } from './wizard-dialog-ref';\n\n@Component({\n selector: 'fk-wizard-dialog-container',\n standalone: true,\n imports: [ButtonComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './wizard-dialog-container.component.html',\n styleUrl: './wizard-dialog-container.component.scss',\n})\nexport class WizardDialogContainerComponent implements AfterViewInit {\n private readonly data = inject(\n DIALOG_DATA as never,\n ) as WizardDialogContainerData;\n private readonly injector = inject(Injector);\n private readonly bodyOutlet = viewChild('bodyOutlet', {\n read: ViewContainerRef,\n });\n private readonly headerOutlet = viewChild('headerOutlet', {\n read: ViewContainerRef,\n });\n private readonly footerOutlet = viewChild('footerOutlet', {\n read: ViewContainerRef,\n });\n private readonly stepperOutlet = viewChild('stepperOutlet', {\n read: ViewContainerRef,\n });\n\n readonly ref: WizardDialogRef = this.data.wizardRef;\n\n readonly classes = computed(() => 'fk-wizard-dialog');\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n ngAfterViewInit(): void {\n // One injector for both regions so a consumer-supplied header can inject\n // WIZARD_DIALOG_REF exactly like the step content does.\n const childInjector = Injector.create({\n parent: this.injector,\n providers: [{ provide: WIZARD_DIALOG_REF as never, useValue: this.ref }],\n });\n\n this.mountRegion(\n this.headerOutlet(),\n this.ref.headerComponent,\n this.ref.headerInputs,\n childInjector,\n );\n\n this.mountRegion(\n this.footerOutlet(),\n this.ref.footerComponent,\n this.ref.footerInputs,\n childInjector,\n );\n\n this.mountRegion(\n this.stepperOutlet(),\n this.ref.stepperComponent,\n this.ref.stepperInputs,\n childInjector,\n );\n\n const outlet = this.bodyOutlet();\n\n if (!outlet) {\n return;\n }\n\n outlet.createComponent(this.data.contentComponent, {\n injector: childInjector,\n });\n }\n\n /**\n * Render an optional consumer-supplied region (header or footer) into its\n * fixed outlet, applying the configured inputs so consumers configure it\n * declaratively from the wizard config. Shares the wizard injector, so the\n * component can inject `WIZARD_DIALOG_REF`.\n */\n private mountRegion(\n outlet: ViewContainerRef | undefined,\n component: Type<unknown> | null,\n inputs: Record<string, unknown>,\n injector: Injector,\n ): void {\n if (!component || !outlet) {\n return;\n }\n\n const componentRef = outlet.createComponent(component, { injector });\n\n for (const [name, value] of Object.entries(inputs)) {\n componentRef.setInput(name, value);\n }\n\n componentRef.changeDetectorRef.detectChanges();\n }\n\n onCancel(): void {\n this.ref.close();\n }\n\n onSubmit(): void {\n this.ref.emitSubmit();\n }\n}\n","<!-- Consumer-supplied header (fixed, above the stepper) -->\n@if (ref.headerComponent) {\n <div class=\"fk-wizard-dialog__header\">\n <ng-template #headerOutlet />\n </div>\n}\n\n<!-- Stepper -->\n@if (ref.stepperComponent) {\n <div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n >\n <ng-template #stepperOutlet />\n </div>\n} @else {\n <div\n class=\"fk-wizard-dialog__stepper\"\n [class.fk-wizard-dialog__stepper--hidden]=\"!ref.chromeVisible()\"\n role=\"tablist\"\n >\n @for (step of ref.steps; track $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"fk-wizard-dialog__step\"\n [class.fk-wizard-dialog__step--active]=\"$index === ref.currentIndex()\"\n [class.fk-wizard-dialog__step--completed]=\"$index < ref.currentIndex()\"\n [attr.aria-selected]=\"$index === ref.currentIndex()\"\n [disabled]=\"ref.linear && $index > ref.highestVisitedIndex()\"\n (click)=\"ref.goTo($index)\"\n >\n <span class=\"fk-wizard-dialog__step-number\">{{ $index + 1 }}</span>\n <span class=\"fk-wizard-dialog__step-title\">{{ step.title }}</span>\n @if (step.optional) {\n <span class=\"fk-wizard-dialog__step-optional\">(Optional)</span>\n }\n </button>\n }\n </div>\n\n <!-- Scrollable body -->\n}\n\n<div class=\"fk-wizard-dialog__body\">\n <ng-template #bodyOutlet />\n</div>\n\n<!-- Footer (moved to dialog footer by DialogService) -->\n<div\n class=\"fk-wizard-dialog__footer\"\n [class.fk-wizard-dialog__footer--hidden]=\"!ref.chromeVisible()\"\n libDialogFooter\n>\n @if (ref.footerComponent) {\n <ng-template #footerOutlet />\n } @else {\n @if (ref.isFirstStep()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"onCancel()\">\n {{ ref.cancelLabel }}\n </fk-button>\n } @else {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"ref.previous()\">\n {{ ref.backLabel }}\n </fk-button>\n }\n\n @if (ref.extraAction(); as action) {\n <fk-button\n [variant]=\"action.variant ?? 'outline'\"\n type=\"button\"\n (click)=\"action.onClick()\"\n >\n {{ action.label }}\n </fk-button>\n }\n\n @if (ref.isLastStep()) {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.submitDisabled()\"\n (click)=\"onSubmit()\"\n >\n {{ ref.submitLabel }}\n </fk-button>\n } @else {\n <fk-button\n variant=\"primary\"\n type=\"button\"\n [disabled]=\"ref.nextDisabled()\"\n (click)=\"ref.next()\"\n >\n {{ ref.nextLabel }}\n </fk-button>\n }\n }\n</div>\n","import { inject, Injectable, Type } from '@angular/core';\n\nimport { DialogService } from '@frame-kit/ui-ng/ui/dialog';\nimport type { DialogRef } from '@frame-kit/ui-ng/ui/dialog';\nimport type { WizardDialogConfig } from './wizard-dialog.types';\nimport { WizardDialogContainerComponent } from './wizard-dialog-container.component';\nimport { WizardDialogRef } from './wizard-dialog-ref';\n\nexport interface WizardDialogContainerData {\n wizardRef: WizardDialogRef;\n contentComponent: Type<unknown>;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class WizardDialogService {\n private readonly dialogService = inject(DialogService);\n\n open<TResult = unknown, TData = unknown>(\n content: Type<unknown>,\n config: WizardDialogConfig<TData>,\n ): WizardDialogRef<TResult, TData> {\n const wizardRef = new WizardDialogRef<TResult, TData>(config);\n\n const dialogRef = this.dialogService.open<\n TResult,\n WizardDialogContainerData\n >(WizardDialogContainerComponent, {\n header: config.header,\n size: config.size ?? 'lg',\n closable: config.closable,\n closeOnEscape: config.closeOnEscape,\n closeOnBackdrop: config.closeOnBackdrop,\n animation: config.animation,\n fullscreenMobile: config.fullscreenMobile,\n className: ['fk-wizard-dialog-host', config.className]\n .filter(Boolean)\n .join(' '),\n ariaLabel: config.ariaLabel,\n ariaDescribedBy: config.ariaDescribedBy,\n flexBody: true,\n data: {\n wizardRef: wizardRef as WizardDialogRef,\n contentComponent: content,\n },\n });\n\n wizardRef.setDialogRef(dialogRef as DialogRef<TResult>);\n\n return wizardRef;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frame-kit/ui-ng",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Style-agnostic, token-driven Angular UI component library for FrameKit.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -39,6 +39,29 @@ interface WizardDialogConfig<TData = unknown> extends Pick<DialogConfig<TData>,
39
39
  * `{ icon: 'key-outline', title: 'Create API Key' }`.
40
40
  */
41
41
  headerInputs?: Record<string, unknown>;
42
+ /**
43
+ * Component rendered in place of the built-in footer buttons. Use this when
44
+ * the default Cancel/Back + Next/Submit row isn't enough (e.g. a step
45
+ * counter, icon-bearing buttons).
46
+ *
47
+ * The component is created with the wizard's injector, so it can inject
48
+ * `WIZARD_DIALOG_REF` to drive navigation (`next()`, `previous()`, `close()`)
49
+ * and read step state.
50
+ */
51
+ footerComponent?: Type<unknown>;
52
+ /** Inputs applied to `footerComponent` via `setInput`. */
53
+ footerInputs?: Record<string, unknown>;
54
+ /**
55
+ * Component rendered in place of the built-in stepper. Use this when the
56
+ * default numbered-pill row isn't enough (e.g. connecting progress lines,
57
+ * per-step kickers, completion checks).
58
+ *
59
+ * The component is created with the wizard's injector, so it can inject
60
+ * `WIZARD_DIALOG_REF` to read `steps` / `currentIndex` and call `goTo()`.
61
+ */
62
+ stepperComponent?: Type<unknown>;
63
+ /** Inputs applied to `stepperComponent` via `setInput`. */
64
+ stepperInputs?: Record<string, unknown>;
42
65
  }
43
66
 
44
67
  /**
@@ -73,6 +96,14 @@ declare class WizardDialogRef<TResult = unknown, TData = unknown> {
73
96
  readonly headerComponent: Type<unknown> | null;
74
97
  /** Inputs applied to `headerComponent` when it is created. */
75
98
  readonly headerInputs: Record<string, unknown>;
99
+ /** Optional consumer-supplied footer, replacing the built-in buttons. */
100
+ readonly footerComponent: Type<unknown> | null;
101
+ /** Inputs applied to `footerComponent` when it is created. */
102
+ readonly footerInputs: Record<string, unknown>;
103
+ /** Optional consumer-supplied stepper, replacing the built-in pill row. */
104
+ readonly stepperComponent: Type<unknown> | null;
105
+ /** Inputs applied to `stepperComponent` when it is created. */
106
+ readonly stepperInputs: Record<string, unknown>;
76
107
  readonly currentIndex: _angular_core.WritableSignal<number>;
77
108
  readonly highestVisitedIndex: _angular_core.WritableSignal<number>;
78
109
  readonly totalSteps: _angular_core.Signal<number>;
@@ -122,16 +153,19 @@ declare class WizardDialogContainerComponent implements AfterViewInit {
122
153
  private readonly injector;
123
154
  private readonly bodyOutlet;
124
155
  private readonly headerOutlet;
156
+ private readonly footerOutlet;
157
+ private readonly stepperOutlet;
125
158
  readonly ref: WizardDialogRef;
126
159
  readonly classes: _angular_core.Signal<string>;
127
160
  get hostClass(): string;
128
161
  ngAfterViewInit(): void;
129
162
  /**
130
- * Render the optional consumer-supplied header into the fixed region above
131
- * the stepper, applying `headerInputs` so the consumer configures it
132
- * declaratively from the wizard config.
163
+ * Render an optional consumer-supplied region (header or footer) into its
164
+ * fixed outlet, applying the configured inputs so consumers configure it
165
+ * declaratively from the wizard config. Shares the wizard injector, so the
166
+ * component can inject `WIZARD_DIALOG_REF`.
133
167
  */
134
- private mountHeader;
168
+ private mountRegion;
135
169
  onCancel(): void;
136
170
  onSubmit(): void;
137
171
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WizardDialogContainerComponent, never>;
@@ -89,6 +89,77 @@ wizardDialog.open(CreateApiKeyWizard, {
89
89
  The header is created with the wizard's own injector, so it can
90
90
  `inject(WIZARD_DIALOG_REF)` to read step state or close the wizard.
91
91
 
92
+ ### Custom footer
93
+
94
+ Same mechanism for the footer. Supply `footerComponent` to replace the built-in
95
+ Cancel/Back + Next/Submit row — useful for a step counter, icon-bearing
96
+ buttons, or any layout the defaults don't cover. It renders inside the wizard's
97
+ own fixed footer region, so it stays pinned while the body scrolls.
98
+
99
+ ```ts
100
+ wizardDialog.open(CreateApiKeyWizard, {
101
+ footerComponent: MyWizardFooterComponent,
102
+ steps: [...],
103
+ });
104
+ ```
105
+
106
+ Like the header, it's created with the wizard's injector — so it can
107
+ `inject(WIZARD_DIALOG_REF)` and drive navigation via `next()`, `previous()`,
108
+ and `close()`.
109
+
110
+ ### Custom stepper
111
+
112
+ `stepperComponent` replaces the built-in numbered-pill row — for progress
113
+ lines, per-step kickers, completion checks, or any other treatment. It renders
114
+ in the same fixed stepper region and respects `chromeVisible`.
115
+
116
+ ```ts
117
+ wizardDialog.open(CreateApiKeyWizard, {
118
+ stepperComponent: MyWizardStepperComponent,
119
+ steps: [...],
120
+ });
121
+ ```
122
+
123
+ It reads `steps`, `currentIndex`, and `highestVisitedIndex` off the injected
124
+ `WIZARD_DIALOG_REF`, and calls `goTo()` to navigate. A custom stepper owns its
125
+ own ARIA — the built-in `role="tablist"` markup is not applied to it.
126
+
127
+ ### Custom chrome: host display requirement
128
+
129
+ Each region wrapper is a flex container that mounts your component's **host
130
+ element** as its flex item. A host defaults to `display: block`, which
131
+ collapses the component's own internal layout (spacers, right-aligned
132
+ buttons). Every custom header / stepper / footer component must declare its
133
+ host display:
134
+
135
+ ```ts
136
+ @Component({
137
+ // ...
138
+ styles: `
139
+ :host {
140
+ display: flex;
141
+ align-items: center;
142
+ width: 100%;
143
+ }
144
+ `,
145
+ })
146
+ export class MyWizardFooterComponent {}
147
+ ```
148
+
149
+ Symptom if you forget: the footer's spacer does nothing and buttons bunch to
150
+ the left.
151
+
152
+ ### Chrome slots at a glance
153
+
154
+ All three regions follow the same pattern, so a consumer can override one and
155
+ keep the library defaults for the rest:
156
+
157
+ | Region | Config option | Falls back to |
158
+ | ------- | ------------------ | ---------------------------------- |
159
+ | Header | `headerComponent` | none (no default header) |
160
+ | Stepper | `stepperComponent` | built-in numbered pills |
161
+ | Footer | `footerComponent` | built-in Cancel/Back + Next/Submit |
162
+
92
163
  ## Features
93
164
 
94
165
  - Service-driven: opens its own dialog with proper footer pinning
@@ -273,6 +344,7 @@ The footer's existing flex layout (`flex: 1 1 0` on each child) stretches all th
273
344
  ## Design Tokens
274
345
 
275
346
  ```scss
347
+ --fk-wizard-dialog-top-offset
276
348
  --fk-wizard-dialog-header-padding
277
349
  --fk-wizard-dialog-stepper-gap
278
350
  --fk-wizard-dialog-stepper-padding