diff --git a/.storybook/stories/modal/side-panel-inline.stories.ts b/.storybook/stories/modal/side-panel-inline.stories.ts
new file mode 100644
index 0000000000..1ddf2e0ea2
--- /dev/null
+++ b/.storybook/stories/modal/side-panel-inline.stories.ts
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016-2024 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
+ * This software is released under MIT license.
+ * The full license information can be found in LICENSE in the root directory of this project.
+ */
+
+import { ClrSidePanel, ClrSidePanelModule, commonStringsDefault } from '@clr/angular';
+import { action } from '@storybook/addon-actions';
+import { moduleMetadata, StoryFn, StoryObj } from '@storybook/angular';
+
+import { CommonModules, removeFocusOutline } from '../../helpers/common';
+
+export default {
+ title: 'Modal/Side Panel (inline)',
+ decorators: [
+ moduleMetadata({
+ imports: [...CommonModules, ClrSidePanelModule],
+ }),
+ ],
+ component: ClrSidePanel,
+ argTypes: {
+ // inputs
+ clrSidePanelSize: {
+ options: ['sm', 'md', 'lg', 'xl', 'full-screen'],
+ control: 'radio',
+ },
+ // outputs
+ clrSidePanelOpenChange: { control: { disable: true } },
+ clrSidePanelAltClose: { control: { disable: true } },
+ // methods
+ fadeDone: { control: { disable: true }, table: { disable: true } },
+ open: { control: { disable: true }, table: { disable: true } },
+ close: { control: { disable: true }, table: { disable: true } },
+ },
+ args: {
+ // inputs
+ clrSidePanelCloseButtonAriaLabel: commonStringsDefault.close,
+ clrSidePanelLabelledById: '',
+ clrSidePanelSize: 'md',
+ clrSidePanelPinnable: false,
+ clrSidePanelSkipAnimation: false,
+ // outputs
+ clrSidePanelOpenChange: action('clrSidePanelOpenChange'),
+ clrSidePanelAltClose: action('clrSidePanelAltClose'),
+ // story helpers
+ title: 'Side Panel Title',
+ body: 'Hello World!',
+ },
+};
+
+const InlineSidePanelTemplate: StoryFn = args => ({
+ template: `
+
+
+
+
+
+
+
{{ title }}
+
+ {{ body }}
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin in neque in ante placerat mattis id sed quam.
+ Proin rhoncus lacus et tempor dignissim. Vivamus sem quam, pellentesque aliquet suscipit eget, pellentesque
+ sed arcu. Vivamus in dui lectus. Suspendisse cursus est ac nisl imperdiet viverra. Aenean sagittis nibh
+ lacus, in eleifend urna ultrices et. Mauris porttitor nisi nec velit pharetra porttitor. Vestibulum
+ vulputate sollicitudin dolor ut tincidunt. Phasellus vitae blandit felis. Nullam posuere ipsum tincidunt
+ velit pellentesque rhoncus. Morbi faucibus ut ipsum at malesuada. Nam vestibulum felis sit amet metus
+ finibus hendrerit. Fusce faucibus odio eget ex vulputate rhoncus. Fusce nec aliquam leo, at suscipit diam.
+
+
+
+
+
+ `,
+ props: args,
+});
+
+export const SidePanel: StoryObj = {
+ render: InlineSidePanelTemplate,
+};
+
+export const SidePanelSmall: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'sm',
+ clrSidePanelStaticBackdrop: true,
+ title: 'Small Side Panel',
+ body: 'This is a small side panel.',
+ },
+};
+
+export const SidePanelMedium: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'md',
+ clrSidePanelStaticBackdrop: true,
+ title: 'Medium Side Panel',
+ body: 'This is a medium side panel.',
+ },
+};
+
+export const SidePanelLarge: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'lg',
+ clrSidePanelStaticBackdrop: true,
+ title: 'Large Side Panel',
+ body: 'This is a large side panel.',
+ },
+};
+
+export const SidePanelExtraLarge: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'xl',
+ clrSidePanelStaticBackdrop: true,
+ title: 'Extra-Large Side Panel',
+ body: 'This is a extra-large side panel.',
+ },
+};
+
+export const SidePanelWithoutBackdrop: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'md',
+ clrSidePanelBackdrop: false,
+ title: 'Side Panel without backdrop',
+ body: 'This is a medium side panel without backdrop.',
+ },
+};
+
+export const SidePanelAlternateClose: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'md',
+ clrSidePanelPreventClose: true,
+ clrSidePanelAltClose: function () {
+ if (confirm('Do you really want to close the side panel?')) {
+ this.clrSidePanelOpen = false;
+ }
+ },
+ title: 'Side Panel with alternate close',
+ body: 'This is a medium side panel without backdrop.',
+ },
+};
+
+export const SidePanelPinnable: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'md',
+ clrSidePanelBackdrop: false,
+ clrSidePanelPinnable: true,
+ title: 'Pinnable Side Panel',
+ body: 'This is a medium pinnable side panel without backdrop.',
+ },
+};
+
+export const SidePanelFullScreen: StoryObj = {
+ render: InlineSidePanelTemplate,
+ play: removeFocusOutline,
+ args: {
+ clrSidePanelOpen: true,
+ clrSidePanelSize: 'full-screen',
+ clrSidePanelStaticBackdrop: true,
+ title: 'Full-Screen Side Panel',
+ body: 'This is a full-screen side panel.',
+ showLongPageContent: false,
+ },
+};
diff --git a/.storybook/stories/modal/side-panel.stories.ts b/.storybook/stories/modal/side-panel.stories.ts
index 094a1e6579..c3f88f621e 100644
--- a/.storybook/stories/modal/side-panel.stories.ts
+++ b/.storybook/stories/modal/side-panel.stories.ts
@@ -38,6 +38,7 @@ export default {
clrSidePanelCloseButtonAriaLabel: commonStringsDefault.close,
clrSidePanelLabelledById: '',
clrSidePanelSize: 'md',
+ clrSidePanelPinnable: false,
clrSidePanelSkipAnimation: false,
// outputs
clrSidePanelOpenChange: action('clrSidePanelOpenChange'),
@@ -56,6 +57,7 @@ const SidePanelTemplate: StoryFn = args => ({
;
// Warning: (ae-forgotten-export) The symbol "i1_49" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_36" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_28" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_29" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_19" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_15" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i6_10" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i7_9" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -1017,6 +1017,7 @@ export interface ClrCommonStrings {
show: string;
showColumns: string;
showColumnsMenuDescription: string;
+ sidePanelPin: string;
signpostClose: string;
signpostToggle: string;
singleActionableAriaLabel: string;
@@ -2696,11 +2697,11 @@ export class ClrLayoutModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_37" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_27" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_20" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_21" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_15" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -2811,7 +2812,7 @@ export class ClrMainContainerModule {
export class ClrModal implements OnChanges, OnDestroy {
// Warning: (ae-forgotten-export) The symbol "ScrollingService" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "ModalStackService" needs to be exported by the entry point index.d.ts
- constructor(_scrollingService: ScrollingService, commonStrings: ClrCommonStringsService, modalStackService: ModalStackService, configuration: ClrModalConfigurationService);
+ constructor(_scrollingService: ScrollingService, commonStrings: ClrCommonStringsService, modalStackService: ModalStackService, configuration: ClrModalConfigurationService, elementRef: ElementRef);
// (undocumented)
altClose: EventEmitter;
// (undocumented)
@@ -2850,9 +2851,15 @@ export class ClrModal implements OnChanges, OnDestroy {
// (undocumented)
_openChanged: EventEmitter;
// (undocumented)
- size: string;
+ pinnable: boolean;
+ // (undocumented)
+ get pinned(): boolean;
+ set pinned(pinned: boolean);
+ // (undocumented)
+ get size(): string;
+ set size(value: string);
// (undocumented)
- skipAnimation: string;
+ skipAnimation: boolean;
// (undocumented)
staticBackdrop: boolean;
// (undocumented)
@@ -2860,7 +2867,9 @@ export class ClrModal implements OnChanges, OnDestroy {
// (undocumented)
title: ElementRef;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ togglePinned(): void;
+ // (undocumented)
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}
@@ -2888,6 +2897,14 @@ export class ClrModalConfigurationService {
static ɵprov: i0.ɵɵInjectableDeclaration;
}
+// @public (undocumented)
+export class ClrModalHostDirective {
+ // (undocumented)
+ static ɵdir: i0.ɵɵDirectiveDeclaration;
+ // (undocumented)
+ static ɵfac: i0.ɵɵFactoryDeclaration;
+}
+
// @public (undocumented)
export class ClrModalModule {
constructor();
@@ -2897,9 +2914,10 @@ export class ClrModalModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_32" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_24" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_19" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -2925,12 +2943,12 @@ export class ClrNavigationModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_39" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_28" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_19" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_20" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_13" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_10" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -3221,10 +3239,10 @@ export class ClrPopoverModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i2_31" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_24" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_25" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public
@@ -3560,7 +3578,7 @@ export enum ClrSide {
}
// @public (undocumented)
-export class ClrSidePanel implements OnInit {
+export class ClrSidePanel implements OnInit, AfterViewInit {
constructor(element: ElementRef, configuration: ClrModalConfigurationService);
// (undocumented)
altClose: EventEmitter;
@@ -3572,8 +3590,13 @@ export class ClrSidePanel implements OnInit {
get clrSidePanelBackdrop(): boolean;
set clrSidePanelBackdrop(backdrop: boolean);
// (undocumented)
+ get clrSidePanelPinnable(): boolean;
+ set clrSidePanelPinnable(pinnable: boolean);
+ // (undocumented)
labelledById: string;
// (undocumented)
+ ngAfterViewInit(): void;
+ // (undocumented)
ngOnInit(): void;
// (undocumented)
open(): void;
@@ -3586,11 +3609,11 @@ export class ClrSidePanel implements OnInit {
// (undocumented)
size: string;
// (undocumented)
- skipAnimation: string;
+ skipAnimation: boolean;
// (undocumented)
staticBackdrop: boolean;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}
@@ -3604,7 +3627,7 @@ export class ClrSidePanelModule {
// Warning: (ae-forgotten-export) The symbol "i1_47" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -3649,10 +3672,10 @@ export class ClrSignpostModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_44" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_32" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_23" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_24" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -3902,13 +3925,13 @@ export class ClrStepperModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_48" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_35" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_27" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_28" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_18" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_14" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i8_9" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -4133,7 +4156,7 @@ export class ClrTabsModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_41" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_29" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_21" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_22" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_14" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_11" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i6_8" needs to be exported by the entry point index.d.ts
@@ -4143,7 +4166,7 @@ export class ClrTabsModule {
// Warning: (ae-forgotten-export) The symbol "i13_3" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -4208,12 +4231,12 @@ export class ClrTimelineModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_51" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_37" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_29" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_30" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_20" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_16" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -4318,10 +4341,10 @@ export class ClrTooltipModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_45" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_33" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_25" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_26" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -4560,12 +4583,12 @@ export class ClrVerticalNavModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_43" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_30" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_22" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_23" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_16" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_12" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public (undocumented)
@@ -4639,7 +4662,7 @@ export class ClrWizard implements OnDestroy, AfterContentInit, DoCheck {
get stopCancel(): boolean;
set stopCancel(value: boolean);
// (undocumented)
- get stopModalAnimations(): string;
+ get stopModalAnimations(): boolean;
_stopModalAnimations: boolean;
get stopNavigation(): boolean;
set stopNavigation(value: boolean);
@@ -4727,7 +4750,7 @@ export class ClrWizardModule {
static ɵinj: i0.ɵɵInjectorDeclaration;
// Warning: (ae-forgotten-export) The symbol "i1_46" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_34" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "i3_26" needs to be exported by the entry point index.d.ts
+ // Warning: (ae-forgotten-export) The symbol "i3_27" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i4_17" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i5_13" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "i6_9" needs to be exported by the entry point index.d.ts
@@ -4738,7 +4761,7 @@ export class ClrWizardModule {
// Warning: (ae-forgotten-export) The symbol "i11_4" needs to be exported by the entry point index.d.ts
//
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public
diff --git a/projects/angular/src/modal/_modal.clarity.scss b/projects/angular/src/modal/_modal.clarity.scss
index df0c5f7295..b4b992e905 100644
--- a/projects/angular/src/modal/_modal.clarity.scss
+++ b/projects/angular/src/modal/_modal.clarity.scss
@@ -14,6 +14,31 @@
@include meta.load-css('properties.modal');
@include mixins.exports('modal.clarity') {
+ .clr-side-panel-pinned-sm {
+ padding-right: modal-variables.$clr-modal-sm-width !important;
+ }
+
+ .clr-side-panel-pinned-md {
+ padding-right: modal-variables.$clr-modal-md-width !important;
+ }
+
+ .clr-side-panel-pinned-lg {
+ padding-right: modal-variables.$clr-modal-lg-width !important;
+ }
+
+ .clr-side-panel-pinned-xl {
+ padding-right: modal-variables.$clr-modal-xl-width !important;
+ }
+
+ .clr-modal-host {
+ overflow: hidden;
+ position: relative;
+ .modal,
+ .modal-backdrop {
+ position: absolute;
+ }
+ }
+
.modal {
position: fixed;
top: 0;
@@ -131,7 +156,8 @@
letter-spacing: modal-variables.$clr-modal-title-letter-spacing;
}
- .close {
+ .close,
+ .pinnable {
font-size: initial;
line-height: initial;
@@ -157,6 +183,16 @@
}
}
}
+
+ .pinnable {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ margin-right: tokens.$cds-global-space-6;
+ appearance: none;
+ -webkit-appearance: none;
+ }
}
.modal-title-wrapper {
diff --git a/projects/angular/src/modal/index.ts b/projects/angular/src/modal/index.ts
index 8f8c816d4a..de16cfd4fc 100644
--- a/projects/angular/src/modal/index.ts
+++ b/projects/angular/src/modal/index.ts
@@ -8,6 +8,7 @@
export * from './modal';
export * from './modal.module';
export * from './modal-configuration.service';
+export * from './modal-host.directive';
export * from './side-panel.module';
export * from './modal-body';
export * from './side-panel';
diff --git a/projects/angular/src/modal/modal-host.directive.ts b/projects/angular/src/modal/modal-host.directive.ts
new file mode 100644
index 0000000000..5828aa52b4
--- /dev/null
+++ b/projects/angular/src/modal/modal-host.directive.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016-2024 Broadcom. All Rights Reserved.
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
+ * This software is released under MIT license.
+ * The full license information can be found in LICENSE in the root directory of this project.
+ */
+
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[clrModalHost]',
+ host: { '[class.clr-modal-host]': 'true' },
+})
+export class ClrModalHostDirective {}
diff --git a/projects/angular/src/modal/modal.html b/projects/angular/src/modal/modal.html
index 4834b69093..cdb637287e 100644
--- a/projects/angular/src/modal/modal.html
+++ b/projects/angular/src/modal/modal.html
@@ -28,6 +28,15 @@
+
@@ -49,6 +58,6 @@
{{commonStrings.keys.modalContentEnd}}
-
-
+
+
diff --git a/projects/angular/src/modal/modal.module.ts b/projects/angular/src/modal/modal.module.ts
index 0746ecb2f8..6bb336aa0f 100644
--- a/projects/angular/src/modal/modal.module.ts
+++ b/projects/angular/src/modal/modal.module.ts
@@ -13,8 +13,9 @@ import { ClrIconModule } from '../icon/icon.module';
import { CdkTrapFocusModule } from '../utils/cdk/cdk-trap-focus.module';
import { ClrModal } from './modal';
import { ClrModalBody } from './modal-body';
+import { ClrModalHostDirective } from './modal-host.directive';
-export const CLR_MODAL_DIRECTIVES: Type[] = [ClrModal, ClrModalBody];
+export const CLR_MODAL_DIRECTIVES: Type[] = [ClrModal, ClrModalBody, ClrModalHostDirective];
@NgModule({
imports: [CommonModule, CdkTrapFocusModule, ClrIconModule],
diff --git a/projects/angular/src/modal/modal.ts b/projects/angular/src/modal/modal.ts
index ebc5628875..c06019496d 100644
--- a/projects/angular/src/modal/modal.ts
+++ b/projects/angular/src/modal/modal.ts
@@ -66,25 +66,62 @@ export class ClrModal implements OnChanges, OnDestroy {
@Input('clrModalClosable') closable = true;
@Input('clrModalCloseButtonAriaLabel') closeButtonAriaLabel = this.commonStrings.keys.close;
- @Input('clrModalSize') size: string;
+
@Input('clrModalStaticBackdrop') staticBackdrop = true;
- @Input('clrModalSkipAnimation') skipAnimation = 'false';
+ @Input('clrModalSkipAnimation') skipAnimation = false;
@Input('clrModalPreventClose') stopClose = false;
@Output('clrModalAlternateClose') altClose = new EventEmitter(false);
@Input('clrModalLabelledById') labelledBy: string;
+ // For now we only want to expose this as input on the side panel
+ pinnable = false;
+
// presently this is only used by inline wizards
@Input('clrModalOverrideScrollService') bypassScrollService = false;
+ private _pinned = false;
+
+ private _size: string;
constructor(
private _scrollingService: ScrollingService,
public commonStrings: ClrCommonStringsService,
private modalStackService: ModalStackService,
- private configuration: ClrModalConfigurationService
+ private configuration: ClrModalConfigurationService,
+ private elementRef: ElementRef
) {}
+ @Input('clrModalSize')
+ get size(): string {
+ return this._size;
+ }
+
+ set size(value: string) {
+ if (this._size !== value) {
+ this._size = value;
+ if (this.pinnable && this.pinned) {
+ this.displayOverlapping();
+ this.displaySideBySide();
+ }
+ }
+ }
+
+ get pinned(): boolean {
+ return this._pinned;
+ }
+
+ set pinned(pinned: boolean) {
+ if (this.pinnable) {
+ this._pinned = pinned;
+ if (pinned) {
+ this.displaySideBySide();
+ } else {
+ this.displayOverlapping();
+ }
+ }
+ }
+
get fadeMove(): string {
return this.skipAnimation ? '' : this.configuration.fadeMove;
}
@@ -96,20 +133,31 @@ export class ClrModal implements OnChanges, OnDestroy {
return this.configuration.backdrop;
}
+ private get hostElement(): HTMLElement {
+ return (this.elementRef.nativeElement as HTMLElement).closest('.clr-modal-host') || document.body;
+ }
+
// Detect when _open is set to true and set no-scrolling to true
ngOnChanges(changes: { [propName: string]: SimpleChange }): void {
if (!this.bypassScrollService && changes && Object.prototype.hasOwnProperty.call(changes, '_open')) {
if (changes._open.currentValue) {
- this.backdrop && this._scrollingService.stopScrolling();
+ !this.pinnable && this._scrollingService.stopScrolling();
this.modalStackService.trackModalOpen(this);
+ if (this.pinnable && this.pinned) {
+ this.displaySideBySide();
+ }
} else {
this._scrollingService.resumeScrolling();
+ if (this.pinnable && this.pinned) {
+ this.displayOverlapping();
+ }
}
}
}
ngOnDestroy(): void {
this._scrollingService.resumeScrolling();
+ this.displayOverlapping();
}
open(): void {
@@ -131,7 +179,7 @@ export class ClrModal implements OnChanges, OnDestroy {
}
close(): void {
- if (this.stopClose) {
+ if (this.stopClose || (this.pinnable && this.pinned)) {
this.altClose.emit(false);
return;
}
@@ -141,6 +189,10 @@ export class ClrModal implements OnChanges, OnDestroy {
this._open = false;
}
+ togglePinned() {
+ this.pinned = !this.pinned;
+ }
+
fadeDone(e: AnimationEvent) {
if (e.toState === 'void') {
// TODO: Investigate if we can decouple from animation events
@@ -148,4 +200,16 @@ export class ClrModal implements OnChanges, OnDestroy {
this.modalStackService.trackModalClose(this);
}
}
+
+ private displaySideBySide() {
+ this.hostElement.classList.add('clr-side-panel-pinned-' + this.size);
+ }
+
+ private displayOverlapping() {
+ this.hostElement.classList.forEach(className => {
+ if (className.startsWith('clr-side-panel-pinned-')) {
+ this.hostElement.classList.remove(className);
+ }
+ });
+ }
}
diff --git a/projects/angular/src/modal/side-panel.module.ts b/projects/angular/src/modal/side-panel.module.ts
index 7faab702de..689b8aca3d 100644
--- a/projects/angular/src/modal/side-panel.module.ts
+++ b/projects/angular/src/modal/side-panel.module.ts
@@ -18,6 +18,6 @@ export const CLR_SIDEPANEL_DIRECTIVES: Type[] = [ClrSidePanel];
@NgModule({
imports: [CommonModule, CdkTrapFocusModule, ClrIconModule, ClrModalModule],
declarations: [CLR_SIDEPANEL_DIRECTIVES],
- exports: [CLR_SIDEPANEL_DIRECTIVES, ClrIconModule],
+ exports: [CLR_SIDEPANEL_DIRECTIVES, ClrModalModule, ClrIconModule],
})
export class ClrSidePanelModule {}
diff --git a/projects/angular/src/modal/side-panel.ts b/projects/angular/src/modal/side-panel.ts
index f890b7817b..8a32b9fea6 100644
--- a/projects/angular/src/modal/side-panel.ts
+++ b/projects/angular/src/modal/side-panel.ts
@@ -5,7 +5,17 @@
* The full license information can be found in LICENSE in the root directory of this project.
*/
-import { Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, ViewChild } from '@angular/core';
+import {
+ AfterViewInit,
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostListener,
+ Input,
+ OnInit,
+ Output,
+ ViewChild,
+} from '@angular/core';
import { ClrModal } from './modal';
import { ClrModalConfigurationService } from './modal-configuration.service';
@@ -17,12 +27,12 @@ import { ClrModalConfigurationService } from './modal-configuration.service';
'[class.side-panel]': 'true',
},
})
-export class ClrSidePanel implements OnInit {
+export class ClrSidePanel implements OnInit, AfterViewInit {
@Input('clrSidePanelOpen') _open = false;
@Output('clrSidePanelOpenChange') openChange = new EventEmitter(false);
@Input('clrSidePanelCloseButtonAriaLabel') closeButtonAriaLabel: string | undefined;
@Input('clrSidePanelSize') size: string;
- @Input('clrSidePanelSkipAnimation') skipAnimation = 'false';
+ @Input('clrSidePanelSkipAnimation') skipAnimation = false;
@Input('clrSidePanelLabelledById') labelledById: string;
@Input('clrSidePanelStaticBackdrop') staticBackdrop = false;
@Input('clrSidePanelPreventClose') preventClose = false;
@@ -30,22 +40,41 @@ export class ClrSidePanel implements OnInit {
@ViewChild(ClrModal) private modal: ClrModal;
+ private _pinnable = false;
+
constructor(private element: ElementRef, private configuration: ClrModalConfigurationService) {}
@Input()
get clrSidePanelBackdrop(): boolean {
return this.configuration.backdrop;
}
+
set clrSidePanelBackdrop(backdrop: boolean) {
if (backdrop !== undefined) {
this.configuration.backdrop = backdrop;
}
}
+ @Input()
+ get clrSidePanelPinnable(): boolean {
+ return this._pinnable;
+ }
+
+ set clrSidePanelPinnable(pinnable: boolean) {
+ this._pinnable = pinnable;
+ if (this.modal) {
+ this.modal.pinnable = pinnable;
+ }
+ }
+
ngOnInit(): void {
this.configuration.fadeMove = 'fadeLeft';
}
+ ngAfterViewInit() {
+ this.modal.pinnable = this._pinnable;
+ }
+
open() {
this.modal.open();
}
diff --git a/projects/angular/src/utils/i18n/common-strings.default.ts b/projects/angular/src/utils/i18n/common-strings.default.ts
index 684c6a5539..0933aef4e8 100644
--- a/projects/angular/src/utils/i18n/common-strings.default.ts
+++ b/projects/angular/src/utils/i18n/common-strings.default.ts
@@ -41,6 +41,7 @@ export const commonStringsDefault: ClrCommonStrings = {
maxValue: 'Max value',
modalContentStart: 'Beginning of Modal Content',
modalContentEnd: 'End of Modal Content',
+ sidePanelPin: 'Pin Side Panel',
showColumnsMenuDescription: 'Show or hide columns menu',
allColumnsSelected: 'All columns selected',
signpostToggle: 'Signpost Toggle',
diff --git a/projects/angular/src/utils/i18n/common-strings.interface.ts b/projects/angular/src/utils/i18n/common-strings.interface.ts
index e324dba373..df13e2e7f5 100644
--- a/projects/angular/src/utils/i18n/common-strings.interface.ts
+++ b/projects/angular/src/utils/i18n/common-strings.interface.ts
@@ -163,6 +163,11 @@ export interface ClrCommonStrings {
*/
modalContentEnd: string;
+ /**
+ * Side Panel pin dialog
+ */
+ sidePanelPin: string;
+
/**
* Datagrid Show columns menu description
*/
diff --git a/projects/angular/src/wizard/wizard.ts b/projects/angular/src/wizard/wizard.ts
index 69eb4a8981..6fe65fff46 100644
--- a/projects/angular/src/wizard/wizard.ts
+++ b/projects/angular/src/wizard/wizard.ts
@@ -263,8 +263,8 @@ export class ClrWizard implements OnDestroy, AfterContentInit, DoCheck {
return this.elementRef.nativeElement.classList.contains('clr-wizard--inline');
}
- get stopModalAnimations(): string {
- return this._stopModalAnimations ? 'true' : 'false';
+ get stopModalAnimations(): boolean {
+ return this._stopModalAnimations;
}
ngAfterContentInit(): void {