@mohamedatia/fly-design-system 2.16.0 → 2.16.1
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,7 +17,7 @@ import { TiptapEditorDirective } from 'ngx-tiptap';
|
|
|
17
17
|
import { Link } from '@tiptap/extension-link';
|
|
18
18
|
import { registerCustomProtocol } from 'linkifyjs';
|
|
19
19
|
import { switchMap, debounceTime, distinctUntilChanged, filter } from 'rxjs/operators';
|
|
20
|
-
import * as
|
|
20
|
+
import * as i2 from '@angular/cdk/a11y';
|
|
21
21
|
import { A11yModule } from '@angular/cdk/a11y';
|
|
22
22
|
import Cropper from 'cropperjs';
|
|
23
23
|
|
|
@@ -4235,10 +4235,18 @@ class FlyDrawerComponent {
|
|
|
4235
4235
|
openChange = output();
|
|
4236
4236
|
/** Whether the panel is mounted (true while open AND during the slide-out). */
|
|
4237
4237
|
rendered = signal(false, ...(ngDevMode ? [{ debugName: "rendered" }] : /* istanbul ignore next */ []));
|
|
4238
|
-
/**
|
|
4239
|
-
|
|
4238
|
+
/** True only while the slide-out animation is playing (drives the exit keyframe). */
|
|
4239
|
+
leaving = signal(false, ...(ngDevMode ? [{ debugName: "leaving" }] : /* istanbul ignore next */ []));
|
|
4240
4240
|
headingId = 'fly-drawer-heading';
|
|
4241
4241
|
labelledBy = computed(() => (this.heading() ? this.headingId : null), ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
|
|
4242
|
+
/** Size/side classes as an ngClass map (kept off the `[class]` string binding,
|
|
4243
|
+
* which can race the leaving toggle and stutter the animation). */
|
|
4244
|
+
panelClass = computed(() => ({
|
|
4245
|
+
['fly-drawer__panel--' + this.size()]: true,
|
|
4246
|
+
['fly-drawer__panel--side-' + this.side()]: true,
|
|
4247
|
+
}), ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
|
|
4248
|
+
/** Name of the exit keyframe — used to ignore bubbled child animationend events. */
|
|
4249
|
+
static EXIT_ANIM = 'fly-drawer-out';
|
|
4242
4250
|
/** Element focused before the drawer opened, restored on close. */
|
|
4243
4251
|
previouslyFocused = null;
|
|
4244
4252
|
/** Parent element whose overflow we lock while open. */
|
|
@@ -4254,32 +4262,54 @@ class FlyDrawerComponent {
|
|
|
4254
4262
|
this.destroyRef.onDestroy(() => this.releaseScrollLock());
|
|
4255
4263
|
}
|
|
4256
4264
|
onOpen() {
|
|
4257
|
-
if (this.rendered())
|
|
4265
|
+
if (this.rendered() && !this.leaving())
|
|
4258
4266
|
return;
|
|
4259
4267
|
this.previouslyFocused =
|
|
4260
4268
|
document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
4261
4269
|
this.lockScroll();
|
|
4270
|
+
// Mount the panel — the enter keyframe plays automatically on insertion, so
|
|
4271
|
+
// there's no rAF/forced-reflow dance to mis-time (the old source of the
|
|
4272
|
+
// open glitch). A re-open mid-close just clears `leaving`.
|
|
4273
|
+
this.leaving.set(false);
|
|
4262
4274
|
this.rendered.set(true);
|
|
4263
|
-
//
|
|
4264
|
-
|
|
4275
|
+
// Focus the first field AFTER paint and WITHOUT scrolling — focusing an
|
|
4276
|
+
// off-screen, sliding-in panel otherwise yanks the app root's scroll
|
|
4277
|
+
// position and stutters the animation.
|
|
4278
|
+
requestAnimationFrame(() => this.focusFirstField());
|
|
4265
4279
|
}
|
|
4266
4280
|
onClose() {
|
|
4267
|
-
if (!this.rendered())
|
|
4281
|
+
if (!this.rendered() || this.leaving())
|
|
4268
4282
|
return;
|
|
4269
|
-
this.
|
|
4283
|
+
this.leaving.set(true);
|
|
4270
4284
|
this.releaseScrollLock();
|
|
4271
4285
|
this.restoreFocus();
|
|
4272
|
-
// Unmount on
|
|
4273
|
-
// event so the
|
|
4274
|
-
setTimeout(() =>
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4286
|
+
// Unmount on the exit animation's end; fallback timer covers reduced-motion
|
|
4287
|
+
// (no animation → no event) so the pointer-grabbing scrim can't linger.
|
|
4288
|
+
setTimeout(() => this.unmount(), 360);
|
|
4289
|
+
}
|
|
4290
|
+
/** Unmount once the slide-out keyframe finishes (filtered to our exit anim). */
|
|
4291
|
+
onPanelAnimationEnd(animationName) {
|
|
4292
|
+
if (animationName === FlyDrawerComponent.EXIT_ANIM)
|
|
4293
|
+
this.unmount();
|
|
4294
|
+
}
|
|
4295
|
+
unmount() {
|
|
4296
|
+
if (this.open())
|
|
4297
|
+
return; // re-opened during the slide-out — keep it mounted
|
|
4298
|
+
this.rendered.set(false);
|
|
4299
|
+
this.leaving.set(false);
|
|
4300
|
+
}
|
|
4301
|
+
/** Focus the first form control in the body (or the panel) without scrolling. */
|
|
4302
|
+
focusFirstField() {
|
|
4303
|
+
const root = this.host.nativeElement;
|
|
4304
|
+
const panel = root.querySelector('.fly-drawer__panel');
|
|
4305
|
+
if (!panel)
|
|
4306
|
+
return;
|
|
4307
|
+
const selector = '[autofocus],input:not([type=hidden]),select,textarea,button,[tabindex]:not([tabindex="-1"]),a[href]';
|
|
4308
|
+
const body = panel.querySelector('.fly-drawer__body');
|
|
4309
|
+
const target = body?.querySelector(selector) ??
|
|
4310
|
+
panel.querySelector(selector) ??
|
|
4311
|
+
panel;
|
|
4312
|
+
target.focus({ preventScroll: true });
|
|
4283
4313
|
}
|
|
4284
4314
|
onEscape() {
|
|
4285
4315
|
if (this.open() && this.dismissOnEscape())
|
|
@@ -4321,11 +4351,11 @@ class FlyDrawerComponent {
|
|
|
4321
4351
|
}
|
|
4322
4352
|
}
|
|
4323
4353
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4324
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: FlyDrawerComponent, isStandalone: true, selector: "fly-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, dismissOnScrim: { classPropertyName: "dismissOnScrim", publicName: "dismissOnScrim", isSignal: true, isRequired: false, transformFunction: null }, dismissOnEscape: { classPropertyName: "dismissOnEscape", publicName: "dismissOnEscape", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hideCloseButton: { classPropertyName: "hideCloseButton", publicName: "hideCloseButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", openChange: "openChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<!--\n Windowed overlay drawer. Renders only while mounted (open or sliding out).\n Scrim + panel are absolutely positioned within the nearest positioned\n ancestor (the consuming app root must be position:relative).\n-->\n@if (rendered()) {\n <div\n class=\"fly-drawer__scrim\"\n [class.fly-drawer__scrim--
|
|
4354
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: FlyDrawerComponent, isStandalone: true, selector: "fly-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, dismissOnScrim: { classPropertyName: "dismissOnScrim", publicName: "dismissOnScrim", isSignal: true, isRequired: false, transformFunction: null }, dismissOnEscape: { classPropertyName: "dismissOnEscape", publicName: "dismissOnEscape", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hideCloseButton: { classPropertyName: "hideCloseButton", publicName: "hideCloseButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", openChange: "openChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<!--\n Windowed overlay drawer. Renders only while mounted (open or sliding out).\n Scrim + panel are absolutely positioned within the nearest positioned\n ancestor (the consuming app root must be position:relative).\n-->\n@if (rendered()) {\n <div\n class=\"fly-drawer__scrim\"\n [class.fly-drawer__scrim--leaving]=\"leaving()\"\n (click)=\"onScrimClick()\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"fly-drawer__panel\"\n [ngClass]=\"panelClass()\"\n [class.fly-drawer__panel--leaving]=\"leaving()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"!labelledBy() && ariaLabel() ? (ariaLabel()! | translate) : null\"\n cdkTrapFocus\n (animationend)=\"onPanelAnimationEnd($event.animationName)\"\n >\n <!-- Header: custom slot, else heading + close \u2715. -->\n <ng-content select=\"[flyDrawerHeader]\">\n @if (heading()) {\n <header class=\"fly-drawer__header\">\n <h2 class=\"fly-drawer__title\" [id]=\"headingId\">{{ heading()! | translate }}</h2>\n @if (!hideCloseButton()) {\n <button\n type=\"button\"\n class=\"fly-drawer__close\"\n (click)=\"requestClose()\"\n [attr.aria-label]=\"'common.action.close' | translate\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n }\n </header>\n }\n </ng-content>\n\n <div class=\"fly-drawer__body\">\n <ng-content></ng-content>\n </div>\n\n <ng-content select=\"[flyDrawerFooter]\"></ng-content>\n </div>\n}\n", styles: [":host{position:absolute;inset:0;z-index:60;pointer-events:none}.fly-drawer__scrim{position:absolute;inset:0;pointer-events:auto;background:#00000061;opacity:1;animation:fly-drawer-scrim-in .24s ease both}.fly-drawer__scrim--leaving{animation-name:fly-drawer-scrim-out;animation-duration:.2s}.fly-drawer__panel{--fly-drawer-offset: 100%;position:absolute;inset-block:0;inset-inline-end:0;pointer-events:auto;display:flex;flex-direction:column;min-block-size:0;inline-size:min(480px,100%);max-inline-size:100%;background:var(--surface-card, #fff);border-inline-start:1px solid var(--surface-border, rgba(0, 0, 0, .08));box-shadow:-16px 0 48px #00000038;animation:fly-drawer-in .28s cubic-bezier(.32,.72,0,1) both;will-change:transform}.fly-drawer__panel--sm{inline-size:min(360px,100%)}.fly-drawer__panel--md{inline-size:min(480px,100%)}.fly-drawer__panel--lg{inline-size:min(640px,100%)}.fly-drawer__panel--xl{inline-size:min(960px,94%)}.fly-drawer__panel--side-start{--fly-drawer-offset: -100%;inset-inline-end:auto;inset-inline-start:0;border-inline-start:none;border-inline-end:1px solid var(--surface-border, rgba(0, 0, 0, .08));box-shadow:16px 0 48px #00000038}.fly-drawer__panel--leaving{animation-name:fly-drawer-out;animation-duration:.24s;animation-timing-function:cubic-bezier(.4,0,1,1)}@keyframes fly-drawer-in{0%{transform:translate(var(--fly-drawer-offset))}to{transform:translate(0)}}@keyframes fly-drawer-out{0%{transform:translate(0)}to{transform:translate(var(--fly-drawer-offset))}}@keyframes fly-drawer-scrim-in{0%{opacity:0}to{opacity:1}}@keyframes fly-drawer-scrim-out{0%{opacity:1}to{opacity:0}}:host-context([dir=rtl]) .fly-drawer__panel{--fly-drawer-offset: -100%;box-shadow:16px 0 48px #00000038;border-inline-start:1px solid var(--surface-border, rgba(0, 0, 0, .08))}:host-context([dir=rtl]) .fly-drawer__panel--side-start{--fly-drawer-offset: 100%}.fly-drawer__header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-block-end:1px solid var(--surface-border, rgba(0, 0, 0, .08));flex:0 0 auto}.fly-drawer__title{margin:0;font-size:16px;font-weight:600;color:var(--label-primary, #1d1d1f)}.fly-drawer__close{display:inline-flex;align-items:center;justify-content:center;inline-size:30px;block-size:30px;border:none;border-radius:8px;background:transparent;color:var(--label-secondary, #6e6e73);cursor:pointer}.fly-drawer__close:hover{background:#0000000f}.fly-drawer__body{flex:1 1 auto;min-block-size:0;overflow-y:auto}:host ::ng-deep [flyDrawerFooter]{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 18px;border-block-start:1px solid var(--surface-border, rgba(0, 0, 0, .08))}:host-context(html.dark-theme) .fly-drawer__panel{background:#1c1c1e}:host-context(html.dark-theme) .fly-drawer__title{color:#f5f5f7}:host-context(html.dark-theme) .fly-drawer__close:hover{background:#ffffff14}@media(prefers-reduced-motion:reduce){.fly-drawer__scrim,.fly-drawer__panel,.fly-drawer__scrim--leaving,.fly-drawer__panel--leaving{animation:none}}@media(forced-colors:active){.fly-drawer__panel{border:1px solid CanvasText}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4325
4355
|
}
|
|
4326
4356
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyDrawerComponent, decorators: [{
|
|
4327
4357
|
type: Component,
|
|
4328
|
-
args: [{ selector: 'fly-drawer', standalone: true, imports: [CommonModule, A11yModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n Windowed overlay drawer. Renders only while mounted (open or sliding out).\n Scrim + panel are absolutely positioned within the nearest positioned\n ancestor (the consuming app root must be position:relative).\n-->\n@if (rendered()) {\n <div\n class=\"fly-drawer__scrim\"\n [class.fly-drawer__scrim--
|
|
4358
|
+
args: [{ selector: 'fly-drawer', standalone: true, imports: [CommonModule, A11yModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n Windowed overlay drawer. Renders only while mounted (open or sliding out).\n Scrim + panel are absolutely positioned within the nearest positioned\n ancestor (the consuming app root must be position:relative).\n-->\n@if (rendered()) {\n <div\n class=\"fly-drawer__scrim\"\n [class.fly-drawer__scrim--leaving]=\"leaving()\"\n (click)=\"onScrimClick()\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"fly-drawer__panel\"\n [ngClass]=\"panelClass()\"\n [class.fly-drawer__panel--leaving]=\"leaving()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"!labelledBy() && ariaLabel() ? (ariaLabel()! | translate) : null\"\n cdkTrapFocus\n (animationend)=\"onPanelAnimationEnd($event.animationName)\"\n >\n <!-- Header: custom slot, else heading + close \u2715. -->\n <ng-content select=\"[flyDrawerHeader]\">\n @if (heading()) {\n <header class=\"fly-drawer__header\">\n <h2 class=\"fly-drawer__title\" [id]=\"headingId\">{{ heading()! | translate }}</h2>\n @if (!hideCloseButton()) {\n <button\n type=\"button\"\n class=\"fly-drawer__close\"\n (click)=\"requestClose()\"\n [attr.aria-label]=\"'common.action.close' | translate\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n }\n </header>\n }\n </ng-content>\n\n <div class=\"fly-drawer__body\">\n <ng-content></ng-content>\n </div>\n\n <ng-content select=\"[flyDrawerFooter]\"></ng-content>\n </div>\n}\n", styles: [":host{position:absolute;inset:0;z-index:60;pointer-events:none}.fly-drawer__scrim{position:absolute;inset:0;pointer-events:auto;background:#00000061;opacity:1;animation:fly-drawer-scrim-in .24s ease both}.fly-drawer__scrim--leaving{animation-name:fly-drawer-scrim-out;animation-duration:.2s}.fly-drawer__panel{--fly-drawer-offset: 100%;position:absolute;inset-block:0;inset-inline-end:0;pointer-events:auto;display:flex;flex-direction:column;min-block-size:0;inline-size:min(480px,100%);max-inline-size:100%;background:var(--surface-card, #fff);border-inline-start:1px solid var(--surface-border, rgba(0, 0, 0, .08));box-shadow:-16px 0 48px #00000038;animation:fly-drawer-in .28s cubic-bezier(.32,.72,0,1) both;will-change:transform}.fly-drawer__panel--sm{inline-size:min(360px,100%)}.fly-drawer__panel--md{inline-size:min(480px,100%)}.fly-drawer__panel--lg{inline-size:min(640px,100%)}.fly-drawer__panel--xl{inline-size:min(960px,94%)}.fly-drawer__panel--side-start{--fly-drawer-offset: -100%;inset-inline-end:auto;inset-inline-start:0;border-inline-start:none;border-inline-end:1px solid var(--surface-border, rgba(0, 0, 0, .08));box-shadow:16px 0 48px #00000038}.fly-drawer__panel--leaving{animation-name:fly-drawer-out;animation-duration:.24s;animation-timing-function:cubic-bezier(.4,0,1,1)}@keyframes fly-drawer-in{0%{transform:translate(var(--fly-drawer-offset))}to{transform:translate(0)}}@keyframes fly-drawer-out{0%{transform:translate(0)}to{transform:translate(var(--fly-drawer-offset))}}@keyframes fly-drawer-scrim-in{0%{opacity:0}to{opacity:1}}@keyframes fly-drawer-scrim-out{0%{opacity:1}to{opacity:0}}:host-context([dir=rtl]) .fly-drawer__panel{--fly-drawer-offset: -100%;box-shadow:16px 0 48px #00000038;border-inline-start:1px solid var(--surface-border, rgba(0, 0, 0, .08))}:host-context([dir=rtl]) .fly-drawer__panel--side-start{--fly-drawer-offset: 100%}.fly-drawer__header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-block-end:1px solid var(--surface-border, rgba(0, 0, 0, .08));flex:0 0 auto}.fly-drawer__title{margin:0;font-size:16px;font-weight:600;color:var(--label-primary, #1d1d1f)}.fly-drawer__close{display:inline-flex;align-items:center;justify-content:center;inline-size:30px;block-size:30px;border:none;border-radius:8px;background:transparent;color:var(--label-secondary, #6e6e73);cursor:pointer}.fly-drawer__close:hover{background:#0000000f}.fly-drawer__body{flex:1 1 auto;min-block-size:0;overflow-y:auto}:host ::ng-deep [flyDrawerFooter]{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 18px;border-block-start:1px solid var(--surface-border, rgba(0, 0, 0, .08))}:host-context(html.dark-theme) .fly-drawer__panel{background:#1c1c1e}:host-context(html.dark-theme) .fly-drawer__title{color:#f5f5f7}:host-context(html.dark-theme) .fly-drawer__close:hover{background:#ffffff14}@media(prefers-reduced-motion:reduce){.fly-drawer__scrim,.fly-drawer__panel,.fly-drawer__scrim--leaving,.fly-drawer__panel--leaving{animation:none}}@media(forced-colors:active){.fly-drawer__panel{border:1px solid CanvasText}}\n"] }]
|
|
4329
4359
|
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], dismissOnScrim: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissOnScrim", required: false }] }], dismissOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissOnEscape", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hideCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideCloseButton", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], openChange: [{ type: i0.Output, args: ["openChange"] }], onEscape: [{
|
|
4330
4360
|
type: HostListener,
|
|
4331
4361
|
args: ['document:keydown.escape']
|