@frame-kit/ui-ng 0.1.15 → 0.1.16

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.
@@ -17,6 +17,10 @@ class WizardDialogRef {
17
17
  cancelLabel;
18
18
  nextLabel;
19
19
  backLabel;
20
+ /** Optional consumer-supplied header rendered above the stepper. */
21
+ headerComponent;
22
+ /** Inputs applied to `headerComponent` when it is created. */
23
+ headerInputs;
20
24
  // ===== NAVIGATION STATE =====
21
25
  currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
22
26
  highestVisitedIndex = signal(0, ...(ngDevMode ? [{ debugName: "highestVisitedIndex" }] : /* istanbul ignore next */ []));
@@ -46,6 +50,8 @@ class WizardDialogRef {
46
50
  this.cancelLabel = config.cancelLabel ?? 'Cancel';
47
51
  this.nextLabel = config.nextLabel ?? 'Next';
48
52
  this.backLabel = config.backLabel ?? 'Back';
53
+ this.headerComponent = config.headerComponent ?? null;
54
+ this.headerInputs = config.headerInputs ?? {};
49
55
  this.consumerData = config.data;
50
56
  }
51
57
  get data() {
@@ -112,24 +118,45 @@ class WizardDialogContainerComponent {
112
118
  data = inject(DIALOG_DATA);
113
119
  injector = inject(Injector);
114
120
  bodyOutlet = viewChild('bodyOutlet', { ...(ngDevMode ? { debugName: "bodyOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
121
+ headerOutlet = viewChild('headerOutlet', { ...(ngDevMode ? { debugName: "headerOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
115
122
  ref = this.data.wizardRef;
116
123
  classes = computed(() => 'fk-wizard-dialog', ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
117
124
  get hostClass() {
118
125
  return this.classes();
119
126
  }
120
127
  ngAfterViewInit() {
121
- const outlet = this.bodyOutlet();
122
- if (!outlet) {
123
- return;
124
- }
128
+ // One injector for both regions so a consumer-supplied header can inject
129
+ // WIZARD_DIALOG_REF exactly like the step content does.
125
130
  const childInjector = Injector.create({
126
131
  parent: this.injector,
127
132
  providers: [{ provide: WIZARD_DIALOG_REF, useValue: this.ref }],
128
133
  });
134
+ this.mountHeader(childInjector);
135
+ const outlet = this.bodyOutlet();
136
+ if (!outlet) {
137
+ return;
138
+ }
129
139
  outlet.createComponent(this.data.contentComponent, {
130
140
  injector: childInjector,
131
141
  });
132
142
  }
143
+ /**
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.
147
+ */
148
+ mountHeader(injector) {
149
+ const headerComponent = this.ref.headerComponent;
150
+ const outlet = this.headerOutlet();
151
+ if (!headerComponent || !outlet) {
152
+ return;
153
+ }
154
+ const componentRef = outlet.createComponent(headerComponent, { injector });
155
+ for (const [name, value] of Object.entries(this.ref.headerInputs)) {
156
+ componentRef.setInput(name, value);
157
+ }
158
+ componentRef.changeDetectorRef.detectChanges();
159
+ }
133
160
  onCancel() {
134
161
  this.ref.close();
135
162
  }
@@ -137,13 +164,15 @@ class WizardDialogContainerComponent {
137
164
  this.ref.emitSubmit();
138
165
  }
139
166
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WizardDialogContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
140
- 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 }], ngImport: i0, template: "<!-- 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__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 });
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 });
141
168
  }
142
169
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WizardDialogContainerComponent, decorators: [{
143
170
  type: Component,
144
- args: [{ selector: 'fk-wizard-dialog-container', standalone: true, imports: [ButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- 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__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"] }]
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"] }]
145
172
  }], propDecorators: { bodyOutlet: [{ type: i0.ViewChild, args: ['bodyOutlet', { ...{
146
173
  read: ViewContainerRef,
174
+ }, isSignal: true }] }], headerOutlet: [{ type: i0.ViewChild, args: ['headerOutlet', { ...{
175
+ read: ViewContainerRef,
147
176
  }, isSignal: true }] }], hostClass: [{
148
177
  type: HostBinding,
149
178
  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 { 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 // ===== 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.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\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 const outlet = this.bodyOutlet();\n\n if (!outlet) {\n return;\n }\n\n const childInjector = Injector.create({\n parent: this.injector,\n providers: [{ provide: WIZARD_DIALOG_REF as never, useValue: this.ref }],\n });\n\n outlet.createComponent(this.data.contentComponent, {\n injector: childInjector,\n });\n }\n\n onCancel(): void {\n this.ref.close();\n }\n\n onSubmit(): void {\n this.ref.emitSubmit();\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 // ===== 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frame-kit/ui-ng",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Style-agnostic, token-driven Angular UI component library for FrameKit.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,6 +24,21 @@ interface WizardDialogConfig<TData = unknown> extends Pick<DialogConfig<TData>,
24
24
  nextLabel?: string;
25
25
  /** Label for the back button. Default: `"Back"`. */
26
26
  backLabel?: string;
27
+ /**
28
+ * Component rendered in a fixed region above the stepper — the wizard's
29
+ * own header. Use this when the plain `header` string isn't enough (e.g.
30
+ * an icon-tile header with a subtitle); pair it with `header: null` so the
31
+ * underlying dialog doesn't also render its title bar.
32
+ *
33
+ * The component is created with the wizard's injector, so it can inject
34
+ * `WIZARD_DIALOG_REF` to read step state or close the wizard.
35
+ */
36
+ headerComponent?: Type<unknown>;
37
+ /**
38
+ * Inputs applied to `headerComponent` via `setInput` — e.g.
39
+ * `{ icon: 'key-outline', title: 'Create API Key' }`.
40
+ */
41
+ headerInputs?: Record<string, unknown>;
27
42
  }
28
43
 
29
44
  /**
@@ -54,6 +69,10 @@ declare class WizardDialogRef<TResult = unknown, TData = unknown> {
54
69
  readonly cancelLabel: string;
55
70
  readonly nextLabel: string;
56
71
  readonly backLabel: string;
72
+ /** Optional consumer-supplied header rendered above the stepper. */
73
+ readonly headerComponent: Type<unknown> | null;
74
+ /** Inputs applied to `headerComponent` when it is created. */
75
+ readonly headerInputs: Record<string, unknown>;
57
76
  readonly currentIndex: _angular_core.WritableSignal<number>;
58
77
  readonly highestVisitedIndex: _angular_core.WritableSignal<number>;
59
78
  readonly totalSteps: _angular_core.Signal<number>;
@@ -102,10 +121,17 @@ declare class WizardDialogContainerComponent implements AfterViewInit {
102
121
  private readonly data;
103
122
  private readonly injector;
104
123
  private readonly bodyOutlet;
124
+ private readonly headerOutlet;
105
125
  readonly ref: WizardDialogRef;
106
126
  readonly classes: _angular_core.Signal<string>;
107
127
  get hostClass(): string;
108
128
  ngAfterViewInit(): void;
129
+ /**
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.
133
+ */
134
+ private mountHeader;
109
135
  onCancel(): void;
110
136
  onSubmit(): void;
111
137
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WizardDialogContainerComponent, never>;
@@ -12,25 +12,27 @@ A service-driven wizard dialog for multi-step workflows. Opens its own dialog vi
12
12
 
13
13
  ### WizardDialogConfig
14
14
 
15
- | Option | Type | Default | Description |
16
- | ------------------ | -------------------- | -------------- | -------------------------------------- |
17
- | `steps` | `WizardDialogStep[]` | required | Step definitions (title + contentKey) |
18
- | `linear` | `boolean` | `true` | Enforce sequential navigation |
19
- | `submitLabel` | `string` | `"Submit"` | Label for the final step action button |
20
- | `cancelLabel` | `string` | `"Cancel"` | Label for cancel button (first step) |
21
- | `nextLabel` | `string` | `"Next"` | Label for next step button |
22
- | `backLabel` | `string` | `"Back"` | Label for back button |
23
- | `header` | `string` | — | Dialog header title |
24
- | `size` | `DialogSize` | `"lg"` | Dialog size |
25
- | `data` | `TData` | — | Data passed to the content component |
26
- | `closable` | `boolean` | `true` | Show close button |
27
- | `closeOnEscape` | `boolean` | `true` | Close on Escape key |
28
- | `closeOnBackdrop` | `boolean` | `false` | Close on backdrop click |
29
- | `animation` | `DialogAnimation` | `"scale-fade"` | Open/close animation |
30
- | `fullscreenMobile` | `boolean` | `false` | Fullscreen on mobile viewports |
31
- | `className` | `string` | — | Additional CSS class on dialog |
32
- | `ariaLabel` | `string` | — | Accessible label for the dialog |
33
- | `ariaDescribedBy` | `string` | — | ID of describing element |
15
+ | Option | Type | Default | Description |
16
+ | ------------------ | ------------------------- | -------------- | ---------------------------------------- |
17
+ | `steps` | `WizardDialogStep[]` | required | Step definitions (title + contentKey) |
18
+ | `linear` | `boolean` | `true` | Enforce sequential navigation |
19
+ | `submitLabel` | `string` | `"Submit"` | Label for the final step action button |
20
+ | `cancelLabel` | `string` | `"Cancel"` | Label for cancel button (first step) |
21
+ | `nextLabel` | `string` | `"Next"` | Label for next step button |
22
+ | `backLabel` | `string` | `"Back"` | Label for back button |
23
+ | `header` | `string` | — | Dialog header title |
24
+ | `headerComponent` | `Type<unknown>` | — | Custom header rendered above the stepper |
25
+ | `headerInputs` | `Record<string, unknown>` | `{}` | Inputs applied to `headerComponent` |
26
+ | `size` | `DialogSize` | `"lg"` | Dialog size |
27
+ | `data` | `TData` | — | Data passed to the content component |
28
+ | `closable` | `boolean` | `true` | Show close button |
29
+ | `closeOnEscape` | `boolean` | `true` | Close on Escape key |
30
+ | `closeOnBackdrop` | `boolean` | `false` | Close on backdrop click |
31
+ | `animation` | `DialogAnimation` | `"scale-fade"` | Open/close animation |
32
+ | `fullscreenMobile` | `boolean` | `false` | Fullscreen on mobile viewports |
33
+ | `className` | `string` | — | Additional CSS class on dialog |
34
+ | `ariaLabel` | `string` | — | Accessible label for the dialog |
35
+ | `ariaDescribedBy` | `string` | — | ID of describing element |
34
36
 
35
37
  ### WizardDialogRef
36
38
 
@@ -68,6 +70,25 @@ Metadata describing the optional third footer button. Set `wizardRef.extraAction
68
70
 
69
71
  The content component is dynamically created inside the wizard body. It receives a `WIZARD_DIALOG_REF` injection token to access navigation state and lifecycle events. Step content is rendered via `@switch` on `wizardRef.currentIndex()`.
70
72
 
73
+ ### Custom header
74
+
75
+ The plain `header` string renders the underlying dialog's title bar. When a
76
+ wizard needs richer chrome — an icon tile, a subtitle, a branded header — pass
77
+ a component instead. It renders in a **fixed region above the stepper**, so it
78
+ stays put while the body scrolls.
79
+
80
+ ```ts
81
+ wizardDialog.open(CreateApiKeyWizard, {
82
+ header: null, // suppress the plain title bar
83
+ headerComponent: MyWizardHeaderComponent,
84
+ headerInputs: { icon: 'key-outline', title: 'Create API Key' },
85
+ steps: [...],
86
+ });
87
+ ```
88
+
89
+ The header is created with the wizard's own injector, so it can
90
+ `inject(WIZARD_DIALOG_REF)` to read step state or close the wizard.
91
+
71
92
  ## Features
72
93
 
73
94
  - Service-driven: opens its own dialog with proper footer pinning
@@ -252,6 +273,7 @@ The footer's existing flex layout (`flex: 1 1 0` on each child) stretches all th
252
273
  ## Design Tokens
253
274
 
254
275
  ```scss
276
+ --fk-wizard-dialog-header-padding
255
277
  --fk-wizard-dialog-stepper-gap
256
278
  --fk-wizard-dialog-stepper-padding
257
279
  --fk-wizard-dialog-step-color