`s.
+
+.nav {
+ // scss-docs-start nav-css-vars
+ --#{$prefix}nav-link-padding-x: #{$nav-link-padding-x};
+ --#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
+ @include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
+ --#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
+ --#{$prefix}nav-link-color: #{$nav-link-color};
+ --#{$prefix}nav-link-hover-color: #{$nav-link-hover-color};
+ --#{$prefix}nav-link-disabled-color: #{$nav-link-disabled-color};
+ // scss-docs-end nav-css-vars
+
+ display: flex;
+ flex-wrap: wrap;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.nav-link {
+ display: block;
+ padding: var(--#{$prefix}nav-link-padding-y) var(--#{$prefix}nav-link-padding-x);
+ @include font-size(var(--#{$prefix}nav-link-font-size));
+ font-weight: var(--#{$prefix}nav-link-font-weight);
+ color: var(--#{$prefix}nav-link-color);
+ text-decoration: if($link-decoration == none, null, none);
+ background: none;
+ border: 0;
+ @include transition($nav-link-transition);
+
+ &:hover,
+ &:focus {
+ color: var(--#{$prefix}nav-link-hover-color);
+ text-decoration: if($link-hover-decoration == underline, none, null);
+ }
+
+ &:focus-visible {
+ outline: 0;
+ box-shadow: $nav-link-focus-box-shadow;
+ }
+
+ // Disabled state lightens text
+ &.disabled,
+ &:disabled {
+ color: var(--#{$prefix}nav-link-disabled-color);
+ pointer-events: none;
+ cursor: default;
+ }
+}
+
+//
+// Tabs
+//
+
+.nav-tabs {
+ // scss-docs-start nav-tabs-css-vars
+ --#{$prefix}nav-tabs-border-width: #{$nav-tabs-border-width};
+ --#{$prefix}nav-tabs-border-color: #{$nav-tabs-border-color};
+ --#{$prefix}nav-tabs-border-radius: #{$nav-tabs-border-radius};
+ --#{$prefix}nav-tabs-link-hover-border-color: #{$nav-tabs-link-hover-border-color};
+ --#{$prefix}nav-tabs-link-active-color: #{$nav-tabs-link-active-color};
+ --#{$prefix}nav-tabs-link-active-bg: #{$nav-tabs-link-active-bg};
+ --#{$prefix}nav-tabs-link-active-border-color: #{$nav-tabs-link-active-border-color};
+ // scss-docs-end nav-tabs-css-vars
+
+ border-bottom: var(--#{$prefix}nav-tabs-border-width) solid var(--#{$prefix}nav-tabs-border-color);
+
+ .nav-link {
+ margin-bottom: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list
+ border: var(--#{$prefix}nav-tabs-border-width) solid transparent;
+ @include border-top-radius(var(--#{$prefix}nav-tabs-border-radius));
+
+ &:hover,
+ &:focus {
+ // Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
+ isolation: isolate;
+ border-color: var(--#{$prefix}nav-tabs-link-hover-border-color);
+ }
+ }
+
+ .nav-link.active,
+ .nav-item.show .nav-link {
+ color: var(--#{$prefix}nav-tabs-link-active-color);
+ background-color: var(--#{$prefix}nav-tabs-link-active-bg);
+ border-color: var(--#{$prefix}nav-tabs-link-active-border-color);
+ }
+
+ .dropdown-menu {
+ // Make dropdown border overlap tab border
+ margin-top: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list
+ // Remove the top rounded corners here since there is a hard edge above the menu
+ @include border-top-radius(0);
+ }
+}
+
+
+//
+// Pills
+//
+
+.nav-pills {
+ // scss-docs-start nav-pills-css-vars
+ --#{$prefix}nav-pills-border-radius: #{$nav-pills-border-radius};
+ --#{$prefix}nav-pills-link-active-color: #{$nav-pills-link-active-color};
+ --#{$prefix}nav-pills-link-active-bg: #{$nav-pills-link-active-bg};
+ // scss-docs-end nav-pills-css-vars
+
+ .nav-link {
+ @include border-radius(var(--#{$prefix}nav-pills-border-radius));
+ }
+
+ .nav-link.active,
+ .show > .nav-link {
+ color: var(--#{$prefix}nav-pills-link-active-color);
+ @include gradient-bg(var(--#{$prefix}nav-pills-link-active-bg));
+ }
+}
+
+
+//
+// Underline
+//
+
+.nav-underline {
+ // scss-docs-start nav-underline-css-vars
+ --#{$prefix}nav-underline-gap: #{$nav-underline-gap};
+ --#{$prefix}nav-underline-border-width: #{$nav-underline-border-width};
+ --#{$prefix}nav-underline-link-active-color: #{$nav-underline-link-active-color};
+ // scss-docs-end nav-underline-css-vars
+
+ gap: var(--#{$prefix}nav-underline-gap);
+
+ .nav-link {
+ padding-right: 0;
+ padding-left: 0;
+ border-bottom: var(--#{$prefix}nav-underline-border-width) solid transparent;
+
+ &:hover,
+ &:focus {
+ border-bottom-color: currentcolor;
+ }
+ }
+
+ .nav-link.active,
+ .show > .nav-link {
+ font-weight: $font-weight-bold;
+ color: var(--#{$prefix}nav-underline-link-active-color);
+ border-bottom-color: currentcolor;
+ }
+}
+
+
+//
+// Justified variants
+//
+
+.nav-fill {
+ > .nav-link,
+ .nav-item {
+ flex: 1 1 auto;
+ text-align: center;
+ }
+}
+
+.nav-justified {
+ > .nav-link,
+ .nav-item {
+ flex-basis: 0;
+ flex-grow: 1;
+ text-align: center;
+ }
+}
+
+.nav-fill,
+.nav-justified {
+ .nav-item .nav-link {
+ width: 100%; // Make sure button will grow
+ }
+}
+
+
+// Tabbable tabs
+//
+// Hide tabbable panes to start, show them when `.active`
+
+.tab-content {
+ > .tab-pane {
+ display: none;
+ }
+ > .active {
+ display: block;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_navbar.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_navbar.scss
new file mode 100644
index 00000000..05c2f6b3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_navbar.scss
@@ -0,0 +1,289 @@
+// Navbar
+//
+// Provide a static navbar from which we expand to create full-width, fixed, and
+// other navbar variations.
+
+.navbar {
+ // scss-docs-start navbar-css-vars
+ --#{$prefix}navbar-padding-x: #{if($navbar-padding-x == null, 0, $navbar-padding-x)};
+ --#{$prefix}navbar-padding-y: #{$navbar-padding-y};
+ --#{$prefix}navbar-color: #{$navbar-light-color};
+ --#{$prefix}navbar-hover-color: #{$navbar-light-hover-color};
+ --#{$prefix}navbar-disabled-color: #{$navbar-light-disabled-color};
+ --#{$prefix}navbar-active-color: #{$navbar-light-active-color};
+ --#{$prefix}navbar-brand-padding-y: #{$navbar-brand-padding-y};
+ --#{$prefix}navbar-brand-margin-end: #{$navbar-brand-margin-end};
+ --#{$prefix}navbar-brand-font-size: #{$navbar-brand-font-size};
+ --#{$prefix}navbar-brand-color: #{$navbar-light-brand-color};
+ --#{$prefix}navbar-brand-hover-color: #{$navbar-light-brand-hover-color};
+ --#{$prefix}navbar-nav-link-padding-x: #{$navbar-nav-link-padding-x};
+ --#{$prefix}navbar-toggler-padding-y: #{$navbar-toggler-padding-y};
+ --#{$prefix}navbar-toggler-padding-x: #{$navbar-toggler-padding-x};
+ --#{$prefix}navbar-toggler-font-size: #{$navbar-toggler-font-size};
+ --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-light-toggler-icon-bg)};
+ --#{$prefix}navbar-toggler-border-color: #{$navbar-light-toggler-border-color};
+ --#{$prefix}navbar-toggler-border-radius: #{$navbar-toggler-border-radius};
+ --#{$prefix}navbar-toggler-focus-width: #{$navbar-toggler-focus-width};
+ --#{$prefix}navbar-toggler-transition: #{$navbar-toggler-transition};
+ // scss-docs-end navbar-css-vars
+
+ position: relative;
+ display: flex;
+ flex-wrap: wrap; // allow us to do the line break for collapsing content
+ align-items: center;
+ justify-content: space-between; // space out brand from logo
+ padding: var(--#{$prefix}navbar-padding-y) var(--#{$prefix}navbar-padding-x);
+ @include gradient-bg();
+
+ // Because flex properties aren't inherited, we need to redeclare these first
+ // few properties so that content nested within behave properly.
+ // The `flex-wrap` property is inherited to simplify the expanded navbars
+ %container-flex-properties {
+ display: flex;
+ flex-wrap: inherit;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ > .container,
+ > .container-fluid {
+ @extend %container-flex-properties;
+ }
+
+ @each $breakpoint, $container-max-width in $container-max-widths {
+ > .container#{breakpoint-infix($breakpoint, $container-max-widths)} {
+ @extend %container-flex-properties;
+ }
+ }
+}
+
+
+// Navbar brand
+//
+// Used for brand, project, or site names.
+
+.navbar-brand {
+ padding-top: var(--#{$prefix}navbar-brand-padding-y);
+ padding-bottom: var(--#{$prefix}navbar-brand-padding-y);
+ margin-right: var(--#{$prefix}navbar-brand-margin-end);
+ @include font-size(var(--#{$prefix}navbar-brand-font-size));
+ color: var(--#{$prefix}navbar-brand-color);
+ text-decoration: if($link-decoration == none, null, none);
+ white-space: nowrap;
+
+ &:hover,
+ &:focus {
+ color: var(--#{$prefix}navbar-brand-hover-color);
+ text-decoration: if($link-hover-decoration == underline, none, null);
+ }
+}
+
+
+// Navbar nav
+//
+// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
+
+.navbar-nav {
+ // scss-docs-start navbar-nav-css-vars
+ --#{$prefix}nav-link-padding-x: 0;
+ --#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
+ @include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
+ --#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
+ --#{$prefix}nav-link-color: var(--#{$prefix}navbar-color);
+ --#{$prefix}nav-link-hover-color: var(--#{$prefix}navbar-hover-color);
+ --#{$prefix}nav-link-disabled-color: var(--#{$prefix}navbar-disabled-color);
+ // scss-docs-end navbar-nav-css-vars
+
+ display: flex;
+ flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+
+ .nav-link {
+ &.active,
+ &.show {
+ color: var(--#{$prefix}navbar-active-color);
+ }
+ }
+
+ .dropdown-menu {
+ position: static;
+ }
+}
+
+
+// Navbar text
+//
+//
+
+.navbar-text {
+ padding-top: $nav-link-padding-y;
+ padding-bottom: $nav-link-padding-y;
+ color: var(--#{$prefix}navbar-color);
+
+ a,
+ a:hover,
+ a:focus {
+ color: var(--#{$prefix}navbar-active-color);
+ }
+}
+
+
+// Responsive navbar
+//
+// Custom styles for responsive collapsing and toggling of navbar contents.
+// Powered by the collapse Bootstrap JavaScript plugin.
+
+// When collapsed, prevent the toggleable navbar contents from appearing in
+// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
+// on the `.navbar` parent.
+.navbar-collapse {
+ flex-basis: 100%;
+ flex-grow: 1;
+ // For always expanded or extra full navbars, ensure content aligns itself
+ // properly vertically. Can be easily overridden with flex utilities.
+ align-items: center;
+}
+
+// Button for toggling the navbar when in its collapsed state
+.navbar-toggler {
+ padding: var(--#{$prefix}navbar-toggler-padding-y) var(--#{$prefix}navbar-toggler-padding-x);
+ @include font-size(var(--#{$prefix}navbar-toggler-font-size));
+ line-height: 1;
+ color: var(--#{$prefix}navbar-color);
+ background-color: transparent; // remove default button style
+ border: var(--#{$prefix}border-width) solid var(--#{$prefix}navbar-toggler-border-color); // remove default button style
+ @include border-radius(var(--#{$prefix}navbar-toggler-border-radius));
+ @include transition(var(--#{$prefix}navbar-toggler-transition));
+
+ &:hover {
+ text-decoration: none;
+ }
+
+ &:focus {
+ text-decoration: none;
+ outline: 0;
+ box-shadow: 0 0 0 var(--#{$prefix}navbar-toggler-focus-width);
+ }
+}
+
+// Keep as a separate element so folks can easily override it with another icon
+// or image file as needed.
+.navbar-toggler-icon {
+ display: inline-block;
+ width: 1.5em;
+ height: 1.5em;
+ vertical-align: middle;
+ background-image: var(--#{$prefix}navbar-toggler-icon-bg);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 100%;
+}
+
+.navbar-nav-scroll {
+ max-height: var(--#{$prefix}scroll-height, 75vh);
+ overflow-y: auto;
+}
+
+// scss-docs-start navbar-expand-loop
+// Generate series of `.navbar-expand-*` responsive classes for configuring
+// where your navbar collapses.
+.navbar-expand {
+ @each $breakpoint in map-keys($grid-breakpoints) {
+ $next: breakpoint-next($breakpoint, $grid-breakpoints);
+ $infix: breakpoint-infix($next, $grid-breakpoints);
+
+ // stylelint-disable-next-line scss/selector-no-union-class-name
+ {$infix} {
+ @include media-breakpoint-up($next) {
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+
+ .navbar-nav {
+ flex-direction: row;
+
+ .dropdown-menu {
+ position: absolute;
+ }
+
+ .nav-link {
+ padding-right: var(--#{$prefix}navbar-nav-link-padding-x);
+ padding-left: var(--#{$prefix}navbar-nav-link-padding-x);
+ }
+ }
+
+ .navbar-nav-scroll {
+ overflow: visible;
+ }
+
+ .navbar-collapse {
+ display: flex !important; // stylelint-disable-line declaration-no-important
+ flex-basis: auto;
+ }
+
+ .navbar-toggler {
+ display: none;
+ }
+
+ .offcanvas {
+ // stylelint-disable declaration-no-important
+ position: static;
+ z-index: auto;
+ flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
+ visibility: visible !important;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
+ @include box-shadow(none);
+ @include transition(none);
+ // stylelint-enable declaration-no-important
+
+ .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ }
+ }
+ }
+ }
+ }
+}
+// scss-docs-end navbar-expand-loop
+
+// Navbar themes
+//
+// Styles for switching between navbars with light or dark background.
+
+.navbar-light {
+ @include deprecate("`.navbar-light`", "v5.2.0", "v6.0.0", true);
+}
+
+.navbar-dark,
+.navbar[data-mdb-theme="dark"] {
+ // scss-docs-start navbar-dark-css-vars
+ --#{$prefix}navbar-color: #{$navbar-dark-color};
+ --#{$prefix}navbar-hover-color: #{$navbar-dark-hover-color};
+ --#{$prefix}navbar-disabled-color: #{$navbar-dark-disabled-color};
+ --#{$prefix}navbar-active-color: #{$navbar-dark-active-color};
+ --#{$prefix}navbar-brand-color: #{$navbar-dark-brand-color};
+ --#{$prefix}navbar-brand-hover-color: #{$navbar-dark-brand-hover-color};
+ --#{$prefix}navbar-toggler-border-color: #{$navbar-dark-toggler-border-color};
+ --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
+ // scss-docs-end navbar-dark-css-vars
+}
+
+@if $enable-dark-mode {
+ @include color-mode(dark) {
+ .navbar-toggler-icon {
+ --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_offcanvas.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_offcanvas.scss
new file mode 100644
index 00000000..04e3ce64
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_offcanvas.scss
@@ -0,0 +1,146 @@
+// stylelint-disable function-disallowed-list
+
+%offcanvas-css-vars {
+ // scss-docs-start offcanvas-css-vars
+ --#{$prefix}offcanvas-zindex: #{$zindex-offcanvas};
+ --#{$prefix}offcanvas-width: #{$offcanvas-horizontal-width};
+ --#{$prefix}offcanvas-height: #{$offcanvas-vertical-height};
+ --#{$prefix}offcanvas-padding-x: #{$offcanvas-padding-x};
+ --#{$prefix}offcanvas-padding-y: #{$offcanvas-padding-y};
+ --#{$prefix}offcanvas-color: #{$offcanvas-color};
+ --#{$prefix}offcanvas-bg: #{$offcanvas-bg-color};
+ --#{$prefix}offcanvas-border-width: #{$offcanvas-border-width};
+ --#{$prefix}offcanvas-border-color: #{$offcanvas-border-color};
+ --#{$prefix}offcanvas-box-shadow: #{$offcanvas-box-shadow};
+ --#{$prefix}offcanvas-transition: #{transform $offcanvas-transition-duration ease-in-out};
+ --#{$prefix}offcanvas-title-line-height: #{$offcanvas-title-line-height};
+ // scss-docs-end offcanvas-css-vars
+}
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+ $next: breakpoint-next($breakpoint, $grid-breakpoints);
+ $infix: breakpoint-infix($next, $grid-breakpoints);
+
+ .offcanvas#{$infix} {
+ @extend %offcanvas-css-vars;
+ }
+}
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+ $next: breakpoint-next($breakpoint, $grid-breakpoints);
+ $infix: breakpoint-infix($next, $grid-breakpoints);
+
+ .offcanvas#{$infix} {
+ @include media-breakpoint-down($next) {
+ position: fixed;
+ bottom: 0;
+ z-index: var(--#{$prefix}offcanvas-zindex);
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--#{$prefix}offcanvas-color);
+ visibility: hidden;
+ background-color: var(--#{$prefix}offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ @include box-shadow(var(--#{$prefix}offcanvas-box-shadow));
+ @include transition(var(--#{$prefix}offcanvas-transition));
+
+ &.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--#{$prefix}offcanvas-width);
+ border-right: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+
+ &.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--#{$prefix}offcanvas-width);
+ border-left: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color);
+ transform: translateX(100%);
+ }
+
+ &.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--#{$prefix}offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+
+ &.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--#{$prefix}offcanvas-height);
+ max-height: 100%;
+ border-top: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color);
+ transform: translateY(100%);
+ }
+
+ &.showing,
+ &.show:not(.hiding) {
+ transform: none;
+ }
+
+ &.showing,
+ &.hiding,
+ &.show {
+ visibility: visible;
+ }
+ }
+
+ @if not ($infix == "") {
+ @include media-breakpoint-up($next) {
+ --#{$prefix}offcanvas-height: auto;
+ --#{$prefix}offcanvas-border-width: 0;
+ background-color: transparent !important; // stylelint-disable-line declaration-no-important
+
+ .offcanvas-header {
+ display: none;
+ }
+
+ .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ // Reset `background-color` in case `.bg-*` classes are used in offcanvas
+ background-color: transparent !important; // stylelint-disable-line declaration-no-important
+ }
+ }
+ }
+ }
+}
+
+.offcanvas-backdrop {
+ @include overlay-backdrop($zindex-offcanvas-backdrop, $offcanvas-backdrop-bg, $offcanvas-backdrop-opacity);
+}
+
+.offcanvas-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--#{$prefix}offcanvas-padding-y) var(--#{$prefix}offcanvas-padding-x);
+
+ .btn-close {
+ padding: calc(var(--#{$prefix}offcanvas-padding-y) * .5) calc(var(--#{$prefix}offcanvas-padding-x) * .5);
+ margin-top: calc(-.5 * var(--#{$prefix}offcanvas-padding-y));
+ margin-right: calc(-.5 * var(--#{$prefix}offcanvas-padding-x));
+ margin-bottom: calc(-.5 * var(--#{$prefix}offcanvas-padding-y));
+ }
+}
+
+.offcanvas-title {
+ margin-bottom: 0;
+ line-height: var(--#{$prefix}offcanvas-title-line-height);
+}
+
+.offcanvas-body {
+ flex-grow: 1;
+ padding: var(--#{$prefix}offcanvas-padding-y) var(--#{$prefix}offcanvas-padding-x);
+ overflow-y: auto;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_pagination.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_pagination.scss
new file mode 100644
index 00000000..f275a62e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_pagination.scss
@@ -0,0 +1,109 @@
+.pagination {
+ // scss-docs-start pagination-css-vars
+ --#{$prefix}pagination-padding-x: #{$pagination-padding-x};
+ --#{$prefix}pagination-padding-y: #{$pagination-padding-y};
+ @include rfs($pagination-font-size, --#{$prefix}pagination-font-size);
+ --#{$prefix}pagination-color: #{$pagination-color};
+ --#{$prefix}pagination-bg: #{$pagination-bg};
+ --#{$prefix}pagination-border-width: #{$pagination-border-width};
+ --#{$prefix}pagination-border-color: #{$pagination-border-color};
+ --#{$prefix}pagination-border-radius: #{$pagination-border-radius};
+ --#{$prefix}pagination-hover-color: #{$pagination-hover-color};
+ --#{$prefix}pagination-hover-bg: #{$pagination-hover-bg};
+ --#{$prefix}pagination-hover-border-color: #{$pagination-hover-border-color};
+ --#{$prefix}pagination-focus-color: #{$pagination-focus-color};
+ --#{$prefix}pagination-focus-bg: #{$pagination-focus-bg};
+ --#{$prefix}pagination-focus-box-shadow: #{$pagination-focus-box-shadow};
+ --#{$prefix}pagination-active-color: #{$pagination-active-color};
+ --#{$prefix}pagination-active-bg: #{$pagination-active-bg};
+ --#{$prefix}pagination-active-border-color: #{$pagination-active-border-color};
+ --#{$prefix}pagination-disabled-color: #{$pagination-disabled-color};
+ --#{$prefix}pagination-disabled-bg: #{$pagination-disabled-bg};
+ --#{$prefix}pagination-disabled-border-color: #{$pagination-disabled-border-color};
+ // scss-docs-end pagination-css-vars
+
+ display: flex;
+ @include list-unstyled();
+}
+
+.page-link {
+ position: relative;
+ display: block;
+ padding: var(--#{$prefix}pagination-padding-y) var(--#{$prefix}pagination-padding-x);
+ @include font-size(var(--#{$prefix}pagination-font-size));
+ color: var(--#{$prefix}pagination-color);
+ text-decoration: if($link-decoration == none, null, none);
+ background-color: var(--#{$prefix}pagination-bg);
+ border: var(--#{$prefix}pagination-border-width) solid var(--#{$prefix}pagination-border-color);
+ @include transition($pagination-transition);
+
+ &:hover {
+ z-index: 2;
+ color: var(--#{$prefix}pagination-hover-color);
+ text-decoration: if($link-hover-decoration == underline, none, null);
+ background-color: var(--#{$prefix}pagination-hover-bg);
+ border-color: var(--#{$prefix}pagination-hover-border-color);
+ }
+
+ &:focus {
+ z-index: 3;
+ color: var(--#{$prefix}pagination-focus-color);
+ background-color: var(--#{$prefix}pagination-focus-bg);
+ outline: $pagination-focus-outline;
+ box-shadow: var(--#{$prefix}pagination-focus-box-shadow);
+ }
+
+ &.active,
+ .active > & {
+ z-index: 3;
+ color: var(--#{$prefix}pagination-active-color);
+ @include gradient-bg(var(--#{$prefix}pagination-active-bg));
+ border-color: var(--#{$prefix}pagination-active-border-color);
+ }
+
+ &.disabled,
+ .disabled > & {
+ color: var(--#{$prefix}pagination-disabled-color);
+ pointer-events: none;
+ background-color: var(--#{$prefix}pagination-disabled-bg);
+ border-color: var(--#{$prefix}pagination-disabled-border-color);
+ }
+}
+
+.page-item {
+ &:not(:first-child) .page-link {
+ margin-left: $pagination-margin-start;
+ }
+
+ @if $pagination-margin-start == calc(#{$pagination-border-width} * -1) {
+ &:first-child {
+ .page-link {
+ @include border-start-radius(var(--#{$prefix}pagination-border-radius));
+ }
+ }
+
+ &:last-child {
+ .page-link {
+ @include border-end-radius(var(--#{$prefix}pagination-border-radius));
+ }
+ }
+ } @else {
+ // Add border-radius to all pageLinks in case they have left margin
+ .page-link {
+ @include border-radius(var(--#{$prefix}pagination-border-radius));
+ }
+ }
+}
+
+
+//
+// Sizing
+//
+
+.pagination-lg {
+ @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $pagination-border-radius-lg);
+}
+
+.pagination-sm {
+ @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $pagination-border-radius-sm);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_placeholders.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_placeholders.scss
new file mode 100644
index 00000000..6e32e1cd
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_placeholders.scss
@@ -0,0 +1,51 @@
+.placeholder {
+ display: inline-block;
+ min-height: 1em;
+ vertical-align: middle;
+ cursor: wait;
+ background-color: currentcolor;
+ opacity: $placeholder-opacity-max;
+
+ &.btn::before {
+ display: inline-block;
+ content: "";
+ }
+}
+
+// Sizing
+.placeholder-xs {
+ min-height: .6em;
+}
+
+.placeholder-sm {
+ min-height: .8em;
+}
+
+.placeholder-lg {
+ min-height: 1.2em;
+}
+
+// Animation
+.placeholder-glow {
+ .placeholder {
+ animation: placeholder-glow 2s ease-in-out infinite;
+ }
+}
+
+@keyframes placeholder-glow {
+ 50% {
+ opacity: $placeholder-opacity-min;
+ }
+}
+
+.placeholder-wave {
+ mask-image: linear-gradient(130deg, $black 55%, rgba(0, 0, 0, (1 - $placeholder-opacity-min)) 75%, $black 95%);
+ mask-size: 200% 100%;
+ animation: placeholder-wave 2s linear infinite;
+}
+
+@keyframes placeholder-wave {
+ 100% {
+ mask-position: -200% 0%;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_popover.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_popover.scss
new file mode 100644
index 00000000..7b69f623
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_popover.scss
@@ -0,0 +1,196 @@
+.popover {
+ // scss-docs-start popover-css-vars
+ --#{$prefix}popover-zindex: #{$zindex-popover};
+ --#{$prefix}popover-max-width: #{$popover-max-width};
+ @include rfs($popover-font-size, --#{$prefix}popover-font-size);
+ --#{$prefix}popover-bg: #{$popover-bg};
+ --#{$prefix}popover-border-width: #{$popover-border-width};
+ --#{$prefix}popover-border-color: #{$popover-border-color};
+ --#{$prefix}popover-border-radius: #{$popover-border-radius};
+ --#{$prefix}popover-inner-border-radius: #{$popover-inner-border-radius};
+ --#{$prefix}popover-box-shadow: #{$popover-box-shadow};
+ --#{$prefix}popover-header-padding-x: #{$popover-header-padding-x};
+ --#{$prefix}popover-header-padding-y: #{$popover-header-padding-y};
+ @include rfs($popover-header-font-size, --#{$prefix}popover-header-font-size);
+ --#{$prefix}popover-header-color: #{$popover-header-color};
+ --#{$prefix}popover-header-bg: #{$popover-header-bg};
+ --#{$prefix}popover-body-padding-x: #{$popover-body-padding-x};
+ --#{$prefix}popover-body-padding-y: #{$popover-body-padding-y};
+ --#{$prefix}popover-body-color: #{$popover-body-color};
+ --#{$prefix}popover-arrow-width: #{$popover-arrow-width};
+ --#{$prefix}popover-arrow-height: #{$popover-arrow-height};
+ --#{$prefix}popover-arrow-border: var(--#{$prefix}popover-border-color);
+ // scss-docs-end popover-css-vars
+
+ z-index: var(--#{$prefix}popover-zindex);
+ display: block;
+ max-width: var(--#{$prefix}popover-max-width);
+ // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+ // So reset our font and text properties to avoid inheriting weird values.
+ @include reset-text();
+ @include font-size(var(--#{$prefix}popover-font-size));
+ // Allow breaking very long words so they don't overflow the popover's bounds
+ word-wrap: break-word;
+ background-color: var(--#{$prefix}popover-bg);
+ background-clip: padding-box;
+ border: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-border-color);
+ @include border-radius(var(--#{$prefix}popover-border-radius));
+ @include box-shadow(var(--#{$prefix}popover-box-shadow));
+
+ .popover-arrow {
+ display: block;
+ width: var(--#{$prefix}popover-arrow-width);
+ height: var(--#{$prefix}popover-arrow-height);
+
+ &::before,
+ &::after {
+ position: absolute;
+ display: block;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ border-width: 0;
+ }
+ }
+}
+
+.bs-popover-top {
+ > .popover-arrow {
+ bottom: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
+
+ &::before,
+ &::after {
+ border-width: var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ }
+
+ &::before {
+ bottom: 0;
+ border-top-color: var(--#{$prefix}popover-arrow-border);
+ }
+
+ &::after {
+ bottom: var(--#{$prefix}popover-border-width);
+ border-top-color: var(--#{$prefix}popover-bg);
+ }
+ }
+}
+
+/* rtl:begin:ignore */
+.bs-popover-end {
+ > .popover-arrow {
+ left: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
+ width: var(--#{$prefix}popover-arrow-height);
+ height: var(--#{$prefix}popover-arrow-width);
+
+ &::before,
+ &::after {
+ border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ }
+
+ &::before {
+ left: 0;
+ border-right-color: var(--#{$prefix}popover-arrow-border);
+ }
+
+ &::after {
+ left: var(--#{$prefix}popover-border-width);
+ border-right-color: var(--#{$prefix}popover-bg);
+ }
+ }
+}
+
+/* rtl:end:ignore */
+
+.bs-popover-bottom {
+ > .popover-arrow {
+ top: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
+
+ &::before,
+ &::after {
+ border-width: 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list
+ }
+
+ &::before {
+ top: 0;
+ border-bottom-color: var(--#{$prefix}popover-arrow-border);
+ }
+
+ &::after {
+ top: var(--#{$prefix}popover-border-width);
+ border-bottom-color: var(--#{$prefix}popover-bg);
+ }
+ }
+
+ // This will remove the popover-header's border just below the arrow
+ .popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: var(--#{$prefix}popover-arrow-width);
+ margin-left: calc(-.5 * var(--#{$prefix}popover-arrow-width)); // stylelint-disable-line function-disallowed-list
+ content: "";
+ border-bottom: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-header-bg);
+ }
+}
+
+/* rtl:begin:ignore */
+.bs-popover-start {
+ > .popover-arrow {
+ right: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
+ width: var(--#{$prefix}popover-arrow-height);
+ height: var(--#{$prefix}popover-arrow-width);
+
+ &::before,
+ &::after {
+ border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list
+ }
+
+ &::before {
+ right: 0;
+ border-left-color: var(--#{$prefix}popover-arrow-border);
+ }
+
+ &::after {
+ right: var(--#{$prefix}popover-border-width);
+ border-left-color: var(--#{$prefix}popover-bg);
+ }
+ }
+}
+
+/* rtl:end:ignore */
+
+.bs-popover-auto {
+ &[data-popper-placement^="top"] {
+ @extend .bs-popover-top;
+ }
+ &[data-popper-placement^="right"] {
+ @extend .bs-popover-end;
+ }
+ &[data-popper-placement^="bottom"] {
+ @extend .bs-popover-bottom;
+ }
+ &[data-popper-placement^="left"] {
+ @extend .bs-popover-start;
+ }
+}
+
+// Offset the popover to account for the popover arrow
+.popover-header {
+ padding: var(--#{$prefix}popover-header-padding-y) var(--#{$prefix}popover-header-padding-x);
+ margin-bottom: 0; // Reset the default from Reboot
+ @include font-size(var(--#{$prefix}popover-header-font-size));
+ color: var(--#{$prefix}popover-header-color);
+ background-color: var(--#{$prefix}popover-header-bg);
+ border-bottom: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-border-color);
+ @include border-top-radius(var(--#{$prefix}popover-inner-border-radius));
+
+ &:empty {
+ display: none;
+ }
+}
+
+.popover-body {
+ padding: var(--#{$prefix}popover-body-padding-y) var(--#{$prefix}popover-body-padding-x);
+ color: var(--#{$prefix}popover-body-color);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_progress.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_progress.scss
new file mode 100644
index 00000000..148c3815
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_progress.scss
@@ -0,0 +1,68 @@
+// Disable animation if transitions are disabled
+
+// scss-docs-start progress-keyframes
+@if $enable-transitions {
+ @keyframes progress-bar-stripes {
+ 0% { background-position-x: $progress-height; }
+ }
+}
+// scss-docs-end progress-keyframes
+
+.progress,
+.progress-stacked {
+ // scss-docs-start progress-css-vars
+ --#{$prefix}progress-height: #{$progress-height};
+ @include rfs($progress-font-size, --#{$prefix}progress-font-size);
+ --#{$prefix}progress-bg: #{$progress-bg};
+ --#{$prefix}progress-border-radius: #{$progress-border-radius};
+ --#{$prefix}progress-box-shadow: #{$progress-box-shadow};
+ --#{$prefix}progress-bar-color: #{$progress-bar-color};
+ --#{$prefix}progress-bar-bg: #{$progress-bar-bg};
+ --#{$prefix}progress-bar-transition: #{$progress-bar-transition};
+ // scss-docs-end progress-css-vars
+
+ display: flex;
+ height: var(--#{$prefix}progress-height);
+ overflow: hidden; // force rounded corners by cropping it
+ @include font-size(var(--#{$prefix}progress-font-size));
+ background-color: var(--#{$prefix}progress-bg);
+ @include border-radius(var(--#{$prefix}progress-border-radius));
+ @include box-shadow(var(--#{$prefix}progress-box-shadow));
+}
+
+.progress-bar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ overflow: hidden;
+ color: var(--#{$prefix}progress-bar-color);
+ text-align: center;
+ white-space: nowrap;
+ background-color: var(--#{$prefix}progress-bar-bg);
+ @include transition(var(--#{$prefix}progress-bar-transition));
+}
+
+.progress-bar-striped {
+ @include gradient-striped();
+ background-size: var(--#{$prefix}progress-height) var(--#{$prefix}progress-height);
+}
+
+.progress-stacked > .progress {
+ overflow: visible;
+}
+
+.progress-stacked > .progress > .progress-bar {
+ width: 100%;
+}
+
+@if $enable-transitions {
+ .progress-bar-animated {
+ animation: $progress-bar-animation-timing progress-bar-stripes;
+
+ @if $enable-reduced-motion {
+ @media (prefers-reduced-motion: reduce) {
+ animation: none;
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_reboot.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_reboot.scss
new file mode 100644
index 00000000..18791753
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_reboot.scss
@@ -0,0 +1,611 @@
+// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
+
+
+// Reboot
+//
+// Normalization of HTML elements, manually forked from Normalize.css to remove
+// styles targeting irrelevant browsers while applying new styles.
+//
+// Normalize is licensed MIT. https://github.com/necolas/normalize.css
+
+
+// Document
+//
+// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+
+// Root
+//
+// Ability to the value of the root font sizes, affecting the value of `rem`.
+// null by default, thus nothing is generated.
+
+:root {
+ @if $font-size-root != null {
+ @include font-size(var(--#{$prefix}root-font-size));
+ }
+
+ @if $enable-smooth-scroll {
+ @media (prefers-reduced-motion: no-preference) {
+ scroll-behavior: smooth;
+ }
+ }
+}
+
+
+// Body
+//
+// 1. Remove the margin in all browsers.
+// 2. As a best practice, apply a default `background-color`.
+// 3. Prevent adjustments of font size after orientation changes in iOS.
+// 4. Change the default tap highlight to be completely transparent in iOS.
+
+// scss-docs-start reboot-body-rules
+body {
+ margin: 0; // 1
+ font-family: var(--#{$prefix}body-font-family);
+ @include font-size(var(--#{$prefix}body-font-size));
+ font-weight: var(--#{$prefix}body-font-weight);
+ line-height: var(--#{$prefix}body-line-height);
+ color: var(--#{$prefix}body-color);
+ text-align: var(--#{$prefix}body-text-align);
+ background-color: var(--#{$prefix}body-bg); // 2
+ -webkit-text-size-adjust: 100%; // 3
+ -webkit-tap-highlight-color: rgba($black, 0); // 4
+}
+// scss-docs-end reboot-body-rules
+
+
+// Content grouping
+//
+// 1. Reset Firefox's gray color
+
+hr {
+ margin: $hr-margin-y 0;
+ color: $hr-color; // 1
+ border: 0;
+ border-top: $hr-border-width solid $hr-border-color;
+ opacity: $hr-opacity;
+}
+
+
+// Typography
+//
+// 1. Remove top margins from headings
+// By default, ``-`` all receive top and bottom margins. We nuke the top
+// margin for easier control within type scales as it avoids margin collapsing.
+
+%heading {
+ margin-top: 0; // 1
+ margin-bottom: $headings-margin-bottom;
+ font-family: $headings-font-family;
+ font-style: $headings-font-style;
+ font-weight: $headings-font-weight;
+ line-height: $headings-line-height;
+ color: var(--#{$prefix}heading-color);
+}
+
+h1 {
+ @extend %heading;
+ @include font-size($h1-font-size);
+}
+
+h2 {
+ @extend %heading;
+ @include font-size($h2-font-size);
+}
+
+h3 {
+ @extend %heading;
+ @include font-size($h3-font-size);
+}
+
+h4 {
+ @extend %heading;
+ @include font-size($h4-font-size);
+}
+
+h5 {
+ @extend %heading;
+ @include font-size($h5-font-size);
+}
+
+h6 {
+ @extend %heading;
+ @include font-size($h6-font-size);
+}
+
+
+// Reset margins on paragraphs
+//
+// Similarly, the top margin on ` `s get reset. However, we also reset the
+// bottom margin to use `rem` units instead of `em`.
+
+p {
+ margin-top: 0;
+ margin-bottom: $paragraph-margin-bottom;
+}
+
+
+// Abbreviations
+//
+// 1. Add the correct text decoration in Chrome, Edge, Opera, and Safari.
+// 2. Add explicit cursor to indicate changed behavior.
+// 3. Prevent the text-decoration to be skipped.
+
+abbr[title] {
+ text-decoration: underline dotted; // 1
+ cursor: help; // 2
+ text-decoration-skip-ink: none; // 3
+}
+
+
+// Address
+
+address {
+ margin-bottom: 1rem;
+ font-style: normal;
+ line-height: inherit;
+}
+
+
+// Lists
+
+ol,
+ul {
+ padding-left: 2rem;
+}
+
+ol,
+ul,
+dl {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+ margin-bottom: 0;
+}
+
+dt {
+ font-weight: $dt-font-weight;
+}
+
+// 1. Undo browser default
+
+dd {
+ margin-bottom: .5rem;
+ margin-left: 0; // 1
+}
+
+
+// Blockquote
+
+blockquote {
+ margin: 0 0 1rem;
+}
+
+
+// Strong
+//
+// Add the correct font weight in Chrome, Edge, and Safari
+
+b,
+strong {
+ font-weight: $font-weight-bolder;
+}
+
+
+// Small
+//
+// Add the correct font size in all browsers
+
+small {
+ @include font-size($small-font-size);
+}
+
+
+// Mark
+
+mark {
+ padding: $mark-padding;
+ color: var(--#{$prefix}highlight-color);
+ background-color: var(--#{$prefix}highlight-bg);
+}
+
+
+// Sub and Sup
+//
+// Prevent `sub` and `sup` elements from affecting the line height in
+// all browsers.
+
+sub,
+sup {
+ position: relative;
+ @include font-size($sub-sup-font-size);
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sub { bottom: -.25em; }
+sup { top: -.5em; }
+
+
+// Links
+
+a {
+ color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1));
+ text-decoration: $link-decoration;
+
+ &:hover {
+ --#{$prefix}link-color-rgb: var(--#{$prefix}link-hover-color-rgb);
+ text-decoration: $link-hover-decoration;
+ }
+}
+
+// And undo these styles for placeholder links/named anchors (without href).
+// It would be more straightforward to just use a[href] in previous block, but that
+// causes specificity issues in many other styles that are too complex to fix.
+// See https://github.com/twbs/bootstrap/issues/19402
+
+a:not([href]):not([class]) {
+ &,
+ &:hover {
+ color: inherit;
+ text-decoration: none;
+ }
+}
+
+
+// Code
+
+pre,
+code,
+kbd,
+samp {
+ font-family: $font-family-code;
+ @include font-size(1em); // Correct the odd `em` font sizing in all browsers.
+}
+
+// 1. Remove browser default top margin
+// 2. Reset browser default of `1em` to use `rem`s
+// 3. Don't allow content to break outside
+
+pre {
+ display: block;
+ margin-top: 0; // 1
+ margin-bottom: 1rem; // 2
+ overflow: auto; // 3
+ @include font-size($code-font-size);
+ color: $pre-color;
+
+ // Account for some code outputs that place code tags in pre tags
+ code {
+ @include font-size(inherit);
+ color: inherit;
+ word-break: normal;
+ }
+}
+
+code {
+ @include font-size($code-font-size);
+ color: var(--#{$prefix}code-color);
+ word-wrap: break-word;
+
+ // Streamline the style when inside anchors to avoid broken underline and more
+ a > & {
+ color: inherit;
+ }
+}
+
+kbd {
+ padding: $kbd-padding-y $kbd-padding-x;
+ @include font-size($kbd-font-size);
+ color: $kbd-color;
+ background-color: $kbd-bg;
+ @include border-radius($border-radius-sm);
+
+ kbd {
+ padding: 0;
+ @include font-size(1em);
+ font-weight: $nested-kbd-font-weight;
+ }
+}
+
+
+// Figures
+//
+// Apply a consistent margin strategy (matches our type styles).
+
+figure {
+ margin: 0 0 1rem;
+}
+
+
+// Images and content
+
+img,
+svg {
+ vertical-align: middle;
+}
+
+
+// Tables
+//
+// Prevent double borders
+
+table {
+ caption-side: bottom;
+ border-collapse: collapse;
+}
+
+caption {
+ padding-top: $table-cell-padding-y;
+ padding-bottom: $table-cell-padding-y;
+ color: $table-caption-color;
+ text-align: left;
+}
+
+// 1. Removes font-weight bold by inheriting
+// 2. Matches default `
` alignment by inheriting `text-align`.
+// 3. Fix alignment for Safari
+
+th {
+ font-weight: $table-th-font-weight; // 1
+ text-align: inherit; // 2
+ text-align: -webkit-match-parent; // 3
+}
+
+thead,
+tbody,
+tfoot,
+tr,
+td,
+th {
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+}
+
+
+// Forms
+//
+// 1. Allow labels to use `margin` for spacing.
+
+label {
+ display: inline-block; // 1
+}
+
+// Remove the default `border-radius` that macOS Chrome adds.
+// See https://github.com/twbs/bootstrap/issues/24093
+
+button {
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: 0;
+}
+
+// Explicitly remove focus outline in Chromium when it shouldn't be
+// visible (e.g. as result of mouse click or touch tap). It already
+// should be doing this automatically, but seems to currently be
+// confused and applies its very visible two-tone outline anyway.
+
+button:focus:not(:focus-visible) {
+ outline: 0;
+}
+
+// 1. Remove the margin in Firefox and Safari
+
+input,
+button,
+select,
+optgroup,
+textarea {
+ margin: 0; // 1
+ font-family: inherit;
+ @include font-size(inherit);
+ line-height: inherit;
+}
+
+// Remove the inheritance of text transform in Firefox
+button,
+select {
+ text-transform: none;
+}
+// Set the cursor for non-`` buttons
+//
+// Details at https://github.com/twbs/bootstrap/pull/30562
+[role="button"] {
+ cursor: pointer;
+}
+
+select {
+ // Remove the inheritance of word-wrap in Safari.
+ // See https://github.com/twbs/bootstrap/issues/24990
+ word-wrap: normal;
+
+ // Undo the opacity change from Chrome
+ &:disabled {
+ opacity: 1;
+ }
+}
+
+// Remove the dropdown arrow only from text type inputs built with datalists in Chrome.
+// See https://stackoverflow.com/a/54997118
+
+[list]:not([type="date"]):not([type="datetime-local"]):not([type="month"]):not([type="week"]):not([type="time"])::-webkit-calendar-picker-indicator {
+ display: none !important;
+}
+
+// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+// controls in Android 4.
+// 2. Correct the inability to style clickable types in iOS and Safari.
+// 3. Opinionated: add "hand" cursor to non-disabled button elements.
+
+button,
+[type="button"], // 1
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button; // 2
+
+ @if $enable-button-pointers {
+ &:not(:disabled) {
+ cursor: pointer; // 3
+ }
+ }
+}
+
+// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
+
+::-moz-focus-inner {
+ padding: 0;
+ border-style: none;
+}
+
+// 1. Textareas should really only resize vertically so they don't break their (horizontal) containers.
+
+textarea {
+ resize: vertical; // 1
+}
+
+// 1. Browsers set a default `min-width: min-content;` on fieldsets,
+// unlike e.g. ``s, which have `min-width: 0;` by default.
+// So we reset that to ensure fieldsets behave more like a standard block element.
+// See https://github.com/twbs/bootstrap/issues/12359
+// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
+// 2. Reset the default outline behavior of fieldsets so they don't affect page layout.
+
+fieldset {
+ min-width: 0; // 1
+ padding: 0; // 2
+ margin: 0; // 2
+ border: 0; // 2
+}
+
+// 1. By using `float: left`, the legend will behave like a block element.
+// This way the border of a fieldset wraps around the legend if present.
+// 2. Fix wrapping bug.
+// See https://github.com/twbs/bootstrap/issues/29712
+
+legend {
+ float: left; // 1
+ width: 100%;
+ padding: 0;
+ margin-bottom: $legend-margin-bottom;
+ @include font-size($legend-font-size);
+ font-weight: $legend-font-weight;
+ line-height: inherit;
+
+ + * {
+ clear: left; // 2
+ }
+}
+
+// Fix height of inputs with a type of datetime-local, date, month, week, or time
+// See https://github.com/twbs/bootstrap/issues/18842
+
+::-webkit-datetime-edit-fields-wrapper,
+::-webkit-datetime-edit-text,
+::-webkit-datetime-edit-minute,
+::-webkit-datetime-edit-hour-field,
+::-webkit-datetime-edit-day-field,
+::-webkit-datetime-edit-month-field,
+::-webkit-datetime-edit-year-field {
+ padding: 0;
+}
+
+::-webkit-inner-spin-button {
+ height: auto;
+}
+
+// 1. This overrides the extra rounded corners on search inputs in iOS so that our
+// `.form-control` class can properly style them. Note that this cannot simply
+// be added to `.form-control` as it's not specific enough. For details, see
+// https://github.com/twbs/bootstrap/issues/11586.
+// 2. Correct the outline style in Safari.
+
+[type="search"] {
+ -webkit-appearance: textfield; // 1
+ outline-offset: -2px; // 2
+}
+
+// 1. A few input types should stay LTR
+// See https://rtlstyling.com/posts/rtl-styling#form-inputs
+// 2. RTL only output
+// See https://rtlcss.com/learn/usage-guide/control-directives/#raw
+
+/* rtl:raw:
+[type="tel"],
+[type="url"],
+[type="email"],
+[type="number"] {
+ direction: ltr;
+}
+*/
+
+// Remove the inner padding in Chrome and Safari on macOS.
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+// Remove padding around color pickers in webkit browsers
+
+::-webkit-color-swatch-wrapper {
+ padding: 0;
+}
+
+
+// 1. Inherit font family and line height for file input buttons
+// 2. Correct the inability to style clickable types in iOS and Safari.
+
+::file-selector-button {
+ font: inherit; // 1
+ -webkit-appearance: button; // 2
+}
+
+// Correct element displays
+
+output {
+ display: inline-block;
+}
+
+// Remove border from iframe
+
+iframe {
+ border: 0;
+}
+
+// Summary
+//
+// 1. Add the correct display in all browsers
+
+summary {
+ display: list-item; // 1
+ cursor: pointer;
+}
+
+
+// Progress
+//
+// Add the correct vertical alignment in Chrome, Firefox, and Opera.
+
+progress {
+ vertical-align: baseline;
+}
+
+
+// Hidden attribute
+//
+// Always hide an element with the `hidden` HTML attribute.
+
+[hidden] {
+ display: none !important;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_root.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_root.scss
new file mode 100644
index 00000000..b822fff4
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_root.scss
@@ -0,0 +1,187 @@
+:root,
+[data-mdb-theme="light"] {
+ // Note: Custom variable values only support SassScript inside `#{}`.
+
+ // Colors
+ //
+ // Generate palettes for full colors, grays, and theme colors.
+
+ @each $color, $value in $colors {
+ --#{$prefix}#{$color}: #{$value};
+ }
+
+ @each $color, $value in $grays {
+ --#{$prefix}gray-#{$color}: #{$value};
+ }
+
+ @each $color, $value in $theme-colors {
+ --#{$prefix}#{$color}: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-rgb {
+ --#{$prefix}#{$color}-rgb: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-text {
+ --#{$prefix}#{$color}-text-emphasis: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-bg-subtle {
+ --#{$prefix}#{$color}-bg-subtle: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-border-subtle {
+ --#{$prefix}#{$color}-border-subtle: #{$value};
+ }
+
+ --#{$prefix}white-rgb: #{to-rgb($white)};
+ --#{$prefix}black-rgb: #{to-rgb($black)};
+
+ // Fonts
+
+ // Note: Use `inspect` for lists so that quoted items keep the quotes.
+ // See https://github.com/sass/sass/issues/2383#issuecomment-336349172
+ --#{$prefix}font-sans-serif: #{inspect($font-family-sans-serif)};
+ --#{$prefix}font-monospace: #{inspect($font-family-monospace)};
+ --#{$prefix}gradient: #{$gradient};
+
+ // Root and body
+ // scss-docs-start root-body-variables
+ @if $font-size-root != null {
+ --#{$prefix}root-font-size: #{$font-size-root};
+ }
+ --#{$prefix}body-font-family: #{inspect($font-family-base)};
+ @include rfs($font-size-base, --#{$prefix}body-font-size);
+ --#{$prefix}body-font-weight: #{$font-weight-base};
+ --#{$prefix}body-line-height: #{$line-height-base};
+ @if $body-text-align != null {
+ --#{$prefix}body-text-align: #{$body-text-align};
+ }
+
+ --#{$prefix}body-color: #{$body-color};
+ --#{$prefix}body-color-rgb: #{to-rgb($body-color)};
+ --#{$prefix}body-bg: #{$body-bg};
+ --#{$prefix}body-bg-rgb: #{to-rgb($body-bg)};
+
+ --#{$prefix}emphasis-color: #{$body-emphasis-color};
+ --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color)};
+
+ --#{$prefix}secondary-color: #{$body-secondary-color};
+ --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color)};
+ --#{$prefix}secondary-bg: #{$body-secondary-bg};
+ --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg)};
+
+ --#{$prefix}tertiary-color: #{$body-tertiary-color};
+ --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color)};
+ --#{$prefix}tertiary-bg: #{$body-tertiary-bg};
+ --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg)};
+ // scss-docs-end root-body-variables
+
+ --#{$prefix}heading-color: #{$headings-color};
+
+ --#{$prefix}link-color: #{$link-color};
+ --#{$prefix}link-color-rgb: #{to-rgb($link-color)};
+ --#{$prefix}link-decoration: #{$link-decoration};
+
+ --#{$prefix}link-hover-color: #{$link-hover-color};
+ --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color)};
+
+ @if $link-hover-decoration != null {
+ --#{$prefix}link-hover-decoration: #{$link-hover-decoration};
+ }
+
+ --#{$prefix}code-color: #{$code-color};
+ --#{$prefix}highlight-color: #{$mark-color};
+ --#{$prefix}highlight-bg: #{$mark-bg};
+
+ // scss-docs-start root-border-var
+ --#{$prefix}border-width: #{$border-width};
+ --#{$prefix}border-style: #{$border-style};
+ --#{$prefix}border-color: #{$border-color};
+ --#{$prefix}border-color-translucent: #{$border-color-translucent};
+
+ --#{$prefix}border-radius: #{$border-radius};
+ --#{$prefix}border-radius-sm: #{$border-radius-sm};
+ --#{$prefix}border-radius-lg: #{$border-radius-lg};
+ --#{$prefix}border-radius-xl: #{$border-radius-xl};
+ --#{$prefix}border-radius-xxl: #{$border-radius-xxl};
+ --#{$prefix}border-radius-2xl: var(--#{$prefix}border-radius-xxl); // Deprecated in v5.3.0 for consistency
+ --#{$prefix}border-radius-pill: #{$border-radius-pill};
+ // scss-docs-end root-border-var
+
+ --#{$prefix}box-shadow: #{$box-shadow};
+ --#{$prefix}box-shadow-sm: #{$box-shadow-sm};
+ --#{$prefix}box-shadow-lg: #{$box-shadow-lg};
+ --#{$prefix}box-shadow-inset: #{$box-shadow-inset};
+
+ // Focus styles
+ // scss-docs-start root-focus-variables
+ --#{$prefix}focus-ring-width: #{$focus-ring-width};
+ --#{$prefix}focus-ring-opacity: #{$focus-ring-opacity};
+ --#{$prefix}focus-ring-color: #{$focus-ring-color};
+ // scss-docs-end root-focus-variables
+
+ // scss-docs-start root-form-validation-variables
+ --#{$prefix}form-valid-color: #{$form-valid-color};
+ --#{$prefix}form-valid-border-color: #{$form-valid-border-color};
+ --#{$prefix}form-invalid-color: #{$form-invalid-color};
+ --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color};
+ // scss-docs-end root-form-validation-variables
+}
+
+@if $enable-dark-mode {
+ @include color-mode(dark, true) {
+ color-scheme: dark;
+
+ // scss-docs-start root-dark-mode-vars
+ --#{$prefix}body-color: #{$body-color-dark};
+ --#{$prefix}body-color-rgb: #{to-rgb($body-color-dark)};
+ --#{$prefix}body-bg: #{$body-bg-dark};
+ --#{$prefix}body-bg-rgb: #{to-rgb($body-bg-dark)};
+
+ --#{$prefix}emphasis-color: #{$body-emphasis-color-dark};
+ --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color-dark)};
+
+ --#{$prefix}secondary-color: #{$body-secondary-color-dark};
+ --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color-dark)};
+ --#{$prefix}secondary-bg: #{$body-secondary-bg-dark};
+ --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg-dark)};
+
+ --#{$prefix}tertiary-color: #{$body-tertiary-color-dark};
+ --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color-dark)};
+ --#{$prefix}tertiary-bg: #{$body-tertiary-bg-dark};
+ --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg-dark)};
+
+ @each $color, $value in $theme-colors-text-dark {
+ --#{$prefix}#{$color}-text-emphasis: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-bg-subtle-dark {
+ --#{$prefix}#{$color}-bg-subtle: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-border-subtle-dark {
+ --#{$prefix}#{$color}-border-subtle: #{$value};
+ }
+
+ --#{$prefix}heading-color: #{$headings-color-dark};
+
+ --#{$prefix}link-color: #{$link-color-dark};
+ --#{$prefix}link-hover-color: #{$link-hover-color-dark};
+ --#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)};
+ --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)};
+
+ --#{$prefix}code-color: #{$code-color-dark};
+ --#{$prefix}highlight-color: #{$mark-color-dark};
+ --#{$prefix}highlight-bg: #{$mark-bg-dark};
+
+ --#{$prefix}border-color: #{$border-color-dark};
+ --#{$prefix}border-color-translucent: #{$border-color-translucent-dark};
+
+ --#{$prefix}form-valid-color: #{$form-valid-color-dark};
+ --#{$prefix}form-valid-border-color: #{$form-valid-border-color-dark};
+ --#{$prefix}form-invalid-color: #{$form-invalid-color-dark};
+ --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color-dark};
+ // scss-docs-end root-dark-mode-vars
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_spinners.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_spinners.scss
new file mode 100644
index 00000000..ec847320
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_spinners.scss
@@ -0,0 +1,85 @@
+//
+// Rotating border
+//
+
+.spinner-grow,
+.spinner-border {
+ display: inline-block;
+ width: var(--#{$prefix}spinner-width);
+ height: var(--#{$prefix}spinner-height);
+ vertical-align: var(--#{$prefix}spinner-vertical-align);
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: 50%;
+ animation: var(--#{$prefix}spinner-animation-speed) linear infinite var(--#{$prefix}spinner-animation-name);
+}
+
+// scss-docs-start spinner-border-keyframes
+@keyframes spinner-border {
+ to { transform: rotate(360deg) #{"/* rtl:ignore */"}; }
+}
+// scss-docs-end spinner-border-keyframes
+
+.spinner-border {
+ // scss-docs-start spinner-border-css-vars
+ --#{$prefix}spinner-width: #{$spinner-width};
+ --#{$prefix}spinner-height: #{$spinner-height};
+ --#{$prefix}spinner-vertical-align: #{$spinner-vertical-align};
+ --#{$prefix}spinner-border-width: #{$spinner-border-width};
+ --#{$prefix}spinner-animation-speed: #{$spinner-animation-speed};
+ --#{$prefix}spinner-animation-name: spinner-border;
+ // scss-docs-end spinner-border-css-vars
+
+ border: var(--#{$prefix}spinner-border-width) solid currentcolor;
+ border-right-color: transparent;
+}
+
+.spinner-border-sm {
+ // scss-docs-start spinner-border-sm-css-vars
+ --#{$prefix}spinner-width: #{$spinner-width-sm};
+ --#{$prefix}spinner-height: #{$spinner-height-sm};
+ --#{$prefix}spinner-border-width: #{$spinner-border-width-sm};
+ // scss-docs-end spinner-border-sm-css-vars
+}
+
+//
+// Growing circle
+//
+
+// scss-docs-start spinner-grow-keyframes
+@keyframes spinner-grow {
+ 0% {
+ transform: scale(0);
+ }
+ 50% {
+ opacity: 1;
+ transform: none;
+ }
+}
+// scss-docs-end spinner-grow-keyframes
+
+.spinner-grow {
+ // scss-docs-start spinner-grow-css-vars
+ --#{$prefix}spinner-width: #{$spinner-width};
+ --#{$prefix}spinner-height: #{$spinner-height};
+ --#{$prefix}spinner-vertical-align: #{$spinner-vertical-align};
+ --#{$prefix}spinner-animation-speed: #{$spinner-animation-speed};
+ --#{$prefix}spinner-animation-name: spinner-grow;
+ // scss-docs-end spinner-grow-css-vars
+
+ background-color: currentcolor;
+ opacity: 0;
+}
+
+.spinner-grow-sm {
+ --#{$prefix}spinner-width: #{$spinner-width-sm};
+ --#{$prefix}spinner-height: #{$spinner-height-sm};
+}
+
+@if $enable-reduced-motion {
+ @media (prefers-reduced-motion: reduce) {
+ .spinner-border,
+ .spinner-grow {
+ --#{$prefix}spinner-animation-speed: #{$spinner-animation-speed * 2};
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_tables.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_tables.scss
new file mode 100644
index 00000000..ebfc00a5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_tables.scss
@@ -0,0 +1,171 @@
+//
+// Basic Bootstrap table
+//
+
+.table {
+ // Reset needed for nesting tables
+ --#{$prefix}table-color-type: initial;
+ --#{$prefix}table-bg-type: initial;
+ --#{$prefix}table-color-state: initial;
+ --#{$prefix}table-bg-state: initial;
+ // End of reset
+ --#{$prefix}table-color: #{$table-color};
+ --#{$prefix}table-bg: #{$table-bg};
+ --#{$prefix}table-border-color: #{$table-border-color};
+ --#{$prefix}table-accent-bg: #{$table-accent-bg};
+ --#{$prefix}table-striped-color: #{$table-striped-color};
+ --#{$prefix}table-striped-bg: #{$table-striped-bg};
+ --#{$prefix}table-active-color: #{$table-active-color};
+ --#{$prefix}table-active-bg: #{$table-active-bg};
+ --#{$prefix}table-hover-color: #{$table-hover-color};
+ --#{$prefix}table-hover-bg: #{$table-hover-bg};
+
+ width: 100%;
+ margin-bottom: $spacer;
+ vertical-align: $table-cell-vertical-align;
+ border-color: var(--#{$prefix}table-border-color);
+
+ // Target th & td
+ // We need the child combinator to prevent styles leaking to nested tables which doesn't have a `.table` class.
+ // We use the universal selectors here to simplify the selector (else we would need 6 different selectors).
+ // Another advantage is that this generates less code and makes the selector less specific making it easier to override.
+ // stylelint-disable-next-line selector-max-universal
+ > :not(caption) > * > * {
+ padding: $table-cell-padding-y $table-cell-padding-x;
+ // Following the precept of cascades: https://codepen.io/miriamsuzanne/full/vYNgodb
+ color: var(--#{$prefix}table-color-state, var(--#{$prefix}table-color-type, var(--#{$prefix}table-color)));
+ background-color: var(--#{$prefix}table-bg);
+ border-bottom-width: $table-border-width;
+ box-shadow: inset 0 0 0 9999px var(--#{$prefix}table-bg-state, var(--#{$prefix}table-bg-type, var(--#{$prefix}table-accent-bg)));
+ }
+
+ > tbody {
+ vertical-align: inherit;
+ }
+
+ > thead {
+ vertical-align: bottom;
+ }
+}
+
+.table-group-divider {
+ border-top: calc(#{$table-border-width} * 2) solid $table-group-separator-color; // stylelint-disable-line function-disallowed-list
+}
+
+//
+// Change placement of captions with a class
+//
+
+.caption-top {
+ caption-side: top;
+}
+
+
+//
+// Condensed table w/ half padding
+//
+
+.table-sm {
+ // stylelint-disable-next-line selector-max-universal
+ > :not(caption) > * > * {
+ padding: $table-cell-padding-y-sm $table-cell-padding-x-sm;
+ }
+}
+
+
+// Border versions
+//
+// Add or remove borders all around the table and between all the columns.
+//
+// When borders are added on all sides of the cells, the corners can render odd when
+// these borders do not have the same color or if they are semi-transparent.
+// Therefor we add top and border bottoms to the `tr`s and left and right borders
+// to the `td`s or `th`s
+
+.table-bordered {
+ > :not(caption) > * {
+ border-width: $table-border-width 0;
+
+ // stylelint-disable-next-line selector-max-universal
+ > * {
+ border-width: 0 $table-border-width;
+ }
+ }
+}
+
+.table-borderless {
+ // stylelint-disable-next-line selector-max-universal
+ > :not(caption) > * > * {
+ border-bottom-width: 0;
+ }
+
+ > :not(:first-child) {
+ border-top-width: 0;
+ }
+}
+
+// Zebra-striping
+//
+// Default zebra-stripe styles (alternating gray and transparent backgrounds)
+
+// For rows
+.table-striped {
+ > tbody > tr:nth-of-type(#{$table-striped-order}) > * {
+ --#{$prefix}table-color-type: var(--#{$prefix}table-striped-color);
+ --#{$prefix}table-bg-type: var(--#{$prefix}table-striped-bg);
+ }
+}
+
+// For columns
+.table-striped-columns {
+ > :not(caption) > tr > :nth-child(#{$table-striped-columns-order}) {
+ --#{$prefix}table-color-type: var(--#{$prefix}table-striped-color);
+ --#{$prefix}table-bg-type: var(--#{$prefix}table-striped-bg);
+ }
+}
+
+// Active table
+//
+// The `.table-active` class can be added to highlight rows or cells
+
+.table-active {
+ --#{$prefix}table-color-state: var(--#{$prefix}table-active-color);
+ --#{$prefix}table-bg-state: var(--#{$prefix}table-active-bg);
+}
+
+// Hover effect
+//
+// Placed here since it has to come after the potential zebra striping
+
+.table-hover {
+ > tbody > tr:hover > * {
+ --#{$prefix}table-color-state: var(--#{$prefix}table-hover-color);
+ --#{$prefix}table-bg-state: var(--#{$prefix}table-hover-bg);
+ }
+}
+
+
+// Table variants
+//
+// Table variants set the table cell backgrounds, border colors
+// and the colors of the striped, hovered & active tables
+
+@each $color, $value in $table-variants {
+ @include table-variant($color, $value);
+}
+
+// Responsive tables
+//
+// Generate series of `.table-responsive-*` classes for configuring the screen
+// size of where your table will overflow.
+
+@each $breakpoint in map-keys($grid-breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ @include media-breakpoint-down($breakpoint) {
+ .table-responsive#{$infix} {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_toasts.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_toasts.scss
new file mode 100644
index 00000000..2ce378d5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_toasts.scss
@@ -0,0 +1,73 @@
+.toast {
+ // scss-docs-start toast-css-vars
+ --#{$prefix}toast-zindex: #{$zindex-toast};
+ --#{$prefix}toast-padding-x: #{$toast-padding-x};
+ --#{$prefix}toast-padding-y: #{$toast-padding-y};
+ --#{$prefix}toast-spacing: #{$toast-spacing};
+ --#{$prefix}toast-max-width: #{$toast-max-width};
+ @include rfs($toast-font-size, --#{$prefix}toast-font-size);
+ --#{$prefix}toast-color: #{$toast-color};
+ --#{$prefix}toast-bg: #{$toast-background-color};
+ --#{$prefix}toast-border-width: #{$toast-border-width};
+ --#{$prefix}toast-border-color: #{$toast-border-color};
+ --#{$prefix}toast-border-radius: #{$toast-border-radius};
+ --#{$prefix}toast-box-shadow: #{$toast-box-shadow};
+ --#{$prefix}toast-header-color: #{$toast-header-color};
+ --#{$prefix}toast-header-bg: #{$toast-header-background-color};
+ --#{$prefix}toast-header-border-color: #{$toast-header-border-color};
+ // scss-docs-end toast-css-vars
+
+ width: var(--#{$prefix}toast-max-width);
+ max-width: 100%;
+ @include font-size(var(--#{$prefix}toast-font-size));
+ color: var(--#{$prefix}toast-color);
+ pointer-events: auto;
+ background-color: var(--#{$prefix}toast-bg);
+ background-clip: padding-box;
+ border: var(--#{$prefix}toast-border-width) solid var(--#{$prefix}toast-border-color);
+ box-shadow: var(--#{$prefix}toast-box-shadow);
+ @include border-radius(var(--#{$prefix}toast-border-radius));
+
+ &.showing {
+ opacity: 0;
+ }
+
+ &:not(.show) {
+ display: none;
+ }
+}
+
+.toast-container {
+ --#{$prefix}toast-zindex: #{$zindex-toast};
+
+ position: absolute;
+ z-index: var(--#{$prefix}toast-zindex);
+ width: max-content;
+ max-width: 100%;
+ pointer-events: none;
+
+ > :not(:last-child) {
+ margin-bottom: var(--#{$prefix}toast-spacing);
+ }
+}
+
+.toast-header {
+ display: flex;
+ align-items: center;
+ padding: var(--#{$prefix}toast-padding-y) var(--#{$prefix}toast-padding-x);
+ color: var(--#{$prefix}toast-header-color);
+ background-color: var(--#{$prefix}toast-header-bg);
+ background-clip: padding-box;
+ border-bottom: var(--#{$prefix}toast-border-width) solid var(--#{$prefix}toast-header-border-color);
+ @include border-top-radius(calc(var(--#{$prefix}toast-border-radius) - var(--#{$prefix}toast-border-width)));
+
+ .btn-close {
+ margin-right: calc(-.5 * var(--#{$prefix}toast-padding-x)); // stylelint-disable-line function-disallowed-list
+ margin-left: var(--#{$prefix}toast-padding-x);
+ }
+}
+
+.toast-body {
+ padding: var(--#{$prefix}toast-padding-x);
+ word-wrap: break-word;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_tooltip.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_tooltip.scss
new file mode 100644
index 00000000..85de90f5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_tooltip.scss
@@ -0,0 +1,119 @@
+// Base class
+.tooltip {
+ // scss-docs-start tooltip-css-vars
+ --#{$prefix}tooltip-zindex: #{$zindex-tooltip};
+ --#{$prefix}tooltip-max-width: #{$tooltip-max-width};
+ --#{$prefix}tooltip-padding-x: #{$tooltip-padding-x};
+ --#{$prefix}tooltip-padding-y: #{$tooltip-padding-y};
+ --#{$prefix}tooltip-margin: #{$tooltip-margin};
+ @include rfs($tooltip-font-size, --#{$prefix}tooltip-font-size);
+ --#{$prefix}tooltip-color: #{$tooltip-color};
+ --#{$prefix}tooltip-bg: #{$tooltip-bg};
+ --#{$prefix}tooltip-border-radius: #{$tooltip-border-radius};
+ --#{$prefix}tooltip-opacity: #{$tooltip-opacity};
+ --#{$prefix}tooltip-arrow-width: #{$tooltip-arrow-width};
+ --#{$prefix}tooltip-arrow-height: #{$tooltip-arrow-height};
+ // scss-docs-end tooltip-css-vars
+
+ z-index: var(--#{$prefix}tooltip-zindex);
+ display: block;
+ margin: var(--#{$prefix}tooltip-margin);
+ @include deprecate("`$tooltip-margin`", "v5", "v5.x", true);
+ // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
+ // So reset our font and text properties to avoid inheriting weird values.
+ @include reset-text();
+ @include font-size(var(--#{$prefix}tooltip-font-size));
+ // Allow breaking very long words so they don't overflow the tooltip's bounds
+ word-wrap: break-word;
+ opacity: 0;
+
+ &.show { opacity: var(--#{$prefix}tooltip-opacity); }
+
+ .tooltip-arrow {
+ display: block;
+ width: var(--#{$prefix}tooltip-arrow-width);
+ height: var(--#{$prefix}tooltip-arrow-height);
+
+ &::before {
+ position: absolute;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ }
+ }
+}
+
+.bs-tooltip-top .tooltip-arrow {
+ bottom: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
+
+ &::before {
+ top: -1px;
+ border-width: var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ border-top-color: var(--#{$prefix}tooltip-bg);
+ }
+}
+
+/* rtl:begin:ignore */
+.bs-tooltip-end .tooltip-arrow {
+ left: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
+ width: var(--#{$prefix}tooltip-arrow-height);
+ height: var(--#{$prefix}tooltip-arrow-width);
+
+ &::before {
+ right: -1px;
+ border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ border-right-color: var(--#{$prefix}tooltip-bg);
+ }
+}
+
+/* rtl:end:ignore */
+
+.bs-tooltip-bottom .tooltip-arrow {
+ top: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
+
+ &::before {
+ bottom: -1px;
+ border-width: 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
+ border-bottom-color: var(--#{$prefix}tooltip-bg);
+ }
+}
+
+/* rtl:begin:ignore */
+.bs-tooltip-start .tooltip-arrow {
+ right: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
+ width: var(--#{$prefix}tooltip-arrow-height);
+ height: var(--#{$prefix}tooltip-arrow-width);
+
+ &::before {
+ left: -1px;
+ border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
+ border-left-color: var(--#{$prefix}tooltip-bg);
+ }
+}
+
+/* rtl:end:ignore */
+
+.bs-tooltip-auto {
+ &[data-popper-placement^="top"] {
+ @extend .bs-tooltip-top;
+ }
+ &[data-popper-placement^="right"] {
+ @extend .bs-tooltip-end;
+ }
+ &[data-popper-placement^="bottom"] {
+ @extend .bs-tooltip-bottom;
+ }
+ &[data-popper-placement^="left"] {
+ @extend .bs-tooltip-start;
+ }
+}
+
+// Wrapper for the tooltip content
+.tooltip-inner {
+ max-width: var(--#{$prefix}tooltip-max-width);
+ padding: var(--#{$prefix}tooltip-padding-y) var(--#{$prefix}tooltip-padding-x);
+ color: var(--#{$prefix}tooltip-color);
+ text-align: center;
+ background-color: var(--#{$prefix}tooltip-bg);
+ @include border-radius(var(--#{$prefix}tooltip-border-radius));
+}
diff --git a/src/angular-bootstrap-md/scss/bootstrap/_transitions.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_transitions.scss
similarity index 56%
rename from src/angular-bootstrap-md/scss/bootstrap/_transitions.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/_transitions.scss
index c8d91e27..bfb26aa8 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/_transitions.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_transitions.scss
@@ -1,5 +1,3 @@
-// stylelint-disable selector-no-qualifying-type
-
.fade {
@include transition($transition-fade);
@@ -8,6 +6,7 @@
}
}
+// scss-docs-start collapse-classes
.collapse {
&:not(.show) {
display: none;
@@ -15,8 +14,14 @@
}
.collapsing {
- position: relative;
height: 0;
overflow: hidden;
@include transition($transition-collapse);
+
+ &.collapse-horizontal {
+ width: 0;
+ height: auto;
+ @include transition($transition-collapse-width);
+ }
}
+// scss-docs-end collapse-classes
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_type.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_type.scss
new file mode 100644
index 00000000..37d64bf8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_type.scss
@@ -0,0 +1,106 @@
+//
+// Headings
+//
+.h1 {
+ @extend h1;
+}
+
+.h2 {
+ @extend h2;
+}
+
+.h3 {
+ @extend h3;
+}
+
+.h4 {
+ @extend h4;
+}
+
+.h5 {
+ @extend h5;
+}
+
+.h6 {
+ @extend h6;
+}
+
+
+.lead {
+ @include font-size($lead-font-size);
+ font-weight: $lead-font-weight;
+}
+
+// Type display classes
+@each $display, $font-size in $display-font-sizes {
+ .display-#{$display} {
+ @include font-size($font-size);
+ font-family: $display-font-family;
+ font-style: $display-font-style;
+ font-weight: $display-font-weight;
+ line-height: $display-line-height;
+ }
+}
+
+//
+// Emphasis
+//
+.small {
+ @extend small;
+}
+
+.mark {
+ @extend mark;
+}
+
+//
+// Lists
+//
+
+.list-unstyled {
+ @include list-unstyled();
+}
+
+// Inline turns list items into inline-block
+.list-inline {
+ @include list-unstyled();
+}
+.list-inline-item {
+ display: inline-block;
+
+ &:not(:last-child) {
+ margin-right: $list-inline-padding;
+ }
+}
+
+
+//
+// Misc
+//
+
+// Builds on `abbr`
+.initialism {
+ @include font-size($initialism-font-size);
+ text-transform: uppercase;
+}
+
+// Blockquotes
+.blockquote {
+ margin-bottom: $blockquote-margin-y;
+ @include font-size($blockquote-font-size);
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+.blockquote-footer {
+ margin-top: -$blockquote-margin-y;
+ margin-bottom: $blockquote-margin-y;
+ @include font-size($blockquote-footer-font-size);
+ color: $blockquote-footer-color;
+
+ &::before {
+ content: "\2014\00A0"; // em dash, nbsp
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_utilities.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_utilities.scss
new file mode 100644
index 00000000..696f906e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_utilities.scss
@@ -0,0 +1,806 @@
+// Utilities
+
+$utilities: () !default;
+// stylelint-disable-next-line scss/dollar-variable-default
+$utilities: map-merge(
+ (
+ // scss-docs-start utils-vertical-align
+ "align": (
+ property: vertical-align,
+ class: align,
+ values: baseline top middle bottom text-bottom text-top
+ ),
+ // scss-docs-end utils-vertical-align
+ // scss-docs-start utils-float
+ "float": (
+ responsive: true,
+ property: float,
+ values: (
+ start: left,
+ end: right,
+ none: none,
+ )
+ ),
+ // scss-docs-end utils-float
+ // Object Fit utilities
+ // scss-docs-start utils-object-fit
+ "object-fit": (
+ responsive: true,
+ property: object-fit,
+ values: (
+ contain: contain,
+ cover: cover,
+ fill: fill,
+ scale: scale-down,
+ none: none,
+ )
+ ),
+ // scss-docs-end utils-object-fit
+ // Opacity utilities
+ // scss-docs-start utils-opacity
+ "opacity": (
+ property: opacity,
+ values: (
+ 0: 0,
+ 25: .25,
+ 50: .5,
+ 75: .75,
+ 100: 1,
+ )
+ ),
+ // scss-docs-end utils-opacity
+ // scss-docs-start utils-overflow
+ "overflow": (
+ property: overflow,
+ values: auto hidden visible scroll,
+ ),
+ "overflow-x": (
+ property: overflow-x,
+ values: auto hidden visible scroll,
+ ),
+ "overflow-y": (
+ property: overflow-y,
+ values: auto hidden visible scroll,
+ ),
+ // scss-docs-end utils-overflow
+ // scss-docs-start utils-display
+ "display": (
+ responsive: true,
+ print: true,
+ property: display,
+ class: d,
+ values: inline inline-block block grid inline-grid table table-row table-cell flex inline-flex none
+ ),
+ // scss-docs-end utils-display
+ // scss-docs-start utils-shadow
+ "shadow": (
+ property: box-shadow,
+ class: shadow,
+ values: (
+ null: var(--#{$prefix}box-shadow),
+ sm: var(--#{$prefix}box-shadow-sm),
+ lg: var(--#{$prefix}box-shadow-lg),
+ none: none,
+ )
+ ),
+ // scss-docs-end utils-shadow
+ // scss-docs-start utils-focus-ring
+ "focus-ring": (
+ css-var: true,
+ css-variable-name: focus-ring-color,
+ class: focus-ring,
+ values: map-loop($theme-colors-rgb, rgba-css-var, "$key", "focus-ring")
+ ),
+ // scss-docs-end utils-focus-ring
+ // scss-docs-start utils-position
+ "position": (
+ property: position,
+ values: static relative absolute fixed sticky
+ ),
+ "top": (
+ property: top,
+ values: $position-values
+ ),
+ "bottom": (
+ property: bottom,
+ values: $position-values
+ ),
+ "start": (
+ property: left,
+ class: start,
+ values: $position-values
+ ),
+ "end": (
+ property: right,
+ class: end,
+ values: $position-values
+ ),
+ "translate-middle": (
+ property: transform,
+ class: translate-middle,
+ values: (
+ null: translate(-50%, -50%),
+ x: translateX(-50%),
+ y: translateY(-50%),
+ )
+ ),
+ // scss-docs-end utils-position
+ // scss-docs-start utils-borders
+ "border": (
+ property: border,
+ values: (
+ null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
+ 0: 0,
+ )
+ ),
+ "border-top": (
+ property: border-top,
+ values: (
+ null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
+ 0: 0,
+ )
+ ),
+ "border-end": (
+ property: border-right,
+ class: border-end,
+ values: (
+ null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
+ 0: 0,
+ )
+ ),
+ "border-bottom": (
+ property: border-bottom,
+ values: (
+ null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
+ 0: 0,
+ )
+ ),
+ "border-start": (
+ property: border-left,
+ class: border-start,
+ values: (
+ null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
+ 0: 0,
+ )
+ ),
+ "border-color": (
+ property: border-color,
+ class: border,
+ local-vars: (
+ "border-opacity": 1
+ ),
+ values: $utilities-border-colors
+ ),
+ "subtle-border-color": (
+ property: border-color,
+ class: border,
+ values: $utilities-border-subtle
+ ),
+ "border-width": (
+ property: border-width,
+ class: border,
+ values: $border-widths
+ ),
+ "border-opacity": (
+ css-var: true,
+ class: border-opacity,
+ values: (
+ 10: .1,
+ 25: .25,
+ 50: .5,
+ 75: .75,
+ 100: 1
+ )
+ ),
+ // scss-docs-end utils-borders
+ // Sizing utilities
+ // scss-docs-start utils-sizing
+ "width": (
+ property: width,
+ class: w,
+ values: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%,
+ auto: auto
+ )
+ ),
+ "max-width": (
+ property: max-width,
+ class: mw,
+ values: (100: 100%)
+ ),
+ "viewport-width": (
+ property: width,
+ class: vw,
+ values: (100: 100vw)
+ ),
+ "min-viewport-width": (
+ property: min-width,
+ class: min-vw,
+ values: (100: 100vw)
+ ),
+ "height": (
+ property: height,
+ class: h,
+ values: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%,
+ auto: auto
+ )
+ ),
+ "max-height": (
+ property: max-height,
+ class: mh,
+ values: (100: 100%)
+ ),
+ "viewport-height": (
+ property: height,
+ class: vh,
+ values: (100: 100vh)
+ ),
+ "min-viewport-height": (
+ property: min-height,
+ class: min-vh,
+ values: (100: 100vh)
+ ),
+ // scss-docs-end utils-sizing
+ // Flex utilities
+ // scss-docs-start utils-flex
+ "flex": (
+ responsive: true,
+ property: flex,
+ values: (fill: 1 1 auto)
+ ),
+ "flex-direction": (
+ responsive: true,
+ property: flex-direction,
+ class: flex,
+ values: row column row-reverse column-reverse
+ ),
+ "flex-grow": (
+ responsive: true,
+ property: flex-grow,
+ class: flex,
+ values: (
+ grow-0: 0,
+ grow-1: 1,
+ )
+ ),
+ "flex-shrink": (
+ responsive: true,
+ property: flex-shrink,
+ class: flex,
+ values: (
+ shrink-0: 0,
+ shrink-1: 1,
+ )
+ ),
+ "flex-wrap": (
+ responsive: true,
+ property: flex-wrap,
+ class: flex,
+ values: wrap nowrap wrap-reverse
+ ),
+ "justify-content": (
+ responsive: true,
+ property: justify-content,
+ values: (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ between: space-between,
+ around: space-around,
+ evenly: space-evenly,
+ )
+ ),
+ "align-items": (
+ responsive: true,
+ property: align-items,
+ values: (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ baseline: baseline,
+ stretch: stretch,
+ )
+ ),
+ "align-content": (
+ responsive: true,
+ property: align-content,
+ values: (
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ between: space-between,
+ around: space-around,
+ stretch: stretch,
+ )
+ ),
+ "align-self": (
+ responsive: true,
+ property: align-self,
+ values: (
+ auto: auto,
+ start: flex-start,
+ end: flex-end,
+ center: center,
+ baseline: baseline,
+ stretch: stretch,
+ )
+ ),
+ "order": (
+ responsive: true,
+ property: order,
+ values: (
+ first: -1,
+ 0: 0,
+ 1: 1,
+ 2: 2,
+ 3: 3,
+ 4: 4,
+ 5: 5,
+ last: 6,
+ ),
+ ),
+ // scss-docs-end utils-flex
+ // Margin utilities
+ // scss-docs-start utils-spacing
+ "margin": (
+ responsive: true,
+ property: margin,
+ class: m,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-x": (
+ responsive: true,
+ property: margin-right margin-left,
+ class: mx,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-y": (
+ responsive: true,
+ property: margin-top margin-bottom,
+ class: my,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-top": (
+ responsive: true,
+ property: margin-top,
+ class: mt,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-end": (
+ responsive: true,
+ property: margin-right,
+ class: me,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-bottom": (
+ responsive: true,
+ property: margin-bottom,
+ class: mb,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ "margin-start": (
+ responsive: true,
+ property: margin-left,
+ class: ms,
+ values: map-merge($spacers, (auto: auto))
+ ),
+ // Negative margin utilities
+ "negative-margin": (
+ responsive: true,
+ property: margin,
+ class: m,
+ values: $negative-spacers
+ ),
+ "negative-margin-x": (
+ responsive: true,
+ property: margin-right margin-left,
+ class: mx,
+ values: $negative-spacers
+ ),
+ "negative-margin-y": (
+ responsive: true,
+ property: margin-top margin-bottom,
+ class: my,
+ values: $negative-spacers
+ ),
+ "negative-margin-top": (
+ responsive: true,
+ property: margin-top,
+ class: mt,
+ values: $negative-spacers
+ ),
+ "negative-margin-end": (
+ responsive: true,
+ property: margin-right,
+ class: me,
+ values: $negative-spacers
+ ),
+ "negative-margin-bottom": (
+ responsive: true,
+ property: margin-bottom,
+ class: mb,
+ values: $negative-spacers
+ ),
+ "negative-margin-start": (
+ responsive: true,
+ property: margin-left,
+ class: ms,
+ values: $negative-spacers
+ ),
+ // Padding utilities
+ "padding": (
+ responsive: true,
+ property: padding,
+ class: p,
+ values: $spacers
+ ),
+ "padding-x": (
+ responsive: true,
+ property: padding-right padding-left,
+ class: px,
+ values: $spacers
+ ),
+ "padding-y": (
+ responsive: true,
+ property: padding-top padding-bottom,
+ class: py,
+ values: $spacers
+ ),
+ "padding-top": (
+ responsive: true,
+ property: padding-top,
+ class: pt,
+ values: $spacers
+ ),
+ "padding-end": (
+ responsive: true,
+ property: padding-right,
+ class: pe,
+ values: $spacers
+ ),
+ "padding-bottom": (
+ responsive: true,
+ property: padding-bottom,
+ class: pb,
+ values: $spacers
+ ),
+ "padding-start": (
+ responsive: true,
+ property: padding-left,
+ class: ps,
+ values: $spacers
+ ),
+ // Gap utility
+ "gap": (
+ responsive: true,
+ property: gap,
+ class: gap,
+ values: $spacers
+ ),
+ "row-gap": (
+ responsive: true,
+ property: row-gap,
+ class: row-gap,
+ values: $spacers
+ ),
+ "column-gap": (
+ responsive: true,
+ property: column-gap,
+ class: column-gap,
+ values: $spacers
+ ),
+ // scss-docs-end utils-spacing
+ // Text
+ // scss-docs-start utils-text
+ "font-family": (
+ property: font-family,
+ class: font,
+ values: (monospace: var(--#{$prefix}font-monospace))
+ ),
+ "font-size": (
+ rfs: true,
+ property: font-size,
+ class: fs,
+ values: $font-sizes
+ ),
+ "font-style": (
+ property: font-style,
+ class: fst,
+ values: italic normal
+ ),
+ "font-weight": (
+ property: font-weight,
+ class: fw,
+ values: (
+ lighter: $font-weight-lighter,
+ light: $font-weight-light,
+ normal: $font-weight-normal,
+ medium: $font-weight-medium,
+ semibold: $font-weight-semibold,
+ bold: $font-weight-bold,
+ bolder: $font-weight-bolder
+ )
+ ),
+ "line-height": (
+ property: line-height,
+ class: lh,
+ values: (
+ 1: 1,
+ sm: $line-height-sm,
+ base: $line-height-base,
+ lg: $line-height-lg,
+ )
+ ),
+ "text-align": (
+ responsive: true,
+ property: text-align,
+ class: text,
+ values: (
+ start: left,
+ end: right,
+ center: center,
+ )
+ ),
+ "text-decoration": (
+ property: text-decoration,
+ values: none underline line-through
+ ),
+ "text-transform": (
+ property: text-transform,
+ class: text,
+ values: lowercase uppercase capitalize
+ ),
+ "white-space": (
+ property: white-space,
+ class: text,
+ values: (
+ wrap: normal,
+ nowrap: nowrap,
+ )
+ ),
+ "word-wrap": (
+ property: word-wrap word-break,
+ class: text,
+ values: (break: break-word),
+ rtl: false
+ ),
+ // scss-docs-end utils-text
+ // scss-docs-start utils-color
+ "color": (
+ property: color,
+ class: text,
+ local-vars: (
+ "text-opacity": 1
+ ),
+ values: map-merge(
+ $utilities-text-colors,
+ (
+ "muted": var(--#{$prefix}secondary-color), // deprecated
+ "black-50": rgba($black, .5), // deprecated
+ "white-50": rgba($white, .5), // deprecated
+ "body-secondary": var(--#{$prefix}secondary-color),
+ "body-tertiary": var(--#{$prefix}tertiary-color),
+ "body-emphasis": var(--#{$prefix}emphasis-color),
+ "reset": inherit,
+ )
+ )
+ ),
+ "text-opacity": (
+ css-var: true,
+ class: text-opacity,
+ values: (
+ 25: .25,
+ 50: .5,
+ 75: .75,
+ 100: 1
+ )
+ ),
+ "text-color": (
+ property: color,
+ class: text,
+ values: $utilities-text-emphasis-colors
+ ),
+ // scss-docs-end utils-color
+ // scss-docs-start utils-links
+ "link-opacity": (
+ css-var: true,
+ class: link-opacity,
+ state: hover,
+ values: (
+ 10: .1,
+ 25: .25,
+ 50: .5,
+ 75: .75,
+ 100: 1
+ )
+ ),
+ "link-offset": (
+ property: text-underline-offset,
+ class: link-offset,
+ state: hover,
+ values: (
+ 1: .125em,
+ 2: .25em,
+ 3: .375em,
+ )
+ ),
+ "link-underline": (
+ property: text-decoration-color,
+ class: link-underline,
+ local-vars: (
+ "link-underline-opacity": 1
+ ),
+ values: map-merge(
+ $utilities-links-underline,
+ (
+ null: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-underline-opacity, 1)),
+ )
+ )
+ ),
+ "link-underline-opacity": (
+ css-var: true,
+ class: link-underline-opacity,
+ state: hover,
+ values: (
+ 0: 0,
+ 10: .1,
+ 25: .25,
+ 50: .5,
+ 75: .75,
+ 100: 1
+ ),
+ ),
+ // scss-docs-end utils-links
+ // scss-docs-start utils-bg-color
+ "background-color": (
+ property: background-color,
+ class: bg,
+ local-vars: (
+ "bg-opacity": 1
+ ),
+ values: map-merge(
+ $utilities-bg-colors,
+ (
+ "transparent": transparent,
+ "body-secondary": rgba(var(--#{$prefix}secondary-bg-rgb), var(--#{$prefix}bg-opacity)),
+ "body-tertiary": rgba(var(--#{$prefix}tertiary-bg-rgb), var(--#{$prefix}bg-opacity)),
+ )
+ )
+ ),
+ "bg-opacity": (
+ css-var: true,
+ class: bg-opacity,
+ values: (
+ 10: .1,
+ 25: .25,
+ 50: .5,
+ 75: .75,
+ 100: 1
+ )
+ ),
+ "subtle-background-color": (
+ property: background-color,
+ class: bg,
+ values: $utilities-bg-subtle
+ ),
+ // scss-docs-end utils-bg-color
+ "gradient": (
+ property: background-image,
+ class: bg,
+ values: (gradient: var(--#{$prefix}gradient))
+ ),
+ // scss-docs-start utils-interaction
+ "user-select": (
+ property: user-select,
+ values: all auto none
+ ),
+ "pointer-events": (
+ property: pointer-events,
+ class: pe,
+ values: none auto,
+ ),
+ // scss-docs-end utils-interaction
+ // scss-docs-start utils-border-radius
+ "rounded": (
+ property: border-radius,
+ class: rounded,
+ values: (
+ null: var(--#{$prefix}border-radius),
+ 0: 0,
+ 1: var(--#{$prefix}border-radius-sm),
+ 2: var(--#{$prefix}border-radius),
+ 3: var(--#{$prefix}border-radius-lg),
+ 4: var(--#{$prefix}border-radius-xl),
+ 5: var(--#{$prefix}border-radius-xxl),
+ circle: 50%,
+ pill: var(--#{$prefix}border-radius-pill)
+ )
+ ),
+ "rounded-top": (
+ property: border-top-left-radius border-top-right-radius,
+ class: rounded-top,
+ values: (
+ null: var(--#{$prefix}border-radius),
+ 0: 0,
+ 1: var(--#{$prefix}border-radius-sm),
+ 2: var(--#{$prefix}border-radius),
+ 3: var(--#{$prefix}border-radius-lg),
+ 4: var(--#{$prefix}border-radius-xl),
+ 5: var(--#{$prefix}border-radius-xxl),
+ circle: 50%,
+ pill: var(--#{$prefix}border-radius-pill)
+ )
+ ),
+ "rounded-end": (
+ property: border-top-right-radius border-bottom-right-radius,
+ class: rounded-end,
+ values: (
+ null: var(--#{$prefix}border-radius),
+ 0: 0,
+ 1: var(--#{$prefix}border-radius-sm),
+ 2: var(--#{$prefix}border-radius),
+ 3: var(--#{$prefix}border-radius-lg),
+ 4: var(--#{$prefix}border-radius-xl),
+ 5: var(--#{$prefix}border-radius-xxl),
+ circle: 50%,
+ pill: var(--#{$prefix}border-radius-pill)
+ )
+ ),
+ "rounded-bottom": (
+ property: border-bottom-right-radius border-bottom-left-radius,
+ class: rounded-bottom,
+ values: (
+ null: var(--#{$prefix}border-radius),
+ 0: 0,
+ 1: var(--#{$prefix}border-radius-sm),
+ 2: var(--#{$prefix}border-radius),
+ 3: var(--#{$prefix}border-radius-lg),
+ 4: var(--#{$prefix}border-radius-xl),
+ 5: var(--#{$prefix}border-radius-xxl),
+ circle: 50%,
+ pill: var(--#{$prefix}border-radius-pill)
+ )
+ ),
+ "rounded-start": (
+ property: border-bottom-left-radius border-top-left-radius,
+ class: rounded-start,
+ values: (
+ null: var(--#{$prefix}border-radius),
+ 0: 0,
+ 1: var(--#{$prefix}border-radius-sm),
+ 2: var(--#{$prefix}border-radius),
+ 3: var(--#{$prefix}border-radius-lg),
+ 4: var(--#{$prefix}border-radius-xl),
+ 5: var(--#{$prefix}border-radius-xxl),
+ circle: 50%,
+ pill: var(--#{$prefix}border-radius-pill)
+ )
+ ),
+ // scss-docs-end utils-border-radius
+ // scss-docs-start utils-visibility
+ "visibility": (
+ property: visibility,
+ class: null,
+ values: (
+ visible: visible,
+ invisible: hidden,
+ )
+ ),
+ // scss-docs-end utils-visibility
+ // scss-docs-start utils-zindex
+ "z-index": (
+ property: z-index,
+ class: z,
+ values: $zindex-levels,
+ )
+ // scss-docs-end utils-zindex
+ ),
+ $utilities
+);
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_variables-dark.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_variables-dark.scss
new file mode 100644
index 00000000..6422b387
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_variables-dark.scss
@@ -0,0 +1,87 @@
+// Dark color mode variables
+//
+// Custom variables for the `[data-bs-theme="dark"]` theme. Use this as a starting point for your own custom color modes by creating a new theme-specific file like `_variables-dark.scss` and adding the variables you need.
+
+//
+// Global colors
+//
+
+// scss-docs-start sass-dark-mode-vars
+// scss-docs-start theme-text-dark-variables
+$primary-text-emphasis-dark: tint-color($primary, 40%) !default;
+$secondary-text-emphasis-dark: tint-color($secondary, 40%) !default;
+$success-text-emphasis-dark: tint-color($success, 40%) !default;
+$info-text-emphasis-dark: tint-color($info, 40%) !default;
+$warning-text-emphasis-dark: tint-color($warning, 40%) !default;
+$danger-text-emphasis-dark: tint-color($danger, 40%) !default;
+$light-text-emphasis-dark: $gray-100 !default;
+$dark-text-emphasis-dark: $gray-300 !default;
+// scss-docs-end theme-text-dark-variables
+
+// scss-docs-start theme-bg-subtle-dark-variables
+$primary-bg-subtle-dark: shade-color($primary, 80%) !default;
+$secondary-bg-subtle-dark: shade-color($secondary, 80%) !default;
+$success-bg-subtle-dark: shade-color($success, 80%) !default;
+$info-bg-subtle-dark: shade-color($info, 80%) !default;
+$warning-bg-subtle-dark: shade-color($warning, 80%) !default;
+$danger-bg-subtle-dark: shade-color($danger, 80%) !default;
+$light-bg-subtle-dark: $gray-800 !default;
+$dark-bg-subtle-dark: mix($gray-800, $black) !default;
+// scss-docs-end theme-bg-subtle-dark-variables
+
+// scss-docs-start theme-border-subtle-dark-variables
+$primary-border-subtle-dark: shade-color($primary, 40%) !default;
+$secondary-border-subtle-dark: shade-color($secondary, 40%) !default;
+$success-border-subtle-dark: shade-color($success, 40%) !default;
+$info-border-subtle-dark: shade-color($info, 40%) !default;
+$warning-border-subtle-dark: shade-color($warning, 40%) !default;
+$danger-border-subtle-dark: shade-color($danger, 40%) !default;
+$light-border-subtle-dark: $gray-700 !default;
+$dark-border-subtle-dark: $gray-800 !default;
+// scss-docs-end theme-border-subtle-dark-variables
+
+$body-color-dark: $gray-300 !default;
+$body-bg-dark: $gray-900 !default;
+$body-secondary-color-dark: rgba($body-color-dark, .75) !default;
+$body-secondary-bg-dark: $gray-800 !default;
+$body-tertiary-color-dark: rgba($body-color-dark, .5) !default;
+$body-tertiary-bg-dark: mix($gray-800, $gray-900, 50%) !default;
+$body-emphasis-color-dark: $white !default;
+$border-color-dark: $gray-700 !default;
+$border-color-translucent-dark: rgba($white, .15) !default;
+$headings-color-dark: inherit !default;
+$link-color-dark: tint-color($primary, 40%) !default;
+$link-hover-color-dark: shift-color($link-color-dark, -$link-shade-percentage) !default;
+$code-color-dark: tint-color($code-color, 40%) !default;
+$mark-color-dark: $body-color-dark !default;
+$mark-bg-dark: $yellow-800 !default;
+
+
+//
+// Forms
+//
+
+$form-select-indicator-color-dark: $body-color-dark !default;
+$form-select-indicator-dark: url("data:image/svg+xml,
") !default;
+
+$form-switch-color-dark: rgba($white, .25) !default;
+$form-switch-bg-image-dark: url("data:image/svg+xml,
") !default;
+
+// scss-docs-start form-validation-colors-dark
+$form-valid-color-dark: $green-300 !default;
+$form-valid-border-color-dark: $green-300 !default;
+$form-invalid-color-dark: $red-300 !default;
+$form-invalid-border-color-dark: $red-300 !default;
+// scss-docs-end form-validation-colors-dark
+
+
+//
+// Accordion
+//
+
+$accordion-icon-color-dark: $primary-text-emphasis-dark !default;
+$accordion-icon-active-color-dark: $primary-text-emphasis-dark !default;
+
+$accordion-button-icon-dark: url("data:image/svg+xml,
") !default;
+$accordion-button-active-icon-dark: url("data:image/svg+xml,
") !default;
+// scss-docs-end sass-dark-mode-vars
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_variables.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_variables.scss
new file mode 100644
index 00000000..5429c017
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/_variables.scss
@@ -0,0 +1,1747 @@
+// Variables
+//
+// Variables should follow the `$component-state-property-size` formula for
+// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
+
+// Color system
+
+// scss-docs-start gray-color-variables
+$white: #fff !default;
+$gray-100: #f8f9fa !default;
+$gray-200: #e9ecef !default;
+$gray-300: #dee2e6 !default;
+$gray-400: #ced4da !default;
+$gray-500: #adb5bd !default;
+$gray-600: #6c757d !default;
+$gray-700: #495057 !default;
+$gray-800: #343a40 !default;
+$gray-900: #212529 !default;
+$black: #000 !default;
+// scss-docs-end gray-color-variables
+
+// fusv-disable
+// scss-docs-start gray-colors-map
+$grays: (
+ "100": $gray-100,
+ "200": $gray-200,
+ "300": $gray-300,
+ "400": $gray-400,
+ "500": $gray-500,
+ "600": $gray-600,
+ "700": $gray-700,
+ "800": $gray-800,
+ "900": $gray-900
+) !default;
+// scss-docs-end gray-colors-map
+// fusv-enable
+
+// scss-docs-start color-variables
+$blue: #0d6efd !default;
+$indigo: #6610f2 !default;
+$purple: #6f42c1 !default;
+$pink: #d63384 !default;
+$red: #dc3545 !default;
+$orange: #fd7e14 !default;
+$yellow: #ffc107 !default;
+$green: #198754 !default;
+$teal: #20c997 !default;
+$cyan: #0dcaf0 !default;
+// scss-docs-end color-variables
+
+// scss-docs-start colors-map
+$colors: (
+ "blue": $blue,
+ "indigo": $indigo,
+ "purple": $purple,
+ "pink": $pink,
+ "red": $red,
+ "orange": $orange,
+ "yellow": $yellow,
+ "green": $green,
+ "teal": $teal,
+ "cyan": $cyan,
+ "black": $black,
+ "white": $white,
+ "gray": $gray-600,
+ "gray-dark": $gray-800
+) !default;
+// scss-docs-end colors-map
+
+// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.
+// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
+$min-contrast-ratio: 4.5 !default;
+
+// Customize the light and dark text colors for use in our color contrast function.
+$color-contrast-dark: $black !default;
+$color-contrast-light: $white !default;
+
+// fusv-disable
+$blue-100: tint-color($blue, 80%) !default;
+$blue-200: tint-color($blue, 60%) !default;
+$blue-300: tint-color($blue, 40%) !default;
+$blue-400: tint-color($blue, 20%) !default;
+$blue-500: $blue !default;
+$blue-600: shade-color($blue, 20%) !default;
+$blue-700: shade-color($blue, 40%) !default;
+$blue-800: shade-color($blue, 60%) !default;
+$blue-900: shade-color($blue, 80%) !default;
+
+$indigo-100: tint-color($indigo, 80%) !default;
+$indigo-200: tint-color($indigo, 60%) !default;
+$indigo-300: tint-color($indigo, 40%) !default;
+$indigo-400: tint-color($indigo, 20%) !default;
+$indigo-500: $indigo !default;
+$indigo-600: shade-color($indigo, 20%) !default;
+$indigo-700: shade-color($indigo, 40%) !default;
+$indigo-800: shade-color($indigo, 60%) !default;
+$indigo-900: shade-color($indigo, 80%) !default;
+
+$purple-100: tint-color($purple, 80%) !default;
+$purple-200: tint-color($purple, 60%) !default;
+$purple-300: tint-color($purple, 40%) !default;
+$purple-400: tint-color($purple, 20%) !default;
+$purple-500: $purple !default;
+$purple-600: shade-color($purple, 20%) !default;
+$purple-700: shade-color($purple, 40%) !default;
+$purple-800: shade-color($purple, 60%) !default;
+$purple-900: shade-color($purple, 80%) !default;
+
+$pink-100: tint-color($pink, 80%) !default;
+$pink-200: tint-color($pink, 60%) !default;
+$pink-300: tint-color($pink, 40%) !default;
+$pink-400: tint-color($pink, 20%) !default;
+$pink-500: $pink !default;
+$pink-600: shade-color($pink, 20%) !default;
+$pink-700: shade-color($pink, 40%) !default;
+$pink-800: shade-color($pink, 60%) !default;
+$pink-900: shade-color($pink, 80%) !default;
+
+$red-100: tint-color($red, 80%) !default;
+$red-200: tint-color($red, 60%) !default;
+$red-300: tint-color($red, 40%) !default;
+$red-400: tint-color($red, 20%) !default;
+$red-500: $red !default;
+$red-600: shade-color($red, 20%) !default;
+$red-700: shade-color($red, 40%) !default;
+$red-800: shade-color($red, 60%) !default;
+$red-900: shade-color($red, 80%) !default;
+
+$orange-100: tint-color($orange, 80%) !default;
+$orange-200: tint-color($orange, 60%) !default;
+$orange-300: tint-color($orange, 40%) !default;
+$orange-400: tint-color($orange, 20%) !default;
+$orange-500: $orange !default;
+$orange-600: shade-color($orange, 20%) !default;
+$orange-700: shade-color($orange, 40%) !default;
+$orange-800: shade-color($orange, 60%) !default;
+$orange-900: shade-color($orange, 80%) !default;
+
+$yellow-100: tint-color($yellow, 80%) !default;
+$yellow-200: tint-color($yellow, 60%) !default;
+$yellow-300: tint-color($yellow, 40%) !default;
+$yellow-400: tint-color($yellow, 20%) !default;
+$yellow-500: $yellow !default;
+$yellow-600: shade-color($yellow, 20%) !default;
+$yellow-700: shade-color($yellow, 40%) !default;
+$yellow-800: shade-color($yellow, 60%) !default;
+$yellow-900: shade-color($yellow, 80%) !default;
+
+$green-100: tint-color($green, 80%) !default;
+$green-200: tint-color($green, 60%) !default;
+$green-300: tint-color($green, 40%) !default;
+$green-400: tint-color($green, 20%) !default;
+$green-500: $green !default;
+$green-600: shade-color($green, 20%) !default;
+$green-700: shade-color($green, 40%) !default;
+$green-800: shade-color($green, 60%) !default;
+$green-900: shade-color($green, 80%) !default;
+
+$teal-100: tint-color($teal, 80%) !default;
+$teal-200: tint-color($teal, 60%) !default;
+$teal-300: tint-color($teal, 40%) !default;
+$teal-400: tint-color($teal, 20%) !default;
+$teal-500: $teal !default;
+$teal-600: shade-color($teal, 20%) !default;
+$teal-700: shade-color($teal, 40%) !default;
+$teal-800: shade-color($teal, 60%) !default;
+$teal-900: shade-color($teal, 80%) !default;
+
+$cyan-100: tint-color($cyan, 80%) !default;
+$cyan-200: tint-color($cyan, 60%) !default;
+$cyan-300: tint-color($cyan, 40%) !default;
+$cyan-400: tint-color($cyan, 20%) !default;
+$cyan-500: $cyan !default;
+$cyan-600: shade-color($cyan, 20%) !default;
+$cyan-700: shade-color($cyan, 40%) !default;
+$cyan-800: shade-color($cyan, 60%) !default;
+$cyan-900: shade-color($cyan, 80%) !default;
+
+$blues: (
+ "blue-100": $blue-100,
+ "blue-200": $blue-200,
+ "blue-300": $blue-300,
+ "blue-400": $blue-400,
+ "blue-500": $blue-500,
+ "blue-600": $blue-600,
+ "blue-700": $blue-700,
+ "blue-800": $blue-800,
+ "blue-900": $blue-900
+) !default;
+
+$indigos: (
+ "indigo-100": $indigo-100,
+ "indigo-200": $indigo-200,
+ "indigo-300": $indigo-300,
+ "indigo-400": $indigo-400,
+ "indigo-500": $indigo-500,
+ "indigo-600": $indigo-600,
+ "indigo-700": $indigo-700,
+ "indigo-800": $indigo-800,
+ "indigo-900": $indigo-900
+) !default;
+
+$purples: (
+ "purple-100": $purple-100,
+ "purple-200": $purple-200,
+ "purple-300": $purple-300,
+ "purple-400": $purple-400,
+ "purple-500": $purple-500,
+ "purple-600": $purple-600,
+ "purple-700": $purple-700,
+ "purple-800": $purple-800,
+ "purple-900": $purple-900
+) !default;
+
+$pinks: (
+ "pink-100": $pink-100,
+ "pink-200": $pink-200,
+ "pink-300": $pink-300,
+ "pink-400": $pink-400,
+ "pink-500": $pink-500,
+ "pink-600": $pink-600,
+ "pink-700": $pink-700,
+ "pink-800": $pink-800,
+ "pink-900": $pink-900
+) !default;
+
+$reds: (
+ "red-100": $red-100,
+ "red-200": $red-200,
+ "red-300": $red-300,
+ "red-400": $red-400,
+ "red-500": $red-500,
+ "red-600": $red-600,
+ "red-700": $red-700,
+ "red-800": $red-800,
+ "red-900": $red-900
+) !default;
+
+$oranges: (
+ "orange-100": $orange-100,
+ "orange-200": $orange-200,
+ "orange-300": $orange-300,
+ "orange-400": $orange-400,
+ "orange-500": $orange-500,
+ "orange-600": $orange-600,
+ "orange-700": $orange-700,
+ "orange-800": $orange-800,
+ "orange-900": $orange-900
+) !default;
+
+$yellows: (
+ "yellow-100": $yellow-100,
+ "yellow-200": $yellow-200,
+ "yellow-300": $yellow-300,
+ "yellow-400": $yellow-400,
+ "yellow-500": $yellow-500,
+ "yellow-600": $yellow-600,
+ "yellow-700": $yellow-700,
+ "yellow-800": $yellow-800,
+ "yellow-900": $yellow-900
+) !default;
+
+$greens: (
+ "green-100": $green-100,
+ "green-200": $green-200,
+ "green-300": $green-300,
+ "green-400": $green-400,
+ "green-500": $green-500,
+ "green-600": $green-600,
+ "green-700": $green-700,
+ "green-800": $green-800,
+ "green-900": $green-900
+) !default;
+
+$teals: (
+ "teal-100": $teal-100,
+ "teal-200": $teal-200,
+ "teal-300": $teal-300,
+ "teal-400": $teal-400,
+ "teal-500": $teal-500,
+ "teal-600": $teal-600,
+ "teal-700": $teal-700,
+ "teal-800": $teal-800,
+ "teal-900": $teal-900
+) !default;
+
+$cyans: (
+ "cyan-100": $cyan-100,
+ "cyan-200": $cyan-200,
+ "cyan-300": $cyan-300,
+ "cyan-400": $cyan-400,
+ "cyan-500": $cyan-500,
+ "cyan-600": $cyan-600,
+ "cyan-700": $cyan-700,
+ "cyan-800": $cyan-800,
+ "cyan-900": $cyan-900
+) !default;
+// fusv-enable
+
+// scss-docs-start theme-color-variables
+$primary: $blue !default;
+$secondary: $gray-600 !default;
+$success: $green !default;
+$info: $cyan !default;
+$warning: $yellow !default;
+$danger: $red !default;
+$light: $gray-100 !default;
+$dark: $gray-900 !default;
+// scss-docs-end theme-color-variables
+
+// scss-docs-start theme-colors-map
+$theme-colors: (
+ "primary": $primary,
+ "secondary": $secondary,
+ "success": $success,
+ "info": $info,
+ "warning": $warning,
+ "danger": $danger,
+ "light": $light,
+ "dark": $dark
+) !default;
+// scss-docs-end theme-colors-map
+
+// scss-docs-start theme-text-variables
+$primary-text-emphasis: shade-color($primary, 60%) !default;
+$secondary-text-emphasis: shade-color($secondary, 60%) !default;
+$success-text-emphasis: shade-color($success, 60%) !default;
+$info-text-emphasis: shade-color($info, 60%) !default;
+$warning-text-emphasis: shade-color($warning, 60%) !default;
+$danger-text-emphasis: shade-color($danger, 60%) !default;
+$light-text-emphasis: $gray-700 !default;
+$dark-text-emphasis: $gray-700 !default;
+// scss-docs-end theme-text-variables
+
+// scss-docs-start theme-bg-subtle-variables
+$primary-bg-subtle: tint-color($primary, 80%) !default;
+$secondary-bg-subtle: tint-color($secondary, 80%) !default;
+$success-bg-subtle: tint-color($success, 80%) !default;
+$info-bg-subtle: tint-color($info, 80%) !default;
+$warning-bg-subtle: tint-color($warning, 80%) !default;
+$danger-bg-subtle: tint-color($danger, 80%) !default;
+$light-bg-subtle: mix($gray-100, $white) !default;
+$dark-bg-subtle: $gray-400 !default;
+// scss-docs-end theme-bg-subtle-variables
+
+// scss-docs-start theme-border-subtle-variables
+$primary-border-subtle: tint-color($primary, 60%) !default;
+$secondary-border-subtle: tint-color($secondary, 60%) !default;
+$success-border-subtle: tint-color($success, 60%) !default;
+$info-border-subtle: tint-color($info, 60%) !default;
+$warning-border-subtle: tint-color($warning, 60%) !default;
+$danger-border-subtle: tint-color($danger, 60%) !default;
+$light-border-subtle: $gray-200 !default;
+$dark-border-subtle: $gray-500 !default;
+// scss-docs-end theme-border-subtle-variables
+
+// Characters which are escaped by the escape-svg function
+$escaped-characters: (
+ ("<", "%3c"),
+ (">", "%3e"),
+ ("#", "%23"),
+ ("(", "%28"),
+ (")", "%29"),
+) !default;
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+$enable-caret: true !default;
+$enable-rounded: true !default;
+$enable-shadows: false !default;
+$enable-gradients: false !default;
+$enable-transitions: true !default;
+$enable-reduced-motion: true !default;
+$enable-smooth-scroll: true !default;
+$enable-grid-classes: true !default;
+$enable-container-classes: true !default;
+$enable-cssgrid: false !default;
+$enable-button-pointers: true !default;
+$enable-rfs: true !default;
+$enable-validation-icons: true !default;
+$enable-negative-margins: false !default;
+$enable-deprecation-messages: true !default;
+$enable-important-utilities: true !default;
+
+$enable-dark-mode: true !default;
+$color-mode-type: data !default; // `data` or `media-query`
+
+// Prefix for :root CSS variables
+
+$variable-prefix: bs- !default; // Deprecated in v5.2.0 for the shorter `$prefix`
+$prefix: $variable-prefix !default;
+
+// Gradient
+//
+// The gradient which is added to components if `$enable-gradients` is `true`
+// This gradient is also added to elements with `.bg-gradient`
+// scss-docs-start variable-gradient
+$gradient: linear-gradient(180deg, rgba($white, .15), rgba($white, 0)) !default;
+// scss-docs-end variable-gradient
+
+// Spacing
+//
+// Control the default styling of most Bootstrap elements by modifying these
+// variables. Mostly focused on spacing.
+// You can add more entries to the $spacers map, should you need more variation.
+
+// scss-docs-start spacer-variables-maps
+$spacer: 1rem !default;
+$spacers: (
+ 0: 0,
+ 1: $spacer * .25,
+ 2: $spacer * .5,
+ 3: $spacer,
+ 4: $spacer * 1.5,
+ 5: $spacer * 3,
+) !default;
+// scss-docs-end spacer-variables-maps
+
+// Position
+//
+// Define the edge positioning anchors of the position utilities.
+
+// scss-docs-start position-map
+$position-values: (
+ 0: 0,
+ 50: 50%,
+ 100: 100%
+) !default;
+// scss-docs-end position-map
+
+// Body
+//
+// Settings for the `` element.
+
+$body-text-align: null !default;
+$body-color: $gray-900 !default;
+$body-bg: $white !default;
+
+$body-secondary-color: rgba($body-color, .75) !default;
+$body-secondary-bg: $gray-200 !default;
+
+$body-tertiary-color: rgba($body-color, .5) !default;
+$body-tertiary-bg: $gray-100 !default;
+
+$body-emphasis-color: $black !default;
+
+// Links
+//
+// Style anchor elements.
+
+$link-color: $primary !default;
+$link-decoration: underline !default;
+$link-shade-percentage: 20% !default;
+$link-hover-color: shift-color($link-color, $link-shade-percentage) !default;
+$link-hover-decoration: null !default;
+
+$stretched-link-pseudo-element: after !default;
+$stretched-link-z-index: 1 !default;
+
+// Icon links
+// scss-docs-start icon-link-variables
+$icon-link-gap: .375rem !default;
+$icon-link-underline-offset: .25em !default;
+$icon-link-icon-size: 1em !default;
+$icon-link-icon-transition: .2s ease-in-out transform !default;
+$icon-link-icon-transform: translate3d(.25em, 0, 0) !default;
+// scss-docs-end icon-link-variables
+
+// Paragraphs
+//
+// Style p element.
+
+$paragraph-margin-bottom: 1rem !default;
+
+
+// Grid breakpoints
+//
+// Define the minimum dimensions at which your layout will change,
+// adapting to different screen sizes, for use in media queries.
+
+// scss-docs-start grid-breakpoints
+$grid-breakpoints: (
+ xs: 0,
+ sm: 576px,
+ md: 768px,
+ lg: 992px,
+ xl: 1200px,
+ xxl: 1400px
+) !default;
+// scss-docs-end grid-breakpoints
+
+@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
+@include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints");
+
+
+// Grid containers
+//
+// Define the maximum width of `.container` for different screen sizes.
+
+// scss-docs-start container-max-widths
+$container-max-widths: (
+ sm: 540px,
+ md: 720px,
+ lg: 960px,
+ xl: 1140px,
+ xxl: 1320px
+) !default;
+// scss-docs-end container-max-widths
+
+@include _assert-ascending($container-max-widths, "$container-max-widths");
+
+
+// Grid columns
+//
+// Set the number of columns and specify the width of the gutters.
+
+$grid-columns: 12 !default;
+$grid-gutter-width: 1.5rem !default;
+$grid-row-columns: 6 !default;
+
+// Container padding
+
+$container-padding-x: $grid-gutter-width !default;
+
+
+// Components
+//
+// Define common padding and border radius sizes and more.
+
+// scss-docs-start border-variables
+$border-width: 1px !default;
+$border-widths: (
+ 1: 1px,
+ 2: 2px,
+ 3: 3px,
+ 4: 4px,
+ 5: 5px
+) !default;
+$border-style: solid !default;
+$border-color: $gray-300 !default;
+$border-color-translucent: rgba($black, .175) !default;
+// scss-docs-end border-variables
+
+// scss-docs-start border-radius-variables
+$border-radius: .375rem !default;
+$border-radius-sm: .25rem !default;
+$border-radius-lg: .5rem !default;
+$border-radius-xl: 1rem !default;
+$border-radius-xxl: 2rem !default;
+$border-radius-pill: 50rem !default;
+// scss-docs-end border-radius-variables
+// fusv-disable
+$border-radius-2xl: $border-radius-xxl !default; // Deprecated in v5.3.0
+// fusv-enable
+
+// scss-docs-start box-shadow-variables
+$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;
+$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;
+$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;
+$box-shadow-inset: inset 0 1px 2px rgba($black, .075) !default;
+// scss-docs-end box-shadow-variables
+
+$component-active-color: $white !default;
+$component-active-bg: $primary !default;
+
+// scss-docs-start focus-ring-variables
+$focus-ring-width: .25rem !default;
+$focus-ring-opacity: .25 !default;
+$focus-ring-color: rgba($primary, $focus-ring-opacity) !default;
+$focus-ring-blur: 0 !default;
+$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default;
+// scss-docs-end focus-ring-variables
+
+// scss-docs-start caret-variables
+$caret-width: .3em !default;
+$caret-vertical-align: $caret-width * .85 !default;
+$caret-spacing: $caret-width * .85 !default;
+// scss-docs-end caret-variables
+
+$transition-base: all .2s ease-in-out !default;
+$transition-fade: opacity .15s linear !default;
+// scss-docs-start collapse-transition
+$transition-collapse: height .35s ease !default;
+$transition-collapse-width: width .35s ease !default;
+// scss-docs-end collapse-transition
+
+// stylelint-disable function-disallowed-list
+// scss-docs-start aspect-ratios
+$aspect-ratios: (
+ "1x1": 100%,
+ "4x3": calc(3 / 4 * 100%),
+ "16x9": calc(9 / 16 * 100%),
+ "21x9": calc(9 / 21 * 100%)
+) !default;
+// scss-docs-end aspect-ratios
+// stylelint-enable function-disallowed-list
+
+// Typography
+//
+// Font, line-height, and color for body text, headings, and more.
+
+// scss-docs-start font-variables
+// stylelint-disable value-keyword-case
+$font-family-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
+$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
+// stylelint-enable value-keyword-case
+$font-family-base: var(--#{$prefix}font-sans-serif) !default;
+$font-family-code: var(--#{$prefix}font-monospace) !default;
+
+// $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins
+// $font-size-base affects the font size of the body text
+$font-size-root: null !default;
+$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`
+$font-size-sm: $font-size-base * .875 !default;
+$font-size-lg: $font-size-base * 1.25 !default;
+
+$font-weight-lighter: lighter !default;
+$font-weight-light: 300 !default;
+$font-weight-normal: 400 !default;
+$font-weight-medium: 500 !default;
+$font-weight-semibold: 600 !default;
+$font-weight-bold: 700 !default;
+$font-weight-bolder: bolder !default;
+
+$font-weight-base: $font-weight-normal !default;
+
+$line-height-base: 1.5 !default;
+$line-height-sm: 1.25 !default;
+$line-height-lg: 2 !default;
+
+$h1-font-size: $font-size-base * 2.5 !default;
+$h2-font-size: $font-size-base * 2 !default;
+$h3-font-size: $font-size-base * 1.75 !default;
+$h4-font-size: $font-size-base * 1.5 !default;
+$h5-font-size: $font-size-base * 1.25 !default;
+$h6-font-size: $font-size-base !default;
+// scss-docs-end font-variables
+
+// scss-docs-start font-sizes
+$font-sizes: (
+ 1: $h1-font-size,
+ 2: $h2-font-size,
+ 3: $h3-font-size,
+ 4: $h4-font-size,
+ 5: $h5-font-size,
+ 6: $h6-font-size
+) !default;
+// scss-docs-end font-sizes
+
+// scss-docs-start headings-variables
+$headings-margin-bottom: $spacer * .5 !default;
+$headings-font-family: null !default;
+$headings-font-style: null !default;
+$headings-font-weight: 500 !default;
+$headings-line-height: 1.2 !default;
+$headings-color: inherit !default;
+// scss-docs-end headings-variables
+
+// scss-docs-start display-headings
+$display-font-sizes: (
+ 1: 5rem,
+ 2: 4.5rem,
+ 3: 4rem,
+ 4: 3.5rem,
+ 5: 3rem,
+ 6: 2.5rem
+) !default;
+
+$display-font-family: null !default;
+$display-font-style: null !default;
+$display-font-weight: 300 !default;
+$display-line-height: $headings-line-height !default;
+// scss-docs-end display-headings
+
+// scss-docs-start type-variables
+$lead-font-size: $font-size-base * 1.25 !default;
+$lead-font-weight: 300 !default;
+
+$small-font-size: .875em !default;
+
+$sub-sup-font-size: .75em !default;
+
+// fusv-disable
+$text-muted: var(--#{$prefix}secondary-color) !default; // Deprecated in 5.3.0
+// fusv-enable
+
+$initialism-font-size: $small-font-size !default;
+
+$blockquote-margin-y: $spacer !default;
+$blockquote-font-size: $font-size-base * 1.25 !default;
+$blockquote-footer-color: $gray-600 !default;
+$blockquote-footer-font-size: $small-font-size !default;
+
+$hr-margin-y: $spacer !default;
+$hr-color: inherit !default;
+
+// fusv-disable
+$hr-bg-color: null !default; // Deprecated in v5.2.0
+$hr-height: null !default; // Deprecated in v5.2.0
+// fusv-enable
+
+$hr-border-color: null !default; // Allows for inherited colors
+$hr-border-width: var(--#{$prefix}border-width) !default;
+$hr-opacity: .25 !default;
+
+// scss-docs-start vr-variables
+$vr-border-width: var(--#{$prefix}border-width) !default;
+// scss-docs-end vr-variables
+
+$legend-margin-bottom: .5rem !default;
+$legend-font-size: 1.5rem !default;
+$legend-font-weight: null !default;
+
+$dt-font-weight: $font-weight-bold !default;
+
+$list-inline-padding: .5rem !default;
+
+$mark-padding: .1875em !default;
+$mark-color: $body-color !default;
+$mark-bg: $yellow-100 !default;
+// scss-docs-end type-variables
+
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+// scss-docs-start table-variables
+$table-cell-padding-y: .5rem !default;
+$table-cell-padding-x: .5rem !default;
+$table-cell-padding-y-sm: .25rem !default;
+$table-cell-padding-x-sm: .25rem !default;
+
+$table-cell-vertical-align: top !default;
+
+$table-color: var(--#{$prefix}emphasis-color) !default;
+$table-bg: var(--#{$prefix}body-bg) !default;
+$table-accent-bg: transparent !default;
+
+$table-th-font-weight: null !default;
+
+$table-striped-color: $table-color !default;
+$table-striped-bg-factor: .05 !default;
+$table-striped-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-striped-bg-factor) !default;
+
+$table-active-color: $table-color !default;
+$table-active-bg-factor: .1 !default;
+$table-active-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-active-bg-factor) !default;
+
+$table-hover-color: $table-color !default;
+$table-hover-bg-factor: .075 !default;
+$table-hover-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-hover-bg-factor) !default;
+
+$table-border-factor: .2 !default;
+$table-border-width: var(--#{$prefix}border-width) !default;
+$table-border-color: var(--#{$prefix}border-color) !default;
+
+$table-striped-order: odd !default;
+$table-striped-columns-order: even !default;
+
+$table-group-separator-color: currentcolor !default;
+
+$table-caption-color: var(--#{$prefix}secondary-color) !default;
+
+$table-bg-scale: -80% !default;
+// scss-docs-end table-variables
+
+// scss-docs-start table-loop
+$table-variants: (
+ "primary": shift-color($primary, $table-bg-scale),
+ "secondary": shift-color($secondary, $table-bg-scale),
+ "success": shift-color($success, $table-bg-scale),
+ "info": shift-color($info, $table-bg-scale),
+ "warning": shift-color($warning, $table-bg-scale),
+ "danger": shift-color($danger, $table-bg-scale),
+ "light": $light,
+ "dark": $dark,
+) !default;
+// scss-docs-end table-loop
+
+
+// Buttons + Forms
+//
+// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.
+
+// scss-docs-start input-btn-variables
+$input-btn-padding-y: .375rem !default;
+$input-btn-padding-x: .75rem !default;
+$input-btn-font-family: null !default;
+$input-btn-font-size: $font-size-base !default;
+$input-btn-line-height: $line-height-base !default;
+
+$input-btn-focus-width: $focus-ring-width !default;
+$input-btn-focus-color-opacity: $focus-ring-opacity !default;
+$input-btn-focus-color: $focus-ring-color !default;
+$input-btn-focus-blur: $focus-ring-blur !default;
+$input-btn-focus-box-shadow: $focus-ring-box-shadow !default;
+
+$input-btn-padding-y-sm: .25rem !default;
+$input-btn-padding-x-sm: .5rem !default;
+$input-btn-font-size-sm: $font-size-sm !default;
+
+$input-btn-padding-y-lg: .5rem !default;
+$input-btn-padding-x-lg: 1rem !default;
+$input-btn-font-size-lg: $font-size-lg !default;
+
+$input-btn-border-width: var(--#{$prefix}border-width) !default;
+// scss-docs-end input-btn-variables
+
+
+// Buttons
+//
+// For each of Bootstrap's buttons, define text, background, and border color.
+
+// scss-docs-start btn-variables
+$btn-color: var(--#{$prefix}body-color) !default;
+$btn-padding-y: $input-btn-padding-y !default;
+$btn-padding-x: $input-btn-padding-x !default;
+$btn-font-family: $input-btn-font-family !default;
+$btn-font-size: $input-btn-font-size !default;
+$btn-line-height: $input-btn-line-height !default;
+$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping
+
+$btn-padding-y-sm: $input-btn-padding-y-sm !default;
+$btn-padding-x-sm: $input-btn-padding-x-sm !default;
+$btn-font-size-sm: $input-btn-font-size-sm !default;
+
+$btn-padding-y-lg: $input-btn-padding-y-lg !default;
+$btn-padding-x-lg: $input-btn-padding-x-lg !default;
+$btn-font-size-lg: $input-btn-font-size-lg !default;
+
+$btn-border-width: $input-btn-border-width !default;
+
+$btn-font-weight: $font-weight-normal !default;
+$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;
+$btn-focus-width: $input-btn-focus-width !default;
+$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;
+$btn-disabled-opacity: .65 !default;
+$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;
+
+$btn-link-color: var(--#{$prefix}link-color) !default;
+$btn-link-hover-color: var(--#{$prefix}link-hover-color) !default;
+$btn-link-disabled-color: $gray-600 !default;
+$btn-link-focus-shadow-rgb: to-rgb(mix(color-contrast($link-color), $link-color, 15%)) !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius: var(--#{$prefix}border-radius) !default;
+$btn-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;
+$btn-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;
+
+$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$btn-hover-bg-shade-amount: 15% !default;
+$btn-hover-bg-tint-amount: 15% !default;
+$btn-hover-border-shade-amount: 20% !default;
+$btn-hover-border-tint-amount: 10% !default;
+$btn-active-bg-shade-amount: 20% !default;
+$btn-active-bg-tint-amount: 20% !default;
+$btn-active-border-shade-amount: 25% !default;
+$btn-active-border-tint-amount: 10% !default;
+// scss-docs-end btn-variables
+
+
+// Forms
+
+// scss-docs-start form-text-variables
+$form-text-margin-top: .25rem !default;
+$form-text-font-size: $small-font-size !default;
+$form-text-font-style: null !default;
+$form-text-font-weight: null !default;
+$form-text-color: var(--#{$prefix}secondary-color) !default;
+// scss-docs-end form-text-variables
+
+// scss-docs-start form-label-variables
+$form-label-margin-bottom: .5rem !default;
+$form-label-font-size: null !default;
+$form-label-font-style: null !default;
+$form-label-font-weight: null !default;
+$form-label-color: null !default;
+// scss-docs-end form-label-variables
+
+// scss-docs-start form-input-variables
+$input-padding-y: $input-btn-padding-y !default;
+$input-padding-x: $input-btn-padding-x !default;
+$input-font-family: $input-btn-font-family !default;
+$input-font-size: $input-btn-font-size !default;
+$input-font-weight: $font-weight-base !default;
+$input-line-height: $input-btn-line-height !default;
+
+$input-padding-y-sm: $input-btn-padding-y-sm !default;
+$input-padding-x-sm: $input-btn-padding-x-sm !default;
+$input-font-size-sm: $input-btn-font-size-sm !default;
+
+$input-padding-y-lg: $input-btn-padding-y-lg !default;
+$input-padding-x-lg: $input-btn-padding-x-lg !default;
+$input-font-size-lg: $input-btn-font-size-lg !default;
+
+$input-bg: var(--#{$prefix}body-bg) !default;
+$input-disabled-color: null !default;
+$input-disabled-bg: var(--#{$prefix}secondary-bg) !default;
+$input-disabled-border-color: null !default;
+
+$input-color: var(--#{$prefix}body-color) !default;
+$input-border-color: var(--#{$prefix}border-color) !default;
+$input-border-width: $input-btn-border-width !default;
+$input-box-shadow: var(--#{$prefix}box-shadow-inset) !default;
+
+$input-border-radius: var(--#{$prefix}border-radius) !default;
+$input-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;
+$input-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;
+
+$input-focus-bg: $input-bg !default;
+$input-focus-border-color: tint-color($component-active-bg, 50%) !default;
+$input-focus-color: $input-color !default;
+$input-focus-width: $input-btn-focus-width !default;
+$input-focus-box-shadow: $input-btn-focus-box-shadow !default;
+
+$input-placeholder-color: var(--#{$prefix}secondary-color) !default;
+$input-plaintext-color: var(--#{$prefix}body-color) !default;
+
+$input-height-border: calc(#{$input-border-width} * 2) !default; // stylelint-disable-line function-disallowed-list
+
+$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;
+$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;
+$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y * .5) !default;
+
+$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;
+$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;
+$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;
+
+$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$form-color-width: 3rem !default;
+// scss-docs-end form-input-variables
+
+// scss-docs-start form-check-variables
+$form-check-input-width: 1em !default;
+$form-check-min-height: $font-size-base * $line-height-base !default;
+$form-check-padding-start: $form-check-input-width + .5em !default;
+$form-check-margin-bottom: .125rem !default;
+$form-check-label-color: null !default;
+$form-check-label-cursor: null !default;
+$form-check-transition: null !default;
+
+$form-check-input-active-filter: brightness(90%) !default;
+
+$form-check-input-bg: $input-bg !default;
+$form-check-input-border: var(--#{$prefix}border-width) solid var(--#{$prefix}border-color) !default;
+$form-check-input-border-radius: .25em !default;
+$form-check-radio-border-radius: 50% !default;
+$form-check-input-focus-border: $input-focus-border-color !default;
+$form-check-input-focus-box-shadow: $focus-ring-box-shadow !default;
+
+$form-check-input-checked-color: $component-active-color !default;
+$form-check-input-checked-bg-color: $component-active-bg !default;
+$form-check-input-checked-border-color: $form-check-input-checked-bg-color !default;
+$form-check-input-checked-bg-image: url("data:image/svg+xml,
") !default;
+$form-check-radio-checked-bg-image: url("data:image/svg+xml,
") !default;
+
+$form-check-input-indeterminate-color: $component-active-color !default;
+$form-check-input-indeterminate-bg-color: $component-active-bg !default;
+$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default;
+$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,
") !default;
+
+$form-check-input-disabled-opacity: .5 !default;
+$form-check-label-disabled-opacity: $form-check-input-disabled-opacity !default;
+$form-check-btn-check-disabled-opacity: $btn-disabled-opacity !default;
+
+$form-check-inline-margin-end: 1rem !default;
+// scss-docs-end form-check-variables
+
+// scss-docs-start form-switch-variables
+$form-switch-color: rgba($black, .25) !default;
+$form-switch-width: 2em !default;
+$form-switch-padding-start: $form-switch-width + .5em !default;
+$form-switch-bg-image: url("data:image/svg+xml,
") !default;
+$form-switch-border-radius: $form-switch-width !default;
+$form-switch-transition: background-position .15s ease-in-out !default;
+
+$form-switch-focus-color: $input-focus-border-color !default;
+$form-switch-focus-bg-image: url("data:image/svg+xml,
") !default;
+
+$form-switch-checked-color: $component-active-color !default;
+$form-switch-checked-bg-image: url("data:image/svg+xml,
") !default;
+$form-switch-checked-bg-position: right center !default;
+// scss-docs-end form-switch-variables
+
+// scss-docs-start input-group-variables
+$input-group-addon-padding-y: $input-padding-y !default;
+$input-group-addon-padding-x: $input-padding-x !default;
+$input-group-addon-font-weight: $input-font-weight !default;
+$input-group-addon-color: $input-color !default;
+$input-group-addon-bg: var(--#{$prefix}tertiary-bg) !default;
+$input-group-addon-border-color: $input-border-color !default;
+// scss-docs-end input-group-variables
+
+// scss-docs-start form-select-variables
+$form-select-padding-y: $input-padding-y !default;
+$form-select-padding-x: $input-padding-x !default;
+$form-select-font-family: $input-font-family !default;
+$form-select-font-size: $input-font-size !default;
+$form-select-indicator-padding: $form-select-padding-x * 3 !default; // Extra padding for background-image
+$form-select-font-weight: $input-font-weight !default;
+$form-select-line-height: $input-line-height !default;
+$form-select-color: $input-color !default;
+$form-select-bg: $input-bg !default;
+$form-select-disabled-color: null !default;
+$form-select-disabled-bg: $input-disabled-bg !default;
+$form-select-disabled-border-color: $input-disabled-border-color !default;
+$form-select-bg-position: right $form-select-padding-x center !default;
+$form-select-bg-size: 16px 12px !default; // In pixels because image dimensions
+$form-select-indicator-color: $gray-800 !default;
+$form-select-indicator: url("data:image/svg+xml,
") !default;
+
+$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 + $form-select-indicator-padding !default;
+$form-select-feedback-icon-position: center right $form-select-indicator-padding !default;
+$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;
+
+$form-select-border-width: $input-border-width !default;
+$form-select-border-color: $input-border-color !default;
+$form-select-border-radius: $input-border-radius !default;
+$form-select-box-shadow: var(--#{$prefix}box-shadow-inset) !default;
+
+$form-select-focus-border-color: $input-focus-border-color !default;
+$form-select-focus-width: $input-focus-width !default;
+$form-select-focus-box-shadow: 0 0 0 $form-select-focus-width $input-btn-focus-color !default;
+
+$form-select-padding-y-sm: $input-padding-y-sm !default;
+$form-select-padding-x-sm: $input-padding-x-sm !default;
+$form-select-font-size-sm: $input-font-size-sm !default;
+$form-select-border-radius-sm: $input-border-radius-sm !default;
+
+$form-select-padding-y-lg: $input-padding-y-lg !default;
+$form-select-padding-x-lg: $input-padding-x-lg !default;
+$form-select-font-size-lg: $input-font-size-lg !default;
+$form-select-border-radius-lg: $input-border-radius-lg !default;
+
+$form-select-transition: $input-transition !default;
+// scss-docs-end form-select-variables
+
+// scss-docs-start form-range-variables
+$form-range-track-width: 100% !default;
+$form-range-track-height: .5rem !default;
+$form-range-track-cursor: pointer !default;
+$form-range-track-bg: var(--#{$prefix}secondary-bg) !default;
+$form-range-track-border-radius: 1rem !default;
+$form-range-track-box-shadow: var(--#{$prefix}box-shadow-inset) !default;
+
+$form-range-thumb-width: 1rem !default;
+$form-range-thumb-height: $form-range-thumb-width !default;
+$form-range-thumb-bg: $component-active-bg !default;
+$form-range-thumb-border: 0 !default;
+$form-range-thumb-border-radius: 1rem !default;
+$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;
+$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;
+$form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in Edge
+$form-range-thumb-active-bg: tint-color($component-active-bg, 70%) !default;
+$form-range-thumb-disabled-bg: var(--#{$prefix}secondary-color) !default;
+$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+// scss-docs-end form-range-variables
+
+// scss-docs-start form-file-variables
+$form-file-button-color: $input-color !default;
+$form-file-button-bg: var(--#{$prefix}tertiary-bg) !default;
+$form-file-button-hover-bg: var(--#{$prefix}secondary-bg) !default;
+// scss-docs-end form-file-variables
+
+// scss-docs-start form-floating-variables
+$form-floating-height: add(3.5rem, $input-height-border) !default;
+$form-floating-line-height: 1.25 !default;
+$form-floating-padding-x: $input-padding-x !default;
+$form-floating-padding-y: 1rem !default;
+$form-floating-input-padding-t: 1.625rem !default;
+$form-floating-input-padding-b: .625rem !default;
+$form-floating-label-height: 1.5em !default;
+$form-floating-label-opacity: .65 !default;
+$form-floating-label-transform: scale(.85) translateY(-.5rem) translateX(.15rem) !default;
+$form-floating-label-disabled-color: $gray-600 !default;
+$form-floating-transition: opacity .1s ease-in-out, transform .1s ease-in-out !default;
+// scss-docs-end form-floating-variables
+
+// Form validation
+
+// scss-docs-start form-feedback-variables
+$form-feedback-margin-top: $form-text-margin-top !default;
+$form-feedback-font-size: $form-text-font-size !default;
+$form-feedback-font-style: $form-text-font-style !default;
+$form-feedback-valid-color: $success !default;
+$form-feedback-invalid-color: $danger !default;
+
+$form-feedback-icon-valid-color: $form-feedback-valid-color !default;
+$form-feedback-icon-valid: url("data:image/svg+xml,
") !default;
+$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;
+$form-feedback-icon-invalid: url("data:image/svg+xml,
") !default;
+// scss-docs-end form-feedback-variables
+
+// scss-docs-start form-validation-colors
+$form-valid-color: $form-feedback-valid-color !default;
+$form-valid-border-color: $form-feedback-valid-color !default;
+$form-invalid-color: $form-feedback-invalid-color !default;
+$form-invalid-border-color: $form-feedback-invalid-color !default;
+// scss-docs-end form-validation-colors
+
+// scss-docs-start form-validation-states
+$form-validation-states: (
+ "valid": (
+ "color": var(--#{$prefix}form-valid-color),
+ "icon": $form-feedback-icon-valid,
+ "tooltip-color": #fff,
+ "tooltip-bg-color": var(--#{$prefix}success),
+ "focus-box-shadow": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}success-rgb), $input-btn-focus-color-opacity),
+ "border-color": var(--#{$prefix}form-valid-border-color),
+ ),
+ "invalid": (
+ "color": var(--#{$prefix}form-invalid-color),
+ "icon": $form-feedback-icon-invalid,
+ "tooltip-color": #fff,
+ "tooltip-bg-color": var(--#{$prefix}danger),
+ "focus-box-shadow": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}danger-rgb), $input-btn-focus-color-opacity),
+ "border-color": var(--#{$prefix}form-invalid-border-color),
+ )
+) !default;
+// scss-docs-end form-validation-states
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+// scss-docs-start zindex-stack
+$zindex-dropdown: 1000 !default;
+$zindex-sticky: 1020 !default;
+$zindex-fixed: 1030 !default;
+$zindex-offcanvas-backdrop: 1040 !default;
+$zindex-offcanvas: 1045 !default;
+$zindex-modal-backdrop: 1050 !default;
+$zindex-modal: 1055 !default;
+$zindex-popover: 1070 !default;
+$zindex-tooltip: 1080 !default;
+$zindex-toast: 1090 !default;
+// scss-docs-end zindex-stack
+
+// scss-docs-start zindex-levels-map
+$zindex-levels: (
+ n1: -1,
+ 0: 0,
+ 1: 1,
+ 2: 2,
+ 3: 3
+) !default;
+// scss-docs-end zindex-levels-map
+
+
+// Navs
+
+// scss-docs-start nav-variables
+$nav-link-padding-y: .5rem !default;
+$nav-link-padding-x: 1rem !default;
+$nav-link-font-size: null !default;
+$nav-link-font-weight: null !default;
+$nav-link-color: var(--#{$prefix}link-color) !default;
+$nav-link-hover-color: var(--#{$prefix}link-hover-color) !default;
+$nav-link-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default;
+$nav-link-disabled-color: var(--#{$prefix}secondary-color) !default;
+$nav-link-focus-box-shadow: $focus-ring-box-shadow !default;
+
+$nav-tabs-border-color: var(--#{$prefix}border-color) !default;
+$nav-tabs-border-width: var(--#{$prefix}border-width) !default;
+$nav-tabs-border-radius: var(--#{$prefix}border-radius) !default;
+$nav-tabs-link-hover-border-color: var(--#{$prefix}secondary-bg) var(--#{$prefix}secondary-bg) $nav-tabs-border-color !default;
+$nav-tabs-link-active-color: var(--#{$prefix}emphasis-color) !default;
+$nav-tabs-link-active-bg: var(--#{$prefix}body-bg) !default;
+$nav-tabs-link-active-border-color: var(--#{$prefix}border-color) var(--#{$prefix}border-color) $nav-tabs-link-active-bg !default;
+
+$nav-pills-border-radius: var(--#{$prefix}border-radius) !default;
+$nav-pills-link-active-color: $component-active-color !default;
+$nav-pills-link-active-bg: $component-active-bg !default;
+
+$nav-underline-gap: 1rem !default;
+$nav-underline-border-width: .125rem !default;
+$nav-underline-link-active-color: var(--#{$prefix}emphasis-color) !default;
+// scss-docs-end nav-variables
+
+
+// Navbar
+
+// scss-docs-start navbar-variables
+$navbar-padding-y: $spacer * .5 !default;
+$navbar-padding-x: null !default;
+
+$navbar-nav-link-padding-x: .5rem !default;
+
+$navbar-brand-font-size: $font-size-lg !default;
+// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
+$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;
+$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;
+$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) * .5 !default;
+$navbar-brand-margin-end: 1rem !default;
+
+$navbar-toggler-padding-y: .25rem !default;
+$navbar-toggler-padding-x: .75rem !default;
+$navbar-toggler-font-size: $font-size-lg !default;
+$navbar-toggler-border-radius: $btn-border-radius !default;
+$navbar-toggler-focus-width: $btn-focus-width !default;
+$navbar-toggler-transition: box-shadow .15s ease-in-out !default;
+
+$navbar-light-color: rgba(var(--#{$prefix}emphasis-color-rgb), .65) !default;
+$navbar-light-hover-color: rgba(var(--#{$prefix}emphasis-color-rgb), .8) !default;
+$navbar-light-active-color: rgba(var(--#{$prefix}emphasis-color-rgb), 1) !default;
+$navbar-light-disabled-color: rgba(var(--#{$prefix}emphasis-color-rgb), .3) !default;
+$navbar-light-icon-color: rgba($body-color, .75) !default;
+$navbar-light-toggler-icon-bg: url("data:image/svg+xml,
") !default;
+$navbar-light-toggler-border-color: rgba(var(--#{$prefix}emphasis-color-rgb), .15) !default;
+$navbar-light-brand-color: $navbar-light-active-color !default;
+$navbar-light-brand-hover-color: $navbar-light-active-color !default;
+// scss-docs-end navbar-variables
+
+// scss-docs-start navbar-dark-variables
+$navbar-dark-color: rgba($white, .55) !default;
+$navbar-dark-hover-color: rgba($white, .75) !default;
+$navbar-dark-active-color: $white !default;
+$navbar-dark-disabled-color: rgba($white, .25) !default;
+$navbar-dark-icon-color: $navbar-dark-color !default;
+$navbar-dark-toggler-icon-bg: url("data:image/svg+xml,
") !default;
+$navbar-dark-toggler-border-color: rgba($white, .1) !default;
+$navbar-dark-brand-color: $navbar-dark-active-color !default;
+$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;
+// scss-docs-end navbar-dark-variables
+
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+// scss-docs-start dropdown-variables
+$dropdown-min-width: 10rem !default;
+$dropdown-padding-x: 0 !default;
+$dropdown-padding-y: .5rem !default;
+$dropdown-spacer: .125rem !default;
+$dropdown-font-size: $font-size-base !default;
+$dropdown-color: var(--#{$prefix}body-color) !default;
+$dropdown-bg: var(--#{$prefix}body-bg) !default;
+$dropdown-border-color: var(--#{$prefix}border-color-translucent) !default;
+$dropdown-border-radius: var(--#{$prefix}border-radius) !default;
+$dropdown-border-width: var(--#{$prefix}border-width) !default;
+$dropdown-inner-border-radius: calc(#{$dropdown-border-radius} - #{$dropdown-border-width}) !default; // stylelint-disable-line function-disallowed-list
+$dropdown-divider-bg: $dropdown-border-color !default;
+$dropdown-divider-margin-y: $spacer * .5 !default;
+$dropdown-box-shadow: var(--#{$prefix}box-shadow) !default;
+
+$dropdown-link-color: var(--#{$prefix}body-color) !default;
+$dropdown-link-hover-color: $dropdown-link-color !default;
+$dropdown-link-hover-bg: var(--#{$prefix}tertiary-bg) !default;
+
+$dropdown-link-active-color: $component-active-color !default;
+$dropdown-link-active-bg: $component-active-bg !default;
+
+$dropdown-link-disabled-color: var(--#{$prefix}tertiary-color) !default;
+
+$dropdown-item-padding-y: $spacer * .25 !default;
+$dropdown-item-padding-x: $spacer !default;
+
+$dropdown-header-color: $gray-600 !default;
+$dropdown-header-padding-x: $dropdown-item-padding-x !default;
+$dropdown-header-padding-y: $dropdown-padding-y !default;
+// fusv-disable
+$dropdown-header-padding: $dropdown-header-padding-y $dropdown-header-padding-x !default; // Deprecated in v5.2.0
+// fusv-enable
+// scss-docs-end dropdown-variables
+
+// scss-docs-start dropdown-dark-variables
+$dropdown-dark-color: $gray-300 !default;
+$dropdown-dark-bg: $gray-800 !default;
+$dropdown-dark-border-color: $dropdown-border-color !default;
+$dropdown-dark-divider-bg: $dropdown-divider-bg !default;
+$dropdown-dark-box-shadow: null !default;
+$dropdown-dark-link-color: $dropdown-dark-color !default;
+$dropdown-dark-link-hover-color: $white !default;
+$dropdown-dark-link-hover-bg: rgba($white, .15) !default;
+$dropdown-dark-link-active-color: $dropdown-link-active-color !default;
+$dropdown-dark-link-active-bg: $dropdown-link-active-bg !default;
+$dropdown-dark-link-disabled-color: $gray-500 !default;
+$dropdown-dark-header-color: $gray-500 !default;
+// scss-docs-end dropdown-dark-variables
+
+
+// Pagination
+
+// scss-docs-start pagination-variables
+$pagination-padding-y: .375rem !default;
+$pagination-padding-x: .75rem !default;
+$pagination-padding-y-sm: .25rem !default;
+$pagination-padding-x-sm: .5rem !default;
+$pagination-padding-y-lg: .75rem !default;
+$pagination-padding-x-lg: 1.5rem !default;
+
+$pagination-font-size: $font-size-base !default;
+
+$pagination-color: var(--#{$prefix}link-color) !default;
+$pagination-bg: var(--#{$prefix}body-bg) !default;
+$pagination-border-radius: var(--#{$prefix}border-radius) !default;
+$pagination-border-width: var(--#{$prefix}border-width) !default;
+$pagination-margin-start: calc(#{$pagination-border-width} * -1) !default; // stylelint-disable-line function-disallowed-list
+$pagination-border-color: var(--#{$prefix}border-color) !default;
+
+$pagination-focus-color: var(--#{$prefix}link-hover-color) !default;
+$pagination-focus-bg: var(--#{$prefix}secondary-bg) !default;
+$pagination-focus-box-shadow: $focus-ring-box-shadow !default;
+$pagination-focus-outline: 0 !default;
+
+$pagination-hover-color: var(--#{$prefix}link-hover-color) !default;
+$pagination-hover-bg: var(--#{$prefix}tertiary-bg) !default;
+$pagination-hover-border-color: var(--#{$prefix}border-color) !default; // Todo in v6: remove this?
+
+$pagination-active-color: $component-active-color !default;
+$pagination-active-bg: $component-active-bg !default;
+$pagination-active-border-color: $component-active-bg !default;
+
+$pagination-disabled-color: var(--#{$prefix}secondary-color) !default;
+$pagination-disabled-bg: var(--#{$prefix}secondary-bg) !default;
+$pagination-disabled-border-color: var(--#{$prefix}border-color) !default;
+
+$pagination-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
+
+$pagination-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;
+$pagination-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;
+// scss-docs-end pagination-variables
+
+
+// Placeholders
+
+// scss-docs-start placeholders
+$placeholder-opacity-max: .5 !default;
+$placeholder-opacity-min: .2 !default;
+// scss-docs-end placeholders
+
+// Cards
+
+// scss-docs-start card-variables
+$card-spacer-y: $spacer !default;
+$card-spacer-x: $spacer !default;
+$card-title-spacer-y: $spacer * .5 !default;
+$card-title-color: null !default;
+$card-subtitle-color: null !default;
+$card-border-width: var(--#{$prefix}border-width) !default;
+$card-border-color: var(--#{$prefix}border-color-translucent) !default;
+$card-border-radius: var(--#{$prefix}border-radius) !default;
+$card-box-shadow: null !default;
+$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;
+$card-cap-padding-y: $card-spacer-y * .5 !default;
+$card-cap-padding-x: $card-spacer-x !default;
+$card-cap-bg: rgba(var(--#{$prefix}body-color-rgb), .03) !default;
+$card-cap-color: null !default;
+$card-height: null !default;
+$card-color: null !default;
+$card-bg: var(--#{$prefix}body-bg) !default;
+$card-img-overlay-padding: $spacer !default;
+$card-group-margin: $grid-gutter-width * .5 !default;
+// scss-docs-end card-variables
+
+// Accordion
+
+// scss-docs-start accordion-variables
+$accordion-padding-y: 1rem !default;
+$accordion-padding-x: 1.25rem !default;
+$accordion-color: var(--#{$prefix}body-color) !default;
+$accordion-bg: var(--#{$prefix}body-bg) !default;
+$accordion-border-width: var(--#{$prefix}border-width) !default;
+$accordion-border-color: var(--#{$prefix}border-color) !default;
+$accordion-border-radius: var(--#{$prefix}border-radius) !default;
+$accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default;
+
+$accordion-body-padding-y: $accordion-padding-y !default;
+$accordion-body-padding-x: $accordion-padding-x !default;
+
+$accordion-button-padding-y: $accordion-padding-y !default;
+$accordion-button-padding-x: $accordion-padding-x !default;
+$accordion-button-color: var(--#{$prefix}body-color) !default;
+$accordion-button-bg: var(--#{$prefix}accordion-bg) !default;
+$accordion-transition: $btn-transition, border-radius .15s ease !default;
+$accordion-button-active-bg: var(--#{$prefix}primary-bg-subtle) !default;
+$accordion-button-active-color: var(--#{$prefix}primary-text-emphasis) !default;
+
+$accordion-button-focus-border-color: $input-focus-border-color !default;
+$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default;
+
+$accordion-icon-width: 1.25rem !default;
+$accordion-icon-color: $body-color !default;
+$accordion-icon-active-color: $primary-text-emphasis !default;
+$accordion-icon-transition: transform .2s ease-in-out !default;
+$accordion-icon-transform: rotate(-180deg) !default;
+
+$accordion-button-icon: url("data:image/svg+xml,
") !default;
+$accordion-button-active-icon: url("data:image/svg+xml,
") !default;
+// scss-docs-end accordion-variables
+
+// Tooltips
+
+// scss-docs-start tooltip-variables
+$tooltip-font-size: $font-size-sm !default;
+$tooltip-max-width: 200px !default;
+$tooltip-color: var(--#{$prefix}body-bg) !default;
+$tooltip-bg: var(--#{$prefix}emphasis-color) !default;
+$tooltip-border-radius: var(--#{$prefix}border-radius) !default;
+$tooltip-opacity: .9 !default;
+$tooltip-padding-y: $spacer * .25 !default;
+$tooltip-padding-x: $spacer * .5 !default;
+$tooltip-margin: null !default; // TODO: remove this in v6
+
+$tooltip-arrow-width: .8rem !default;
+$tooltip-arrow-height: .4rem !default;
+// fusv-disable
+$tooltip-arrow-color: null !default; // Deprecated in Bootstrap 5.2.0 for CSS variables
+// fusv-enable
+// scss-docs-end tooltip-variables
+
+// Form tooltips must come after regular tooltips
+// scss-docs-start tooltip-feedback-variables
+$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;
+$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;
+$form-feedback-tooltip-font-size: $tooltip-font-size !default;
+$form-feedback-tooltip-line-height: null !default;
+$form-feedback-tooltip-opacity: $tooltip-opacity !default;
+$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;
+// scss-docs-end tooltip-feedback-variables
+
+
+// Popovers
+
+// scss-docs-start popover-variables
+$popover-font-size: $font-size-sm !default;
+$popover-bg: var(--#{$prefix}body-bg) !default;
+$popover-max-width: 276px !default;
+$popover-border-width: var(--#{$prefix}border-width) !default;
+$popover-border-color: var(--#{$prefix}border-color-translucent) !default;
+$popover-border-radius: var(--#{$prefix}border-radius-lg) !default;
+$popover-inner-border-radius: calc(#{$popover-border-radius} - #{$popover-border-width}) !default; // stylelint-disable-line function-disallowed-list
+$popover-box-shadow: var(--#{$prefix}box-shadow) !default;
+
+$popover-header-font-size: $font-size-base !default;
+$popover-header-bg: var(--#{$prefix}secondary-bg) !default;
+$popover-header-color: $headings-color !default;
+$popover-header-padding-y: .5rem !default;
+$popover-header-padding-x: $spacer !default;
+
+$popover-body-color: var(--#{$prefix}body-color) !default;
+$popover-body-padding-y: $spacer !default;
+$popover-body-padding-x: $spacer !default;
+
+$popover-arrow-width: 1rem !default;
+$popover-arrow-height: .5rem !default;
+// scss-docs-end popover-variables
+
+// fusv-disable
+// Deprecated in Bootstrap 5.2.0 for CSS variables
+$popover-arrow-color: $popover-bg !default;
+$popover-arrow-outer-color: var(--#{$prefix}border-color-translucent) !default;
+// fusv-enable
+
+
+// Toasts
+
+// scss-docs-start toast-variables
+$toast-max-width: 350px !default;
+$toast-padding-x: .75rem !default;
+$toast-padding-y: .5rem !default;
+$toast-font-size: .875rem !default;
+$toast-color: null !default;
+$toast-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default;
+$toast-border-width: var(--#{$prefix}border-width) !default;
+$toast-border-color: var(--#{$prefix}border-color-translucent) !default;
+$toast-border-radius: var(--#{$prefix}border-radius) !default;
+$toast-box-shadow: var(--#{$prefix}box-shadow) !default;
+$toast-spacing: $container-padding-x !default;
+
+$toast-header-color: var(--#{$prefix}secondary-color) !default;
+$toast-header-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default;
+$toast-header-border-color: $toast-border-color !default;
+// scss-docs-end toast-variables
+
+
+// Badges
+
+// scss-docs-start badge-variables
+$badge-font-size: .75em !default;
+$badge-font-weight: $font-weight-bold !default;
+$badge-color: $white !default;
+$badge-padding-y: .35em !default;
+$badge-padding-x: .65em !default;
+$badge-border-radius: var(--#{$prefix}border-radius) !default;
+// scss-docs-end badge-variables
+
+
+// Modals
+
+// scss-docs-start modal-variables
+$modal-inner-padding: $spacer !default;
+
+$modal-footer-margin-between: .5rem !default;
+
+$modal-dialog-margin: .5rem !default;
+$modal-dialog-margin-y-sm-up: 1.75rem !default;
+
+$modal-title-line-height: $line-height-base !default;
+
+$modal-content-color: null !default;
+$modal-content-bg: var(--#{$prefix}body-bg) !default;
+$modal-content-border-color: var(--#{$prefix}border-color-translucent) !default;
+$modal-content-border-width: var(--#{$prefix}border-width) !default;
+$modal-content-border-radius: var(--#{$prefix}border-radius-lg) !default;
+$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;
+$modal-content-box-shadow-xs: var(--#{$prefix}box-shadow-sm) !default;
+$modal-content-box-shadow-sm-up: var(--#{$prefix}box-shadow) !default;
+
+$modal-backdrop-bg: $black !default;
+$modal-backdrop-opacity: .5 !default;
+
+$modal-header-border-color: var(--#{$prefix}border-color) !default;
+$modal-header-border-width: $modal-content-border-width !default;
+$modal-header-padding-y: $modal-inner-padding !default;
+$modal-header-padding-x: $modal-inner-padding !default;
+$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility
+
+$modal-footer-bg: null !default;
+$modal-footer-border-color: $modal-header-border-color !default;
+$modal-footer-border-width: $modal-header-border-width !default;
+
+$modal-sm: 300px !default;
+$modal-md: 500px !default;
+$modal-lg: 800px !default;
+$modal-xl: 1140px !default;
+
+$modal-fade-transform: translate(0, -50px) !default;
+$modal-show-transform: none !default;
+$modal-transition: transform .3s ease-out !default;
+$modal-scale-transform: scale(1.02) !default;
+// scss-docs-end modal-variables
+
+
+// Alerts
+//
+// Define alert colors, border radius, and padding.
+
+// scss-docs-start alert-variables
+$alert-padding-y: $spacer !default;
+$alert-padding-x: $spacer !default;
+$alert-margin-bottom: 1rem !default;
+$alert-border-radius: var(--#{$prefix}border-radius) !default;
+$alert-link-font-weight: $font-weight-bold !default;
+$alert-border-width: var(--#{$prefix}border-width) !default;
+$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side
+// scss-docs-end alert-variables
+
+// fusv-disable
+$alert-bg-scale: -80% !default; // Deprecated in v5.2.0, to be removed in v6
+$alert-border-scale: -70% !default; // Deprecated in v5.2.0, to be removed in v6
+$alert-color-scale: 40% !default; // Deprecated in v5.2.0, to be removed in v6
+// fusv-enable
+
+// Progress bars
+
+// scss-docs-start progress-variables
+$progress-height: 1rem !default;
+$progress-font-size: $font-size-base * .75 !default;
+$progress-bg: var(--#{$prefix}secondary-bg) !default;
+$progress-border-radius: var(--#{$prefix}border-radius) !default;
+$progress-box-shadow: var(--#{$prefix}box-shadow-inset) !default;
+$progress-bar-color: $white !default;
+$progress-bar-bg: $primary !default;
+$progress-bar-animation-timing: 1s linear infinite !default;
+$progress-bar-transition: width .6s ease !default;
+// scss-docs-end progress-variables
+
+
+// List group
+
+// scss-docs-start list-group-variables
+$list-group-color: var(--#{$prefix}body-color) !default;
+$list-group-bg: var(--#{$prefix}body-bg) !default;
+$list-group-border-color: var(--#{$prefix}border-color) !default;
+$list-group-border-width: var(--#{$prefix}border-width) !default;
+$list-group-border-radius: var(--#{$prefix}border-radius) !default;
+
+$list-group-item-padding-y: $spacer * .5 !default;
+$list-group-item-padding-x: $spacer !default;
+// fusv-disable
+$list-group-item-bg-scale: -80% !default; // Deprecated in v5.3.0
+$list-group-item-color-scale: 40% !default; // Deprecated in v5.3.0
+// fusv-enable
+
+$list-group-hover-bg: var(--#{$prefix}tertiary-bg) !default;
+$list-group-active-color: $component-active-color !default;
+$list-group-active-bg: $component-active-bg !default;
+$list-group-active-border-color: $list-group-active-bg !default;
+
+$list-group-disabled-color: var(--#{$prefix}secondary-color) !default;
+$list-group-disabled-bg: $list-group-bg !default;
+
+$list-group-action-color: var(--#{$prefix}secondary-color) !default;
+$list-group-action-hover-color: var(--#{$prefix}emphasis-color) !default;
+
+$list-group-action-active-color: var(--#{$prefix}body-color) !default;
+$list-group-action-active-bg: var(--#{$prefix}secondary-bg) !default;
+// scss-docs-end list-group-variables
+
+
+// Image thumbnails
+
+// scss-docs-start thumbnail-variables
+$thumbnail-padding: .25rem !default;
+$thumbnail-bg: var(--#{$prefix}body-bg) !default;
+$thumbnail-border-width: var(--#{$prefix}border-width) !default;
+$thumbnail-border-color: var(--#{$prefix}border-color) !default;
+$thumbnail-border-radius: var(--#{$prefix}border-radius) !default;
+$thumbnail-box-shadow: var(--#{$prefix}box-shadow-sm) !default;
+// scss-docs-end thumbnail-variables
+
+
+// Figures
+
+// scss-docs-start figure-variables
+$figure-caption-font-size: $small-font-size !default;
+$figure-caption-color: var(--#{$prefix}secondary-color) !default;
+// scss-docs-end figure-variables
+
+
+// Breadcrumbs
+
+// scss-docs-start breadcrumb-variables
+$breadcrumb-font-size: null !default;
+$breadcrumb-padding-y: 0 !default;
+$breadcrumb-padding-x: 0 !default;
+$breadcrumb-item-padding-x: .5rem !default;
+$breadcrumb-margin-bottom: 1rem !default;
+$breadcrumb-bg: null !default;
+$breadcrumb-divider-color: var(--#{$prefix}secondary-color) !default;
+$breadcrumb-active-color: var(--#{$prefix}secondary-color) !default;
+$breadcrumb-divider: quote("/") !default;
+$breadcrumb-divider-flipped: $breadcrumb-divider !default;
+$breadcrumb-border-radius: null !default;
+// scss-docs-end breadcrumb-variables
+
+// Carousel
+
+// scss-docs-start carousel-variables
+$carousel-control-color: $white !default;
+$carousel-control-width: 15% !default;
+$carousel-control-opacity: .5 !default;
+$carousel-control-hover-opacity: .9 !default;
+$carousel-control-transition: opacity .15s ease !default;
+
+$carousel-indicator-width: 30px !default;
+$carousel-indicator-height: 3px !default;
+$carousel-indicator-hit-area-height: 10px !default;
+$carousel-indicator-spacer: 3px !default;
+$carousel-indicator-opacity: .5 !default;
+$carousel-indicator-active-bg: $white !default;
+$carousel-indicator-active-opacity: 1 !default;
+$carousel-indicator-transition: opacity .6s ease !default;
+
+$carousel-caption-width: 70% !default;
+$carousel-caption-color: $white !default;
+$carousel-caption-padding-y: 1.25rem !default;
+$carousel-caption-spacer: 1.25rem !default;
+
+$carousel-control-icon-width: 2rem !default;
+
+$carousel-control-prev-icon-bg: url("data:image/svg+xml,
") !default;
+$carousel-control-next-icon-bg: url("data:image/svg+xml,
") !default;
+
+$carousel-transition-duration: .6s !default;
+$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)
+// scss-docs-end carousel-variables
+
+// scss-docs-start carousel-dark-variables
+$carousel-dark-indicator-active-bg: $black !default;
+$carousel-dark-caption-color: $black !default;
+$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default;
+// scss-docs-end carousel-dark-variables
+
+
+// Spinners
+
+// scss-docs-start spinner-variables
+$spinner-width: 2rem !default;
+$spinner-height: $spinner-width !default;
+$spinner-vertical-align: -.125em !default;
+$spinner-border-width: .25em !default;
+$spinner-animation-speed: .75s !default;
+
+$spinner-width-sm: 1rem !default;
+$spinner-height-sm: $spinner-width-sm !default;
+$spinner-border-width-sm: .2em !default;
+// scss-docs-end spinner-variables
+
+
+// Close
+
+// scss-docs-start close-variables
+$btn-close-width: 1em !default;
+$btn-close-height: $btn-close-width !default;
+$btn-close-padding-x: .25em !default;
+$btn-close-padding-y: $btn-close-padding-x !default;
+$btn-close-color: $black !default;
+$btn-close-bg: url("data:image/svg+xml,
") !default;
+$btn-close-focus-shadow: $focus-ring-box-shadow !default;
+$btn-close-opacity: .5 !default;
+$btn-close-hover-opacity: .75 !default;
+$btn-close-focus-opacity: 1 !default;
+$btn-close-disabled-opacity: .25 !default;
+$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default;
+// scss-docs-end close-variables
+
+
+// Offcanvas
+
+// scss-docs-start offcanvas-variables
+$offcanvas-padding-y: $modal-inner-padding !default;
+$offcanvas-padding-x: $modal-inner-padding !default;
+$offcanvas-horizontal-width: 400px !default;
+$offcanvas-vertical-height: 30vh !default;
+$offcanvas-transition-duration: .3s !default;
+$offcanvas-border-color: $modal-content-border-color !default;
+$offcanvas-border-width: $modal-content-border-width !default;
+$offcanvas-title-line-height: $modal-title-line-height !default;
+$offcanvas-bg-color: var(--#{$prefix}body-bg) !default;
+$offcanvas-color: var(--#{$prefix}body-color) !default;
+$offcanvas-box-shadow: $modal-content-box-shadow-xs !default;
+$offcanvas-backdrop-bg: $modal-backdrop-bg !default;
+$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default;
+// scss-docs-end offcanvas-variables
+
+// Code
+
+$code-font-size: $small-font-size !default;
+$code-color: $pink !default;
+
+$kbd-padding-y: .1875rem !default;
+$kbd-padding-x: .375rem !default;
+$kbd-font-size: $code-font-size !default;
+$kbd-color: var(--#{$prefix}body-bg) !default;
+$kbd-bg: var(--#{$prefix}body-color) !default;
+$nested-kbd-font-weight: null !default; // Deprecated in v5.2.0, removing in v6
+
+$pre-color: null !default;
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-grid.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-grid.scss
new file mode 100644
index 00000000..52bd577e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-grid.scss
@@ -0,0 +1,62 @@
+@import "mixins/banner";
+@include bsBanner(Grid);
+
+$include-column-box-sizing: true !default;
+
+@import "functions";
+@import "variables";
+@import "variables-dark";
+@import "maps";
+
+@import "mixins/breakpoints";
+@import "mixins/container";
+@import "mixins/grid";
+@import "mixins/utilities";
+
+@import "vendor/rfs";
+
+@import "containers";
+@import "grid";
+
+@import "utilities";
+// Only use the utilities we need
+// stylelint-disable-next-line scss/dollar-variable-default
+$utilities: map-get-multiple(
+ $utilities,
+ (
+ "display",
+ "order",
+ "flex",
+ "flex-direction",
+ "flex-grow",
+ "flex-shrink",
+ "flex-wrap",
+ "justify-content",
+ "align-items",
+ "align-content",
+ "align-self",
+ "margin",
+ "margin-x",
+ "margin-y",
+ "margin-top",
+ "margin-end",
+ "margin-bottom",
+ "margin-start",
+ "negative-margin",
+ "negative-margin-x",
+ "negative-margin-y",
+ "negative-margin-top",
+ "negative-margin-end",
+ "negative-margin-bottom",
+ "negative-margin-start",
+ "padding",
+ "padding-x",
+ "padding-y",
+ "padding-top",
+ "padding-end",
+ "padding-bottom",
+ "padding-start",
+ )
+);
+
+@import "utilities/api";
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-reboot.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-reboot.scss
new file mode 100644
index 00000000..5b69b955
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-reboot.scss
@@ -0,0 +1,10 @@
+@import "mixins/banner";
+@include bsBanner(Reboot);
+
+@import "functions";
+@import "variables";
+@import "variables-dark";
+@import "maps";
+@import "mixins";
+@import "root";
+@import "reboot";
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-utilities.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-utilities.scss
new file mode 100644
index 00000000..99c4a359
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap-utilities.scss
@@ -0,0 +1,19 @@
+@import "mixins/banner";
+@include bsBanner(Utilities);
+
+// Configuration
+@import "functions";
+@import "variables";
+@import "variables-dark";
+@import "maps";
+@import "mixins";
+@import "utilities";
+
+// Layout & components
+@import "root";
+
+// Helpers
+@import "helpers";
+
+// Utilities
+@import "utilities/api";
diff --git a/src/angular-bootstrap-md/scss/bootstrap/bootstrap.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap.scss
similarity index 58%
rename from src/angular-bootstrap-md/scss/bootstrap/bootstrap.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap.scss
index e05ce1cf..449d7048 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/bootstrap.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/bootstrap.scss
@@ -1,18 +1,22 @@
-/*!
- * Bootstrap v4.1.0 (https://getbootstrap.com/)
- * Copyright 2011-2018 The Bootstrap Authors
- * Copyright 2011-2018 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
+@import "mixins/banner";
+@include bsBanner("");
+
+// scss-docs-start import-stack
+// Configuration
@import "functions";
@import "variables";
+@import "variables-dark";
+@import "maps";
@import "mixins";
+@import "utilities";
+
+// Layout & components
@import "root";
@import "reboot";
@import "type";
@import "images";
-@import "code";
+@import "containers";
@import "grid";
@import "tables";
@import "forms";
@@ -20,23 +24,29 @@
@import "transitions";
@import "dropdown";
@import "button-group";
-@import "input-group";
-@import "custom-forms";
@import "nav";
@import "navbar";
@import "card";
+@import "accordion";
@import "breadcrumb";
@import "pagination";
@import "badge";
-@import "jumbotron";
@import "alert";
@import "progress";
-@import "media";
@import "list-group";
@import "close";
+@import "toasts";
@import "modal";
@import "tooltip";
@import "popover";
@import "carousel";
-@import "utilities";
-@import "print";
+@import "spinners";
+@import "offcanvas";
+@import "placeholders";
+
+// Helpers
+@import "helpers";
+
+// Utilities
+@import "utilities/api";
+// scss-docs-end import-stack
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_floating-labels.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_floating-labels.scss
new file mode 100644
index 00000000..2cf04704
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_floating-labels.scss
@@ -0,0 +1,95 @@
+.form-floating {
+ position: relative;
+
+ > .form-control,
+ > .form-control-plaintext,
+ > .form-select {
+ height: $form-floating-height;
+ min-height: $form-floating-height;
+ line-height: $form-floating-line-height;
+ }
+
+ > label {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ height: 100%; // allow textareas
+ padding: $form-floating-padding-y $form-floating-padding-x;
+ overflow: hidden;
+ text-align: start;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ pointer-events: none;
+ border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model
+ transform-origin: 0 0;
+ @include transition($form-floating-transition);
+ }
+
+ > .form-control,
+ > .form-control-plaintext {
+ padding: $form-floating-padding-y $form-floating-padding-x;
+
+ &::placeholder {
+ color: transparent;
+ }
+
+ &:focus,
+ &:not(:placeholder-shown) {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+ // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
+ &:-webkit-autofill {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+ }
+
+ > .form-select {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+
+ > .form-control:focus,
+ > .form-control:not(:placeholder-shown),
+ > .form-control-plaintext,
+ > .form-select {
+ ~ label {
+ color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
+ transform: $form-floating-label-transform;
+
+ &::after {
+ position: absolute;
+ inset: $form-floating-padding-y ($form-floating-padding-x * .5);
+ z-index: -1;
+ height: $form-floating-label-height;
+ content: "";
+ background-color: $input-bg;
+ @include border-radius($input-border-radius);
+ }
+ }
+ }
+ // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
+ > .form-control:-webkit-autofill {
+ ~ label {
+ color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
+ transform: $form-floating-label-transform;
+ }
+ }
+
+ > .form-control-plaintext {
+ ~ label {
+ border-width: $input-border-width 0; // Required to properly position label text - as explained above
+ }
+ }
+
+ > :disabled ~ label,
+ > .form-control:disabled ~ label { // Required for `.form-control`s because of specificity
+ color: $form-floating-label-disabled-color;
+
+ &::after {
+ background-color: $input-disabled-bg;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-check.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-check.scss
new file mode 100644
index 00000000..d3a8fe60
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-check.scss
@@ -0,0 +1,189 @@
+//
+// Check/radio
+//
+
+.form-check {
+ display: block;
+ min-height: $form-check-min-height;
+ padding-left: $form-check-padding-start;
+ margin-bottom: $form-check-margin-bottom;
+
+ .form-check-input {
+ float: left;
+ margin-left: $form-check-padding-start * -1;
+ }
+}
+
+.form-check-reverse {
+ padding-right: $form-check-padding-start;
+ padding-left: 0;
+ text-align: right;
+
+ .form-check-input {
+ float: right;
+ margin-right: $form-check-padding-start * -1;
+ margin-left: 0;
+ }
+}
+
+.form-check-input {
+ --#{$prefix}form-check-bg: #{$form-check-input-bg};
+
+ flex-shrink: 0;
+ width: $form-check-input-width;
+ height: $form-check-input-width;
+ margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height
+ vertical-align: top;
+ appearance: none;
+ background-color: var(--#{$prefix}form-check-bg);
+ background-image: var(--#{$prefix}form-check-bg-image);
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ border: $form-check-input-border;
+ print-color-adjust: exact; // Keep themed appearance for print
+ @include transition($form-check-transition);
+
+ &[type="checkbox"] {
+ @include border-radius($form-check-input-border-radius);
+ }
+
+ &[type="radio"] {
+ // stylelint-disable-next-line property-disallowed-list
+ border-radius: $form-check-radio-border-radius;
+ }
+
+ &:active {
+ filter: $form-check-input-active-filter;
+ }
+
+ &:focus {
+ border-color: $form-check-input-focus-border;
+ outline: 0;
+ box-shadow: $form-check-input-focus-box-shadow;
+ }
+
+ &:checked {
+ background-color: $form-check-input-checked-bg-color;
+ border-color: $form-check-input-checked-border-color;
+
+ &[type="checkbox"] {
+ @if $enable-gradients {
+ --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)}, var(--#{$prefix}gradient);
+ } @else {
+ --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)};
+ }
+ }
+
+ &[type="radio"] {
+ @if $enable-gradients {
+ --#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)}, var(--#{$prefix}gradient);
+ } @else {
+ --#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)};
+ }
+ }
+ }
+
+ &[type="checkbox"]:indeterminate {
+ background-color: $form-check-input-indeterminate-bg-color;
+ border-color: $form-check-input-indeterminate-border-color;
+
+ @if $enable-gradients {
+ --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)}, var(--#{$prefix}gradient);
+ } @else {
+ --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)};
+ }
+ }
+
+ &:disabled {
+ pointer-events: none;
+ filter: none;
+ opacity: $form-check-input-disabled-opacity;
+ }
+
+ // Use disabled attribute in addition of :disabled pseudo-class
+ // See: https://github.com/twbs/bootstrap/issues/28247
+ &[disabled],
+ &:disabled {
+ ~ .form-check-label {
+ cursor: default;
+ opacity: $form-check-label-disabled-opacity;
+ }
+ }
+}
+
+.form-check-label {
+ color: $form-check-label-color;
+ cursor: $form-check-label-cursor;
+}
+
+//
+// Switch
+//
+
+.form-switch {
+ padding-left: $form-switch-padding-start;
+
+ .form-check-input {
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)};
+
+ width: $form-switch-width;
+ margin-left: $form-switch-padding-start * -1;
+ background-image: var(--#{$prefix}form-switch-bg);
+ background-position: left center;
+ @include border-radius($form-switch-border-radius);
+ @include transition($form-switch-transition);
+
+ &:focus {
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-focus-bg-image)};
+ }
+
+ &:checked {
+ background-position: $form-switch-checked-bg-position;
+
+ @if $enable-gradients {
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}, var(--#{$prefix}gradient);
+ } @else {
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)};
+ }
+ }
+ }
+
+ &.form-check-reverse {
+ padding-right: $form-switch-padding-start;
+ padding-left: 0;
+
+ .form-check-input {
+ margin-right: $form-switch-padding-start * -1;
+ margin-left: 0;
+ }
+ }
+}
+
+.form-check-inline {
+ display: inline-block;
+ margin-right: $form-check-inline-margin-end;
+}
+
+.btn-check {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
+
+ &[disabled],
+ &:disabled {
+ + .btn {
+ pointer-events: none;
+ filter: none;
+ opacity: $form-check-btn-check-disabled-opacity;
+ }
+ }
+}
+
+@if $enable-dark-mode {
+ @include color-mode(dark) {
+ .form-switch .form-check-input:not(:checked):not(:focus) {
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image-dark)};
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-control.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-control.scss
new file mode 100644
index 00000000..67ae5f4f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-control.scss
@@ -0,0 +1,214 @@
+//
+// General form controls (plus a few specific high-level interventions)
+//
+
+.form-control {
+ display: block;
+ width: 100%;
+ padding: $input-padding-y $input-padding-x;
+ font-family: $input-font-family;
+ @include font-size($input-font-size);
+ font-weight: $input-font-weight;
+ line-height: $input-line-height;
+ color: $input-color;
+ appearance: none; // Fix appearance for date inputs in Safari
+ background-color: $input-bg;
+ background-clip: padding-box;
+ border: $input-border-width solid $input-border-color;
+
+ // Note: This has no effect on
s in some browsers, due to the limited stylability of ``s in CSS.
+ @include border-radius($input-border-radius, 0);
+
+ @include box-shadow($input-box-shadow);
+ @include transition($input-transition);
+
+ &[type="file"] {
+ overflow: hidden; // prevent pseudo element button overlap
+
+ &:not(:disabled):not([readonly]) {
+ cursor: pointer;
+ }
+ }
+
+ // Customize the `:focus` state to imitate native WebKit styles.
+ &:focus {
+ color: $input-focus-color;
+ background-color: $input-focus-bg;
+ border-color: $input-focus-border-color;
+ outline: 0;
+ @if $enable-shadows {
+ @include box-shadow($input-box-shadow, $input-focus-box-shadow);
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: $input-focus-box-shadow;
+ }
+ }
+
+ &::-webkit-date-and-time-value {
+ // On Android Chrome, form-control's "width: 100%" makes the input width too small
+ // Tested under Android 11 / Chrome 89, Android 12 / Chrome 100, Android 13 / Chrome 109
+ //
+ // On iOS Safari, form-control's "appearance: none" + "width: 100%" makes the input width too small
+ // Tested under iOS 16.2 / Safari 16.2
+ min-width: 85px; // Seems to be a good minimum safe width
+
+ // Add some height to date inputs on iOS
+ // https://github.com/twbs/bootstrap/issues/23307
+ // TODO: we can remove this workaround once https://bugs.webkit.org/show_bug.cgi?id=198959 is resolved
+ // Multiply line-height by 1em if it has no unit
+ height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
+
+ // Android Chrome type="date" is taller than the other inputs
+ // because of "margin: 1px 24px 1px 4px" inside the shadow DOM
+ // Tested under Android 11 / Chrome 89, Android 12 / Chrome 100, Android 13 / Chrome 109
+ margin: 0;
+ }
+
+ // Prevent excessive date input height in Webkit
+ // https://github.com/twbs/bootstrap/issues/34433
+ &::-webkit-datetime-edit {
+ display: block;
+ padding: 0;
+ }
+
+ // Placeholder
+ &::placeholder {
+ color: $input-placeholder-color;
+ // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
+ opacity: 1;
+ }
+
+ // Disabled inputs
+ //
+ // HTML5 says that controls under a fieldset > legend:first-child won't be
+ // disabled if the fieldset is disabled. Due to implementation difficulty, we
+ // don't honor that edge case; we style them as disabled anyway.
+ &:disabled {
+ color: $input-disabled-color;
+ background-color: $input-disabled-bg;
+ border-color: $input-disabled-border-color;
+ // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
+ opacity: 1;
+ }
+
+ // File input buttons theming
+ &::file-selector-button {
+ padding: $input-padding-y $input-padding-x;
+ margin: (-$input-padding-y) (-$input-padding-x);
+ margin-inline-end: $input-padding-x;
+ color: $form-file-button-color;
+ @include gradient-bg($form-file-button-bg);
+ pointer-events: none;
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+ border-inline-end-width: $input-border-width;
+ border-radius: 0; // stylelint-disable-line property-disallowed-list
+ @include transition($btn-transition);
+ }
+
+ &:hover:not(:disabled):not([readonly])::file-selector-button {
+ background-color: $form-file-button-hover-bg;
+ }
+}
+
+// Readonly controls as plain text
+//
+// Apply class to a readonly input to make it appear like regular plain
+// text (without any border, background color, focus indicator)
+
+.form-control-plaintext {
+ display: block;
+ width: 100%;
+ padding: $input-padding-y 0;
+ margin-bottom: 0; // match inputs if this class comes on inputs with default margins
+ line-height: $input-line-height;
+ color: $input-plaintext-color;
+ background-color: transparent;
+ border: solid transparent;
+ border-width: $input-border-width 0;
+
+ &:focus {
+ outline: 0;
+ }
+
+ &.form-control-sm,
+ &.form-control-lg {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+// Form control sizing
+//
+// Build on `.form-control` with modifier classes to decrease or increase the
+// height and font-size of form controls.
+//
+// Repeated in `_input_group.scss` to avoid Sass extend issues.
+
+.form-control-sm {
+ min-height: $input-height-sm;
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ @include font-size($input-font-size-sm);
+ @include border-radius($input-border-radius-sm);
+
+ &::file-selector-button {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
+ margin-inline-end: $input-padding-x-sm;
+ }
+}
+
+.form-control-lg {
+ min-height: $input-height-lg;
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ @include font-size($input-font-size-lg);
+ @include border-radius($input-border-radius-lg);
+
+ &::file-selector-button {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
+ margin-inline-end: $input-padding-x-lg;
+ }
+}
+
+// Make sure textareas don't shrink too much when resized
+// https://github.com/twbs/bootstrap/pull/29124
+// stylelint-disable selector-no-qualifying-type
+textarea {
+ &.form-control {
+ min-height: $input-height;
+ }
+
+ &.form-control-sm {
+ min-height: $input-height-sm;
+ }
+
+ &.form-control-lg {
+ min-height: $input-height-lg;
+ }
+}
+// stylelint-enable selector-no-qualifying-type
+
+.form-control-color {
+ width: $form-color-width;
+ height: $input-height;
+ padding: $input-padding-y;
+
+ &:not(:disabled):not([readonly]) {
+ cursor: pointer;
+ }
+
+ &::-moz-color-swatch {
+ border: 0 !important; // stylelint-disable-line declaration-no-important
+ @include border-radius($input-border-radius);
+ }
+
+ &::-webkit-color-swatch {
+ border: 0 !important; // stylelint-disable-line declaration-no-important
+ @include border-radius($input-border-radius);
+ }
+
+ &.form-control-sm { height: $input-height-sm; }
+ &.form-control-lg { height: $input-height-lg; }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-range.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-range.scss
new file mode 100644
index 00000000..4732213e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-range.scss
@@ -0,0 +1,91 @@
+// Range
+//
+// Style range inputs the same across browsers. Vendor-specific rules for pseudo
+// elements cannot be mixed. As such, there are no shared styles for focus or
+// active states on prefixed selectors.
+
+.form-range {
+ width: 100%;
+ height: add($form-range-thumb-height, $form-range-thumb-focus-box-shadow-width * 2);
+ padding: 0; // Need to reset padding
+ appearance: none;
+ background-color: transparent;
+
+ &:focus {
+ outline: 0;
+
+ // Pseudo-elements must be split across multiple rulesets to have an effect.
+ // No box-shadow() mixin for focus accessibility.
+ &::-webkit-slider-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ &::-moz-range-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ }
+
+ &::-moz-focus-outer {
+ border: 0;
+ }
+
+ &::-webkit-slider-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ margin-top: ($form-range-track-height - $form-range-thumb-height) * .5; // Webkit specific
+ appearance: none;
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($form-range-thumb-transition);
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-webkit-slider-runnable-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent; // Why?
+ cursor: $form-range-track-cursor;
+ background-color: $form-range-track-bg;
+ border-color: transparent;
+ @include border-radius($form-range-track-border-radius);
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &::-moz-range-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ appearance: none;
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($form-range-thumb-transition);
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-moz-range-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent;
+ cursor: $form-range-track-cursor;
+ background-color: $form-range-track-bg;
+ border-color: transparent; // Firefox specific?
+ @include border-radius($form-range-track-border-radius);
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &:disabled {
+ pointer-events: none;
+
+ &::-webkit-slider-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+
+ &::-moz-range-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-select.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-select.scss
new file mode 100644
index 00000000..69ace529
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-select.scss
@@ -0,0 +1,80 @@
+// Select
+//
+// Replaces the browser default select with a custom one, mostly pulled from
+// https://primer.github.io/.
+
+.form-select {
+ --#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator)};
+
+ display: block;
+ width: 100%;
+ padding: $form-select-padding-y $form-select-indicator-padding $form-select-padding-y $form-select-padding-x;
+ font-family: $form-select-font-family;
+ @include font-size($form-select-font-size);
+ font-weight: $form-select-font-weight;
+ line-height: $form-select-line-height;
+ color: $form-select-color;
+ appearance: none;
+ background-color: $form-select-bg;
+ background-image: var(--#{$prefix}form-select-bg-img), var(--#{$prefix}form-select-bg-icon, none);
+ background-repeat: no-repeat;
+ background-position: $form-select-bg-position;
+ background-size: $form-select-bg-size;
+ border: $form-select-border-width solid $form-select-border-color;
+ @include border-radius($form-select-border-radius, 0);
+ @include box-shadow($form-select-box-shadow);
+ @include transition($form-select-transition);
+
+ &:focus {
+ border-color: $form-select-focus-border-color;
+ outline: 0;
+ @if $enable-shadows {
+ @include box-shadow($form-select-box-shadow, $form-select-focus-box-shadow);
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: $form-select-focus-box-shadow;
+ }
+ }
+
+ &[multiple],
+ &[size]:not([size="1"]) {
+ padding-right: $form-select-padding-x;
+ background-image: none;
+ }
+
+ &:disabled {
+ color: $form-select-disabled-color;
+ background-color: $form-select-disabled-bg;
+ border-color: $form-select-disabled-border-color;
+ }
+
+ // Remove outline from select box in FF
+ &:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 $form-select-color;
+ }
+}
+
+.form-select-sm {
+ padding-top: $form-select-padding-y-sm;
+ padding-bottom: $form-select-padding-y-sm;
+ padding-left: $form-select-padding-x-sm;
+ @include font-size($form-select-font-size-sm);
+ @include border-radius($form-select-border-radius-sm);
+}
+
+.form-select-lg {
+ padding-top: $form-select-padding-y-lg;
+ padding-bottom: $form-select-padding-y-lg;
+ padding-left: $form-select-padding-x-lg;
+ @include font-size($form-select-font-size-lg);
+ @include border-radius($form-select-border-radius-lg);
+}
+
+@if $enable-dark-mode {
+ @include color-mode(dark) {
+ .form-select {
+ --#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator-dark)};
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-text.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-text.scss
new file mode 100644
index 00000000..f080d1a2
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_form-text.scss
@@ -0,0 +1,11 @@
+//
+// Form text
+//
+
+.form-text {
+ margin-top: $form-text-margin-top;
+ @include font-size($form-text-font-size);
+ font-style: $form-text-font-style;
+ font-weight: $form-text-font-weight;
+ color: $form-text-color;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_input-group.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_input-group.scss
new file mode 100644
index 00000000..58e4d409
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_input-group.scss
@@ -0,0 +1,132 @@
+//
+// Base styles
+//
+
+.input-group {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap; // For form validation feedback
+ align-items: stretch;
+ width: 100%;
+
+ > .form-control,
+ > .form-select,
+ > .form-floating {
+ position: relative; // For focus state's z-index
+ flex: 1 1 auto;
+ width: 1%;
+ min-width: 0; // https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size
+ }
+
+ // Bring the "active" form control to the top of surrounding elements
+ > .form-control:focus,
+ > .form-select:focus,
+ > .form-floating:focus-within {
+ z-index: 5;
+ }
+
+ // Ensure buttons are always above inputs for more visually pleasing borders.
+ // This isn't needed for `.input-group-text` since it shares the same border-color
+ // as our inputs.
+ .btn {
+ position: relative;
+ z-index: 2;
+
+ &:focus {
+ z-index: 5;
+ }
+ }
+}
+
+
+// Textual addons
+//
+// Serves as a catch-all element for any text or radio/checkbox input you wish
+// to prepend or append to an input.
+
+.input-group-text {
+ display: flex;
+ align-items: center;
+ padding: $input-group-addon-padding-y $input-group-addon-padding-x;
+ @include font-size($input-font-size); // Match inputs
+ font-weight: $input-group-addon-font-weight;
+ line-height: $input-line-height;
+ color: $input-group-addon-color;
+ text-align: center;
+ white-space: nowrap;
+ background-color: $input-group-addon-bg;
+ border: $input-border-width solid $input-group-addon-border-color;
+ @include border-radius($input-border-radius);
+}
+
+
+// Sizing
+//
+// Remix the default form control sizing classes into new ones for easier
+// manipulation.
+
+.input-group-lg > .form-control,
+.input-group-lg > .form-select,
+.input-group-lg > .input-group-text,
+.input-group-lg > .btn {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ @include font-size($input-font-size-lg);
+ @include border-radius($input-border-radius-lg);
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .form-select,
+.input-group-sm > .input-group-text,
+.input-group-sm > .btn {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ @include font-size($input-font-size-sm);
+ @include border-radius($input-border-radius-sm);
+}
+
+.input-group-lg > .form-select,
+.input-group-sm > .form-select {
+ padding-right: $form-select-padding-x + $form-select-indicator-padding;
+}
+
+
+// Rounded corners
+//
+// These rulesets must come after the sizing ones to properly override sm and lg
+// border-radius values when extending. They're more specific than we'd like
+// with the `.input-group >` part, but without it, we cannot override the sizing.
+
+// stylelint-disable-next-line no-duplicate-selectors
+.input-group {
+ &:not(.has-validation) {
+ > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
+ > .dropdown-toggle:nth-last-child(n + 3),
+ > .form-floating:not(:last-child) > .form-control,
+ > .form-floating:not(:last-child) > .form-select {
+ @include border-end-radius(0);
+ }
+ }
+
+ &.has-validation {
+ > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
+ > .dropdown-toggle:nth-last-child(n + 4),
+ > .form-floating:nth-last-child(n + 3) > .form-control,
+ > .form-floating:nth-last-child(n + 3) > .form-select {
+ @include border-end-radius(0);
+ }
+ }
+
+ $validation-messages: "";
+ @each $state in map-keys($form-validation-states) {
+ $validation-messages: $validation-messages + ":not(." + unquote($state) + "-tooltip)" + ":not(." + unquote($state) + "-feedback)";
+ }
+
+ > :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
+ margin-left: calc(#{$input-border-width} * -1); // stylelint-disable-line function-disallowed-list
+ @include border-start-radius(0);
+ }
+
+ > .form-floating:not(:first-child) > .form-control,
+ > .form-floating:not(:first-child) > .form-select {
+ @include border-start-radius(0);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_labels.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_labels.scss
new file mode 100644
index 00000000..39ecafcd
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_labels.scss
@@ -0,0 +1,36 @@
+//
+// Labels
+//
+
+.form-label {
+ margin-bottom: $form-label-margin-bottom;
+ @include font-size($form-label-font-size);
+ font-style: $form-label-font-style;
+ font-weight: $form-label-font-weight;
+ color: $form-label-color;
+}
+
+// For use with horizontal and inline forms, when you need the label (or legend)
+// text to align with the form controls.
+.col-form-label {
+ padding-top: add($input-padding-y, $input-border-width);
+ padding-bottom: add($input-padding-y, $input-border-width);
+ margin-bottom: 0; // Override the `` default
+ @include font-size(inherit); // Override the `` default
+ font-style: $form-label-font-style;
+ font-weight: $form-label-font-weight;
+ line-height: $input-line-height;
+ color: $form-label-color;
+}
+
+.col-form-label-lg {
+ padding-top: add($input-padding-y-lg, $input-border-width);
+ padding-bottom: add($input-padding-y-lg, $input-border-width);
+ @include font-size($input-font-size-lg);
+}
+
+.col-form-label-sm {
+ padding-top: add($input-padding-y-sm, $input-border-width);
+ padding-bottom: add($input-padding-y-sm, $input-border-width);
+ @include font-size($input-font-size-sm);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_validation.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_validation.scss
new file mode 100644
index 00000000..c48123a7
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/forms/_validation.scss
@@ -0,0 +1,12 @@
+// Form validation
+//
+// Provide feedback to users when form field values are valid or invalid. Works
+// primarily for client-side validation via scoped `:invalid` and `:valid`
+// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
+// server-side validation.
+
+// scss-docs-start form-validation-states-loop
+@each $state, $data in $form-validation-states {
+ @include form-validation-state($state, $data...);
+}
+// scss-docs-end form-validation-states-loop
diff --git a/src/angular-bootstrap-md/scss/bootstrap/utilities/_clearfix.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_clearfix.scss
similarity index 100%
rename from src/angular-bootstrap-md/scss/bootstrap/utilities/_clearfix.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_clearfix.scss
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_color-bg.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_color-bg.scss
new file mode 100644
index 00000000..1a3a4cff
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_color-bg.scss
@@ -0,0 +1,7 @@
+// All-caps `RGBA()` function used because of this Sass bug: https://github.com/sass/node-sass/issues/2251
+@each $color, $value in $theme-colors {
+ .text-bg-#{$color} {
+ color: color-contrast($value) if($enable-important-utilities, !important, null);
+ background-color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}bg-opacity, 1)) if($enable-important-utilities, !important, null);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_colored-links.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_colored-links.scss
new file mode 100644
index 00000000..5f868578
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_colored-links.scss
@@ -0,0 +1,30 @@
+// All-caps `RGBA()` function used because of this Sass bug: https://github.com/sass/node-sass/issues/2251
+@each $color, $value in $theme-colors {
+ .link-#{$color} {
+ color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}link-opacity, 1)) if($enable-important-utilities, !important, null);
+ text-decoration-color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}link-underline-opacity, 1)) if($enable-important-utilities, !important, null);
+
+ @if $link-shade-percentage != 0 {
+ &:hover,
+ &:focus {
+ $hover-color: if(color-contrast($value) == $color-contrast-light, shade-color($value, $link-shade-percentage), tint-color($value, $link-shade-percentage));
+ color: RGBA(#{to-rgb($hover-color)}, var(--#{$prefix}link-opacity, 1)) if($enable-important-utilities, !important, null);
+ text-decoration-color: RGBA(to-rgb($hover-color), var(--#{$prefix}link-underline-opacity, 1)) if($enable-important-utilities, !important, null);
+ }
+ }
+ }
+}
+
+// One-off special link helper as a bridge until v6
+.link-body-emphasis {
+ color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-opacity, 1)) if($enable-important-utilities, !important, null);
+ text-decoration-color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-underline-opacity, 1)) if($enable-important-utilities, !important, null);
+
+ @if $link-shade-percentage != 0 {
+ &:hover,
+ &:focus {
+ color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-opacity, .75)) if($enable-important-utilities, !important, null);
+ text-decoration-color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-underline-opacity, .75)) if($enable-important-utilities, !important, null);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_focus-ring.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_focus-ring.scss
new file mode 100644
index 00000000..26508a8d
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_focus-ring.scss
@@ -0,0 +1,5 @@
+.focus-ring:focus {
+ outline: 0;
+ // By default, there is no `--bs-focus-ring-x`, `--bs-focus-ring-y`, or `--bs-focus-ring-blur`, but we provide CSS variables with fallbacks to initial `0` values
+ box-shadow: var(--#{$prefix}focus-ring-x, 0) var(--#{$prefix}focus-ring-y, 0) var(--#{$prefix}focus-ring-blur, 0) var(--#{$prefix}focus-ring-width) var(--#{$prefix}focus-ring-color);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_icon-link.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_icon-link.scss
new file mode 100644
index 00000000..3f8bcb33
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_icon-link.scss
@@ -0,0 +1,25 @@
+.icon-link {
+ display: inline-flex;
+ gap: $icon-link-gap;
+ align-items: center;
+ text-decoration-color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, .5));
+ text-underline-offset: $icon-link-underline-offset;
+ backface-visibility: hidden;
+
+ > .bi {
+ flex-shrink: 0;
+ width: $icon-link-icon-size;
+ height: $icon-link-icon-size;
+ fill: currentcolor;
+ @include transition($icon-link-icon-transition);
+ }
+}
+
+.icon-link-hover {
+ &:hover,
+ &:focus-visible {
+ > .bi {
+ transform: var(--#{$prefix}icon-link-transform, $icon-link-icon-transform);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_position.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_position.scss
new file mode 100644
index 00000000..59103d94
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_position.scss
@@ -0,0 +1,36 @@
+// Shorthand
+
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: $zindex-fixed;
+}
+
+.fixed-bottom {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $zindex-fixed;
+}
+
+// Responsive sticky top and bottom
+@each $breakpoint in map-keys($grid-breakpoints) {
+ @include media-breakpoint-up($breakpoint) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ .sticky#{$infix}-top {
+ position: sticky;
+ top: 0;
+ z-index: $zindex-sticky;
+ }
+
+ .sticky#{$infix}-bottom {
+ position: sticky;
+ bottom: 0;
+ z-index: $zindex-sticky;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_ratio.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_ratio.scss
new file mode 100644
index 00000000..b6a7654c
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_ratio.scss
@@ -0,0 +1,26 @@
+// Credit: Nicolas Gallagher and SUIT CSS.
+
+.ratio {
+ position: relative;
+ width: 100%;
+
+ &::before {
+ display: block;
+ padding-top: var(--#{$prefix}aspect-ratio);
+ content: "";
+ }
+
+ > * {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+}
+
+@each $key, $ratio in $aspect-ratios {
+ .ratio-#{$key} {
+ --#{$prefix}aspect-ratio: #{$ratio};
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_stacks.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_stacks.scss
new file mode 100644
index 00000000..6cd237ae
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_stacks.scss
@@ -0,0 +1,15 @@
+// scss-docs-start stacks
+.hstack {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ align-self: stretch;
+}
+
+.vstack {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ align-self: stretch;
+}
+// scss-docs-end stacks
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_stretched-link.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_stretched-link.scss
new file mode 100644
index 00000000..71a1c755
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_stretched-link.scss
@@ -0,0 +1,15 @@
+//
+// Stretched link
+//
+
+.stretched-link {
+ &::#{$stretched-link-pseudo-element} {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $stretched-link-z-index;
+ content: "";
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_text-truncation.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_text-truncation.scss
new file mode 100644
index 00000000..6421dac9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_text-truncation.scss
@@ -0,0 +1,7 @@
+//
+// Text truncation
+//
+
+.text-truncate {
+ @include text-truncate();
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_visually-hidden.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_visually-hidden.scss
new file mode 100644
index 00000000..4760ff03
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_visually-hidden.scss
@@ -0,0 +1,8 @@
+//
+// Visually hidden
+//
+
+.visually-hidden,
+.visually-hidden-focusable:not(:focus):not(:focus-within) {
+ @include visually-hidden();
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_vr.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_vr.scss
new file mode 100644
index 00000000..b6f9d42c
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/helpers/_vr.scss
@@ -0,0 +1,8 @@
+.vr {
+ display: inline-block;
+ align-self: stretch;
+ width: $vr-border-width;
+ min-height: 1em;
+ background-color: currentcolor;
+ opacity: $hr-opacity;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_alert.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_alert.scss
new file mode 100644
index 00000000..fb524af1
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_alert.scss
@@ -0,0 +1,18 @@
+@include deprecate("`alert-variant()`", "v5.3.0", "v6.0.0");
+
+// scss-docs-start alert-variant-mixin
+@mixin alert-variant($background, $border, $color) {
+ --#{$prefix}alert-color: #{$color};
+ --#{$prefix}alert-bg: #{$background};
+ --#{$prefix}alert-border-color: #{$border};
+ --#{$prefix}alert-link-color: #{shade-color($color, 20%)};
+
+ @if $enable-gradients {
+ background-image: var(--#{$prefix}gradient);
+ }
+
+ .alert-link {
+ color: var(--#{$prefix}alert-link-color);
+ }
+}
+// scss-docs-end alert-variant-mixin
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_backdrop.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_backdrop.scss
new file mode 100644
index 00000000..9705ae9e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_backdrop.scss
@@ -0,0 +1,14 @@
+// Shared between modals and offcanvases
+@mixin overlay-backdrop($zindex, $backdrop-bg, $backdrop-opacity) {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: $zindex;
+ width: 100vw;
+ height: 100vh;
+ background-color: $backdrop-bg;
+
+ // Fade for backdrop
+ &.fade { opacity: 0; }
+ &.show { opacity: $backdrop-opacity; }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_banner.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_banner.scss
new file mode 100644
index 00000000..ee2c5842
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_banner.scss
@@ -0,0 +1,7 @@
+@mixin bsBanner($file) {
+ /*!
+ * Bootstrap #{$file} v5.3.2 (https://getbootstrap.com/)
+ * Copyright 2011-2023 The Bootstrap Authors
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_border-radius.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_border-radius.scss
new file mode 100644
index 00000000..616decbc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_border-radius.scss
@@ -0,0 +1,78 @@
+// stylelint-disable property-disallowed-list
+// Single side border-radius
+
+// Helper function to replace negative values with 0
+@function valid-radius($radius) {
+ $return: ();
+ @each $value in $radius {
+ @if type-of($value) == number {
+ $return: append($return, max($value, 0));
+ } @else {
+ $return: append($return, $value);
+ }
+ }
+ @return $return;
+}
+
+// scss-docs-start border-radius-mixins
+@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {
+ @if $enable-rounded {
+ border-radius: valid-radius($radius);
+ }
+ @else if $fallback-border-radius != false {
+ border-radius: $fallback-border-radius;
+ }
+}
+
+@mixin border-top-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-left-radius: valid-radius($radius);
+ border-top-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-end-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-right-radius: valid-radius($radius);
+ border-bottom-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-bottom-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-bottom-right-radius: valid-radius($radius);
+ border-bottom-left-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-start-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-left-radius: valid-radius($radius);
+ border-bottom-left-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-top-start-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-left-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-top-end-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-top-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-bottom-end-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-bottom-right-radius: valid-radius($radius);
+ }
+}
+
+@mixin border-bottom-start-radius($radius: $border-radius) {
+ @if $enable-rounded {
+ border-bottom-left-radius: valid-radius($radius);
+ }
+}
+// scss-docs-end border-radius-mixins
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_box-shadow.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_box-shadow.scss
new file mode 100644
index 00000000..4172541f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_box-shadow.scss
@@ -0,0 +1,18 @@
+@mixin box-shadow($shadow...) {
+ @if $enable-shadows {
+ $result: ();
+
+ @each $value in $shadow {
+ @if $value != null {
+ $result: append($result, $value, "comma");
+ }
+ @if $value == none and length($shadow) > 1 {
+ @warn "The keyword 'none' must be used as a single argument.";
+ }
+ }
+
+ @if (length($result) > 0) {
+ box-shadow: $result;
+ }
+ }
+}
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_breakpoints.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_breakpoints.scss
similarity index 80%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_breakpoints.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_breakpoints.scss
index d1ad684c..286be893 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/mixins/_breakpoints.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_breakpoints.scss
@@ -2,7 +2,7 @@
//
// Breakpoints are defined as a map of (name: minimum width), order from small to large:
//
-// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
+// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)
//
// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
@@ -10,44 +10,47 @@
//
// >> breakpoint-next(sm)
// md
-// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
// md
-// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
+// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl))
// md
@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
$n: index($breakpoint-names, $name);
+ @if not $n {
+ @error "breakpoint `#{$name}` not found in `#{$breakpoints}`";
+ }
@return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
}
// Minimum breakpoint width. Null for the smallest (first) breakpoint.
//
-// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
// 576px
@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
$min: map-get($breakpoints, $name);
@return if($min != 0, $min, null);
}
-// Maximum breakpoint width. Null for the largest (last) breakpoint.
-// The maximum value is calculated as the minimum of the next one less 0.02px
-// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.
+// Maximum breakpoint width.
+// The maximum value is reduced by 0.02px to work around the limitations of
+// `min-` and `max-` prefixes and viewports with fractional widths.
// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
// See https://bugs.webkit.org/show_bug.cgi?id=178261
//
-// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
// 767.98px
@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
- $next: breakpoint-next($name, $breakpoints);
- @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);
+ $max: map-get($breakpoints, $name);
+ @return if($max and $max > 0, $max - .02, null);
}
-// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
+// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
// Useful for making responsive utilities.
//
-// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
// "" (Returns a blank string)
-// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
+// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
// "-sm"
@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
@return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
@@ -104,8 +107,9 @@
// No minimum for the smallest breakpoint, and no maximum for the largest one.
// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
- $min: breakpoint-min($name, $breakpoints);
- $max: breakpoint-max($name, $breakpoints);
+ $min: breakpoint-min($name, $breakpoints);
+ $next: breakpoint-next($name, $breakpoints);
+ $max: breakpoint-max($next, $breakpoints);
@if $min != null and $max != null {
@media (min-width: $min) and (max-width: $max) {
@@ -116,7 +120,7 @@
@content;
}
} @else if $min == null {
- @include media-breakpoint-down($name, $breakpoints) {
+ @include media-breakpoint-down($next, $breakpoints) {
@content;
}
}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_buttons.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_buttons.scss
new file mode 100644
index 00000000..cf087fda
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_buttons.scss
@@ -0,0 +1,70 @@
+// Button variants
+//
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+
+// scss-docs-start btn-variant-mixin
+@mixin button-variant(
+ $background,
+ $border,
+ $color: color-contrast($background),
+ $hover-background: if($color == $color-contrast-light, shade-color($background, $btn-hover-bg-shade-amount), tint-color($background, $btn-hover-bg-tint-amount)),
+ $hover-border: if($color == $color-contrast-light, shade-color($border, $btn-hover-border-shade-amount), tint-color($border, $btn-hover-border-tint-amount)),
+ $hover-color: color-contrast($hover-background),
+ $active-background: if($color == $color-contrast-light, shade-color($background, $btn-active-bg-shade-amount), tint-color($background, $btn-active-bg-tint-amount)),
+ $active-border: if($color == $color-contrast-light, shade-color($border, $btn-active-border-shade-amount), tint-color($border, $btn-active-border-tint-amount)),
+ $active-color: color-contrast($active-background),
+ $disabled-background: $background,
+ $disabled-border: $border,
+ $disabled-color: color-contrast($disabled-background)
+) {
+ --#{$prefix}btn-color: #{$color};
+ --#{$prefix}btn-bg: #{$background};
+ --#{$prefix}btn-border-color: #{$border};
+ --#{$prefix}btn-hover-color: #{$hover-color};
+ --#{$prefix}btn-hover-bg: #{$hover-background};
+ --#{$prefix}btn-hover-border-color: #{$hover-border};
+ --#{$prefix}btn-focus-shadow-rgb: #{to-rgb(mix($color, $border, 15%))};
+ --#{$prefix}btn-active-color: #{$active-color};
+ --#{$prefix}btn-active-bg: #{$active-background};
+ --#{$prefix}btn-active-border-color: #{$active-border};
+ --#{$prefix}btn-active-shadow: #{$btn-active-box-shadow};
+ --#{$prefix}btn-disabled-color: #{$disabled-color};
+ --#{$prefix}btn-disabled-bg: #{$disabled-background};
+ --#{$prefix}btn-disabled-border-color: #{$disabled-border};
+}
+// scss-docs-end btn-variant-mixin
+
+// scss-docs-start btn-outline-variant-mixin
+@mixin button-outline-variant(
+ $color,
+ $color-hover: color-contrast($color),
+ $active-background: $color,
+ $active-border: $color,
+ $active-color: color-contrast($active-background)
+) {
+ --#{$prefix}btn-color: #{$color};
+ --#{$prefix}btn-border-color: #{$color};
+ --#{$prefix}btn-hover-color: #{$color-hover};
+ --#{$prefix}btn-hover-bg: #{$active-background};
+ --#{$prefix}btn-hover-border-color: #{$active-border};
+ --#{$prefix}btn-focus-shadow-rgb: #{to-rgb($color)};
+ --#{$prefix}btn-active-color: #{$active-color};
+ --#{$prefix}btn-active-bg: #{$active-background};
+ --#{$prefix}btn-active-border-color: #{$active-border};
+ --#{$prefix}btn-active-shadow: #{$btn-active-box-shadow};
+ --#{$prefix}btn-disabled-color: #{$color};
+ --#{$prefix}btn-disabled-bg: transparent;
+ --#{$prefix}btn-disabled-border-color: #{$color};
+ --#{$prefix}gradient: none;
+}
+// scss-docs-end btn-outline-variant-mixin
+
+// scss-docs-start btn-size-mixin
+@mixin button-size($padding-y, $padding-x, $font-size, $border-radius) {
+ --#{$prefix}btn-padding-y: #{$padding-y};
+ --#{$prefix}btn-padding-x: #{$padding-x};
+ @include rfs($font-size, --#{$prefix}btn-font-size);
+ --#{$prefix}btn-border-radius: #{$border-radius};
+}
+// scss-docs-end btn-size-mixin
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_caret.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_caret.scss
new file mode 100644
index 00000000..be731165
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_caret.scss
@@ -0,0 +1,69 @@
+// scss-docs-start caret-mixins
+@mixin caret-down($width: $caret-width) {
+ border-top: $width solid;
+ border-right: $width solid transparent;
+ border-bottom: 0;
+ border-left: $width solid transparent;
+}
+
+@mixin caret-up($width: $caret-width) {
+ border-top: 0;
+ border-right: $width solid transparent;
+ border-bottom: $width solid;
+ border-left: $width solid transparent;
+}
+
+@mixin caret-end($width: $caret-width) {
+ border-top: $width solid transparent;
+ border-right: 0;
+ border-bottom: $width solid transparent;
+ border-left: $width solid;
+}
+
+@mixin caret-start($width: $caret-width) {
+ border-top: $width solid transparent;
+ border-right: $width solid;
+ border-bottom: $width solid transparent;
+}
+
+@mixin caret(
+ $direction: down,
+ $width: $caret-width,
+ $spacing: $caret-spacing,
+ $vertical-align: $caret-vertical-align
+) {
+ @if $enable-caret {
+ &::after {
+ display: inline-block;
+ margin-left: $spacing;
+ vertical-align: $vertical-align;
+ content: "";
+ @if $direction == down {
+ @include caret-down($width);
+ } @else if $direction == up {
+ @include caret-up($width);
+ } @else if $direction == end {
+ @include caret-end($width);
+ }
+ }
+
+ @if $direction == start {
+ &::after {
+ display: none;
+ }
+
+ &::before {
+ display: inline-block;
+ margin-right: $spacing;
+ vertical-align: $vertical-align;
+ content: "";
+ @include caret-start($width);
+ }
+ }
+
+ &:empty::after {
+ margin-left: 0;
+ }
+ }
+}
+// scss-docs-end caret-mixins
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_clearfix.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_clearfix.scss
similarity index 63%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_clearfix.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_clearfix.scss
index 11a977b7..ffc62bb2 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/mixins/_clearfix.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_clearfix.scss
@@ -1,3 +1,4 @@
+// scss-docs-start clearfix
@mixin clearfix() {
&::after {
display: block;
@@ -5,3 +6,4 @@
content: "";
}
}
+// scss-docs-end clearfix
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_color-mode.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_color-mode.scss
new file mode 100644
index 00000000..e8cbf4cb
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_color-mode.scss
@@ -0,0 +1,21 @@
+// scss-docs-start color-mode-mixin
+@mixin color-mode($mode: light, $root: false) {
+ @if $color-mode-type == "media-query" {
+ @if $root == true {
+ @media (prefers-color-scheme: $mode) {
+ :root {
+ @content;
+ }
+ }
+ } @else {
+ @media (prefers-color-scheme: $mode) {
+ @content;
+ }
+ }
+ } @else {
+ [data-mdb-theme="#{$mode}"] {
+ @content;
+ }
+ }
+}
+// scss-docs-end color-mode-mixin
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_color-scheme.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_color-scheme.scss
new file mode 100644
index 00000000..90497aa0
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_color-scheme.scss
@@ -0,0 +1,7 @@
+// scss-docs-start mixin-color-scheme
+@mixin color-scheme($name) {
+ @media (prefers-color-scheme: #{$name}) {
+ @content;
+ }
+}
+// scss-docs-end mixin-color-scheme
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_container.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_container.scss
new file mode 100644
index 00000000..b9f33519
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_container.scss
@@ -0,0 +1,11 @@
+// Container mixins
+
+@mixin make-container($gutter: $container-padding-x) {
+ --#{$prefix}gutter-x: #{$gutter};
+ --#{$prefix}gutter-y: 0;
+ width: 100%;
+ padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list
+ padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list
+ margin-right: auto;
+ margin-left: auto;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_deprecate.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_deprecate.scss
new file mode 100644
index 00000000..df070bc5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_deprecate.scss
@@ -0,0 +1,10 @@
+// Deprecate mixin
+//
+// This mixin can be used to deprecate mixins or functions.
+// `$enable-deprecation-messages` is a global variable, `$ignore-warning` is a variable that can be passed to
+// some deprecated mixins to suppress the warning (for example if the mixin is still be used in the current version of Bootstrap)
+@mixin deprecate($name, $deprecate-version, $remove-version, $ignore-warning: false) {
+ @if ($enable-deprecation-messages != false and $ignore-warning != true) {
+ @warn "#{$name} has been deprecated as of #{$deprecate-version}. It will be removed entirely in #{$remove-version}.";
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_forms.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_forms.scss
new file mode 100644
index 00000000..d47b1e41
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_forms.scss
@@ -0,0 +1,153 @@
+// This mixin uses an `if()` technique to be compatible with Dart Sass
+// See https://github.com/sass/sass/issues/1873#issuecomment-152293725 for more details
+
+// scss-docs-start form-validation-mixins
+@mixin form-validation-state-selector($state) {
+ @if ($state == "valid" or $state == "invalid") {
+ .was-validated #{if(&, "&", "")}:#{$state},
+ #{if(&, "&", "")}.is-#{$state} {
+ @content;
+ }
+ } @else {
+ #{if(&, "&", "")}.is-#{$state} {
+ @content;
+ }
+ }
+}
+
+@mixin form-validation-state(
+ $state,
+ $color,
+ $icon,
+ $tooltip-color: color-contrast($color),
+ $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity),
+ $focus-box-shadow: 0 0 $input-btn-focus-blur $input-focus-width rgba($color, $input-btn-focus-color-opacity),
+ $border-color: $color
+) {
+ .#{$state}-feedback {
+ display: none;
+ width: 100%;
+ margin-top: $form-feedback-margin-top;
+ @include font-size($form-feedback-font-size);
+ font-style: $form-feedback-font-style;
+ color: $color;
+ }
+
+ .#{$state}-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%; // Contain to parent when possible
+ padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x;
+ margin-top: .1rem;
+ @include font-size($form-feedback-tooltip-font-size);
+ line-height: $form-feedback-tooltip-line-height;
+ color: $tooltip-color;
+ background-color: $tooltip-bg-color;
+ @include border-radius($form-feedback-tooltip-border-radius);
+ }
+
+ @include form-validation-state-selector($state) {
+ ~ .#{$state}-feedback,
+ ~ .#{$state}-tooltip {
+ display: block;
+ }
+ }
+
+ .form-control {
+ @include form-validation-state-selector($state) {
+ border-color: $border-color;
+
+ @if $enable-validation-icons {
+ padding-right: $input-height-inner;
+ background-image: escape-svg($icon);
+ background-repeat: no-repeat;
+ background-position: right $input-height-inner-quarter center;
+ background-size: $input-height-inner-half $input-height-inner-half;
+ }
+
+ &:focus {
+ border-color: $border-color;
+ box-shadow: $focus-box-shadow;
+ }
+ }
+ }
+
+ // stylelint-disable-next-line selector-no-qualifying-type
+ textarea.form-control {
+ @include form-validation-state-selector($state) {
+ @if $enable-validation-icons {
+ padding-right: $input-height-inner;
+ background-position: top $input-height-inner-quarter right $input-height-inner-quarter;
+ }
+ }
+ }
+
+ .form-select {
+ @include form-validation-state-selector($state) {
+ border-color: $border-color;
+
+ @if $enable-validation-icons {
+ &:not([multiple]):not([size]),
+ &:not([multiple])[size="1"] {
+ --#{$prefix}form-select-bg-icon: #{escape-svg($icon)};
+ padding-right: $form-select-feedback-icon-padding-end;
+ background-position: $form-select-bg-position, $form-select-feedback-icon-position;
+ background-size: $form-select-bg-size, $form-select-feedback-icon-size;
+ }
+ }
+
+ &:focus {
+ border-color: $border-color;
+ box-shadow: $focus-box-shadow;
+ }
+ }
+ }
+
+ .form-control-color {
+ @include form-validation-state-selector($state) {
+ @if $enable-validation-icons {
+ width: add($form-color-width, $input-height-inner);
+ }
+ }
+ }
+
+ .form-check-input {
+ @include form-validation-state-selector($state) {
+ border-color: $border-color;
+
+ &:checked {
+ background-color: $color;
+ }
+
+ &:focus {
+ box-shadow: $focus-box-shadow;
+ }
+
+ ~ .form-check-label {
+ color: $color;
+ }
+ }
+ }
+ .form-check-inline .form-check-input {
+ ~ .#{$state}-feedback {
+ margin-left: .5em;
+ }
+ }
+
+ .input-group {
+ > .form-control:not(:focus),
+ > .form-select:not(:focus),
+ > .form-floating:not(:focus-within) {
+ @include form-validation-state-selector($state) {
+ @if $state == "valid" {
+ z-index: 3;
+ } @else if $state == "invalid" {
+ z-index: 4;
+ }
+ }
+ }
+ }
+}
+// scss-docs-end form-validation-mixins
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_gradients.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_gradients.scss
similarity index 80%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_gradients.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_gradients.scss
index 88c4d64b..608e18df 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/mixins/_gradients.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_gradients.scss
@@ -1,45 +1,47 @@
// Gradients
-@mixin gradient-bg($color) {
+// scss-docs-start gradient-bg-mixin
+@mixin gradient-bg($color: null) {
+ background-color: $color;
+
@if $enable-gradients {
- background: $color linear-gradient(180deg, mix($body-bg, $color, 15%), $color) repeat-x;
- } @else {
- background-color: $color;
+ background-image: var(--#{$prefix}gradient);
}
}
+// scss-docs-end gradient-bg-mixin
+// scss-docs-start gradient-mixins
// Horizontal gradient, from left to right
//
// Creates two color stops, start and end, by specifying a color and position for each color stop.
@mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent);
- background-repeat: repeat-x;
}
// Vertical gradient, from top to bottom
//
// Creates two color stops, start and end, by specifying a color and position for each color stop.
-@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) {
+@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: null, $end-percent: null) {
background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent);
- background-repeat: repeat-x;
}
@mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) {
background-image: linear-gradient($deg, $start-color, $end-color);
- background-repeat: repeat-x;
}
+
@mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color);
- background-repeat: no-repeat;
}
+
@mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) {
background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color);
- background-repeat: no-repeat;
}
+
@mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) {
background-image: radial-gradient(circle, $inner-color, $outer-color);
- background-repeat: no-repeat;
}
+
@mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) {
background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent);
}
+// scss-docs-end gradient-mixins
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_grid.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_grid.scss
new file mode 100644
index 00000000..99b1e7de
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_grid.scss
@@ -0,0 +1,151 @@
+// Grid system
+//
+// Generate semantic grid columns with these mixins.
+
+@mixin make-row($gutter: $grid-gutter-width) {
+ --#{$prefix}gutter-x: #{$gutter};
+ --#{$prefix}gutter-y: 0;
+ display: flex;
+ flex-wrap: wrap;
+ // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed
+ margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list
+ margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list
+ margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list
+}
+
+@mixin make-col-ready() {
+ // Add box sizing if only the grid is loaded
+ box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);
+ // Prevent columns from becoming too narrow when at smaller grid tiers by
+ // always setting `width: 100%;`. This works because we set the width
+ // later on to override this initial width.
+ flex-shrink: 0;
+ width: 100%;
+ max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid
+ padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list
+ padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list
+ margin-top: var(--#{$prefix}gutter-y);
+}
+
+@mixin make-col($size: false, $columns: $grid-columns) {
+ @if $size {
+ flex: 0 0 auto;
+ width: percentage(divide($size, $columns));
+
+ } @else {
+ flex: 1 1 0;
+ max-width: 100%;
+ }
+}
+
+@mixin make-col-auto() {
+ flex: 0 0 auto;
+ width: auto;
+}
+
+@mixin make-col-offset($size, $columns: $grid-columns) {
+ $num: divide($size, $columns);
+ margin-left: if($num == 0, 0, percentage($num));
+}
+
+// Row columns
+//
+// Specify on a parent element(e.g., .row) to force immediate children into NN
+// number of columns. Supports wrapping to new lines, but does not do a Masonry
+// style grid.
+@mixin row-cols($count) {
+ > * {
+ flex: 0 0 auto;
+ width: percentage(divide(1, $count));
+ }
+}
+
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `$grid-columns`.
+
+@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
+ @each $breakpoint in map-keys($breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $breakpoints);
+
+ @include media-breakpoint-up($breakpoint, $breakpoints) {
+ // Provide basic `.col-{bp}` classes for equal-width flexbox columns
+ .col#{$infix} {
+ flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
+ }
+
+ .row-cols#{$infix}-auto > * {
+ @include make-col-auto();
+ }
+
+ @if $grid-row-columns > 0 {
+ @for $i from 1 through $grid-row-columns {
+ .row-cols#{$infix}-#{$i} {
+ @include row-cols($i);
+ }
+ }
+ }
+
+ .col#{$infix}-auto {
+ @include make-col-auto();
+ }
+
+ @if $columns > 0 {
+ @for $i from 1 through $columns {
+ .col#{$infix}-#{$i} {
+ @include make-col($i, $columns);
+ }
+ }
+
+ // `$columns - 1` because offsetting by the width of an entire row isn't possible
+ @for $i from 0 through ($columns - 1) {
+ @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
+ .offset#{$infix}-#{$i} {
+ @include make-col-offset($i, $columns);
+ }
+ }
+ }
+ }
+
+ // Gutters
+ //
+ // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns.
+ @each $key, $value in $gutters {
+ .g#{$infix}-#{$key},
+ .gx#{$infix}-#{$key} {
+ --#{$prefix}gutter-x: #{$value};
+ }
+
+ .g#{$infix}-#{$key},
+ .gy#{$infix}-#{$key} {
+ --#{$prefix}gutter-y: #{$value};
+ }
+ }
+ }
+ }
+}
+
+@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) {
+ @each $breakpoint in map-keys($breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $breakpoints);
+
+ @include media-breakpoint-up($breakpoint, $breakpoints) {
+ @if $columns > 0 {
+ @for $i from 1 through $columns {
+ .g-col#{$infix}-#{$i} {
+ grid-column: auto / span $i;
+ }
+ }
+
+ // Start with `1` because `0` is and invalid value.
+ // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
+ @for $i from 1 through ($columns - 1) {
+ .g-start#{$infix}-#{$i} {
+ grid-column-start: $i;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_image.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_image.scss
new file mode 100644
index 00000000..e1df779f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_image.scss
@@ -0,0 +1,16 @@
+// Image Mixins
+// - Responsive image
+// - Retina image
+
+
+// Responsive image
+//
+// Keep images from scaling beyond the width of their parents.
+
+@mixin img-fluid {
+ // Part 1: Set a maximum relative to the parent
+ max-width: 100%;
+ // Part 2: Override the height to auto, otherwise images will be stretched
+ // when setting a width and height attribute on the img element.
+ height: auto;
+}
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_list-group.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_list-group.scss
similarity index 60%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_list-group.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_list-group.scss
index cd47a4e9..6274f343 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/mixins/_list-group.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_list-group.scss
@@ -1,14 +1,18 @@
+@include deprecate("`list-group-item-variant()`", "v5.3.0", "v6.0.0");
+
// List Groups
+// scss-docs-start list-group-mixin
@mixin list-group-item-variant($state, $background, $color) {
.list-group-item-#{$state} {
color: $color;
background-color: $background;
&.list-group-item-action {
- @include hover-focus {
+ &:hover,
+ &:focus {
color: $color;
- background-color: darken($background, 5%);
+ background-color: shade-color($background, 10%);
}
&.active {
@@ -19,3 +23,4 @@
}
}
}
+// scss-docs-end list-group-mixin
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_lists.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_lists.scss
similarity index 100%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_lists.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_lists.scss
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_pagination.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_pagination.scss
new file mode 100644
index 00000000..0d657964
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_pagination.scss
@@ -0,0 +1,10 @@
+// Pagination
+
+// scss-docs-start pagination-mixin
+@mixin pagination-size($padding-y, $padding-x, $font-size, $border-radius) {
+ --#{$prefix}pagination-padding-x: #{$padding-x};
+ --#{$prefix}pagination-padding-y: #{$padding-y};
+ @include rfs($font-size, --#{$prefix}pagination-font-size);
+ --#{$prefix}pagination-border-radius: #{$border-radius};
+}
+// scss-docs-end pagination-mixin
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_reset-text.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_reset-text.scss
similarity index 73%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_reset-text.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_reset-text.scss
index 71edb006..f5bd1afe 100644
--- a/src/angular-bootstrap-md/scss/bootstrap/mixins/_reset-text.scss
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_reset-text.scss
@@ -1,17 +1,17 @@
@mixin reset-text {
font-family: $font-family-base;
- // We deliberately do NOT reset font-size or word-wrap.
+ // We deliberately do NOT reset font-size or overflow-wrap / word-wrap.
font-style: normal;
font-weight: $font-weight-normal;
line-height: $line-height-base;
text-align: left; // Fallback for where `start` is not supported
- text-align: start; // stylelint-disable-line declaration-block-no-duplicate-properties
+ text-align: start;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-break: normal;
- word-spacing: normal;
white-space: normal;
+ word-spacing: normal;
line-break: auto;
}
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_resize.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_resize.scss
similarity index 100%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_resize.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_resize.scss
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_table-variants.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_table-variants.scss
new file mode 100644
index 00000000..5fe1b9b2
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_table-variants.scss
@@ -0,0 +1,24 @@
+// scss-docs-start table-variant
+@mixin table-variant($state, $background) {
+ .table-#{$state} {
+ $color: color-contrast(opaque($body-bg, $background));
+ $hover-bg: mix($color, $background, percentage($table-hover-bg-factor));
+ $striped-bg: mix($color, $background, percentage($table-striped-bg-factor));
+ $active-bg: mix($color, $background, percentage($table-active-bg-factor));
+ $table-border-color: mix($color, $background, percentage($table-border-factor));
+
+ --#{$prefix}table-color: #{$color};
+ --#{$prefix}table-bg: #{$background};
+ --#{$prefix}table-border-color: #{$table-border-color};
+ --#{$prefix}table-striped-bg: #{$striped-bg};
+ --#{$prefix}table-striped-color: #{color-contrast($striped-bg)};
+ --#{$prefix}table-active-bg: #{$active-bg};
+ --#{$prefix}table-active-color: #{color-contrast($active-bg)};
+ --#{$prefix}table-hover-bg: #{$hover-bg};
+ --#{$prefix}table-hover-color: #{color-contrast($hover-bg)};
+
+ color: var(--#{$prefix}table-color);
+ border-color: var(--#{$prefix}table-border-color);
+ }
+}
+// scss-docs-end table-variant
diff --git a/src/angular-bootstrap-md/scss/bootstrap/mixins/_text-truncate.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_text-truncate.scss
similarity index 100%
rename from src/angular-bootstrap-md/scss/bootstrap/mixins/_text-truncate.scss
rename to projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_text-truncate.scss
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_transition.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_transition.scss
new file mode 100644
index 00000000..d437f6d8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_transition.scss
@@ -0,0 +1,26 @@
+// stylelint-disable property-disallowed-list
+@mixin transition($transition...) {
+ @if length($transition) == 0 {
+ $transition: $transition-base;
+ }
+
+ @if length($transition) > 1 {
+ @each $value in $transition {
+ @if $value == null or $value == none {
+ @warn "The keyword 'none' or 'null' must be used as a single argument.";
+ }
+ }
+ }
+
+ @if $enable-transitions {
+ @if nth($transition, 1) != null {
+ transition: $transition;
+ }
+
+ @if $enable-reduced-motion and nth($transition, 1) != null and nth($transition, 1) != none {
+ @media (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_utilities.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_utilities.scss
new file mode 100644
index 00000000..4795e894
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_utilities.scss
@@ -0,0 +1,97 @@
+// Utility generator
+// Used to generate utilities & print utilities
+@mixin generate-utility($utility, $infix: "", $is-rfs-media-query: false) {
+ $values: map-get($utility, values);
+
+ // If the values are a list or string, convert it into a map
+ @if type-of($values) == "string" or type-of(nth($values, 1)) != "list" {
+ $values: zip($values, $values);
+ }
+
+ @each $key, $value in $values {
+ $properties: map-get($utility, property);
+
+ // Multiple properties are possible, for example with vertical or horizontal margins or paddings
+ @if type-of($properties) == "string" {
+ $properties: append((), $properties);
+ }
+
+ // Use custom class if present
+ $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));
+ $property-class: if($property-class == null, "", $property-class);
+
+ // Use custom CSS variable name if present, otherwise default to `class`
+ $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class));
+
+ // State params to generate pseudo-classes
+ $state: if(map-has-key($utility, state), map-get($utility, state), ());
+
+ $infix: if($property-class == "" and str-slice($infix, 1, 1) == "-", str-slice($infix, 2), $infix);
+
+ // Don't prefix if value key is null (e.g. with shadow class)
+ $property-class-modifier: if($key, if($property-class == "" and $infix == "", "", "-") + $key, "");
+
+ @if map-get($utility, rfs) {
+ // Inside the media query
+ @if $is-rfs-media-query {
+ $val: rfs-value($value);
+
+ // Do not render anything if fluid and non fluid values are the same
+ $value: if($val == rfs-fluid-value($value), null, $val);
+ }
+ @else {
+ $value: rfs-fluid-value($value);
+ }
+ }
+
+ $is-css-var: map-get($utility, css-var);
+ $is-local-vars: map-get($utility, local-vars);
+ $is-rtl: map-get($utility, rtl);
+
+ @if $value != null {
+ @if $is-rtl == false {
+ /* rtl:begin:remove */
+ }
+
+ @if $is-css-var {
+ .#{$property-class + $infix + $property-class-modifier} {
+ --#{$prefix}#{$css-variable-name}: #{$value};
+ }
+
+ @each $pseudo in $state {
+ .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
+ --#{$prefix}#{$css-variable-name}: #{$value};
+ }
+ }
+ } @else {
+ .#{$property-class + $infix + $property-class-modifier} {
+ @each $property in $properties {
+ @if $is-local-vars {
+ @each $local-var, $variable in $is-local-vars {
+ --#{$prefix}#{$local-var}: #{$variable};
+ }
+ }
+ #{$property}: $value if($enable-important-utilities, !important, null);
+ }
+ }
+
+ @each $pseudo in $state {
+ .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
+ @each $property in $properties {
+ @if $is-local-vars {
+ @each $local-var, $variable in $is-local-vars {
+ --#{$prefix}#{$local-var}: #{$variable};
+ }
+ }
+ #{$property}: $value if($enable-important-utilities, !important, null);
+ }
+ }
+ }
+ }
+
+ @if $is-rtl == false {
+ /* rtl:end:remove */
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_visually-hidden.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_visually-hidden.scss
new file mode 100644
index 00000000..082aeec9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/mixins/_visually-hidden.scss
@@ -0,0 +1,33 @@
+// stylelint-disable declaration-no-important
+
+// Hide content visually while keeping it accessible to assistive technologies
+//
+// See: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/
+// See: https://kittygiraudel.com/2016/10/13/css-hide-and-seek/
+
+@mixin visually-hidden() {
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important; // Fix for https://github.com/twbs/bootstrap/issues/25686
+ overflow: hidden !important;
+ clip: rect(0, 0, 0, 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+
+ // Fix for positioned table caption that could become anonymous cells
+ &:not(caption) {
+ position: absolute !important;
+ }
+}
+
+// Use to only display content when it's focused, or one of its child elements is focused
+// (i.e. when focus is within the element/container that the class was applied to)
+//
+// Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+
+@mixin visually-hidden-focusable() {
+ &:not(:focus):not(:focus-within) {
+ @include visually-hidden();
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/utilities/_api.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/utilities/_api.scss
new file mode 100644
index 00000000..62e1d398
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/utilities/_api.scss
@@ -0,0 +1,47 @@
+// Loop over each breakpoint
+@each $breakpoint in map-keys($grid-breakpoints) {
+
+ // Generate media query if needed
+ @include media-breakpoint-up($breakpoint) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ // Loop over each utility property
+ @each $key, $utility in $utilities {
+ // The utility can be disabled with `false`, thus check if the utility is a map first
+ // Only proceed if responsive media queries are enabled or if it's the base media query
+ @if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") {
+ @include generate-utility($utility, $infix);
+ }
+ }
+ }
+}
+
+// RFS rescaling
+@media (min-width: $rfs-mq-value) {
+ @each $breakpoint in map-keys($grid-breakpoints) {
+ $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
+
+ @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {
+ // Loop over each utility property
+ @each $key, $utility in $utilities {
+ // The utility can be disabled with `false`, thus check if the utility is a map first
+ // Only proceed if responsive media queries are enabled or if it's the base media query
+ @if type-of($utility) == "map" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == "") {
+ @include generate-utility($utility, $infix, true);
+ }
+ }
+ }
+ }
+}
+
+
+// Print utilities
+@media print {
+ @each $key, $utility in $utilities {
+ // The utility can be disabled with `false`, thus check if the utility is a map first
+ // Then check if the utility needs print styles
+ @if type-of($utility) == "map" and map-get($utility, print) == true {
+ @include generate-utility($utility, "-print");
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/bootstrap/vendor/_rfs.scss b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/vendor/_rfs.scss
new file mode 100644
index 00000000..53196abc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/bootstrap/vendor/_rfs.scss
@@ -0,0 +1,402 @@
+// stylelint-disable scss/dimension-no-non-numeric-values
+
+// SCSS RFS mixin
+//
+// Automated responsive values for font sizes, paddings, margins and much more
+//
+// Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE)
+
+// Configuration
+
+// Base value
+$rfs-base-value: 1.25rem !default;
+$rfs-unit: rem !default;
+
+@if $rfs-unit != rem and $rfs-unit != px {
+ @error "`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`.";
+}
+
+// Breakpoint at where values start decreasing if screen width is smaller
+$rfs-breakpoint: 1200px !default;
+$rfs-breakpoint-unit: px !default;
+
+@if $rfs-breakpoint-unit !=
+ px and
+ $rfs-breakpoint-unit !=
+ em and
+ $rfs-breakpoint-unit !=
+ rem
+{
+ @error "`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.";
+}
+
+// Resize values based on screen height and width
+$rfs-two-dimensional: false !default;
+
+// Factor of decrease
+$rfs-factor: 10 !default;
+
+@if type-of($rfs-factor) != number or $rfs-factor <= 1 {
+ @error "`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.";
+}
+
+// Mode. Possibilities: "min-media-query", "max-media-query"
+$rfs-mode: min-media-query !default;
+
+// Generate enable or disable classes. Possibilities: false, "enable" or "disable"
+$rfs-class: false !default;
+
+// 1 rem = $rfs-rem-value px
+$rfs-rem-value: 16 !default;
+
+// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14
+$rfs-safari-iframe-resize-bug-fix: false !default;
+
+// Disable RFS by setting $enable-rfs to false
+$enable-rfs: true !default;
+
+// Cache $rfs-base-value unit
+$rfs-base-value-unit: unit($rfs-base-value);
+
+@function divide($dividend, $divisor, $precision: 10) {
+ $sign: if(
+ $dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0,
+ 1,
+ -1
+ );
+ $dividend: abs($dividend);
+ $divisor: abs($divisor);
+ @if $dividend == 0 {
+ @return 0;
+ }
+ @if $divisor == 0 {
+ @error "Cannot divide by 0";
+ }
+ $remainder: $dividend;
+ $result: 0;
+ $factor: 10;
+ @while ($remainder > 0 and $precision >= 0) {
+ $quotient: 0;
+ @while ($remainder >= $divisor) {
+ $remainder: $remainder - $divisor;
+ $quotient: $quotient + 1;
+ }
+ $result: $result * 10 + $quotient;
+ $factor: $factor * 0.1;
+ $remainder: $remainder * 10;
+ $precision: $precision - 1;
+ @if ($precision < 0 and $remainder >= $divisor * 5) {
+ $result: $result + 1;
+ }
+ }
+ $result: $result * $factor * $sign;
+ $dividend-unit: unit($dividend);
+ $divisor-unit: unit($divisor);
+ $unit-map: (
+ "px": 1px,
+ "rem": 1rem,
+ "em": 1em,
+ "%": 1%,
+ );
+ @if (
+ $dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)
+ ) {
+ $result: $result * map-get($unit-map, $dividend-unit);
+ }
+ @return $result;
+}
+
+// Remove px-unit from $rfs-base-value for calculations
+@if $rfs-base-value-unit == px {
+ $rfs-base-value: divide($rfs-base-value, $rfs-base-value * 0 + 1);
+} @else if $rfs-base-value-unit == rem {
+ $rfs-base-value: divide(
+ $rfs-base-value,
+ divide($rfs-base-value * 0 + 1, $rfs-rem-value)
+ );
+}
+
+// Cache $rfs-breakpoint unit to prevent multiple calls
+$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);
+
+// Remove unit from $rfs-breakpoint for calculations
+@if $rfs-breakpoint-unit-cache == px {
+ $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1);
+} @else if
+ $rfs-breakpoint-unit-cache ==
+ rem or
+ $rfs-breakpoint-unit-cache ==
+ "em"
+{
+ $rfs-breakpoint: divide(
+ $rfs-breakpoint,
+ divide($rfs-breakpoint * 0 + 1, $rfs-rem-value)
+ );
+}
+
+// Calculate the media query value
+$rfs-mq-value: if(
+ $rfs-breakpoint-unit == px,
+ #{$rfs-breakpoint}px,
+ #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit}
+);
+$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width);
+$rfs-mq-property-height: if(
+ $rfs-mode == max-media-query,
+ max-height,
+ min-height
+);
+
+// Internal mixin used to determine which media query needs to be used
+@mixin _rfs-media-query {
+ @if $rfs-two-dimensional {
+ @if $rfs-mode == max-media-query {
+ @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {
+ @content;
+ }
+ } @else {
+ @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {
+ @content;
+ }
+ }
+ } @else {
+ @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) {
+ @content;
+ }
+ }
+}
+
+// Internal mixin that adds disable classes to the selector if needed.
+@mixin _rfs-rule {
+ @if $rfs-class == disable and $rfs-mode == max-media-query {
+ // Adding an extra class increases specificity, which prevents the media query to override the property
+ &,
+ .disable-rfs &,
+ &.disable-rfs {
+ @content;
+ }
+ } @else if $rfs-class == enable and $rfs-mode == min-media-query {
+ .enable-rfs &,
+ &.enable-rfs {
+ @content;
+ }
+ } @else {
+ @content;
+ }
+}
+
+// Internal mixin that adds enable classes to the selector if needed.
+@mixin _rfs-media-query-rule {
+ @if $rfs-class == enable {
+ @if $rfs-mode == min-media-query {
+ @content;
+ }
+
+ @include _rfs-media-query() {
+ .enable-rfs &,
+ &.enable-rfs {
+ @content;
+ }
+ }
+ } @else {
+ @if $rfs-class == disable and $rfs-mode == min-media-query {
+ .disable-rfs &,
+ &.disable-rfs {
+ @content;
+ }
+ }
+ @include _rfs-media-query() {
+ @content;
+ }
+ }
+}
+
+// Helper function to get the formatted non-responsive value
+@function rfs-value($values) {
+ // Convert to list
+ $values: if(type-of($values) != list, ($values), $values);
+
+ $val: "";
+
+ // Loop over each value and calculate value
+ @each $value in $values {
+ @if $value == 0 {
+ $val: $val + " 0";
+ } @else {
+ // Cache $value unit
+ $unit: if(type-of($value) == "number", unit($value), false);
+
+ @if $unit == px {
+ // Convert to rem if needed
+ $val: $val +
+ " " +
+ if(
+ $rfs-unit == rem,
+ #{divide($value, $value * 0 + $rfs-rem-value)}rem,
+ $value
+ );
+ } @else if $unit == rem {
+ // Convert to px if needed
+ $val: $val +
+ " " +
+ if(
+ $rfs-unit == px,
+ #{divide($value, $value * 0 + 1) * $rfs-rem-value}px,
+ $value
+ );
+ } @else {
+ // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
+ $val: $val + " " + $value;
+ }
+ }
+ }
+
+ // Remove first space
+ @return unquote(str-slice($val, 2));
+}
+
+// Helper function to get the responsive value calculated by RFS
+@function rfs-fluid-value($values) {
+ // Convert to list
+ $values: if(type-of($values) != list, ($values), $values);
+
+ $val: "";
+
+ // Loop over each value and calculate value
+ @each $value in $values {
+ @if $value == 0 {
+ $val: $val + " 0";
+ } @else {
+ // Cache $value unit
+ $unit: if(type-of($value) == "number", unit($value), false);
+
+ // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
+ @if not $unit or $unit != px and $unit != rem {
+ $val: $val + " " + $value;
+ } @else {
+ // Remove unit from $value for calculations
+ $value: divide(
+ $value,
+ $value * 0 + if($unit == px, 1, divide(1, $rfs-rem-value))
+ );
+
+ // Only add the media query if the value is greater than the minimum value
+ @if abs($value) <= $rfs-base-value or not $enable-rfs {
+ $val: $val +
+ " " +
+ if(
+ $rfs-unit == rem,
+ #{divide($value, $rfs-rem-value)}rem,
+ #{$value}px
+ );
+ } @else {
+ // Calculate the minimum value
+ $value-min: $rfs-base-value +
+ divide(abs($value) - $rfs-base-value, $rfs-factor);
+
+ // Calculate difference between $value and the minimum value
+ $value-diff: abs($value) - $value-min;
+
+ // Base value formatting
+ $min-width: if(
+ $rfs-unit == rem,
+ #{divide($value-min, $rfs-rem-value)}rem,
+ #{$value-min}px
+ );
+
+ // Use negative value if needed
+ $min-width: if($value < 0, -$min-width, $min-width);
+
+ // Use `vmin` if two-dimensional is enabled
+ $variable-unit: if($rfs-two-dimensional, vmin, vw);
+
+ // Calculate the variable width between 0 and $rfs-breakpoint
+ $variable-width: #{divide(
+ $value-diff * 100,
+ $rfs-breakpoint
+ )}#{$variable-unit};
+
+ // Return the calculated value
+ $val: $val +
+ " calc(" +
+ $min-width +
+ if($value < 0, " - ", " + ") +
+ $variable-width +
+ ")";
+ }
+ }
+ }
+ }
+
+ // Remove first space
+ @return unquote(str-slice($val, 2));
+}
+
+// RFS mixin
+@mixin rfs($values, $property: font-size) {
+ @if $values != null {
+ $val: rfs-value($values);
+ $fluid-val: rfs-fluid-value($values);
+
+ // Do not print the media query if responsive & non-responsive values are the same
+ @if $val == $fluid-val {
+ #{$property}: $val;
+ } @else {
+ @include _rfs-rule() {
+ #{$property}: if($rfs-mode == max-media-query, $val, $fluid-val);
+
+ // Include safari iframe resize fix if needed
+ min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);
+ }
+
+ @include _rfs-media-query-rule() {
+ #{$property}: if($rfs-mode == max-media-query, $fluid-val, $val);
+ }
+ }
+ }
+}
+
+// Shorthand helper mixins
+@mixin font-size($value) {
+ @include rfs($value);
+}
+
+@mixin padding($value) {
+ @include rfs($value, padding);
+}
+
+@mixin padding-top($value) {
+ @include rfs($value, padding-top);
+}
+
+@mixin padding-right($value) {
+ @include rfs($value, padding-right);
+}
+
+@mixin padding-bottom($value) {
+ @include rfs($value, padding-bottom);
+}
+
+@mixin padding-left($value) {
+ @include rfs($value, padding-left);
+}
+
+@mixin margin($value) {
+ @include rfs($value, margin);
+}
+
+@mixin margin-top($value) {
+ @include rfs($value, margin-top);
+}
+
+@mixin margin-right($value) {
+ @include rfs($value, margin-right);
+}
+
+@mixin margin-bottom($value) {
+ @include rfs($value, margin-bottom);
+}
+
+@mixin margin-left($value) {
+ @include rfs($value, margin-left);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_accordion.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_accordion.scss
new file mode 100644
index 00000000..c913cd24
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_accordion.scss
@@ -0,0 +1,45 @@
+// Accordion
+
+.accordion-button {
+ &:not(.collapsed) {
+ &:focus {
+ box-shadow: var(--#{$prefix}accordion-btn-focus-box-shadow);
+ }
+ }
+
+ &:focus {
+ border-color: var(--#{$prefix}accordion-btn-focus-border-color);
+ outline: 0;
+ box-shadow: none;
+ }
+}
+
+.accordion-flush {
+ --#{$prefix}accordion-flush-btn-box-shadow: #{$accordion-flush-button-box-shadow};
+ --#{$prefix}accordion-flush-border-bottom: #{$accordion-flush-item-border-bottom};
+
+ .accordion-button:not(.collapsed) {
+ box-shadow: var(--#{$prefix}accordion-flush-btn-box-shadow);
+ }
+ .accordion-item {
+ border-bottom: var(--#{$prefix}accordion-flush-border-bottom);
+ }
+}
+
+.accordion-borderless {
+ --#{$prefix}accordion-borderless-btn-border-radius: #{$accordion-borderless-button-border-radius};
+ --#{$prefix}accordion-borderless-btn-bg: #{$accordion-borderless-button-bgc};
+ --#{$prefix}accordion-borderless-btn-color: #{$accordion-borderless-button-color};
+
+ .accordion-item {
+ border: 0;
+ .accordion-button {
+ border-radius: var(--#{$prefix}accordion-borderless-btn-border-radius);
+ &:not(.collapsed) {
+ background-color: var(--#{$prefix}accordion-borderless-btn-bg);
+ color: var(--#{$prefix}accordion-borderless-btn-color);
+ box-shadow: none;
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_alert.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_alert.scss
new file mode 100644
index 00000000..f986a02b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_alert.scss
@@ -0,0 +1,39 @@
+// Alert
+
+.alert {
+ border: 0;
+}
+
+.alert-absolute {
+ position: absolute;
+}
+
+.alert-fixed {
+ --#{$prefix}alert-fixed-z-index: #{$alert-zindex};
+
+ position: fixed;
+ z-index: var(--#{$prefix}alert-fixed-z-index);
+}
+
+.parent-alert-relative {
+ position: relative;
+}
+
+@each $color, $value in $theme-colors {
+ .alert-#{$color} {
+ background-color: var(--#{$prefix}#{$color}-bg-subtle);
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+
+ i {
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+ }
+
+ .alert-link {
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+
+ &:hover {
+ color: rgba(var(--#{$prefix}#{$color}-text-emphasis), var(--#{$prefix}text-hover-opacity));
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_badge.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_badge.scss
new file mode 100644
index 00000000..e8123d55
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_badge.scss
@@ -0,0 +1,51 @@
+//
+// Badge styles
+//
+// Additional badge options tailored to the MDB style
+
+// A "dot" badge, which is useful, for example, for icons
+.badge-dot {
+ --#{$prefix}badge-border-radius: #{$badge-dot-border-radius};
+ --#{$prefix}badge-height: #{$badge-dot-height};
+ --#{$prefix}badge-width: #{$badge-dot-width};
+ --#{$prefix}badge-margin-left: #{$badge-dot-margin-left};
+
+ position: absolute;
+ min-width: 0;
+ width: var(--#{$prefix}badge-width);
+ height: var(--#{$prefix}badge-height);
+ border-radius: var(--#{$prefix}badge-border-radius);
+ padding: 0;
+ margin-left: var(--#{$prefix}badge-margin-left);
+
+ &:empty {
+ display: inline-block;
+ }
+}
+
+// "Notification" badge that shows the number of notifications, e.g. the number of unread messages
+.badge-notification {
+ --#{$prefix}badge-font-size: #{$badge-notification-font-size};
+ --#{$prefix}badge-padding-x: #{$badge-notification-padding-x};
+ --#{$prefix}badge-padding-y: #{$badge-notification-padding-y};
+ --#{$prefix}badge-margin-top: #{$badge-notification-margin-top};
+ --#{$prefix}badge-margin-left: #{$badge-notification-margin-left};
+
+ position: absolute;
+ font-size: var(--#{$prefix}badge-font-size);
+ padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x);
+ margin-top: var(--#{$prefix}badge-margin-top);
+ margin-left: var(--#{$prefix}badge-margin-left);
+}
+
+// Contextual colors options
+@each $color, $value in $theme-colors {
+ .badge-#{$color} {
+ background-color: var(--#{$prefix}#{$color}-bg-subtle);
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+
+ i {
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_breadcrumb.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_breadcrumb.scss
new file mode 100644
index 00000000..de6e66e6
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_breadcrumb.scss
@@ -0,0 +1,35 @@
+//
+// Breadcrumb styles
+//
+
+.navbar {
+ .breadcrumb {
+ // scss-docs-start breadcrumb-css-vars
+ --#{$prefix}breadcrumb-item-color: #{$breadcrumb-item-color};
+ --#{$prefix}breadcrumb-item-hover-color: #{$breadcrumb-item-hover-color};
+ --#{$prefix}breadcrumb-item-before-color: #{$breadcrumb-item-before-color};
+ --#{$prefix}breadcrumb-item-transition: #{$breadcrumb-item-transition};
+ // scss-docs-end breadcrumb-css-vars
+
+ background-color: transparent;
+ margin-bottom: 0;
+
+ .breadcrumb-item {
+ a {
+ color: var(--#{$prefix}breadcrumb-item-color);
+ transition: var(--#{$prefix}breadcrumb-item-transition);
+
+ &:hover,
+ &:focus {
+ color: var(--#{$prefix}breadcrumb-item-hover-color);
+ }
+ }
+
+ + .breadcrumb-item {
+ &:before {
+ color: var(--#{$prefix}breadcrumb-item-before-color);
+ }
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_button-group.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_button-group.scss
new file mode 100644
index 00000000..39e0a4a2
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_button-group.scss
@@ -0,0 +1,100 @@
+//
+// Button group styles
+//
+
+.btn-group,
+.btn-group-vertical {
+ --#{$prefix}btn-box-shadow: #{$btn-box-shadow};
+ --#{$prefix}btn-hover-box-shadow: #{$btn-hover-box-shadow};
+ --#{$prefix}btn-focus-box-shadow: #{$btn-focus-box-shadow};
+ --#{$prefix}btn-active-box-shadow: #{$btn-active-box-shadow};
+ --#{$prefix}btn-group-transition: #{$btn-group-transition};
+
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ transition: var(--#{$prefix}btn-group-transition);
+
+ &:hover {
+ box-shadow: var(--#{$prefix}btn-hover-box-shadow);
+ }
+
+ &:focus,
+ &.focus {
+ box-shadow: var(--#{$prefix}btn-focus-box-shadow);
+ }
+
+ &:active,
+ &.active {
+ box-shadow: var(--#{$prefix}btn-active-box-shadow);
+
+ &:focus {
+ box-shadow: var(--#{$prefix}btn-focus-box-shadow);
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ border: 0;
+ }
+
+ > .btn {
+ box-shadow: none;
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ box-shadow: none !important;
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ box-shadow: none;
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ box-shadow: none;
+
+ &:focus {
+ box-shadow: none;
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: none;
+ }
+ }
+
+ > .btn-group {
+ box-shadow: none;
+ }
+
+ > .btn-link,
+ > .btn-tertiary {
+ &:first-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ &:last-child {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ }
+}
+
+.btn-group,
+.btn-group-lg > .btn,
+.btn-group-sm > .btn {
+ --#{$prefix}btn-border-radius: #{$btn-border-radius};
+
+ border-radius: var(--#{$prefix}btn-border-radius);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_buttons.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_buttons.scss
new file mode 100644
index 00000000..1e857443
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_buttons.scss
@@ -0,0 +1,742 @@
+//
+// Button base styles
+//
+
+.btn {
+ // scss-docs-start btn-css-vars
+ --#{$prefix}btn-padding-top: #{$btn-padding-top};
+ --#{$prefix}btn-padding-bottom: #{$btn-padding-bottom};
+ --#{$prefix}btn-border-width: 0;
+ --#{$prefix}btn-border-color: none;
+ --#{$prefix}btn-border-radius: #{$btn-border-radius};
+ --#{$prefix}btn-box-shadow: #{$btn-box-shadow};
+ --#{$prefix}btn-hover-box-shadow: #{$btn-hover-box-shadow};
+ --#{$prefix}btn-focus-box-shadow: #{$btn-focus-box-shadow};
+ --#{$prefix}btn-active-box-shadow: #{$btn-active-box-shadow};
+ // scss-docs-end btn-css-vars
+
+ padding-top: var(--#{$prefix}btn-padding-top);
+ padding-bottom: var(--#{$prefix}btn-padding-bottom);
+ text-transform: uppercase;
+ vertical-align: bottom;
+ border: 0;
+ @include border-radius(var(--#{$prefix}btn-border-radius));
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ box-shadow: var(--#{$prefix}btn-hover-box-shadow);
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ box-shadow: var(--#{$prefix}btn-focus-box-shadow);
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ box-shadow: var(--#{$prefix}btn-active-box-shadow);
+
+ &:focus {
+ box-shadow: var(--#{$prefix}btn-focus-box-shadow);
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+}
+
+//
+// Outline styles
+//
+
+[class*='btn-outline-'] {
+ // scss-docs-start btn-outline-css-vars
+ --#{$prefix}btn-padding-top: #{$btn-outline-padding-top};
+ --#{$prefix}btn-padding-bottom: #{$btn-outline-padding-bottom};
+ --#{$prefix}btn-padding-x: #{$btn-outline-padding-x};
+ --#{$prefix}btn-border-width: #{$btn-outline-border-width};
+ --#{$prefix}btn-line-height: #{$btn-line-height};
+ // scss-docs-end btn-outline-css-vars
+
+ padding: var(--#{$prefix}btn-padding-top) var(--#{$prefix}btn-padding-x)
+ var(--#{$prefix}btn-padding-bottom);
+ border-width: var(--#{$prefix}btn-border-width);
+ border-style: solid;
+ box-shadow: none;
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ box-shadow: none;
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ box-shadow: none;
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ box-shadow: none;
+
+ &:focus {
+ box-shadow: none;
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: none;
+ }
+
+ &.btn-lg {
+ @include button-outline-size(
+ $btn-outline-padding-top-lg,
+ $btn-outline-padding-bottom-lg,
+ $btn-outline-padding-x-lg,
+ $btn-outline-font-size-lg,
+ $btn-outline-line-height-lg
+ );
+ }
+
+ &.btn-sm {
+ @include button-outline-size(
+ $btn-outline-padding-top-sm,
+ $btn-outline-padding-bottom-sm,
+ $btn-outline-padding-x-sm,
+ $btn-outline-font-size-sm,
+ $btn-outline-line-height-sm
+ );
+ }
+}
+
+//
+// Alternate buttons
+//
+
+// scss-docs-start btn-secondary
+.btn-secondary {
+ box-shadow: none;
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ box-shadow: none !important;
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ box-shadow: none;
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ box-shadow: none;
+
+ &:focus {
+ box-shadow: none;
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: none;
+ }
+}
+// scss-docs-end btn-secondary
+
+// scss-docs-start btn-variant-loop
+@each $color, $value in $theme-colors {
+ .btn-#{$color} {
+ @if $color == 'secondary' {
+ @include button-variant(
+ tint-color($primary, $btn-secondary-bg-tint-amount),
+ $value,
+ $color: shade-color($primary, $btn-secondary-color-shade-amount),
+ $hover-background:
+ shade-color(
+ tint-color($primary, $btn-secondary-bg-tint-amount),
+ $btn-hover-bg-shade-amount
+ ),
+ $focus-background:
+ shade-color(
+ tint-color($primary, $btn-secondary-bg-tint-amount),
+ $btn-hover-bg-shade-amount
+ ),
+ $active-background:
+ shade-color(
+ tint-color($primary, $btn-secondary-bg-tint-amount),
+ $btn-hover-bg-shade-amount
+ )
+ );
+ } @else if $color == 'light' {
+ @include button-variant(
+ $light-bg-subtle,
+ $light-border-subtle,
+ $color: $light-text-emphasis,
+ $hover-background: shade-color($light-bg-subtle, $btn-hover-bg-shade-amount),
+ $hover-border: shade-color($light-border-subtle, $btn-hover-border-shade-amount),
+ $active-background: shade-color($light-bg-subtle, $btn-active-bg-shade-amount),
+ $active-border: shade-color($light-border-subtle, $btn-active-border-shade-amount)
+ );
+ } @else if $color == 'dark' {
+ @include button-variant(
+ $dark-bg-subtle,
+ $dark-border-subtle,
+ $color: $dark-text-emphasis,
+ $hover-background: tint-color($dark-bg-subtle, $btn-hover-bg-tint-amount),
+ $hover-border: tint-color($dark-border-subtle, $btn-hover-border-tint-amount),
+ $active-background: tint-color($dark-bg-subtle, $btn-active-bg-tint-amount),
+ $active-border: tint-color($dark-border-subtle, $btn-active-border-tint-amount)
+ );
+ } @else {
+ @include button-variant($value, $value);
+ }
+
+ @if $color == 'secondary' {
+ --#{$prefix}btn-box-shadow-state: transparent;
+ } @else {
+ --#{$prefix}btn-box-shadow-state: #{$btn-contextual-box-shadow-state-first-part
+ rgba(shade-color($value, $btn-hover-bg-shade-amount), 0.3),
+ $btn-contextual-box-shadow-state-second-part
+ rgba(shade-color($value, $btn-hover-bg-shade-amount), 0.2)};
+ }
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ box-shadow: var(--#{$prefix}btn-box-shadow-state);
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ box-shadow: var(--#{$prefix}btn-box-shadow-state);
+ background-color: var(--#{$prefix}btn-focus-bg);
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ box-shadow: var(--#{$prefix}btn-box-shadow-state);
+
+ &:focus {
+ box-shadow: var(--#{$prefix}btn-box-shadow-state);
+ }
+
+ &:hover {
+ background-color: var(--#{$prefix}btn-active-bg);
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+ }
+
+ [data-mdb-theme='dark'] .btn-#{$color} {
+ @if $color != 'secondary' {
+ box-shadow: 0 4px 9px -4px rgba($black, 0.35);
+ &:hover,
+ &:active,
+ &:focus {
+ box-shadow: 0 4px 18px -2px rgba($black, 0.7);
+ }
+ }
+ }
+}
+
+// scss-docs-end btn-variant-loop
+
+// scss-docs-start btn-outline-variant-loop
+@each $color, $value in $theme-colors {
+ .btn-outline-#{$color} {
+ @if $color == 'secondary' {
+ @include button-outline-variant(
+ shade-color($primary, $btn-secondary-color-shade-amount),
+ shade-color($primary, $btn-secondary-color-shade-amount),
+ shade-color($primary, $btn-secondary-color-shade-amount),
+ shade-color($primary, $btn-secondary-color-shade-amount)
+ );
+
+ --#{$prefix}btn-outline-border-color: #{tint-color($primary, $btn-secondary-bg-tint-amount)};
+ --#{$prefix}btn-outline-focus-border-color: #{shade-color(
+ tint-color($primary, $btn-secondary-bg-tint-amount),
+ $btn-focus-bg-shade-amount
+ )};
+ --#{$prefix}btn-outline-hover-border-color: #{shade-color(
+ tint-color($primary, $btn-secondary-bg-tint-amount),
+ $btn-hover-bg-shade-amount
+ )};
+
+ border-color: var(--#{$prefix}btn-outline-border-color);
+ } @else {
+ @include button-outline-variant(
+ $value,
+ shade-color($value, $btn-hover-bg-shade-amount),
+ shade-color($value, $btn-focus-bg-shade-amount),
+ shade-color($value, $btn-active-bg-shade-amount)
+ );
+
+ --#{$prefix}btn-outline-border-color: #{$value};
+ --#{$prefix}btn-outline-focus-border-color: #{shade-color(
+ $value,
+ $btn-outline-focus-bg-shade-amount
+ )};
+ --#{$prefix}btn-outline-hover-border-color: #{shade-color(
+ $value,
+ $btn-outline-hover-bg-shade-amount
+ )};
+
+ border-color: var(--#{$prefix}btn-outline-border-color);
+ }
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ border-color: var(--#{$prefix}btn-outline-hover-border-color);
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ border-color: var(--#{$prefix}btn-outline-focus-border-color);
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ border-color: var(--#{$prefix}btn-outline-active-border-color);
+
+ &:focus {
+ border-color: var(--#{$prefix}btn-outline-focus-border-color);
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ border-color: var(--#{$prefix}btn-outline-border-color);
+ }
+ }
+
+ [data-mdb-theme='dark'] .btn-outline-#{$color} {
+ @if $color == 'secondary' {
+ @include button-outline-variant(
+ tint-color($primary, 70%),
+ tint-color($primary, 60%),
+ tint-color($primary, 60%),
+ tint-color($primary, 60%),
+ shade-color($primary, 60%),
+ shade-color($primary, 60%),
+ shade-color($primary, 60%)
+ );
+
+ --#{$prefix}btn-outline-border-color: #{tint-color($primary, 50%)};
+ --#{$prefix}btn-outline-focus-border-color: #{shade-color(
+ tint-color($primary, 50%),
+ $btn-focus-bg-shade-amount
+ )};
+ --#{$prefix}btn-outline-hover-border-color: #{shade-color(
+ tint-color($primary, 50%),
+ $btn-hover-bg-shade-amount
+ )};
+
+ border-color: var(--#{$prefix}btn-outline-border-color);
+ } @else {
+ @include button-outline-variant(
+ tint-color($value, 20%),
+ shade-color($value, $btn-hover-bg-shade-amount),
+ shade-color($value, $btn-focus-bg-shade-amount),
+ shade-color($value, $btn-active-bg-shade-amount),
+ shade-color($value, 70%),
+ shade-color($value, 70%),
+ shade-color($value, 70%)
+ );
+ }
+ }
+}
+
+// scss-docs-end btn-outline-variant-loop
+
+//
+// Link buttons
+//
+
+// Make a button look and behave like a link
+.btn-link {
+ --#{$prefix}btn-font-weight: #{$font-weight-medium};
+ --#{$prefix}btn-color: #{$btn-link-color};
+ --#{$prefix}btn-hover-color: #{$btn-link-hover-color};
+ --#{$prefix}btn-hover-bg: #{$btn-link-hover-bg};
+ --#{$prefix}btn-focus-color: #{$btn-link-focus-color};
+ --#{$prefix}btn-active-color: #{$btn-link-active-color};
+ --#{$prefix}btn-disabled-color: #{$btn-link-disabled-color};
+ --#{$prefix}btn-box-shadow: none;
+
+ text-decoration: $link-decoration;
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ text-decoration: $link-hover-decoration;
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ color: var(--#{$prefix}btn-focus-color);
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ color: var(--#{$prefix}btn-active-color);
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+
+ &:focus {
+ color: var(--#{$prefix}btn-focus-color);
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+}
+
+.btn-tertiary {
+ --#{$prefix}btn-font-weight: #{$font-weight-medium};
+ --#{$prefix}btn-color: #{$btn-link-color};
+ --#{$prefix}btn-hover-color: #{$btn-link-hover-color};
+ --#{$prefix}btn-hover-bg: transparent;
+ --#{$prefix}btn-focus-color: #{$btn-link-focus-color};
+ --#{$prefix}btn-active-color: #{$btn-link-active-color};
+ --#{$prefix}btn-disabled-color: #{$btn-link-disabled-color};
+ --#{$prefix}btn-box-shadow: none;
+
+ padding-left: 0px;
+ padding-right: 0px;
+ text-decoration: $link-decoration;
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+
+ :not(.btn-check) + &:hover,
+ &:first-child:hover,
+ &:focus-visible,
+ &:hover {
+ text-decoration: $link-hover-decoration;
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+
+ .btn-check:focus-visible + &,
+ .btn-check:focus + &,
+ &:focus {
+ color: var(--#{$prefix}btn-focus-color);
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+
+ .btn-check:checked + &,
+ .btn-check:active + &,
+ &:active,
+ &.active,
+ &.show {
+ color: var(--#{$prefix}btn-active-color);
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+
+ &:focus {
+ color: var(--#{$prefix}btn-focus-color);
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+ }
+
+ &:disabled,
+ &.disabled,
+ fieldset:disabled & {
+ box-shadow: var(--#{$prefix}btn-box-shadow);
+ }
+}
+
+[data-mdb-theme='dark'] .btn-secondary {
+ --#{$prefix}btn-bg: #{tint-color($primary, 60%)};
+ --#{$prefix}btn-hover-bg: #{tint-color($primary, 50%)};
+ --#{$prefix}btn-focus-bg: #{tint-color($primary, 50%)};
+ --#{$prefix}btn-active-bg: #{tint-color($primary, 50%)};
+}
+
+[data-mdb-theme='dark'] .btn-link {
+ --#{$prefix}btn-color: #{$secondary};
+ --#{$prefix}btn-hover-color: #{tint-color($secondary, 30%)};
+ --#{$prefix}btn-hover-bg: #{shade-color($secondary, 60%)};
+ --#{$prefix}btn-focus-color: #{tint-color($secondary, 30%)};
+ --#{$prefix}btn-active-color: #{tint-color($secondary, 30%)};
+}
+
+[data-mdb-theme='dark'] .btn-tertiary {
+ --#{$prefix}btn-color: #{$secondary};
+ --#{$prefix}btn-hover-color: #{tint-color($secondary, 30%)};
+ --#{$prefix}btn-focus-color: #{tint-color($secondary, 30%)};
+ --#{$prefix}btn-active-color: #{tint-color($secondary, 30%)};
+}
+
+//
+// Button Sizes
+//
+
+.btn-lg {
+ @include button-size(
+ $btn-padding-top-lg,
+ $btn-padding-bottom-lg,
+ $btn-padding-x-lg,
+ $btn-font-size-lg,
+ $btn-line-height-lg
+ );
+}
+
+.btn-sm {
+ @include button-size(
+ $btn-padding-top-sm,
+ $btn-padding-bottom-sm,
+ $btn-padding-x-sm,
+ $btn-font-size-sm,
+ $btn-line-height-sm
+ );
+}
+
+//
+// Rounded option
+//
+
+.btn-rounded {
+ --#{$prefix}btn-border-radius: #{$btn-rounded-border-radius};
+
+ border-radius: var(--#{$prefix}btn-border-radius);
+}
+
+//
+// Floating option
+//
+
+.btn-floating,
+[class*='btn-outline-'].btn-floating {
+ --#{$prefix}btn-border-radius: #{$btn-floating-border-radius};
+
+ border-radius: var(--#{$prefix}btn-border-radius);
+ padding: 0;
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.btn-floating {
+ --#{$prefix}btn-width: #{$btn-floating-width};
+ --#{$prefix}btn-height: #{$btn-floating-height};
+ --#{$prefix}btn-icon-width: #{$btn-floating-icon-width};
+ --#{$prefix}btn-icon-line-height: #{$btn-floating-icon-line-height};
+ --#{$prefix}btn-width-lg: #{$btn-floating-width-lg};
+ --#{$prefix}btn-height-lg: #{$btn-floating-height-lg};
+ --#{$prefix}btn-icon-width-lg: #{$btn-floating-icon-width-lg};
+ --#{$prefix}btn-icon-line-height-lg: #{$btn-floating-icon-line-height-lg};
+ --#{$prefix}btn-width-sm: #{$btn-floating-width-sm};
+ --#{$prefix}btn-height-sm: #{$btn-floating-height-sm};
+ --#{$prefix}btn-icon-width-sm: #{$btn-floating-icon-width-sm};
+ --#{$prefix}btn-icon-line-height-sm: #{$btn-floating-icon-line-height-sm};
+
+ width: var(--#{$prefix}btn-width);
+ height: var(--#{$prefix}btn-height);
+
+ .fas,
+ .far,
+ .fab {
+ width: var(--#{$prefix}btn-icon-width);
+ line-height: var(--#{$prefix}btn-icon-line-height);
+ }
+
+ &.btn-lg {
+ width: var(--#{$prefix}btn-width-lg);
+ height: var(--#{$prefix}btn-height-lg);
+
+ .fas,
+ .far,
+ .fab {
+ width: var(--#{$prefix}btn-icon-width-lg);
+ line-height: var(--#{$prefix}btn-icon-line-height-lg);
+ }
+ }
+
+ &.btn-sm {
+ width: var(--#{$prefix}btn-width-sm);
+ height: var(--#{$prefix}btn-height-sm);
+
+ .fas,
+ .far,
+ .fab {
+ width: var(--#{$prefix}btn-icon-width-sm);
+ line-height: var(--#{$prefix}btn-icon-line-height-sm);
+ }
+ }
+}
+
+[class*='btn-outline-'].btn-floating {
+ --#{$prefix}btn-icon-width: #{$btn-outline-floating-icon-width};
+ --#{$prefix}btn-icon-width-lg: #{$btn-outline-floating-icon-width-lg};
+ --#{$prefix}btn-icon-width-sm: #{$btn-outline-floating-icon-width-sm};
+ --#{$prefix}btn-icon-line-height: #{$btn-outline-floating-icon-line-height};
+ --#{$prefix}btn-icon-line-height-lg: #{$btn-outline-floating-icon-line-height-lg};
+ --#{$prefix}btn-icon-line-height-sm: #{$btn-outline-floating-icon-line-height-sm};
+
+ .fas,
+ .far,
+ .fab {
+ width: var(--#{$prefix}btn-icon-width);
+ line-height: var(--#{$prefix}btn-icon-line-height);
+ }
+
+ &.btn-lg {
+ .fas,
+ .far,
+ .fab {
+ width: var(--#{$prefix}btn-icon-width-lg);
+ line-height: var(--#{$prefix}btn-icon-line-height-lg);
+ }
+ }
+
+ &.btn-sm {
+ .fas,
+ .far,
+ .fab {
+ width: var(--#{$prefix}btn-icon-width-sm);
+ line-height: var(--#{$prefix}btn-icon-line-height-sm);
+ }
+ }
+}
+
+//
+// Fixed option
+//
+
+.fixed-action-btn {
+ --#{$prefix}btn-right: #{$fixed-action-btn-right};
+ --#{$prefix}btn-bottom: #{$fixed-action-btn-bottom};
+ --#{$prefix}btn-zindex: #{$fixed-action-button-zindex};
+ --#{$prefix}btn-padding-top: #{$fixed-action-btn-padding-top};
+ --#{$prefix}btn-padding-bottom: #{$fixed-action-btn-padding-bottom};
+ --#{$prefix}btn-padding-x: #{$fixed-action-btn-padding-x};
+ --#{$prefix}btn-margin-bottom: #{$fixed-action-btn-li-margin-bottom};
+
+ position: fixed;
+ right: var(--#{$prefix}btn-right);
+ bottom: var(--#{$prefix}btn-bottom);
+ z-index: var(--#{$prefix}btn-zindex);
+ display: flex;
+ flex-flow: column-reverse nowrap;
+ align-items: center;
+ padding: var(--#{$prefix}btn-padding-top) var(--#{$prefix}btn-padding-x)
+ var(--#{$prefix}btn-padding-bottom);
+ margin-bottom: 0;
+ height: auto;
+ overflow: hidden;
+
+ & > .btn-floating {
+ position: relative;
+ transform: scale(1.2);
+ z-index: 10;
+ }
+
+ ul {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ flex-direction: column;
+ padding: 0;
+ margin: 0;
+ margin-bottom: 0;
+ text-align: center;
+ opacity: 0;
+ transition: transform 0.4s, opacity 0.4s;
+ z-index: -1;
+
+ li {
+ z-index: 0;
+ display: flex;
+ margin-right: auto;
+ margin-bottom: var(--#{$prefix}btn-margin-bottom);
+ margin-left: auto;
+
+ &:first-of-type {
+ margin-top: calc(var(--#{$prefix}btn-margin-bottom) * 0.5);
+ }
+ }
+
+ a {
+ &.btn {
+ opacity: 0;
+ transition: opacity 0.4s ease-in;
+
+ &.shown {
+ opacity: 1;
+ }
+ }
+ }
+ }
+
+ &.active ul {
+ opacity: 1;
+ }
+}
+
+//
+// Button block
+//
+
+.btn-block {
+ --#{$prefix}btn-margin-top: #{$btn-block-margin-top};
+
+ display: block;
+ width: 100%;
+
+ // Vertically space out multiple block buttons
+ + .btn-block {
+ margin-top: var(--#{$prefix}btn-margin-top);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_card.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_card.scss
new file mode 100644
index 00000000..666601d0
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_card.scss
@@ -0,0 +1,53 @@
+//
+// Card styles
+//
+
+.card {
+ border: 0;
+
+ .bg-image {
+ border-top-left-radius: var(--#{$prefix}card-border-radius);
+ border-top-right-radius: var(--#{$prefix}card-border-radius);
+ }
+
+ &[class*='bg-'] {
+ .card-header {
+ --#{$prefix}card-header-border-bottom-color: #{$card-border-color};
+
+ border-bottom-color: var(--#{$prefix}card-header-border-bottom-color);
+ }
+ .card-footer {
+ --#{$prefix}card-footer-border-top-color: #{$card-border-color};
+
+ border-top-color: var(--#{$prefix}card-footer-border-top-color);
+ }
+ }
+}
+
+.card-header {
+ --#{$prefix}card-header-border-width: #{$card-header-border-width};
+ --#{$prefix}card-header-border-color: #{$card-header-border-color};
+
+ border-bottom: var(--#{$prefix}card-header-border-width) solid
+ var(--#{$prefix}card-header-border-color);
+}
+
+.card-body {
+ &[class*='bg-'] {
+ border-bottom-left-radius: var(--#{$prefix}card-border-radius);
+ border-bottom-right-radius: var(--#{$prefix}card-border-radius);
+ }
+}
+
+.card-footer {
+ --#{$prefix}card-footer-border-color: #{$card-footer-border-color};
+ --#{$prefix}card-footer-border-width: #{$card-footer-border-width};
+
+ border-top: var(--#{$prefix}card-footer-border-width) solid
+ var(--#{$prefix}card-footer-border-color);
+}
+
+.card-img-left {
+ border-top-left-radius: var(--#{$prefix}card-border-radius);
+ border-bottom-left-radius: var(--#{$prefix}card-border-radius);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_carousel.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_carousel.scss
new file mode 100644
index 00000000..d278a2f7
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_carousel.scss
@@ -0,0 +1,61 @@
+//
+// Carousel styles
+//
+
+.carousel-control-prev-icon {
+ &::after {
+ content: '\f053';
+ --#{$prefix}carousel-control-icon-font-size: #{$carousel-control-icon-font-size};
+ --#{$prefix}carousel-control-icon-font-weight: #{$font-weight-bold};
+
+ font-weight: var(--#{$prefix}carousel-control-icon-font-weight);
+ font-size: var(--#{$prefix}carousel-control-icon-font-size);
+ font-family: 'Font Awesome 6 Pro', 'Font Awesome 6 Free';
+ }
+}
+.carousel-control-next-icon {
+ &::after {
+ content: '\f054';
+ --#{$prefix}carousel-control-icon-font-size: #{$carousel-control-icon-font-size};
+ --#{$prefix}carousel-control-icon-font-weight: #{$font-weight-bold};
+
+ font-weight: var(--#{$prefix}carousel-control-icon-font-weight);
+ font-size: var(--#{$prefix}carousel-control-icon-font-size);
+ font-family: 'Font Awesome 6 Pro', 'Font Awesome 6 Free';
+ }
+}
+.carousel-indicators {
+ [data-mdb-target] {
+ @extend [data-bs-target] !optional;
+ }
+}
+
+// Additional MDB Angular styles
+.carousel-indicators {
+ button {
+ box-sizing: content-box;
+ flex: 0 1 auto;
+ width: $carousel-indicator-width;
+ height: $carousel-indicator-height;
+ padding: 0;
+ margin-right: $carousel-indicator-spacer;
+ margin-left: $carousel-indicator-spacer;
+ text-indent: -999px;
+ cursor: pointer;
+ background-color: $carousel-indicator-active-bg;
+ background-clip: padding-box;
+ border: 0;
+ // Use transparent borders to increase the hit area by 10px on top and bottom.
+ border-top: $carousel-indicator-hit-area-height solid transparent;
+ border-bottom: $carousel-indicator-hit-area-height solid transparent;
+ opacity: $carousel-indicator-opacity;
+ @include transition($carousel-indicator-transition);
+ }
+}
+.carousel-dark,
+[data-mdb-theme='dark'] {
+ .carousel-indicators button {
+ background-color: $carousel-dark-indicator-active-bg;
+ }
+}
+// Additional MDB Angular styles
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_close.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_close.scss
new file mode 100644
index 00000000..9bff1746
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_close.scss
@@ -0,0 +1,10 @@
+// transparent background and border properties included for button version.
+// iOS requires the button element instead of an anchor tag.
+// If you want the anchor version, it requires `href="#"`.
+// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
+
+.btn-close {
+ &:focus {
+ box-shadow: none;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_colors.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_colors.scss
new file mode 100644
index 00000000..2b6e7137
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_colors.scss
@@ -0,0 +1,11 @@
+//
+// Color styles
+//
+
+@each $color, $value in $theme-colors {
+ .bg-#{$color} {
+ --#{$prefix}-bg-opacity: 1;
+
+ background-color: rgba($value, var(--#{$prefix}-bg-opacity));
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_deprecated.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_deprecated.scss
new file mode 100644
index 00000000..f2f5bd8f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_deprecated.scss
@@ -0,0 +1,32 @@
+// To be removed
+
+hr.divider-horizontal:not([size]) {
+ height: 2px;
+}
+
+.divider-horizontal {
+ opacity: 1;
+ background-color: #f5f5f5;
+ height: 2px;
+}
+
+.divider-vertical {
+ opacity: 1;
+ background-color: #f5f5f5;
+ display: inline-block;
+ width: 2px;
+ margin: 0 1rem;
+}
+
+hr.divider-horizontal-blurry {
+ background-image: linear-gradient(90deg, transparent, hsl(0, 0%, 40%), transparent);
+ background-color: transparent;
+}
+
+hr.divider-vertical-blurry {
+ background-image: linear-gradient(180deg, transparent, hsl(0, 0%, 40%), transparent);
+ background-color: transparent;
+ width: 1px;
+ top: 0;
+ right: 0;
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_dropdown.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_dropdown.scss
new file mode 100644
index 00000000..a85f1888
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_dropdown.scss
@@ -0,0 +1,157 @@
+//
+// Dropdown styles
+//
+
+.dropdown-menu {
+ --#{$prefix}dropdown-item-border-radius: #{$dropdown-item-border-radius};
+
+ color: var(--#{$prefix}dropdown-color);
+ margin: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ border: 0;
+ box-shadow: var(--#{$prefix}dropdown-box-shadow);
+ font-size: var(--#{$prefix}dropdown-font-size);
+ // top: 100%;
+ // left: 0;
+ margin-top: var(--#{$prefix}dropdown-spacer);
+
+ // Additional MDB Angular styles
+ display: block;
+ position: inherit;
+ // Additional MDB Angular styles
+
+ > li {
+ border-radius: 0;
+ &:first-child {
+ border-top-left-radius: var(--#{$prefix}dropdown-item-border-radius);
+ border-top-right-radius: var(--#{$prefix}dropdown-item-border-radius);
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ .dropdown-item {
+ border-top-left-radius: var(--#{$prefix}dropdown-item-border-radius);
+ border-top-right-radius: var(--#{$prefix}dropdown-item-border-radius);
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ }
+
+ &:not(:first-child):not(:last-child) {
+ .dropdown-item {
+ border-radius: 0;
+ }
+ }
+
+ &:last-child {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: var(--#{$prefix}dropdown-item-border-radius);
+ border-bottom-right-radius: var(--#{$prefix}dropdown-item-border-radius);
+ .dropdown-item {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: var(--#{$prefix}dropdown-item-border-radius);
+ border-bottom-right-radius: var(--#{$prefix}dropdown-item-border-radius);
+ }
+ }
+ }
+
+ // &.animation {
+ // --#{$prefix}dropdown-menu-animated-animation-duration: #{$dropdown-menu-animated-animation-duration};
+ // --#{$prefix}dropdown-menu-animated-animation-timing-function: #{$dropdown-menu-animated-animation-timing-function};
+
+ // display: block;
+ // /* Speed up animations */
+ // animation-duration: var(--#{$prefix}dropdown-menu-animated-animation-duration);
+ // animation-timing-function: var(--#{$prefix}dropdown-menu-animated-animation-timing-function);
+ // }
+}
+
+.dropdown-item {
+ --#{$prefix}dropdown-state-color: #{$dropdown-state-color};
+ --#{$prefix}dropdown-state-background-color: #{$dropdown-state-background-color};
+
+ padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
+ color: var(--#{$prefix}dropdown-color);
+ border-radius: 0;
+
+ &:hover,
+ &:focus {
+ color: var(--#{$prefix}dropdown-state-color);
+ background-color: var(--#{$prefix}dropdown-state-background-color);
+ }
+
+ &.active,
+ &:active {
+ color: var(--#{$prefix}dropdown-state-color);
+ background-color: var(--#{$prefix}dropdown-state-background-color);
+ }
+
+ &:focus {
+ outline: none;
+ }
+}
+
+.hidden-arrow {
+ &.dropdown-toggle:after {
+ display: none;
+ }
+}
+
+// .animation {
+// animation-duration: 1s;
+// animation-fill-mode: both;
+// padding: auto;
+// }
+
+// @media (prefers-reduced-motion) {
+// .animation {
+// transition: none !important;
+// animation: unset !important;
+// }
+// }
+
+// @keyframes fade-in {
+// from {
+// opacity: 0;
+// }
+
+// to {
+// opacity: 1;
+// }
+// }
+
+// .fade-in {
+// animation-name: fade-in;
+// }
+
+// @keyframes fade-out {
+// from {
+// opacity: 1;
+// }
+
+// to {
+// opacity: 0;
+// }
+// }
+
+// .fade-out {
+// animation-name: fade-out;
+// }
+
+.dropdown-divider {
+ --#{$prefix}dropdown-divider-border-top-width: #{$dropdown-divider-border-top-width};
+ --#{$prefix}dropdown-divider-border-top-bg: #{$dropdown-divider-bg};
+
+ border-top: var(--#{$prefix}dropdown-divider-border-top-width) solid
+ var(--#{$prefix}dropdown-divider-border-top-bg);
+ opacity: 1;
+}
+
+// .dropdown-menu {
+// INPUT:not(:-webkit-autofill),
+// SELECT:not(:-webkit-autofill),
+// TEXTAREA:not(:-webkit-autofill) {
+// animation-name: none !important;
+// }
+// }
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_flag.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_flag.scss
new file mode 100644
index 00000000..7cb360ae
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_flag.scss
@@ -0,0 +1,1046 @@
+/*!
+ * # Semantic UI 2.4.2 - Flag
+ * http://github.com/semantic-org/semantic-ui/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+/*******************************
+ Flag
+*******************************/
+#mdb-table-flag tr {
+ cursor: pointer;
+}
+.mdb-flag-selected {
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ text-align: center;
+ max-width: 150px;
+ margin: 0 auto;
+ margin-top: 10px;
+}
+.mdb-selected-flag-text {
+ margin: 0 auto;
+ max-width: 150px;
+}
+
+i.flag:not(.icon) {
+ display: inline-block;
+ width: 16px;
+ height: 11px;
+ margin: 0 0.5em 0 0;
+ line-height: 11px;
+ text-decoration: inherit;
+ vertical-align: baseline;
+ backface-visibility: hidden;
+}
+
+/* Sprite */
+i.flag {
+ &::before {
+ display: inline-block;
+ width: 16px;
+ height: 11px;
+ content: '';
+ background: url('https://mdbootstrap.com/img/svg/flags.png') no-repeat -108px -1976px;
+ }
+
+ /* Flag Sprite Based On http://www.famfamfam.com/lab/icons/flags/ */
+
+ /*******************************
+ Theme Overrides
+*******************************/
+
+ &-ad:before,
+ &-andorra:before {
+ background-position: 0 0 !important;
+ }
+
+ &-ae:before,
+ &-united-arab-emirates:before,
+ &-uae:before {
+ background-position: 0 -26px !important;
+ }
+ &-af:before,
+ &-afghanistan:before {
+ background-position: 0 -52px !important;
+ }
+ &-ag:before,
+ &-antigua:before {
+ background-position: 0 -78px !important;
+ }
+ &-ai:before,
+ &-anguilla:before {
+ background-position: 0 -104px !important;
+ }
+ &-al:before,
+ &-albania:before {
+ background-position: 0 -130px !important;
+ }
+ &-am:before,
+ &-armenia:before {
+ background-position: 0 -156px !important;
+ }
+ &-an:before,
+ &-netherlands-antilles:before {
+ background-position: 0 -182px !important;
+ }
+ &-ao:before,
+ &-angola:before {
+ background-position: 0 -208px !important;
+ }
+ &-ar:before,
+ &-argentina:before {
+ background-position: 0 -234px !important;
+ }
+ &-as:before,
+ &-american-samoa:before {
+ background-position: 0 -260px !important;
+ }
+ &-at:before,
+ &-austria:before {
+ background-position: 0 -286px !important;
+ }
+ &-au:before,
+ &-australia:before {
+ background-position: 0 -312px !important;
+ }
+ &-aw:before,
+ &-aruba:before {
+ background-position: 0 -338px !important;
+ }
+ &-ax:before,
+ &-aland-islands:before {
+ background-position: 0 -364px !important;
+ }
+ &-az:before,
+ &-azerbaijan:before {
+ background-position: 0 -390px !important;
+ }
+ &-ba:before,
+ &-bosnia:before {
+ background-position: 0 -416px !important;
+ }
+ &-bb:before,
+ &-barbados:before {
+ background-position: 0 -442px !important;
+ }
+ &-bd:before,
+ &-bangladesh:before {
+ background-position: 0 -468px !important;
+ }
+ &-be:before,
+ &-belgium:before {
+ background-position: 0 -494px !important;
+ }
+ &-bf:before,
+ &-burkina-faso:before {
+ background-position: 0 -520px !important;
+ }
+ &-bg:before,
+ &-bulgaria:before {
+ background-position: 0 -546px !important;
+ }
+ &-bh:before,
+ &-bahrain:before {
+ background-position: 0 -572px !important;
+ }
+ &-bi:before,
+ &-burundi:before {
+ background-position: 0 -598px !important;
+ }
+ &-bj:before,
+ &-benin:before {
+ background-position: 0 -624px !important;
+ }
+ &-bm:before,
+ &-bermuda:before {
+ background-position: 0 -650px !important;
+ }
+ &-bn:before,
+ &-brunei:before {
+ background-position: 0 -676px !important;
+ }
+ &-bo:before,
+ &-bolivia:before {
+ background-position: 0 -702px !important;
+ }
+ &-br:before,
+ &-brazil:before {
+ background-position: 0 -728px !important;
+ }
+ &-bs:before,
+ &-bahamas:before {
+ background-position: 0 -754px !important;
+ }
+ &-bt:before,
+ &-bhutan:before {
+ background-position: 0 -780px !important;
+ }
+ &-bv:before,
+ &-bouvet-island:before {
+ background-position: 0 -806px !important;
+ }
+ &-bw:before,
+ &-botswana:before {
+ background-position: 0 -832px !important;
+ }
+ &-by:before,
+ &-belarus:before {
+ background-position: 0 -858px !important;
+ }
+ &-bz:before,
+ &-belize:before {
+ background-position: 0 -884px !important;
+ }
+ &-ca:before,
+ &-canada:before {
+ background-position: 0 -910px !important;
+ }
+ &-cc:before,
+ &-cocos-islands:before {
+ background-position: 0 -962px !important;
+ }
+ &-cd:before,
+ &-congo:before {
+ background-position: 0 -988px !important;
+ }
+ &-cf:before,
+ &-central-african-republic:before {
+ background-position: 0 -1014px !important;
+ }
+ &-cg:before,
+ &-congo-brazzaville:before {
+ background-position: 0 -1040px !important;
+ }
+ &-ch:before,
+ &-switzerland:before {
+ background-position: 0 -1066px !important;
+ }
+ &-ci:before,
+ &-cote-divoire:before {
+ background-position: 0 -1092px !important;
+ }
+ &-ck:before,
+ &-cook-islands:before {
+ background-position: 0 -1118px !important;
+ }
+ &-cl:before,
+ &-chile:before {
+ background-position: 0 -1144px !important;
+ }
+ &-cm:before,
+ &-cameroon:before {
+ background-position: 0 -1170px !important;
+ }
+ &-cn:before,
+ &-china:before {
+ background-position: 0 -1196px !important;
+ }
+ &-co:before,
+ &-colombia:before {
+ background-position: 0 -1222px !important;
+ }
+ &-cr:before,
+ &-costa-rica:before {
+ background-position: 0 -1248px !important;
+ }
+ &-cs:before,
+ &-serbia:before {
+ background-position: 0 -1274px !important;
+ }
+ &-cu:before,
+ &-cuba:before {
+ background-position: 0 -1300px !important;
+ }
+ &-cv:before,
+ &-cape-verde:before {
+ background-position: 0 -1326px !important;
+ }
+ &-cx:before,
+ &-christmas-island:before {
+ background-position: 0 -1352px !important;
+ }
+ &-cy:before,
+ &-cyprus:before {
+ background-position: 0 -1378px !important;
+ }
+ &-cz:before,
+ &-czech-republic:before {
+ background-position: 0 -1404px !important;
+ }
+ &-de:before,
+ &-germany:before {
+ background-position: 0 -1430px !important;
+ }
+ &-dj:before,
+ &-djibouti:before {
+ background-position: 0 -1456px !important;
+ }
+ &-dk:before,
+ &-denmark:before {
+ background-position: 0 -1482px !important;
+ }
+ &-dm:before,
+ &-dominica:before {
+ background-position: 0 -1508px !important;
+ }
+ &-do:before,
+ &-dominican-republic:before {
+ background-position: 0 -1534px !important;
+ }
+ &-dz:before,
+ &-algeria:before {
+ background-position: 0 -1560px !important;
+ }
+ &-ec:before,
+ &-ecuador:before {
+ background-position: 0 -1586px !important;
+ }
+ &-ee:before,
+ &-estonia:before {
+ background-position: 0 -1612px !important;
+ }
+ &-eg:before,
+ &-egypt:before {
+ background-position: 0 -1638px !important;
+ }
+ &-eh:before,
+ &-western-sahara:before {
+ background-position: 0 -1664px !important;
+ }
+ &-gb-eng:before,
+ &-england:before {
+ background-position: 0 -1690px !important;
+ }
+ &-er:before,
+ &-eritrea:before {
+ background-position: 0 -1716px !important;
+ }
+ &-es:before,
+ &-spain:before {
+ background-position: 0 -1742px !important;
+ }
+ &-et:before,
+ &-ethiopia:before {
+ background-position: 0 -1768px !important;
+ }
+ &-eu:before,
+ &-european-union:before {
+ background-position: 0 -1794px !important;
+ }
+ &-fi:before,
+ &-finland:before {
+ background-position: 0 -1846px !important;
+ }
+ &-fj:before,
+ &-fiji:before {
+ background-position: 0 -1872px !important;
+ }
+ &-fk:before,
+ &-falkland-islands:before {
+ background-position: 0 -1898px !important;
+ }
+ &-fm:before,
+ &-micronesia:before {
+ background-position: 0 -1924px !important;
+ }
+ &-fo:before,
+ &-faroe-islands:before {
+ background-position: 0 -1950px !important;
+ }
+ &-fr:before,
+ &-france:before {
+ background-position: 0 -1976px !important;
+ }
+ &-ga:before,
+ &-gabon:before {
+ background-position: -36px 0 !important;
+ }
+ &-gb:before,
+ &-uk:before,
+ &-united-kingdom:before {
+ background-position: -36px -26px !important;
+ }
+ &-gd:before,
+ &-grenada:before {
+ background-position: -36px -52px !important;
+ }
+ &-ge:before,
+ &-georgia:before {
+ background-position: -36px -78px !important;
+ }
+ &-gf:before,
+ &-french-guiana:before {
+ background-position: -36px -104px !important;
+ }
+ &-gh:before,
+ &-ghana:before {
+ background-position: -36px -130px !important;
+ }
+ &-gi:before,
+ &-gibraltar:before {
+ background-position: -36px -156px !important;
+ }
+ &-gl:before,
+ &-greenland:before {
+ background-position: -36px -182px !important;
+ }
+ &-gm:before,
+ &-gambia:before {
+ background-position: -36px -208px !important;
+ }
+ &-gn:before,
+ &-guinea:before {
+ background-position: -36px -234px !important;
+ }
+ &-gp:before,
+ &-guadeloupe:before {
+ background-position: -36px -260px !important;
+ }
+ &-gq:before,
+ &-equatorial-guinea:before {
+ background-position: -36px -286px !important;
+ }
+ &-gr:before,
+ &-greece:before {
+ background-position: -36px -312px !important;
+ }
+ &-gs:before,
+ &-sandwich-islands:before {
+ background-position: -36px -338px !important;
+ }
+ &-gt:before,
+ &-guatemala:before {
+ background-position: -36px -364px !important;
+ }
+ &-gu:before,
+ &-guam:before {
+ background-position: -36px -390px !important;
+ }
+ &-gw:before,
+ &-guinea-bissau:before {
+ background-position: -36px -416px !important;
+ }
+ &-gy:before,
+ &-guyana:before {
+ background-position: -36px -442px !important;
+ }
+ &-hk:before,
+ &-hong-kong:before {
+ background-position: -36px -468px !important;
+ }
+ &-hm:before,
+ &-heard-island:before {
+ background-position: -36px -494px !important;
+ }
+ &-hn:before,
+ &-honduras:before {
+ background-position: -36px -520px !important;
+ }
+ &-hr:before,
+ &-croatia:before {
+ background-position: -36px -546px !important;
+ }
+ &-ht:before,
+ &-haiti:before {
+ background-position: -36px -572px !important;
+ }
+ &-hu:before,
+ &-hungary:before {
+ background-position: -36px -598px !important;
+ }
+ &-id:before,
+ &-indonesia:before {
+ background-position: -36px -624px !important;
+ }
+ &-ie:before,
+ &-ireland:before {
+ background-position: -36px -650px !important;
+ }
+ &-il:before,
+ &-israel:before {
+ background-position: -36px -676px !important;
+ }
+ &-in:before,
+ &-india:before {
+ background-position: -36px -702px !important;
+ }
+ &-io:before,
+ &-indian-ocean-territory:before {
+ background-position: -36px -728px !important;
+ }
+ &-iq:before,
+ &-iraq:before {
+ background-position: -36px -754px !important;
+ }
+ &-ir:before,
+ &-iran:before {
+ background-position: -36px -780px !important;
+ }
+ &-is:before,
+ &-iceland:before {
+ background-position: -36px -806px !important;
+ }
+ &-it:before,
+ &-italy:before {
+ background-position: -36px -832px !important;
+ }
+ &-jm:before,
+ &-jamaica:before {
+ background-position: -36px -858px !important;
+ }
+ &-jo:before,
+ &-jordan:before {
+ background-position: -36px -884px !important;
+ }
+ &-jp:before,
+ &-japan:before {
+ background-position: -36px -910px !important;
+ }
+ &-ke:before,
+ &-kenya:before {
+ background-position: -36px -936px !important;
+ }
+ &-kg:before,
+ &-kyrgyzstan:before {
+ background-position: -36px -962px !important;
+ }
+ &-kh:before,
+ &-cambodia:before {
+ background-position: -36px -988px !important;
+ }
+ &-ki:before,
+ &-kiribati:before {
+ background-position: -36px -1014px !important;
+ }
+ &-km:before,
+ &-comoros:before {
+ background-position: -36px -1040px !important;
+ }
+ &-kn:before,
+ &-saint-kitts-and-nevis:before {
+ background-position: -36px -1066px !important;
+ }
+ &-kp:before,
+ &-north-korea:before {
+ background-position: -36px -1092px !important;
+ }
+ &-kr:before,
+ &-south-korea:before {
+ background-position: -36px -1118px !important;
+ }
+ &-kw:before,
+ &-kuwait:before {
+ background-position: -36px -1144px !important;
+ }
+ &-ky:before,
+ &-cayman-islands:before {
+ background-position: -36px -1170px !important;
+ }
+ &-kz:before,
+ &-kazakhstan:before {
+ background-position: -36px -1196px !important;
+ }
+ &-la:before,
+ &-laos:before {
+ background-position: -36px -1222px !important;
+ }
+ &-lb:before,
+ &-lebanon:before {
+ background-position: -36px -1248px !important;
+ }
+ &-lc:before,
+ &-saint-lucia:before {
+ background-position: -36px -1274px !important;
+ }
+ &-li:before,
+ &-liechtenstein:before {
+ background-position: -36px -1300px !important;
+ }
+ &-lk:before,
+ &-sri-lanka:before {
+ background-position: -36px -1326px !important;
+ }
+ &-lr:before,
+ &-liberia:before {
+ background-position: -36px -1352px !important;
+ }
+ &-ls:before,
+ &-lesotho:before {
+ background-position: -36px -1378px !important;
+ }
+ &-lt:before,
+ &-lithuania:before {
+ background-position: -36px -1404px !important;
+ }
+ &-lu:before,
+ &-luxembourg:before {
+ background-position: -36px -1430px !important;
+ }
+ &-lv:before,
+ &-latvia:before {
+ background-position: -36px -1456px !important;
+ }
+ &-ly:before,
+ &-libya:before {
+ background-position: -36px -1482px !important;
+ }
+ &-ma:before,
+ &-morocco:before {
+ background-position: -36px -1508px !important;
+ }
+ &-mc:before,
+ &-monaco:before {
+ background-position: -36px -1534px !important;
+ }
+ &-md:before,
+ &-moldova:before {
+ background-position: -36px -1560px !important;
+ }
+ &-me:before,
+ &-montenegro:before {
+ background-position: -36px -1586px !important;
+ }
+ &-mg:before,
+ &-madagascar:before {
+ background-position: -36px -1613px !important;
+ }
+ &-mh:before,
+ &-marshall-islands:before {
+ background-position: -36px -1639px !important;
+ }
+ &-mk:before,
+ &-macedonia:before {
+ background-position: -36px -1665px !important;
+ }
+ &-ml:before,
+ &-mali:before {
+ background-position: -36px -1691px !important;
+ }
+ &-mm:before,
+ &-myanmar:before,
+ &-burma:before {
+ background-position: -73px -1821px !important;
+ }
+ &-mn:before,
+ &-mongolia:before {
+ background-position: -36px -1743px !important;
+ }
+ &-mo:before,
+ &-macau:before {
+ background-position: -36px -1769px !important;
+ }
+ &-mp:before,
+ &-northern-mariana-islands:before {
+ background-position: -36px -1795px !important;
+ }
+ &-mq:before,
+ &-martinique:before {
+ background-position: -36px -1821px !important;
+ }
+ &-mr:before,
+ &-mauritania:before {
+ background-position: -36px -1847px !important;
+ }
+ &-ms:before,
+ &-montserrat:before {
+ background-position: -36px -1873px !important;
+ }
+ &-mt:before,
+ &-malta:before {
+ background-position: -36px -1899px !important;
+ }
+ &-mu:before,
+ &-mauritius:before {
+ background-position: -36px -1925px !important;
+ }
+ &-mv:before,
+ &-maldives:before {
+ background-position: -36px -1951px !important;
+ }
+ &-mw:before,
+ &-malawi:before {
+ background-position: -36px -1977px !important;
+ }
+ &-mx:before,
+ &-mexico:before {
+ background-position: -72px 0 !important;
+ }
+ &-my:before,
+ &-malaysia:before {
+ background-position: -72px -26px !important;
+ }
+ &-mz:before,
+ &-mozambique:before {
+ background-position: -72px -52px !important;
+ }
+ &-na:before,
+ &-namibia:before {
+ background-position: -72px -78px !important;
+ }
+ &-nc:before,
+ &-new-caledonia:before {
+ background-position: -72px -104px !important;
+ }
+ &-ne:before,
+ &-niger:before {
+ background-position: -72px -130px !important;
+ }
+ &-nf:before,
+ &-norfolk-island:before {
+ background-position: -72px -156px !important;
+ }
+ &-ng:before,
+ &-nigeria:before {
+ background-position: -72px -182px !important;
+ }
+ &-ni:before,
+ &-nicaragua:before {
+ background-position: -72px -208px !important;
+ }
+ &-nl:before,
+ &-netherlands:before {
+ background-position: -72px -234px !important;
+ }
+ &-no:before,
+ &-norway:before {
+ background-position: -72px -260px !important;
+ }
+ &-np:before,
+ &-nepal:before {
+ background-position: -72px -286px !important;
+ }
+ &-nr:before,
+ &-nauru:before {
+ background-position: -72px -312px !important;
+ }
+ &-nu:before,
+ &-niue:before {
+ background-position: -72px -338px !important;
+ }
+ &-nz:before,
+ &-new-zealand:before {
+ background-position: -72px -364px !important;
+ }
+ &-om:before,
+ &-oman:before {
+ background-position: -72px -390px !important;
+ }
+ &-pa:before,
+ &-panama:before {
+ background-position: -72px -416px !important;
+ }
+ &-pe:before,
+ &-peru:before {
+ background-position: -72px -442px !important;
+ }
+ &-pf:before,
+ &-french-polynesia:before {
+ background-position: -72px -468px !important;
+ }
+ &-pg:before,
+ &-new-guinea:before {
+ background-position: -72px -494px !important;
+ }
+ &-ph:before,
+ &-philippines:before {
+ background-position: -72px -520px !important;
+ }
+ &-pk:before,
+ &-pakistan:before {
+ background-position: -72px -546px !important;
+ }
+ &-pl:before,
+ &-poland:before {
+ background-position: -72px -572px !important;
+ }
+ &-pm:before,
+ &-saint-pierre:before {
+ background-position: -72px -598px !important;
+ }
+ &-pn:before,
+ &-pitcairn-islands:before {
+ background-position: -72px -624px !important;
+ }
+ &-pr:before,
+ &-puerto-rico:before {
+ background-position: -72px -650px !important;
+ }
+ &-ps:before,
+ &-palestine:before {
+ background-position: -72px -676px !important;
+ }
+ &-pt:before,
+ &-portugal:before {
+ background-position: -72px -702px !important;
+ }
+ &-pw:before,
+ &-palau:before {
+ background-position: -72px -728px !important;
+ }
+ &-py:before,
+ &-paraguay:before {
+ background-position: -72px -754px !important;
+ }
+ &-qa:before,
+ &-qatar:before {
+ background-position: -72px -780px !important;
+ }
+ &-re:before,
+ &-reunion:before {
+ background-position: -72px -806px !important;
+ }
+ &-ro:before,
+ &-romania:before {
+ background-position: -72px -832px !important;
+ }
+ &-rs:before,
+ &-serbia:before {
+ background-position: -72px -858px !important;
+ }
+ &-ru:before,
+ &-russia:before {
+ background-position: -72px -884px !important;
+ }
+ &-rw:before,
+ &-rwanda:before {
+ background-position: -72px -910px !important;
+ }
+ &-sa:before,
+ &-saudi-arabia:before {
+ background-position: -72px -936px !important;
+ }
+ &-sb:before,
+ &-solomon-islands:before {
+ background-position: -72px -962px !important;
+ }
+ &-sc:before,
+ &-seychelles:before {
+ background-position: -72px -988px !important;
+ }
+ &-gb-sct:before,
+ &-scotland:before {
+ background-position: -72px -1014px !important;
+ }
+ &-sd:before,
+ &-sudan:before {
+ background-position: -72px -1040px !important;
+ }
+ &-se:before,
+ &-sweden:before {
+ background-position: -72px -1066px !important;
+ }
+ &-sg:before,
+ &-singapore:before {
+ background-position: -72px -1092px !important;
+ }
+ &-sh:before,
+ &-saint-helena:before {
+ background-position: -72px -1118px !important;
+ }
+ &-si:before,
+ &-slovenia:before {
+ background-position: -72px -1144px !important;
+ }
+ &-sj:before,
+ &-svalbard:before,
+ &-jan-mayen:before {
+ background-position: -72px -1170px !important;
+ }
+ &-sk:before,
+ &-slovakia:before {
+ background-position: -72px -1196px !important;
+ }
+ &-sl:before,
+ &-sierra-leone:before {
+ background-position: -72px -1222px !important;
+ }
+ &-sm:before,
+ &-san-marino:before {
+ background-position: -72px -1248px !important;
+ }
+ &-sn:before,
+ &-senegal:before {
+ background-position: -72px -1274px !important;
+ }
+ &-so:before,
+ &-somalia:before {
+ background-position: -72px -1300px !important;
+ }
+ &-sr:before,
+ &-suriname:before {
+ background-position: -72px -1326px !important;
+ }
+ &-st:before,
+ &-sao-tome:before {
+ background-position: -72px -1352px !important;
+ }
+ &-sv:before,
+ &-el-salvador:before {
+ background-position: -72px -1378px !important;
+ }
+ &-sy:before,
+ &-syria:before {
+ background-position: -72px -1404px !important;
+ }
+ &-sz:before,
+ &-swaziland:before {
+ background-position: -72px -1430px !important;
+ }
+ &-tc:before,
+ &-caicos-islands:before {
+ background-position: -72px -1456px !important;
+ }
+ &-td:before,
+ &-chad:before {
+ background-position: -72px -1482px !important;
+ }
+ &-tf:before,
+ &-french-territories:before {
+ background-position: -72px -1508px !important;
+ }
+ &-tg:before,
+ &-togo:before {
+ background-position: -72px -1534px !important;
+ }
+ &-th:before,
+ &-thailand:before {
+ background-position: -72px -1560px !important;
+ }
+ &-tj:before,
+ &-tajikistan:before {
+ background-position: -72px -1586px !important;
+ }
+ &-tk:before,
+ &-tokelau:before {
+ background-position: -72px -1612px !important;
+ }
+ &-tl:before,
+ &-timorleste:before {
+ background-position: -72px -1638px !important;
+ }
+ &-tm:before,
+ &-turkmenistan:before {
+ background-position: -72px -1664px !important;
+ }
+ &-tn:before,
+ &-tunisia:before {
+ background-position: -72px -1690px !important;
+ }
+ &-to:before,
+ &-tonga:before {
+ background-position: -72px -1716px !important;
+ }
+ &-tr:before,
+ &-turkey:before {
+ background-position: -72px -1742px !important;
+ }
+ &-tt:before,
+ &-trinidad:before {
+ background-position: -72px -1768px !important;
+ }
+ &-tv:before,
+ &-tuvalu:before {
+ background-position: -72px -1794px !important;
+ }
+ &-tw:before,
+ &-taiwan:before {
+ background-position: -72px -1820px !important;
+ }
+ &-tz:before,
+ &-tanzania:before {
+ background-position: -72px -1846px !important;
+ }
+ &-ua:before,
+ &-ukraine:before {
+ background-position: -72px -1872px !important;
+ }
+ &-ug:before,
+ &-uganda:before {
+ background-position: -72px -1898px !important;
+ }
+ &-um:before,
+ &-us-minor-islands:before {
+ background-position: -72px -1924px !important;
+ }
+ &-us:before,
+ &-america:before,
+ &-united-states:before {
+ background-position: -72px -1950px !important;
+ }
+ &-uy:before,
+ &-uruguay:before {
+ background-position: -72px -1976px !important;
+ }
+ &-uz:before,
+ &-uzbekistan:before {
+ background-position: -108px 0 !important;
+ }
+ &-va:before,
+ &-vatican-city:before {
+ background-position: -108px -26px !important;
+ }
+ &-vc:before,
+ &-saint-vincent:before {
+ background-position: -108px -52px !important;
+ }
+ &-ve:before,
+ &-venezuela:before {
+ background-position: -108px -78px !important;
+ }
+ &-vg:before,
+ &-british-virgin-islands:before {
+ background-position: -108px -104px !important;
+ }
+ &-vi:before,
+ &-us-virgin-islands:before {
+ background-position: -108px -130px !important;
+ }
+ &-vn:before,
+ &-vietnam:before {
+ background-position: -108px -156px !important;
+ }
+ &-vu:before,
+ &-vanuatu:before {
+ background-position: -108px -182px !important;
+ }
+ &-gb-wls:before,
+ &-wales:before {
+ background-position: -108px -208px !important;
+ }
+ &-wf:before,
+ &-wallis-and-futuna:before {
+ background-position: -108px -234px !important;
+ }
+ &-ws:before,
+ &-samoa:before {
+ background-position: -108px -260px !important;
+ }
+ &-ye:before,
+ &-yemen:before {
+ background-position: -108px -286px !important;
+ }
+ &-yt:before,
+ &-mayotte:before {
+ background-position: -108px -312px !important;
+ }
+ &-za:before,
+ &-south-africa:before {
+ background-position: -108px -338px !important;
+ }
+ &-zm:before,
+ &-zambia:before {
+ background-position: -108px -364px !important;
+ }
+ &-zw:before,
+ &-zimbabwe:before {
+ background-position: -108px -390px !important;
+ }
+}
+/*******************************
+ Site Overrides
+*******************************/
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_functions.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_functions.scss
new file mode 100644
index 00000000..f9432faf
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_functions.scss
@@ -0,0 +1,18 @@
+// Functions
+
+// Dynamically change text color based on background color
+
+@function set-notification-text-color($color) {
+ @if (lightness($color) > 70) {
+ @return $gray-800;
+ } @else {
+ @return $white;
+ }
+}
+
+@function color-level($color: $primary, $level: 0) {
+ $color-base: if($level > 0, $black, $white);
+ $level: abs($level);
+
+ @return mix($color-base, $color, $level);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_images.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_images.scss
new file mode 100644
index 00000000..0a78e443
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_images.scss
@@ -0,0 +1,87 @@
+//
+// Image styles
+//
+
+.bg-fixed {
+ background-attachment: fixed;
+}
+
+.bg-image {
+ position: relative;
+ overflow: hidden;
+ background-repeat: no-repeat;
+ background-size: cover;
+ background-position: center center;
+}
+
+.mask {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ background-attachment: fixed;
+}
+
+.hover-overlay {
+ .mask {
+ --#{$prefix}image-hover-transition: #{$image-hover-overlay-transition};
+
+ opacity: 0;
+ transition: var(--#{$prefix}image-hover-transition);
+ &:hover {
+ opacity: 1;
+ }
+ }
+}
+
+.hover-zoom {
+ --#{$prefix}image-hover-zoom-transition: #{$image-hover-zoom-transition};
+ --#{$prefix}image-hover-zoom-transform: #{$image-hover-zoom-transform};
+
+ img,
+ video {
+ transition: var(--#{$prefix}image-hover-zoom-transition);
+ }
+ &:hover {
+ img,
+ video {
+ transform: var(--#{$prefix}image-hover-zoom-transform);
+ }
+ }
+}
+
+.hover-shadow,
+.card.hover-shadow,
+.hover-shadow-soft,
+.card.hover-shadow-soft {
+ --#{$prefix}image-hover-shadow-transition: #{$image-hover-shadow-transition};
+
+ transition: var(--#{$prefix}image-hover-shadow-transition);
+ &:hover {
+ transition: var(--#{$prefix}image-hover-shadow-transition);
+ }
+}
+
+.hover-shadow,
+.card.hover-shadow {
+ --#{$prefix}image-hover-shadow-box-shadow: #{$image-hover-shadow-box-shadow};
+
+ box-shadow: none;
+ &:hover {
+ box-shadow: var(--#{$prefix}image-hover-shadow-box-shadow);
+ }
+}
+
+.hover-shadow-soft,
+.card.hover-shadow-soft {
+ --#{$prefix}image-hover-shadow-box-shadow-soft: #{$image-hover-shadow-box-shadow-soft};
+
+ box-shadow: none;
+ &:hover {
+ box-shadow: var(--#{$prefix}image-hover-shadow-box-shadow-soft);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_list-group.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_list-group.scss
new file mode 100644
index 00000000..a9a1a978
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_list-group.scss
@@ -0,0 +1,68 @@
+//
+// List group styles
+//
+
+.list-group {
+ --#{$prefix}list-group-item-transition-time: #{$list-group-item-transition-time};
+}
+
+.list-group-item-action {
+ transition: var(--#{$prefix}list-group-item-transition-time);
+
+ // Hover state
+ &:hover {
+ transition: var(--#{$prefix}list-group-item-transition-time);
+ }
+}
+
+.list-group-light {
+ --#{$prefix}list-group-light-item-py: #{$list-group-light-item-py};
+ --#{$prefix}list-group-light-item-border: #{$list-group-light-item-border};
+ --#{$prefix}list-group-light-item-border-width: #{$list-group-light-item-border-width};
+ --#{$prefix}list-group-light-active-border-radius: #{$list-group-light-active-border-radius};
+ --#{$prefix}list-group-light-active-bg: #{$list-group-light-active-bg};
+ --#{$prefix}list-group-light-active-color: #{$list-group-light-active-color};
+
+ .list-group-item {
+ padding: var(--#{$prefix}list-group-light-item-py) 0;
+ border: var(--#{$prefix}list-group-light-item-border);
+ }
+ > .list-group-item {
+ border-width: 0 0 var(--#{$prefix}list-group-light-item-border-width);
+ &:last-of-type {
+ border: none;
+ }
+ }
+ .active {
+ border: none;
+ border-radius: var(--#{$prefix}list-group-light-active-border-radius);
+ background-color: var(--#{$prefix}list-group-light-active-bg);
+ color: var(--#{$prefix}list-group-light-active-color);
+ }
+ .list-group-item-action {
+ &:hover {
+ border-radius: var(--#{$prefix}list-group-light-active-border-radius);
+ }
+ &:focus {
+ border-radius: var(--#{$prefix}list-group-light-active-border-radius);
+ }
+ }
+}
+
+.list-group-small {
+ --#{$prefix}list-group-small-item-py: #{$list-group-small-item-py};
+ .list-group-item {
+ padding: var(--#{$prefix}list-group-small-item-py) 0;
+ }
+}
+
+@each $color, $value in $theme-colors {
+ .list-group-item-#{$color} {
+ background-color: var(--#{$prefix}#{$color}-bg-subtle);
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+
+ i {
+ color: var(--#{$prefix}#{$color}-link-emphasis);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_mixins.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_mixins.scss
new file mode 100644
index 00000000..b8a90095
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_mixins.scss
@@ -0,0 +1,8 @@
+// Toggles
+//
+// Used in conjunction with global variables to enable certain theme features.
+
+// Components
+@import './mixins/buttons';
+@import './mixins/ripple';
+@import './mixins/table-variants';
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_modal.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_modal.scss
new file mode 100644
index 00000000..6034eaba
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_modal.scss
@@ -0,0 +1,39 @@
+//
+// Modal styles
+//
+
+.modal-content {
+ // scss-docs-start modal-css-vars
+ --#{$prefix}modal-box-shadow: #{$modal-box-shadow};
+ // scss-docs-end modal-css-vars
+
+ border: 0;
+ box-shadow: var(--#{$prefix}modal-box-shadow);
+}
+
+// Additional MDB Angular styles
+.mdb-backdrop {
+ background-color: rgba(0, 0, 0, 0.5);
+ transition: opacity 0.15s linear;
+}
+
+.modal-dynamic > :first-child {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.modal-dialog-scrollable {
+ .modal-content,
+ .modal-content > :first-child {
+ max-height: 100%;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .modal-body {
+ overflow-y: auto;
+ }
+}
+// Additional MDB Angular styles
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_nav.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_nav.scss
new file mode 100644
index 00000000..d2ac82cf
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_nav.scss
@@ -0,0 +1,101 @@
+//
+// Navs styles
+//
+
+//
+// Tabs styles
+//
+
+.nav-tabs {
+ border-bottom: 0;
+
+ .nav-link {
+ --#{$prefix}nav-tabs-link-font-weight: #{$nav-tabs-link-font-weight};
+ --#{$prefix}nav-tabs-link-font-size: #{$nav-tabs-link-font-size};
+ --#{$prefix}nav-tabs-link-color: #{$nav-tabs-link-color};
+ --#{$prefix}nav-tabs-link-padding-top: #{$nav-tabs-link-padding-top};
+ --#{$prefix}nav-tabs-link-padding-bottom: #{$nav-tabs-link-padding-bottom};
+ --#{$prefix}nav-tabs-link-padding-x: #{$nav-tabs-link-padding-x};
+ --#{$prefix}nav-tabs-link-hover-bgc: #{$nav-tabs-link-hover-bgc};
+ --#{$prefix}nav-tabs-link-border-bottom-width: #{$nav-tabs-link-border-bottom-width};
+ --#{$prefix}nav-tabs-link-active-color: #{$nav-tabs-link-active-color};
+ --#{$prefix}nav-tabs-link-active-border-color: #{$nav-tabs-link-active-border-color};
+ border-width: 0;
+ border-bottom: var(--#{$prefix}nav-tabs-link-border-bottom-width) solid transparent;
+ border-radius: 0;
+ text-transform: uppercase;
+ line-height: 1;
+ font-weight: var(--#{$prefix}nav-tabs-link-font-weight);
+ font-size: var(--#{$prefix}nav-tabs-link-font-size);
+ color: var(--#{$prefix}nav-tabs-link-color);
+ padding: var(--#{$prefix}nav-tabs-link-padding-top) var(--#{$prefix}nav-tabs-link-padding-x)
+ var(--#{$prefix}nav-tabs-link-padding-bottom) var(--#{$prefix}nav-tabs-link-padding-x);
+
+ &:hover {
+ background-color: var(--#{$prefix}nav-tabs-link-hover-bgc);
+ border-color: transparent;
+ }
+
+ &:focus {
+ border-color: transparent;
+ }
+ }
+
+ .nav-link.active,
+ .nav-item.show .nav-link {
+ color: var(--#{$prefix}nav-tabs-link-active-color);
+ border-color: var(--#{$prefix}nav-tabs-link-active-border-color);
+ }
+}
+
+//
+// Pills styles
+//
+
+.nav-pills {
+ margin-left: -$nav-pills-margin;
+ margin-right: -$nav-pills-margin;
+
+ .nav-link {
+ --#{$prefix}nav-pills-link-border-radius: #{$nav-pills-link-border-radius};
+ --#{$prefix}nav-pills-link-font-size: #{$nav-pills-link-font-size};
+ --#{$prefix}nav-pills-link-padding-top: #{$nav-pills-link-padding-top};
+ --#{$prefix}nav-pills-link-padding-bottom: #{$nav-pills-link-padding-bottom};
+ --#{$prefix}nav-pills-link-padding-x: #{$nav-pills-link-padding-x};
+ --#{$prefix}nav-pills-link-line-height: #{$nav-pills-link-line-height};
+ --#{$prefix}nav-pills-link-hover-bg: #{$nav-pills-link-hover-bg};
+ --#{$prefix}nav-pills-link-font-weight: #{$nav-pills-link-font-weight};
+ --#{$prefix}nav-pills-link-color: #{$nav-pills-link-color};
+ --#{$prefix}nav-pills-margin: #{$nav-pills-margin};
+
+ border-radius: var(--#{$prefix}nav-pills-link-border-radius);
+ font-size: var(--#{$prefix}nav-pills-link-font-size);
+ text-transform: uppercase;
+ padding: var(--#{$prefix}nav-pills-link-padding-top) var(--#{$prefix}nav-pills-link-padding-x)
+ var(--#{$prefix}nav-pills-link-padding-bottom) var(--#{$prefix}nav-pills-link-padding-x);
+ line-height: var(--#{$prefix}nav-pills-link-line-height);
+ background-color: var(--#{$prefix}nav-pills-link-hover-bg);
+ font-weight: var(--#{$prefix}nav-pills-link-font-weight);
+ color: var(--#{$prefix}nav-pills-link-color);
+ margin: var(--#{$prefix}nav-pills-margin);
+ }
+
+ .nav-link.active,
+ .show > .nav-link {
+ --#{$prefix}nav-pills-link-active-bg: #{$nav-pills-link-active-bg};
+ --#{$prefix}nav-pills-link-active-color: #{$nav-pills-link-active-color};
+
+ background-color: var(--#{$prefix}nav-pills-link-active-bg);
+ color: var(--#{$prefix}nav-pills-link-active-color);
+ }
+}
+
+// Fix for keeping margins of pills
+.nav-fill,
+.nav-justified {
+ .nav-item {
+ .nav-link {
+ width: auto;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_navbar.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_navbar.scss
new file mode 100644
index 00000000..eac43a71
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_navbar.scss
@@ -0,0 +1,64 @@
+//
+// Navbar styles
+//
+
+.navbar {
+ --#{$prefix}navbar-box-shadow: #{$navbar-box-shadow};
+ --#{$prefix}navbar-padding-top: #{$navbar-padding-top};
+ --#{$prefix}navbar-brand-img-margin-right: #{$navbar-brand-img-margin-right};
+
+ box-shadow: var(--#{$prefix}navbar-box-shadow);
+ padding-top: var(--#{$prefix}navbar-padding-top);
+}
+
+.navbar-toggler {
+ border: 0;
+
+ &:focus {
+ box-shadow: none;
+ }
+}
+
+.navbar-dark .navbar-toggler,
+.navbar-light .navbar-toggler {
+ border: 0;
+}
+
+.navbar-brand {
+ display: flex;
+ align-items: center;
+ img {
+ margin-right: var(--#{$prefix}navbar-brand-img-margin-right);
+ }
+}
+
+.navbar-nav .dropdown-menu {
+ position: absolute;
+}
+
+// Navbar themes
+.navbar-light {
+ .navbar-toggler-icon {
+ background-image: none;
+ }
+}
+
+.navbar-dark {
+ .navbar-toggler-icon {
+ background-image: none;
+ }
+}
+
+.navbar-dark,
+.navbar[data-mdb-theme='dark'] {
+ // scss-docs-start navbar-dark-css-vars
+ --#{$prefix}navbar-color: #{$navbar-dark-color};
+ --#{$prefix}navbar-hover-color: #{$navbar-dark-hover-color};
+ --#{$prefix}navbar-disabled-color: #{$navbar-dark-disabled-color};
+ --#{$prefix}navbar-active-color: #{$navbar-dark-active-color};
+ --#{$prefix}navbar-brand-color: #{$navbar-dark-brand-color};
+ --#{$prefix}navbar-brand-hover-color: #{$navbar-dark-brand-hover-color};
+ --#{$prefix}navbar-toggler-border-color: #{$navbar-dark-toggler-border-color};
+ --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
+ // scss-docs-end navbar-dark-css-vars
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_overlay.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_overlay.scss
new file mode 100644
index 00000000..82106716
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_overlay.scss
@@ -0,0 +1,17 @@
+// Owerride default overlay z-index. Change required to allow elements inside the overlay to have z-index value up to 1200.
+
+div.cdk-overlay-container {
+ z-index: 1200;
+ .cdk-overlay-backdrop {
+ z-index: 1200;
+ }
+ .cdk-global-overlay-wrapper {
+ z-index: 1200
+ }
+ .cdk-overlay-pane {
+ z-index: 1200;
+ }
+ .cdk-overlay-connected-position-bounding-box {
+ z-index: 1200;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_pagination.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_pagination.scss
new file mode 100644
index 00000000..fcf1f08f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_pagination.scss
@@ -0,0 +1,97 @@
+//
+// Pagination styles
+//
+
+.pagination {
+ // scss-docs-start pagination-css-vars
+ --#{$prefix}pagination-border-radius: #{$pagination-border-radius};
+ --#{$prefix}pagination-active-transition: #{$pagination-active-transition};
+ --#{$prefix}pagination-active-font-weight: #{$pagination-active-font-weight};
+ --#{$prefix}pagination-circle-border-radius: #{$pagination-circle-border-radius};
+ --#{$prefix}pagination-circle-padding-x: #{$pagination-circle-padding-x};
+ --#{$prefix}pagination-circle-padding-l-lg: #{$pagination-circle-lg-padding-left};
+ --#{$prefix}pagination-circle-padding-r-lg: #{$pagination-circle-lg-padding-right};
+ --#{$prefix}pagination-circle-padding-l-sm: #{$pagination-circle-sm-padding-left};
+ --#{$prefix}pagination-circle-padding-r-sm: #{$pagination-circle-sm-padding-right};
+ // scss-docs-end pagination-css-vars
+}
+
+.page-link {
+ background-color: transparent;
+ border: 0;
+ outline: 0;
+ border-radius: var(--#{$prefix}pagination-border-radius);
+
+ &:focus {
+ box-shadow: none;
+ }
+
+ &.active,
+ .active > & {
+ border: 0;
+ transition: var(--#{$prefix}pagination-active-transition);
+ font-weight: var(--#{$prefix}pagination-active-font-weight);
+ }
+}
+
+.page-item {
+ &:not(:first-child) {
+ .page-link {
+ margin-left: 0;
+ }
+ }
+
+ &:first-child {
+ .page-link {
+ border-top-left-radius: var(--#{$prefix}pagination-border-radius);
+ border-bottom-left-radius: var(--#{$prefix}pagination-border-radius);
+ }
+ }
+
+ &:last-child {
+ .page-link {
+ border-top-right-radius: var(--#{$prefix}pagination-border-radius);
+ border-bottom-right-radius: var(--#{$prefix}pagination-border-radius);
+ }
+ }
+}
+
+//
+// Pagination circle option
+//
+
+.pagination-circle {
+ .page-item {
+ &:first-child {
+ .page-link {
+ border-radius: var(--#{$prefix}pagination-circle-border-radius);
+ }
+ }
+
+ &:last-child {
+ .page-link {
+ border-radius: var(--#{$prefix}pagination-circle-border-radius);
+ }
+ }
+ }
+
+ .page-link {
+ border-radius: var(--#{$prefix}pagination-circle-border-radius);
+ padding-left: var(--#{$prefix}pagination-circle-padding-x);
+ padding-right: var(--#{$prefix}pagination-circle-padding-x);
+ }
+
+ &.pagination-lg {
+ .page-link {
+ padding-left: var(--#{$prefix}pagination-circle-padding-l-lg);
+ padding-right: var(--#{$prefix}pagination-circle-padding-r-lg);
+ }
+ }
+
+ &.pagination-sm {
+ .page-link {
+ padding-left: var(--#{$prefix}pagination-circle-padding-l-sm);
+ padding-right: var(--#{$prefix}pagination-circle-padding-r-sm);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_popover.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_popover.scss
new file mode 100644
index 00000000..0d603b2e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_popover.scss
@@ -0,0 +1,21 @@
+// Popover
+
+.popover {
+ // scss-docs-start popover-css-vars
+ --#{$prefix}popover-border-bottom-width: #{$popover-border-bottom-width};
+ // scss-docs-end popover-css-vars
+
+ // Additional MDB Angular styles
+ position: unset;
+ opacity: 0;
+ // Additional MDB Angular styles
+
+ // .popover-arrow {
+ // display: none;
+ // }
+}
+
+.popover-header {
+ border-bottom: var(--#{$prefix}popover-border-bottom-width) solid
+ var(--#{$prefix}popover-border-color);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_progress.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_progress.scss
new file mode 100644
index 00000000..f8ca123b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_progress.scss
@@ -0,0 +1,57 @@
+// Progress
+
+.progress {
+ border-radius: 0;
+ box-shadow: none;
+}
+
+.progress-circular {
+ --#{$prefix}progress-circular-size: #{$progress-circular-size};
+ --#{$prefix}progress-circular-bar-width: #{$progress-circular-bar-width};
+ --#{$prefix}progress-circular-color: var(--#{$prefix}emphasis-color);
+
+ position: relative;
+ width: var(--#{$prefix}progress-circular-size);
+ height: var(--#{$prefix}progress-circular-size);
+ background-color: transparent;
+ display: inline-block;
+
+ .progress-bar {
+ background-color: var(--#{$prefix}progress-bar-bg);
+ }
+
+ .progress-bar::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ box-sizing: border-box;
+
+ background: conic-gradient(
+ transparent calc(var(--percentage) * 1%),
+ var(--#{$prefix}progress-bg) 0%
+ );
+ -webkit-mask: radial-gradient(
+ farthest-side,
+ transparent calc(100% - var(--#{$prefix}progress-circular-bar-width)),
+ black calc(100% - var(--#{$prefix}progress-circular-bar-width) + 1px)
+ );
+ mask: radial-gradient(
+ farthest-side,
+ transparent calc(100% - var(--#{$prefix}progress-circular-bar-width)),
+ black calc(100% - var(--#{$prefix}progress-circular-bar-width) + 1px)
+ );
+ background-color: inherit;
+ }
+
+ .progress-label {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: var(--#{$prefix}progress-circular-color);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_range.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_range.scss
new file mode 100644
index 00000000..bef6ae3b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_range.scss
@@ -0,0 +1,58 @@
+// range
+.range {
+ --#{$prefix}range-thumb-height: #{$range-thumb-height};
+ --#{$prefix}range-thumb-width: #{$range-thumb-width};
+ --#{$prefix}range-thumb-top: #{$range-thumb-top};
+ --#{$prefix}range-thumb-margin-left: #{$range-thumb-margin-left};
+ --#{$prefix}range-thumb-border-radius: #{$range-thumb-border-radius};
+ --#{$prefix}range-thumb-transform: #{$range-thumb-transform};
+ --#{$prefix}range-thumb-transition: #{$range-thumb-transition};
+ --#{$prefix}range-thumb-value-font-size: #{$range-thumb-value-font-size};
+ --#{$prefix}range-thumb-value-line-height: #{$range-thumb-value-line-height};
+ --#{$prefix}range-thumb-value-color: #{$range-thumb-value-color};
+ --#{$prefix}range-thumb-value-font-weight: #{$range-thumb-value-font-weight};
+ --#{$prefix}range-thumb-background: #{$range-thumb-background};
+
+ position: relative;
+
+ .thumb {
+ position: absolute;
+ display: block;
+ height: var(--#{$prefix}range-thumb-height);
+ width: var(--#{$prefix}range-thumb-width);
+ top: var(--#{$prefix}range-thumb-top);
+ margin-left: var(--#{$prefix}range-thumb-margin-left);
+ text-align: center;
+ border-radius: var(--#{$prefix}range-thumb-border-radius);
+ transform: var(--#{$prefix}range-thumb-transform);
+ transform-origin: bottom;
+ transition: var(--#{$prefix}range-thumb-transition);
+
+ &:after {
+ position: absolute;
+ display: block;
+ content: '';
+ transform: translateX(-50%);
+ width: 100%;
+ height: 100%;
+ top: 0;
+ border-radius: var(--#{$prefix}range-thumb-border-radius);
+ transform: rotate(-45deg);
+ background: var(--#{$prefix}range-thumb-background);
+ z-index: -1;
+ }
+
+ .thumb-value {
+ display: block;
+ font-size: var(--#{$prefix}range-thumb-value-font-size);
+ line-height: var(--#{$prefix}range-thumb-value-line-height);
+ color: var(--#{$prefix}range-thumb-value-color);
+ font-weight: var(--#{$prefix}range-thumb-value-font-weight);
+ z-index: 2;
+ }
+
+ &.thumb-active {
+ transform: scale(1);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_reboot.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_reboot.scss
new file mode 100644
index 00000000..cc117aeb
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_reboot.scss
@@ -0,0 +1,68 @@
+// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
+
+// Reboot
+//
+// Normalization of HTML elements, manually forked from Normalize.css to remove
+// styles targeting irrelevant browsers while applying new styles.
+
+// Dividers
+
+hr:not([size]) {
+ &.hr {
+ --#{$prefix}divider-height: #{$divider-height};
+ --#{$prefix}divider-bg: #{$divider-bg};
+ --#{$prefix}divider-opacity: #{$divider-opacity};
+ --#{$prefix}divider-blurry-bg: transparent;
+ --#{$prefix}divider-blurry-bg-image: #{$divider-blurry-bg-image};
+ --#{$prefix}divider-blurry-height: #{$divider-blurry-height};
+ --#{$prefix}divider-blurry-opacity: #{$divider-blurry-opacity};
+
+ height: var(--#{$prefix}divider-height);
+ background-color: var(--#{$prefix}divider-bg);
+ opacity: var(--#{$prefix}divider-opacity);
+
+ &.hr-blurry {
+ background-color: var(--#{$prefix}divider-blurry-bg);
+ background-image: var(--#{$prefix}divider-blurry-bg-image);
+ height: var(--#{$prefix}divider-blurry-height);
+ opacity: var(--#{$prefix}divider-blurry-opacity);
+ }
+ }
+
+ &.vr {
+ height: auto;
+ }
+}
+
+hr.hr,
+hr.vr {
+ border-top: none !important;
+}
+
+.vr {
+ --#{$prefix}divider-width: #{$divider-width};
+ --#{$prefix}divider-bg: #{$divider-bg};
+ --#{$prefix}divider-opacity: #{$divider-opacity};
+
+ width: var(--#{$prefix}divider-width);
+ background-color: var(--#{$prefix}divider-bg);
+ opacity: var(--#{$prefix}divider-opacity);
+}
+
+.vr-blurry {
+ --#{$prefix}divider-blurry-vr-bg-image: #{$divider-blurry-vr-bg-image};
+ --#{$prefix}divider-blurry-vr-width: #{$divider-blurry-vr-width};
+ --#{$prefix}divider-blurry-opacity: #{$divider-blurry-opacity};
+
+ background-image: var(--#{$prefix}divider-blurry-vr-bg-image);
+ width: var(--#{$prefix}divider-blurry-vr-width);
+ opacity: var(--#{$prefix}divider-blurry-opacity);
+}
+
+// Links
+
+a {
+ --#{$prefix}link-decoration: #{$link-decoration};
+
+ text-decoration: var(--#{$prefix}link-decoration);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_ripple.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_ripple.scss
new file mode 100644
index 00000000..7a5add65
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_ripple.scss
@@ -0,0 +1,51 @@
+.ripple-surface {
+ position: relative;
+ overflow: hidden;
+ display: inline-block;
+ vertical-align: bottom;
+}
+
+.ripple-surface-unbound {
+ overflow: visible;
+}
+
+.ripple-wave {
+ --#{$prefix}ripple-wave-cubicBezier: #{$ripple-wave-cubicBezier};
+ --#{$prefix}ripple-wave-border-radius: #{$ripple-wave-border-radius};
+ --#{$prefix}ripple-wave-opacity: #{$ripple-wave-opacity};
+ --#{$prefix}ripple-wave-transform: #{$ripple-wave-transform};
+ --#{$prefix}ripple-wave-z-index: #{$ripple-wave-z-index};
+ --#{$prefix}ripple-wave-active-transform: #{$ripple-wave-active-transform};
+
+ @include ripple-variant(black);
+ border-radius: var(--#{$prefix}ripple-wave-border-radius);
+ opacity: var(--#{$prefix}ripple-wave-opacity);
+ pointer-events: none;
+ position: absolute;
+ touch-action: none;
+ transform: var(--#{$prefix}ripple-wave-transform);
+ transition-property: transform, opacity;
+ transition-timing-function: var(--#{$prefix}ripple-wave-cubicBezier),
+ var(--#{$prefix}ripple-wave-cubicBezier);
+ z-index: var(--#{$prefix}ripple-wave-z-index);
+ &.active {
+ transform: var(--#{$prefix}ripple-wave-active-transform);
+ opacity: 0;
+ }
+}
+
+.btn .ripple-wave {
+ @include ripple-variant(white);
+}
+
+.input-wrapper .ripple-wave {
+ @include ripple-variant(white);
+}
+
+@each $color, $value in $ripple {
+ .ripple-surface-#{$color} {
+ .ripple-wave {
+ @include ripple-variant($value);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_root.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_root.scss
new file mode 100644
index 00000000..6e28d795
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_root.scss
@@ -0,0 +1,60 @@
+//
+// Root and body
+//
+
+:root,
+[data-mdb-theme='light'] {
+ --mdb-font-roboto: #{inspect($font-family-roboto)};
+ --mdb-bg-opacity: 1;
+ --mdb-text-hover-opacity: 0.8;
+
+ --#{$prefix}surface-color: #{$surface-color};
+ --#{$prefix}surface-color-rgb: #{to-rgb($surface-color)};
+ --#{$prefix}surface-bg: #{$surface-bg};
+ --#{$prefix}surface-inverted-color: #{$surface-inverted-color};
+ --#{$prefix}surface-inverted-color-rgb: #{to-rgb($surface-inverted-color)};
+ --#{$prefix}surface-inverted-bg: #{$surface-inverted-bg};
+ --#{$prefix}divider-color: #{$divider-color};
+ --#{$prefix}divider-blurry-color: #{$divider-blurry-color};
+ --#{$prefix}highlight-bg-color: #{$highlight-bg-color};
+ --#{$prefix}scrollbar-rail-bg: #{$scrollbar-rail-bg};
+ --#{$prefix}scrollbar-thumb-bg: #{$scrollbar-thumb-bg};
+ --#{$prefix}picker-header-bg: #{$picker-header-bg};
+ --#{$prefix}timepicker-clock-face-bg: #{$timepicker-clock-face-bg};
+ --#{$prefix}sidenav-backdrop-opacity: #{$sidenav-backdrop-opacity};
+ --#{$prefix}form-control-border-color: #{$form-control-border-color};
+ --#{$prefix}form-control-label-color: #{$form-control-label-color};
+ --#{$prefix}form-control-disabled-bg: #{$form-control-disabled-bg};
+ --#{$prefix}box-shadow-color: #{$box-shadow-color};
+ --#{$prefix}box-shadow-color-rgb: #{to-rgb($box-shadow-color)};
+ --#{$prefix}stepper-mobile-bg: #{$stepper-mobile-bg};
+}
+
+@if $enable-dark-mode {
+ @include color-mode(dark, true) {
+ color-scheme: dark;
+
+ // scss-docs-start root-dark-mode-vars
+ --#{$prefix}surface-color: #{$surface-color-dark};
+ --#{$prefix}surface-color-rgb: #{to-rgb($surface-color-dark)};
+ --#{$prefix}surface-bg: #{$surface-bg-dark};
+ --#{$prefix}surface-inverted-color: #{$surface-inverted-color-dark};
+ --#{$prefix}surface-inverted-color-rgb: #{to-rgb($surface-inverted-color-dark)};
+ --#{$prefix}surface-inverted-bg: #{$surface-inverted-bg-dark};
+ --#{$prefix}divider-color: #{$divider-color-dark};
+ --#{$prefix}divider-blurry-color: #{$divider-blurry-color-dark};
+ --#{$prefix}highlight-bg-color: #{$highlight-bg-color-dark};
+ --#{$prefix}scrollbar-rail-bg: #{$scrollbar-rail-bg-dark};
+ --#{$prefix}scrollbar-thumb-bg: #{$scrollbar-thumb-bg-dark};
+ --#{$prefix}picker-header-bg: #{$picker-header-bg-dark};
+ --#{$prefix}timepicker-clock-face-bg: #{$timepicker-clock-face-bg-dark};
+ --#{$prefix}sidenav-backdrop-opacity: #{$sidenav-backdrop-opacity-dark};
+ --#{$prefix}form-control-border-color: #{$form-control-border-color-dark};
+ --#{$prefix}form-control-label-color: #{$form-control-label-color-dark};
+ --#{$prefix}form-control-disabled-bg: #{$form-control-disabled-bg-dark};
+ --#{$prefix}box-shadow-color: #{$box-shadow-color-dark};
+ --#{$prefix}box-shadow-color-rgb: #{to-rgb($box-shadow-color-dark)};
+ --#{$prefix}stepper-mobile-bg: #{$stepper-mobile-bg-dark};
+ // scss-docs-end root-dark-mode-vars
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_scrollspy.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_scrollspy.scss
new file mode 100644
index 00000000..b9f9466c
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_scrollspy.scss
@@ -0,0 +1,49 @@
+// Scrollspy
+
+.nav-pills {
+ &.menu-sidebar {
+ .nav-link {
+ --#{$prefix}scrollspy-menu-sidebar-font-size: #{$scrollspy-menu-sidebar-font-size};
+ --#{$prefix}scrollspy-menu-sidebar-color: #{$scrollspy-menu-sidebar-color};
+ --#{$prefix}scrollspy-menu-sidebar-line-height: #{$scrollspy-menu-sidebar-line-height};
+ --#{$prefix}scrollspy-menu-sidebar-padding-x: #{$scrollspy-menu-sidebar-padding-x};
+ --#{$prefix}scrollspy-menu-sidebar-font-weight: #{$scrollspy-menu-sidebar-font-weight};
+ --#{$prefix}scrollspy-menu-sidebar-transition: #{$scrollspy-menu-sidebar-transition};
+ --#{$prefix}scrollspy-menu-sidebar-margin-y: #{$scrollspy-menu-sidebar-margin-y};
+
+ font-size: var(--#{$prefix}scrollspy-menu-sidebar-font-size);
+ background-color: transparent;
+ color: var(--#{$prefix}scrollspy-menu-sidebar-color);
+ line-height: var(--#{$prefix}scrollspy-menu-sidebar-line-height);
+ padding: 0 var(--#{$prefix}scrollspy-menu-sidebar-padding-x);
+ font-weight: var(--#{$prefix}scrollspy-menu-sidebar-font-weight);
+ transition: var(--#{$prefix}scrollspy-menu-sidebar-transition);
+ text-transform: initial;
+ margin-top: var(--#{$prefix}scrollspy-menu-sidebar-margin-y);
+ margin-bottom: var(--#{$prefix}scrollspy-menu-sidebar-margin-y);
+ }
+
+ .nav-link.active,
+ .show > .nav-link {
+ --#{$prefix}scrollspy-menu-sidebar-active-color: #{$scrollspy-menu-sidebar-active-color};
+ --#{$prefix}scrollspy-menu-sidebar-active-font-weight: #{$scrollspy-menu-sidebar-active-font-weight};
+ --#{$prefix}scrollspy-menu-sidebar-active-border-width: #{$scrollspy-menu-sidebar-active-border-width};
+ --#{$prefix}scrollspy-menu-sidebar-active-border-color: #{$scrollspy-menu-sidebar-active-border-color};
+
+ background-color: transparent;
+ box-shadow: none;
+ color: var(--#{$prefix}scrollspy-menu-sidebar-active-color);
+ font-weight: var(--#{$prefix}scrollspy-menu-sidebar-active-font-weight);
+ border-left: var(--#{$prefix}scrollspy-menu-sidebar-active-border-width) solid
+ var(--#{$prefix}scrollspy-menu-sidebar-active-border-color);
+ border-radius: 0;
+ }
+
+ // .collapsible-scrollspy ~ .nav {
+ // --#{$prefix}scrollspy-collapsible-nav-transition-time: #{$scrollspy-collapsible-nav-transition-time};
+
+ // transition: height var(--#{$prefix}scrollspy-collapsible-nav-transition-time) ease;
+ // flex-wrap: nowrap;
+ // }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_shadows.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_shadows.scss
new file mode 100644
index 00000000..0c011bec
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_shadows.scss
@@ -0,0 +1 @@
+// Shadows
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_tables.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_tables.scss
new file mode 100644
index 00000000..8d036936
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_tables.scss
@@ -0,0 +1,45 @@
+//
+// Basic MDB table
+//
+
+.table {
+ --#{$prefix}table-font-size: #{$table-font-size};
+ --#{$prefix}table-divider-color: #{$table-divider-color};
+
+ font-size: var(--#{$prefix}table-font-size);
+
+ th {
+ font-weight: $font-weight-medium;
+ }
+
+ tbody {
+ font-weight: $font-weight-normal;
+ }
+
+ > :not(:last-child) > :last-child > * {
+ border-bottom-color: $table-group-separator-color;
+ }
+}
+
+@each $color, $value in $table-variants {
+ @include table-variant($color, $value);
+}
+
+.table-hover {
+ > tbody > tr {
+ transition: $table-hover-transition;
+ }
+ > tbody > tr:hover {
+ --#{$prefix}table-accent-bg: transparent;
+ background-color: var(--#{$prefix}table-hover-bg);
+ }
+}
+
+.table-group-divider {
+ border-top: calc(2 * $table-border-width) solid;
+ border-top-color: inherit;
+}
+
+.table-divider-color {
+ border-top-color: var(--#{$prefix}table-divider-color);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_toasts.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_toasts.scss
new file mode 100644
index 00000000..4854b9e2
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_toasts.scss
@@ -0,0 +1,70 @@
+//
+// Toast styles
+//
+
+.toast {
+ // scss-docs-start toast-css-vars
+ --#{$prefix}toast-border-bottom-width: #{$toast-border-bottom-width};
+ --#{$prefix}toast-btn-close-width: #{$toast-btn-close-width};
+ --#{$prefix}toast-btn-close-mr: #{$toast-btn-close-mr};
+ --#{$prefix}toast-btn-close-ml: #{$toast-btn-close-ml};
+ --#{$prefix}toast-bg: #{$toast-background-color};
+ --#{$prefix}toast-header-bg: #{$toast-header-background-color};
+ // scss-docs-end toast-css-vars
+
+ border: 0;
+
+ // Additional MDB Angular styles
+ margin-bottom: 10px;
+ // Additional MDB Angular styles
+
+ .btn-close {
+ width: var(--#{$prefix}toast-btn-close-width);
+ }
+
+ // Additional MDB Angular styles
+ &:not(.show) {
+ display: block;
+ }
+ // Additional MDB Angular styles
+}
+
+.toast-header {
+ border-bottom-width: var(--#{$prefix}toast-border-bottom-width);
+
+ .btn-close {
+ margin-right: var(--#{$prefix}toast-btn-close-mr);
+ margin-left: var(--#{$prefix}toast-btn-close-ml);
+ }
+}
+
+.parent-toast-relative {
+ position: relative;
+}
+
+.toast-absolute {
+ position: absolute;
+}
+
+.toast-fixed {
+ position: fixed;
+ z-index: var(--#{$prefix}toast-zindex);
+}
+
+// Additional MDB Angular styles
+.toast:not(.showing):not(.show) {
+ opacity: 1;
+}
+// Additional MDB Angular styles
+
+@each $color, $value in $theme-colors {
+ .toast-#{$color} {
+ background-color: var(--#{$prefix}#{$color}-bg-subtle);
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+ border-color: var(--#{$prefix}#{$color}-border-subtle);
+
+ i {
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_tooltip.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_tooltip.scss
new file mode 100644
index 00000000..986df091
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_tooltip.scss
@@ -0,0 +1,31 @@
+// Tooltip
+
+.tooltip {
+ --#{$prefix}tooltip-font-size: #{$tooltip-font-size};
+
+ // &.show {
+ // opacity: 1;
+ // }
+
+ // .tooltip-arrow {
+ // display: none;
+ // }
+
+ // Additional MDB Angular styles
+ opacity: 1;
+ // Additional MDB Angular styles
+}
+
+.tooltip-inner {
+ font-size: var(--#{$prefix}tooltip-font-size);
+
+ // Additional MDB Angular styles
+ opacity: 0;
+ // Additional MDB Angular styles
+}
+
+// Additional MDB Angular styles
+.notification-open .cdk-overlay-container {
+ z-index: $zindex-toast;
+}
+// Additional MDB Angular styles
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_type.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_type.scss
new file mode 100644
index 00000000..ce66965a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_type.scss
@@ -0,0 +1,27 @@
+// Type
+.note {
+ --#{$prefix}note-padding: #{$note-padding};
+ --#{$prefix}note-border-width: #{$note-border-width};
+ --#{$prefix}note-border-radius: #{$note-border-radius};
+ --#{$prefix}note-strong-font-weight: #{$note-strong-font-weight};
+
+ padding: var(--#{$prefix}note-padding);
+ border-left: var(--#{$prefix}note-border-width) solid;
+ border-radius: var(--#{$prefix}note-border-radius);
+ strong {
+ font-weight: var(--#{$prefix}note-strong-font-weight);
+ }
+}
+
+@each $color, $value in $theme-colors {
+ .note-#{$color} {
+ background-color: var(--#{$prefix}#{$color}-bg-subtle);
+ color: var(--#{$prefix}#{$color}-text-emphasis);
+ }
+}
+
+.w-responsive {
+ @media (min-width: 1199px) {
+ width: 75%;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_utilities.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_utilities.scss
new file mode 100644
index 00000000..0bd931a3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_utilities.scss
@@ -0,0 +1,216 @@
+//
+// Utilities
+//
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'margin-bottom':
+ map-merge(
+ map-get($utilities, 'margin-bottom'),
+ (
+ values:
+ map-merge(
+ map-get(map-get($utilities, 'margin-bottom'), 'values'),
+ (
+ 6: 3.5rem,
+ 7: 4rem,
+ 8: 5rem,
+ 9: 6rem,
+ 10: 8rem,
+ 11: 10rem,
+ 12: 12rem,
+ 13: 14rem,
+ 14: 16rem,
+ )
+ ),
+ )
+ ),
+ )
+);
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'rounded':
+ map-merge(
+ map-get($utilities, 'rounded'),
+ (
+ values:
+ map-merge(
+ map-get(map-get($utilities, 'rounded'), 'values'),
+ (
+ 4: 0.375rem,
+ 5: 0.5rem,
+ 6: 0.75rem,
+ 7: 1rem,
+ 8: 1.25rem,
+ 9: 1.5rem,
+ )
+ ),
+ )
+ ),
+ )
+);
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'opacity': (
+ property: opacity,
+ values: (
+ 0: 0,
+ 5: 0.05,
+ 10: 0.1,
+ 15: 0.15,
+ 20: 0.2,
+ 25: 0.25,
+ 30: 0.3,
+ 35: 0.35,
+ 40: 0.4,
+ 45: 0.45,
+ 50: 0.5,
+ 55: 0.55,
+ 60: 0.6,
+ 65: 0.65,
+ 70: 0.7,
+ 75: 0.75,
+ 80: 0.8,
+ 85: 0.85,
+ 90: 0.9,
+ 95: 0.95,
+ 100: 1,
+ ),
+ ),
+ )
+);
+
+/* Numeric */
+.diagonal-fractions {
+ font-variant-numeric: diagonal-fractions;
+}
+
+/* Background colors */
+.bg-super-light {
+ --#{$prefix}bg-super-light: #fbfbfb;
+
+ background-color: var(--#{$prefix}bg-super-light);
+}
+
+/* Background attachment */
+.bg-fixed {
+ background-attachment: fixed;
+}
+.bg-local {
+ background-attachment: local;
+}
+.bg-scroll {
+ background-attachment: scroll;
+}
+
+/* Overflow */
+.overflow-y-scroll {
+ overflow-y: scroll;
+}
+
+.overflow-x-scroll {
+ overflow-x: scroll;
+}
+
+/* Tables */
+.table-fixed {
+ table-layout: fixed;
+}
+.table-auto {
+ table-layout: auto;
+}
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'shadow':
+ map-merge(
+ map-get($utilities, 'shadow'),
+ (
+ values:
+ map-merge(
+ map-get(map-get($utilities, 'shadow'), 'values'),
+ (
+ 0: $box-shadow-0,
+ 1: $box-shadow-1,
+ 2: $box-shadow-2,
+ 3: $box-shadow-3,
+ 4: $box-shadow-4,
+ 5: $box-shadow-5,
+ 6: $box-shadow-6,
+ 1-soft: $box-shadow-1-soft,
+ 2-soft: $box-shadow-2-soft,
+ 3-soft: $box-shadow-3-soft,
+ 4-soft: $box-shadow-4-soft,
+ 5-soft: $box-shadow-5-soft,
+ 6-soft: $box-shadow-6-soft,
+ 1-strong: $box-shadow-1-strong,
+ 2-strong: $box-shadow-2-strong,
+ 3-strong: $box-shadow-3-strong,
+ 4-strong: $box-shadow-4-strong,
+ 5-strong: $box-shadow-5-strong,
+ 6-strong: $box-shadow-6-strong,
+ inner: $box-shadow-inner,
+ )
+ ),
+ )
+ ),
+ )
+);
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'letter-spacing': (
+ property: letter-spacing,
+ class: ls,
+ values: (
+ tighter: -0.05em,
+ tight: -0.025em,
+ normal: 0em,
+ wide: 0.025em,
+ wider: 0.05em,
+ widest: 0.1em,
+ ),
+ ),
+ )
+);
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'object-fit': (
+ property: object-fit,
+ class: object,
+ values: (
+ cover: cover,
+ ),
+ ),
+ )
+);
+
+$utilities: map-merge(
+ $utilities,
+ (
+ 'object-position': (
+ property: object-position,
+ class: object,
+ values: (
+ top: top,
+ center: center,
+ bottom: bottom,
+ ),
+ ),
+ )
+);
+
+@each $name, $color in $theme-colors {
+ .link-#{$name} {
+ transition: color 0.15s;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_validation.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_validation.scss
new file mode 100644
index 00000000..8c343a18
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_validation.scss
@@ -0,0 +1,97 @@
+.error-message,
+.success-message {
+ position: absolute;
+ top: 40px;
+ left: 0;
+ font-size: 0.875rem;
+}
+
+.form-check .error-message,
+.form-check .success-message {
+ top: 29px;
+ left: 26px;
+}
+
+textarea ~ .error-message,
+textarea ~ .success-message {
+ top: unset;
+ bottom: -26px;
+}
+
+.error-message {
+ color: $danger;
+}
+
+.success-message {
+ color: $success;
+}
+
+.form-outline .validate-success.ng-valid.ng-dirty,
+.form-outline .validate-success.ng-valid.ng-touched {
+ ~ .form-label {
+ color: $success;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $success;
+ }
+}
+
+.form-outline .validate-error.ng-invalid.ng-dirty,
+.form-outline .validate-error.ng-invalid.ng-touched {
+ ~ .form-label {
+ color: $danger;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $danger;
+ }
+}
+
+.select.validate-success.ng-valid.ng-dirty,
+.select.validate-success.ng-valid.ng-touched {
+ ~ .form-label {
+ color: $success;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $success;
+ }
+}
+
+.select.validate-error.ng-invalid.ng-dirty,
+.select.validate-error.ng-invalid.ng-touched {
+ ~ .form-label {
+ color: $danger;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $danger;
+ }
+}
+
+.form-check .validate-success.ng-valid.ng-dirty,
+.form-check .validate-success.ng-valid.ng-touched {
+ border-color: $success;
+
+ &:checked {
+ background-color: $success;
+ }
+
+ ~ .form-check-label {
+ color: $success;
+ }
+}
+
+.form-check .validate-error.ng-invalid.ng-dirty,
+.form-check .validate-error.ng-invalid.ng-touched {
+ border-color: $danger;
+
+ ~ .form-check-label {
+ color: $danger;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_variables-dark.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_variables-dark.scss
new file mode 100644
index 00000000..08e65253
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_variables-dark.scss
@@ -0,0 +1,78 @@
+// Dark color mode variables
+//
+// Custom variables for the `[data-mdb-theme="dark"]` theme. Use this as a starting point for your own custom color modes by creating a new theme-specific file like `_variables-dark.scss` and adding the variables you need.
+
+//
+// Global colors
+//
+
+// scss-docs-start sass-dark-mode-vars
+
+// scss-docs-start theme-text-variables
+$primary-text-emphasis-dark: tint-color($primary, 20%) !default;
+$secondary-text-emphasis-dark: tint-color($secondary, 60%) !default;
+$success-text-emphasis-dark: tint-color($success, 40%) !default;
+$info-text-emphasis-dark: tint-color($info, 30%) !default;
+$warning-text-emphasis-dark: tint-color($warning, 40%) !default;
+$danger-text-emphasis-dark: tint-color($danger, 20%) !default;
+$light-text-emphasis-dark: $gray-100 !default;
+$dark-text-emphasis-dark: $gray-200 !default;
+// scss-docs-end theme-text-variables
+
+// scss-docs-start theme-bg-subtle-variables
+$primary-bg-subtle-dark: shade-color($primary, 80%) !default;
+$secondary-bg-subtle-dark: shade-color($secondary, 80%) !default;
+$success-bg-subtle-dark: shade-color($success, 80%) !default;
+$info-bg-subtle-dark: shade-color($info, 80%) !default;
+$warning-bg-subtle-dark: shade-color($warning, 80%) !default;
+$danger-bg-subtle-dark: shade-color($danger, 80%) !default;
+$light-bg-subtle-dark: $gray-800 !default;
+$dark-bg-subtle-dark: $gray-900 !default;
+// scss-docs-end theme-bg-subtle-variables
+
+// scss-docs-start theme-border-subtle-variables
+$primary-border-subtle-dark: shade-color($primary, 40%) !default;
+$secondary-border-subtle-dark: shade-color($secondary, 40%) !default;
+$success-border-subtle-dark: shade-color($success, 40%) !default;
+$info-border-subtle-dark: shade-color($info, 40%) !default;
+$warning-border-subtle-dark: shade-color($warning, 40%) !default;
+$danger-border-subtle-dark: shade-color($danger, 40%) !default;
+$light-border-subtle-dark: $gray-700 !default;
+$dark-border-subtle-dark: $gray-800 !default;
+// scss-docs-end theme-border-subtle-variables
+
+$body-color-dark: $white !default;
+$body-bg-dark: #303030 !default;
+$body-secondary-bg-dark: $gray-800 !default;
+$body-tertiary-bg-dark: mix($gray-800, $gray-900, 50%) !default;
+$border-color-dark: rgba(255, 255, 255, 0.12) !default;
+$border-color-translucent-dark: rgba($white, 0.15) !default;
+
+// Global MDB dark theme variables
+
+// scss-docs-start mdb-global-dark-theme-variables
+$surface-color-dark: $white !default;
+$surface-bg-dark: #424242 !default;
+$surface-inverted-color-dark: $white !default;
+$surface-inverted-bg-dark: #757575 !default;
+$divider-color-dark: rgba(255, 255, 255, 0.12) !default;
+$divider-blurry-color-dark: hsl(0, 0%, 70%) !default;
+$highlight-bg-color-dark: tint-color($gray-900, 10%) !default;
+$scrollbar-rail-bg-dark: $gray-500 !default;
+$scrollbar-thumb-bg-dark: $gray-200 !default;
+$picker-header-bg-dark: #323232 !default;
+$timepicker-clock-face-bg-dark: $gray-700 !default;
+$sidenav-backdrop-opacity-dark: 0.5 !default;
+$form-control-border-color-dark: rgba(255, 255, 255, 0.7) !default;
+$form-control-label-color-dark: $gray-400 !default;
+$form-control-disabled-bg-dark: $gray-700 !default;
+$box-shadow-color-dark: $black !default;
+$stepper-mobile-bg-dark: $body-tertiary-bg-dark !default;
+// scss-docs-start mdb-global-dark-theme-variables
+
+// Accordion
+
+// scss-docs-start accordion-variables
+$accordion-icon-color-dark: $surface-color-dark !default;
+$accordion-icon-active-color-dark: $primary !default;
+// scss-docs-end accordion-variables
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/_variables.scss b/projects/mdb-angular-ui-kit/assets/scss/free/_variables.scss
new file mode 100644
index 00000000..1b607c61
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/_variables.scss
@@ -0,0 +1,1681 @@
+// Variables
+
+// Color system
+
+// scss-docs-start gray-color-variables
+$white: #fff !default;
+$gray-50: #fbfbfb !default;
+$gray-100: #f5f5f5 !default;
+$gray-200: #eeeeee !default;
+$gray-300: #e0e0e0 !default;
+$gray-400: #bdbdbd !default;
+$gray-500: #9e9e9e !default;
+$gray-600: #757575 !default;
+$gray-700: #616161 !default;
+$gray-800: #4f4f4f !default;
+$gray-900: #262626 !default;
+$black: #000 !default;
+// scss-docs-end gray-color-variables
+
+// scss-docs-start gray-colors-map
+$grays: (
+ '50': $gray-50,
+ '100': $gray-100,
+ '200': $gray-200,
+ '300': $gray-300,
+ '400': $gray-400,
+ '500': $gray-500,
+ '600': $gray-600,
+ '700': $gray-700,
+ '800': $gray-800,
+ '900': $gray-900,
+) !default;
+// scss-docs-end gray-colors-map
+
+// scss-docs-start material-design-color-system-variables
+$red-50: #ffebee !default;
+$red-100: #ffcdd2 !default;
+$red-200: #ef9a9a !default;
+$red-300: #e57373 !default;
+$red-400: #ef5350 !default;
+$red-500: #f44336 !default;
+$red-600: #e53935 !default;
+$red-700: #d32f2f !default;
+$red-800: #c62828 !default;
+$red-900: #b71c1c !default;
+$red-accent-100: #ff8a80 !default;
+$red-accent-200: #ff5252 !default;
+$red-accent-400: #ff1744 !default;
+$red-accent-700: #d50000 !default;
+
+$pink-50: #fce4ec !default;
+$pink-100: #f8bbd0 !default;
+$pink-200: #f48fb1 !default;
+$pink-300: #f06292 !default;
+$pink-400: #ec407a !default;
+$pink-500: #e91e63 !default;
+$pink-600: #d81b60 !default;
+$pink-700: #c2185b !default;
+$pink-800: #ad1457 !default;
+$pink-900: #880e4f !default;
+$pink-accent-100: #ff80ab !default;
+$pink-accent-200: #ff4081 !default;
+$pink-accent-400: #f50057 !default;
+$pink-accent-700: #c51162 !default;
+
+$purple-50: #f3e5f5 !default;
+$purple-100: #e1bee7 !default;
+$purple-200: #ce93d8 !default;
+$purple-300: #ba68c8 !default;
+$purple-400: #ab47bc !default;
+$purple-500: #9c27b0 !default;
+$purple-600: #8e24aa !default;
+$purple-700: #7b1fa2 !default;
+$purple-800: #6a1b9a !default;
+$purple-900: #4a148c !default;
+$purple-accent-100: #ea80fc !default;
+$purple-accent-200: #e040fb !default;
+$purple-accent-400: #d500f9 !default;
+$purple-accent-700: #aa00ff !default;
+
+$deep-purple-50: #ede7f6 !default;
+$deep-purple-100: #d1c4e9 !default;
+$deep-purple-200: #b39ddb !default;
+$deep-purple-300: #9575cd !default;
+$deep-purple-400: #7e57c2 !default;
+$deep-purple-500: #673ab7 !default;
+$deep-purple-600: #5e35b1 !default;
+$deep-purple-700: #512da8 !default;
+$deep-purple-800: #4527a0 !default;
+$deep-purple-900: #311b92 !default;
+$deep-purple-accent-100: #b388ff !default;
+$deep-purple-accent-200: #7c4dff !default;
+$deep-purple-accent-400: #651fff !default;
+$deep-purple-accent-700: #6200ea !default;
+
+$indigo-50: #e8eaf6 !default;
+$indigo-100: #c5cae9 !default;
+$indigo-200: #9fa8da !default;
+$indigo-300: #7986cb !default;
+$indigo-400: #5c6bc0 !default;
+$indigo-500: #3f51b5 !default;
+$indigo-600: #3949ab !default;
+$indigo-700: #303f9f !default;
+$indigo-800: #283593 !default;
+$indigo-900: #1a237e !default;
+$indigo-accent-100: #8c9eff !default;
+$indigo-accent-200: #536dfe !default;
+$indigo-accent-400: #3d5afe !default;
+$indigo-accent-700: #304ffe !default;
+
+$blue-50: #e3f2fd !default;
+$blue-100: #bbdefb !default;
+$blue-200: #90caf9 !default;
+$blue-300: #64b5f6 !default;
+$blue-400: #42a5f5 !default;
+$blue-500: #2196f3 !default;
+$blue-600: #1e88e5 !default;
+$blue-700: #1976d2 !default;
+$blue-800: #1565c0 !default;
+$blue-900: #0d47a1 !default;
+$blue-accent-100: #82b1ff !default;
+$blue-accent-200: #448aff !default;
+$blue-accent-400: #2979ff !default;
+$blue-accent-700: #2962ff !default;
+
+$light-blue-50: #e1f5fe !default;
+$light-blue-100: #b3e5fc !default;
+$light-blue-200: #81d4fa !default;
+$light-blue-300: #4fc3f7 !default;
+$light-blue-400: #29b6f6 !default;
+$light-blue-500: #03a9f4 !default;
+$light-blue-600: #039be5 !default;
+$light-blue-700: #0288d1 !default;
+$light-blue-800: #0277bd !default;
+$light-blue-900: #01579b !default;
+$light-blue-accent-100: #80d8ff !default;
+$light-blue-accent-200: #40c4ff !default;
+$light-blue-accent-400: #00b0ff !default;
+$light-blue-accent-700: #0091ea !default;
+
+$cyan-50: #e0f7fa !default;
+$cyan-100: #b2ebf2 !default;
+$cyan-200: #80deea !default;
+$cyan-300: #4dd0e1 !default;
+$cyan-400: #26c6da !default;
+$cyan-500: #00bcd4 !default;
+$cyan-600: #00acc1 !default;
+$cyan-700: #0097a7 !default;
+$cyan-800: #00838f !default;
+$cyan-900: #006064 !default;
+$cyan-accent-100: #84ffff !default;
+$cyan-accent-200: #18ffff !default;
+$cyan-accent-400: #00e5ff !default;
+$cyan-accent-700: #00b8d4 !default;
+
+$teal-50: #e0f2f1 !default;
+$teal-100: #b2dfdb !default;
+$teal-200: #80cbc4 !default;
+$teal-300: #4db6ac !default;
+$teal-400: #26a69a !default;
+$teal-500: #009688 !default;
+$teal-600: #00897b !default;
+$teal-700: #00796b !default;
+$teal-800: #00695c !default;
+$teal-900: #004d40 !default;
+$teal-accent-100: #a7ffeb !default;
+$teal-accent-200: #64ffda !default;
+$teal-accent-400: #1de9b6 !default;
+$teal-accent-700: #00bfa5 !default;
+
+$green-50: #e8f5e9 !default;
+$green-100: #c8e6c9 !default;
+$green-200: #a5d6a7 !default;
+$green-300: #81c784 !default;
+$green-400: #66bb6a !default;
+$green-500: #4caf50 !default;
+$green-600: #43a047 !default;
+$green-700: #388e3c !default;
+$green-800: #2e7d32 !default;
+$green-900: #1b5e20 !default;
+$green-accent-100: #b9f6ca !default;
+$green-accent-200: #69f0ae !default;
+$green-accent-400: #00e676 !default;
+$green-accent-700: #00c853 !default;
+
+$light-green-50: #f1f8e9 !default;
+$light-green-100: #dcedc8 !default;
+$light-green-200: #c5e1a5 !default;
+$light-green-300: #aed581 !default;
+$light-green-400: #9ccc65 !default;
+$light-green-500: #8bc34a !default;
+$light-green-600: #7cb342 !default;
+$light-green-700: #689f38 !default;
+$light-green-800: #558b2f !default;
+$light-green-900: #33691e !default;
+$light-green-accent-100: #ccff90 !default;
+$light-green-accent-200: #b2ff59 !default;
+$light-green-accent-400: #76ff03 !default;
+$light-green-accent-700: #64dd17 !default;
+
+$lime-50: #f9fbe7 !default;
+$lime-100: #f0f4c3 !default;
+$lime-200: #e6ee9c !default;
+$lime-300: #dce775 !default;
+$lime-400: #d4e157 !default;
+$lime-500: #cddc39 !default;
+$lime-600: #c0ca33 !default;
+$lime-700: #afb42b !default;
+$lime-800: #9e9d24 !default;
+$lime-900: #827717 !default;
+$lime-accent-100: #f4ff81 !default;
+$lime-accent-200: #eeff41 !default;
+$lime-accent-400: #c6ff00 !default;
+$lime-accent-700: #aeea00 !default;
+
+$yellow-50: #fffde7 !default;
+$yellow-100: #fff9c4 !default;
+$yellow-200: #fff59d !default;
+$yellow-300: #fff176 !default;
+$yellow-400: #ffee58 !default;
+$yellow-500: #ffeb3b !default;
+$yellow-600: #fdd835 !default;
+$yellow-700: #fbc02d !default;
+$yellow-800: #f9a825 !default;
+$yellow-900: #f57f17 !default;
+$yellow-accent-100: #ffff8d !default;
+$yellow-accent-200: #ffff00 !default;
+$yellow-accent-400: #ffea00 !default;
+$yellow-accent-700: #ffd600 !default;
+
+$amber-50: #fff8e1 !default;
+$amber-100: #ffecb3 !default;
+$amber-200: #ffe082 !default;
+$amber-300: #ffd54f !default;
+$amber-400: #ffca28 !default;
+$amber-500: #ffc107 !default;
+$amber-600: #ffb300 !default;
+$amber-700: #ffa000 !default;
+$amber-800: #ff8f00 !default;
+$amber-900: #ff6f00 !default;
+$amber-accent-100: #ffe57f !default;
+$amber-accent-200: #ffd740 !default;
+$amber-accent-400: #ffc400 !default;
+$amber-accent-700: #ffab00 !default;
+
+$orange-50: #fff3e0 !default;
+$orange-100: #ffe0b2 !default;
+$orange-200: #ffcc80 !default;
+$orange-300: #ffb74d !default;
+$orange-400: #ffa726 !default;
+$orange-500: #ff9800 !default;
+$orange-600: #fb8c00 !default;
+$orange-700: #f57c00 !default;
+$orange-800: #ef6c00 !default;
+$orange-900: #e65100 !default;
+$orange-accent-100: #ffd180 !default;
+$orange-accent-200: #ffab40 !default;
+$orange-accent-400: #ff9100 !default;
+$orange-accent-700: #ff6d00 !default;
+
+$deep-orange-50: #fbe9e7 !default;
+$deep-orange-100: #ffccbc !default;
+$deep-orange-200: #ffab91 !default;
+$deep-orange-300: #ff8a65 !default;
+$deep-orange-400: #ff7043 !default;
+$deep-orange-500: #ff5722 !default;
+$deep-orange-600: #f4511e !default;
+$deep-orange-700: #e64a19 !default;
+$deep-orange-800: #d84315 !default;
+$deep-orange-900: #bf360c !default;
+$deep-orange-accent-100: #ff9e80 !default;
+$deep-orange-accent-200: #ff6e40 !default;
+$deep-orange-accent-400: #ff3d00 !default;
+$deep-orange-accent-700: #dd2c00 !default;
+
+$brown-50: #efebe9 !default;
+$brown-100: #d7ccc8 !default;
+$brown-200: #bcaaa4 !default;
+$brown-300: #a1887f !default;
+$brown-400: #8d6e63 !default;
+$brown-500: #795548 !default;
+$brown-600: #6d4c41 !default;
+$brown-700: #5d4037 !default;
+$brown-800: #4e342e !default;
+$brown-900: #3e2723 !default;
+
+$blue-gray-50: #eceff1 !default;
+$blue-gray-100: #cfd8dc !default;
+$blue-gray-200: #b0bec5 !default;
+$blue-gray-300: #90a4ae !default;
+$blue-gray-400: #78909c !default;
+$blue-gray-500: #607d8b !default;
+$blue-gray-600: #546e7a !default;
+$blue-gray-700: #455a64 !default;
+$blue-gray-800: #37474f !default;
+$blue-gray-900: #263238 !default;
+// scss-docs-end material-design-color-system-variables
+
+// scss-docs-start material-design-color-system-map
+$reds: (
+ 'red-50': $red-50,
+ 'red-100': $red-100,
+ 'red-200': $red-200,
+ 'red-300': $red-300,
+ 'red-400': $red-400,
+ 'red-500': $red-500,
+ 'red-600': $red-600,
+ 'red-700': $red-700,
+ 'red-800': $red-800,
+ 'red-900': $red-900,
+ 'red-accent-100': $red-accent-100,
+ 'red-accent-200': $red-accent-200,
+ 'red-accent-400': $red-accent-400,
+ 'red-accent-700': $red-accent-700,
+) !default;
+
+$pinks: (
+ 'pink-50': $pink-50,
+ 'pink-100': $pink-100,
+ 'pink-200': $pink-200,
+ 'pink-300': $pink-300,
+ 'pink-400': $pink-400,
+ 'pink-500': $pink-500,
+ 'pink-600': $pink-600,
+ 'pink-700': $pink-700,
+ 'pink-800': $pink-800,
+ 'pink-900': $pink-900,
+ 'pink-accent-100': $pink-accent-100,
+ 'pink-accent-200': $pink-accent-200,
+ 'pink-accent-400': $pink-accent-400,
+ 'pink-accent-700': $pink-accent-700,
+) !default;
+
+$purples: (
+ 'purple-50': $purple-50,
+ 'purple-100': $purple-100,
+ 'purple-200': $purple-200,
+ 'purple-300': $purple-300,
+ 'purple-400': $purple-400,
+ 'purple-500': $purple-500,
+ 'purple-600': $purple-600,
+ 'purple-700': $purple-700,
+ 'purple-800': $purple-800,
+ 'purple-900': $purple-900,
+ 'purple-accent-100': $purple-accent-100,
+ 'purple-accent-200': $purple-accent-200,
+ 'purple-accent-400': $purple-accent-400,
+ 'purple-accent-700': $purple-accent-700,
+) !default;
+
+$deep-purples: (
+ 'deep-purple-50': $deep-purple-50,
+ 'deep-purple-100': $deep-purple-100,
+ 'deep-purple-200': $deep-purple-200,
+ 'deep-purple-300': $deep-purple-300,
+ 'deep-purple-400': $deep-purple-400,
+ 'deep-purple-500': $deep-purple-500,
+ 'deep-purple-600': $deep-purple-600,
+ 'deep-purple-700': $deep-purple-700,
+ 'deep-purple-800': $deep-purple-800,
+ 'deep-purple-900': $deep-purple-900,
+ 'deep-purple-accent-100': $deep-purple-accent-100,
+ 'deep-purple-accent-200': $deep-purple-accent-200,
+ 'deep-purple-accent-400': $deep-purple-accent-400,
+ 'deep-purple-accent-700': $deep-purple-accent-700,
+) !default;
+
+$indigos: (
+ 'indigo-50': $indigo-50,
+ 'indigo-100': $indigo-100,
+ 'indigo-200': $indigo-200,
+ 'indigo-300': $indigo-300,
+ 'indigo-400': $indigo-400,
+ 'indigo-500': $indigo-500,
+ 'indigo-600': $indigo-600,
+ 'indigo-700': $indigo-700,
+ 'indigo-800': $indigo-800,
+ 'indigo-900': $indigo-900,
+ 'indigo-accent-100': $indigo-accent-100,
+ 'indigo-accent-200': $indigo-accent-200,
+ 'indigo-accent-400': $indigo-accent-400,
+ 'indigo-accent-700': $indigo-accent-700,
+) !default;
+
+$blues: (
+ 'blue-50': $blue-50,
+ 'blue-100': $blue-100,
+ 'blue-200': $blue-200,
+ 'blue-300': $blue-300,
+ 'blue-400': $blue-400,
+ 'blue-500': $blue-500,
+ 'blue-600': $blue-600,
+ 'blue-700': $blue-700,
+ 'blue-800': $blue-800,
+ 'blue-900': $blue-900,
+ 'blue-accent-100': $blue-accent-100,
+ 'blue-accent-200': $blue-accent-200,
+ 'blue-accent-400': $blue-accent-400,
+ 'blue-accent-700': $blue-accent-700,
+) !default;
+
+$light-blues: (
+ 'light-blue-50': $light-blue-50,
+ 'light-blue-100': $light-blue-100,
+ 'light-blue-200': $light-blue-200,
+ 'light-blue-300': $light-blue-300,
+ 'light-blue-400': $light-blue-400,
+ 'light-blue-500': $light-blue-500,
+ 'light-blue-600': $light-blue-600,
+ 'light-blue-700': $light-blue-700,
+ 'light-blue-800': $light-blue-800,
+ 'light-blue-900': $light-blue-900,
+ 'light-blue-accent-100': $light-blue-accent-100,
+ 'light-blue-accent-200': $light-blue-accent-200,
+ 'light-blue-accent-400': $light-blue-accent-400,
+ 'light-blue-accent-700': $light-blue-accent-700,
+) !default;
+
+$cyans: (
+ 'cyan-50': $cyan-50,
+ 'cyan-100': $cyan-100,
+ 'cyan-200': $cyan-200,
+ 'cyan-300': $cyan-300,
+ 'cyan-400': $cyan-400,
+ 'cyan-500': $cyan-500,
+ 'cyan-600': $cyan-600,
+ 'cyan-700': $cyan-700,
+ 'cyan-800': $cyan-800,
+ 'cyan-900': $cyan-900,
+ 'cyan-accent-100': $cyan-accent-100,
+ 'cyan-accent-200': $cyan-accent-200,
+ 'cyan-accent-400': $cyan-accent-400,
+ 'cyan-accent-700': $cyan-accent-700,
+) !default;
+
+$teals: (
+ 'teal-50': $teal-50,
+ 'teal-100': $teal-100,
+ 'teal-200': $teal-200,
+ 'teal-300': $teal-300,
+ 'teal-400': $teal-400,
+ 'teal-500': $teal-500,
+ 'teal-600': $teal-600,
+ 'teal-700': $teal-700,
+ 'teal-800': $teal-800,
+ 'teal-900': $teal-900,
+ 'teal-accent-100': $teal-accent-100,
+ 'teal-accent-200': $teal-accent-200,
+ 'teal-accent-400': $teal-accent-400,
+ 'teal-accent-700': $teal-accent-700,
+) !default;
+
+$greens: (
+ 'green-50': $green-50,
+ 'green-100': $green-100,
+ 'green-200': $green-200,
+ 'green-300': $green-300,
+ 'green-400': $green-400,
+ 'green-500': $green-500,
+ 'green-600': $green-600,
+ 'green-700': $green-700,
+ 'green-800': $green-800,
+ 'green-900': $green-900,
+ 'green-accent-100': $green-accent-100,
+ 'green-accent-200': $green-accent-200,
+ 'green-accent-400': $green-accent-400,
+ 'green-accent-700': $green-accent-700,
+) !default;
+
+$light-greens: (
+ 'light-green-50': $light-green-50,
+ 'light-green-100': $light-green-100,
+ 'light-green-200': $light-green-200,
+ 'light-green-300': $light-green-300,
+ 'light-green-400': $light-green-400,
+ 'light-green-500': $light-green-500,
+ 'light-green-600': $light-green-600,
+ 'light-green-700': $light-green-700,
+ 'light-green-800': $light-green-800,
+ 'light-green-900': $light-green-900,
+ 'light-green-accent-100': $light-green-accent-100,
+ 'light-green-accent-200': $light-green-accent-200,
+ 'light-green-accent-400': $light-green-accent-400,
+ 'light-green-accent-700': $light-green-accent-700,
+) !default;
+
+$limes: (
+ 'lime-50': $lime-50,
+ 'lime-100': $lime-100,
+ 'lime-200': $lime-200,
+ 'lime-300': $lime-300,
+ 'lime-400': $lime-400,
+ 'lime-500': $lime-500,
+ 'lime-600': $lime-600,
+ 'lime-700': $lime-700,
+ 'lime-800': $lime-800,
+ 'lime-900': $lime-900,
+ 'lime-accent-100': $lime-accent-100,
+ 'lime-accent-200': $lime-accent-200,
+ 'lime-accent-400': $lime-accent-400,
+ 'lime-accent-700': $lime-accent-700,
+) !default;
+
+$yellows: (
+ 'yellow-50': $yellow-50,
+ 'yellow-100': $yellow-100,
+ 'yellow-200': $yellow-200,
+ 'yellow-300': $yellow-300,
+ 'yellow-400': $yellow-400,
+ 'yellow-500': $yellow-500,
+ 'yellow-600': $yellow-600,
+ 'yellow-700': $yellow-700,
+ 'yellow-800': $yellow-800,
+ 'yellow-900': $yellow-900,
+ 'yellow-accent-100': $yellow-accent-100,
+ 'yellow-accent-200': $yellow-accent-200,
+ 'yellow-accent-400': $yellow-accent-400,
+ 'yellow-accent-700': $yellow-accent-700,
+) !default;
+
+$ambers: (
+ 'amber-50': $amber-50,
+ 'amber-100': $amber-100,
+ 'amber-200': $amber-200,
+ 'amber-300': $amber-300,
+ 'amber-400': $amber-400,
+ 'amber-500': $amber-500,
+ 'amber-600': $amber-600,
+ 'amber-700': $amber-700,
+ 'amber-800': $amber-800,
+ 'amber-900': $amber-900,
+ 'amber-accent-100': $amber-accent-100,
+ 'amber-accent-200': $amber-accent-200,
+ 'amber-accent-400': $amber-accent-400,
+ 'amber-accent-700': $amber-accent-700,
+) !default;
+
+$oranges: (
+ 'orange-50': $orange-50,
+ 'orange-100': $orange-100,
+ 'orange-200': $orange-200,
+ 'orange-300': $orange-300,
+ 'orange-400': $orange-400,
+ 'orange-500': $orange-500,
+ 'orange-600': $orange-600,
+ 'orange-700': $orange-700,
+ 'orange-800': $orange-800,
+ 'orange-900': $orange-900,
+ 'orange-accent-100': $orange-accent-100,
+ 'orange-accent-200': $orange-accent-200,
+ 'orange-accent-400': $orange-accent-400,
+ 'orange-accent-700': $orange-accent-700,
+) !default;
+
+$deep-oranges: (
+ 'deep-orange-50': $deep-orange-50,
+ 'deep-orange-100': $deep-orange-100,
+ 'deep-orange-200': $deep-orange-200,
+ 'deep-orange-300': $deep-orange-300,
+ 'deep-orange-400': $deep-orange-400,
+ 'deep-orange-500': $deep-orange-500,
+ 'deep-orange-600': $deep-orange-600,
+ 'deep-orange-700': $deep-orange-700,
+ 'deep-orange-800': $deep-orange-800,
+ 'deep-orange-900': $deep-orange-900,
+ 'deep-orange-accent-100': $deep-orange-accent-100,
+ 'deep-orange-accent-200': $deep-orange-accent-200,
+ 'deep-orange-accent-400': $deep-orange-accent-400,
+ 'deep-orange-accent-700': $deep-orange-accent-700,
+) !default;
+
+$browns: (
+ 'brown-50': $brown-50,
+ 'brown-100': $brown-100,
+ 'brown-200': $brown-200,
+ 'brown-300': $brown-300,
+ 'brown-400': $brown-400,
+ 'brown-500': $brown-500,
+ 'brown-600': $brown-600,
+ 'brown-700': $brown-700,
+ 'brown-800': $brown-800,
+ 'brown-900': $brown-900,
+) !default;
+
+$blue-grays: (
+ 'blue-gray-50': $blue-gray-50,
+ 'blue-gray-100': $blue-gray-100,
+ 'blue-gray-200': $blue-gray-200,
+ 'blue-gray-300': $blue-gray-300,
+ 'blue-gray-400': $blue-gray-400,
+ 'blue-gray-500': $blue-gray-500,
+ 'blue-gray-600': $blue-gray-600,
+ 'blue-gray-700': $blue-gray-700,
+ 'blue-gray-800': $blue-gray-800,
+ 'blue-gray-900': $blue-gray-900,
+) !default;
+// scss-docs-end material-design-color-system-map
+
+// scss-docs-start color-variables
+$red: $red-500 !default;
+$pink: $pink-500 !default;
+$purple: $purple-500 !default;
+$indigo: $indigo-500 !default;
+$blue: $blue-500 !default;
+$cyan: $cyan-500 !default;
+$teal: $teal-500 !default;
+$green: $green-500 !default;
+$yellow: $yellow-500 !default;
+$orange: $orange-500 !default;
+// scss-docs-end color-variables
+
+// scss-docs-start colors-map
+$colors: (
+ 'red': $red,
+ 'pink': $pink,
+ 'purple': $purple,
+ 'indigo': $indigo,
+ 'blue': $blue,
+ 'cyan': $cyan,
+ 'teal': $teal,
+ 'green': $green,
+ 'yellow': $yellow,
+ 'orange': $orange,
+ 'white': $white,
+ 'black': $black,
+ 'gray': $gray-600,
+ 'gray-dark': $gray-800,
+) !default;
+// scss-docs-end colors-map
+
+// Customize the light and dark text colors for use in our color contrast function.
+$color-contrast-dark: $black !default;
+$color-contrast-light: $white !default;
+
+$min-contrast-ratio: 2 !default;
+
+// scss-docs-start theme-color-variables
+$primary: #3b71ca !default;
+$secondary: #9fa6b2 !default;
+$success: #14a44d !default;
+$danger: #dc4c64 !default;
+$warning: #e4a11b !default;
+$info: #54b4d3 !default;
+$light: #fbfbfb !default;
+$dark: #332d2d !default;
+// scss-docs-end theme-color-variables
+
+// scss-docs-start theme-colors-map
+$theme-colors: (
+ 'primary': $primary,
+ 'secondary': $secondary,
+ 'success': $success,
+ 'danger': $danger,
+ 'warning': $warning,
+ 'info': $info,
+ 'light': $light,
+ 'dark': $dark,
+) !default;
+// scss-docs-end theme-colors-map
+
+// scss-docs-start theme-text-variables
+$primary-text-emphasis: shade-color($primary, 20%) !default;
+$secondary-text-emphasis: shade-color($secondary, 60%) !default;
+$success-text-emphasis: shade-color($success, 40%) !default;
+$info-text-emphasis: shade-color($info, 30%) !default;
+$warning-text-emphasis: shade-color($warning, 40%) !default;
+$danger-text-emphasis: shade-color($danger, 20%) !default;
+$light-text-emphasis: $gray-700 !default;
+$dark-text-emphasis: $gray-200 !default;
+// scss-docs-end theme-text-variables
+
+// scss-docs-start theme-bg-subtle-variables
+$primary-bg-subtle: tint-color($primary, 85%) !default;
+$secondary-bg-subtle: tint-color($secondary, 85%) !default;
+$success-bg-subtle: tint-color($success, 85%) !default;
+$info-bg-subtle: tint-color($info, 85%) !default;
+$warning-bg-subtle: tint-color($warning, 85%) !default;
+$danger-bg-subtle: tint-color($danger, 85%) !default;
+$light-bg-subtle: $gray-100 !default;
+$dark-bg-subtle: $gray-900 !default;
+// scss-docs-end theme-bg-subtle-variables
+
+// scss-docs-start theme-border-subtle-variables
+$primary-border-subtle: tint-color($primary, 60%) !default;
+$secondary-border-subtle: tint-color($secondary, 60%) !default;
+$success-border-subtle: tint-color($success, 60%) !default;
+$info-border-subtle: tint-color($info, 60%) !default;
+$warning-border-subtle: tint-color($warning, 60%) !default;
+$danger-border-subtle: tint-color($danger, 60%) !default;
+$light-border-subtle: $gray-200 !default;
+$dark-border-subtle: $gray-500 !default;
+// scss-docs-end theme-border-subtle-variables
+
+// Options
+//
+// Quickly modify global styling by enabling or disabling optional features.
+
+$enable-shadows: true !default;
+$enable-negative-margins: true !default;
+
+// Prefix for :root CSS variables.
+
+$variable-prefix: mdb- !default;
+$prefix: $variable-prefix !default;
+
+// Body
+//
+// Settings for the `` element.
+
+$body-color: $gray-800 !default;
+$body-tertiary-bg: #fbfbfb !default;
+
+// Links
+//
+// Style anchor elements.
+
+$link-color: $primary !default;
+$link-decoration: none !default;
+$link-shade-percentage: 5% !default;
+$link-shade-focus-percentage: 10% !default;
+$link-shade-active-percentage: 15% !default;
+$link-hover-decoration: none !default;
+$link-hover-color: shift-color($link-color, $link-shade-percentage) !default;
+$link-focus-color: shift-color($link-color, $link-shade-focus-percentage) !default;
+$link-active-color: shift-color($link-color, $link-shade-active-percentage) !default;
+// Components
+//
+// Define common box shadow and border and more.
+
+// scss-docs-start border-variables
+$border-width-alternate: 2px !default;
+$border-style-alternate: solid !default;
+$border-color-alternate: hsl(0, 0%, 96%) !default;
+// scss-docs-end border-variables
+
+// scss-docs-start border-radius-variables
+$border-radius: 0.25rem !default;
+$border-radius-xs: 0.2rem !default;
+$border-radius-md: 0.3rem !default;
+$border-radius-lg: 0.5rem !default;
+// scss-docs-end border-radius-variables
+
+// Shadows
+//
+// Shadows offset, blur, spread and color.
+
+// scss-docs-start box-shadow-variables
+$box-shadow-0: none !default;
+
+$box-shadow-1: 0 0px 2px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.07),
+ 0 1px 1px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.04) !default;
+$box-shadow-2: 0 0px 3px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.07),
+ 0 2px 2px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.04) !default;
+$box-shadow-3: 0 2px 6px -1px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.07),
+ 0 6px 18px -1px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.04) !default;
+$box-shadow-4: 0 2px 15px -3px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.07),
+ 0 10px 20px -2px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.04) !default;
+$box-shadow-5: 0 2px 25px -5px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.07),
+ 0 25px 21px -5px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.04) !default;
+$box-shadow-6: 0 2px 35px -12px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.21),
+ 0 50px 40px -5px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.04) !default;
+
+$box-shadow-1-soft: 0 1px 5px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$box-shadow-2-soft: 0 2px 10px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$box-shadow-3-soft: 0 5px 15px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$box-shadow-4-soft: 0 10px 20px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$box-shadow-5-soft: 0 15px 30px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$box-shadow-6-soft: 0 20px 40px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+
+$box-shadow-1-strong: 0 0px 2px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.16),
+ 0 1px 1px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+$box-shadow-2-strong: 0 0px 3px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.16),
+ 0 2px 2px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+$box-shadow-3-strong: 0 2px 6px -1px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.16),
+ 0 6px 18px -1px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+$box-shadow-4-strong: 0 2px 15px -3px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.16),
+ 0 10px 20px -2px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+$box-shadow-5-strong: 0 2px 25px -5px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.16),
+ 0 25px 21px -5px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+$box-shadow-6-strong: 0 2px 35px -12px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.26),
+ 0 50px 40px -5px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+
+$box-shadow-3-top: 0 -10px 20px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$box-shadow-inner: inset 0 2px 4px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.06) !default;
+// scss-docs-end box-shadow-variables
+
+// scss-docs-start standard-shadows-map
+$shadows: (
+ '0': $box-shadow-0,
+ '1': $box-shadow-1,
+ '2': $box-shadow-2,
+ '3': $box-shadow-3,
+ '4': $box-shadow-4,
+ '5': $box-shadow-5,
+ '6': $box-shadow-6,
+) !default;
+// scss-docs-end standard-shadows-map
+
+// scss-docs-start soft-shadows-map
+$shadows-soft: (
+ '1': $box-shadow-1-soft,
+ '2': $box-shadow-2-soft,
+ '3': $box-shadow-3-soft,
+ '4': $box-shadow-4-soft,
+ '5': $box-shadow-5-soft,
+ '6': $box-shadow-6-soft,
+) !default;
+// scss-docs-end soft-shadows-map
+
+// scss-docs-start strong-shadows-map
+$shadows-strong: (
+ '1': $box-shadow-1-strong,
+ '2': $box-shadow-2-strong,
+ '3': $box-shadow-3-strong,
+ '4': $box-shadow-4-strong,
+ '5': $box-shadow-5-strong,
+ '6': $box-shadow-6-strong,
+) !default;
+// scss-docs-end strong-shadows-map
+
+// Typography
+//
+// Font, line-height, and color for body text, headings, and more.
+
+// scss-docs-start font-variables
+// stylelint-disable value-keyword-case
+$font-family-roboto: 'Roboto', sans-serif !default;
+// stylelint-enable value-keyword-case
+$font-family-base: var(--#{$prefix}font-roboto) !default;
+
+// $font-size-base affects the font size of the body text
+$font-size-base: 1rem !default;
+$font-weight-medium: 500 !default;
+$line-height-base: 1.6 !default;
+// scss-docs-end font-variables
+
+// scss-docs-start note-variables
+$note-padding: 10px !default;
+$note-border-width: 6px !default;
+$note-border-radius: 5px !default;
+$note-strong-font-weight: 600 !default;
+// scss-docs-end note-variables
+
+// Tables
+//
+// Customizes the `.table` component with basic values, each used across all table variations.
+
+// scss-docs-start table-variables
+$table-cell-padding-y: 1rem !default;
+$table-cell-padding-x: 1.4rem !default;
+$table-cell-padding-y-sm: 0.5rem !default;
+$table-cell-padding-x-sm: 0.5rem !default;
+
+$table-color: var(--#{$prefix}body-color) !default;
+$table-font-size: 0.9rem !default;
+$table-striped-bg: rgba(0, 0, 0, 0.02) !default;
+$table-hover-transition: 0.5s !default;
+$table-hover-bg: rgba(0, 0, 0, 0.025) !default;
+$table-group-separator-color: inherit !default;
+$table-divider-color: currentcolor !default;
+$table-bg-scale: -80% !default;
+$table-active-bg: rgba(var(--#{$prefix}emphasis-color-rgb), 0.1) !default;
+$table-hover-bg: rgba(var(--#{$prefix}emphasis-color-rgb), 0.025) !default;
+$table-striped-bg: rgba(var(--#{$prefix}emphasis-color-rgb), 0.02) !default;
+// scss-docs-end table-variables
+
+// scss-docs-start table-loop
+$table-variants: (
+ 'primary': shift-color($primary, $table-bg-scale),
+ 'secondary': shift-color($secondary, $table-bg-scale),
+ 'success': shift-color($success, $table-bg-scale),
+ 'info': shift-color($info, $table-bg-scale),
+ 'warning': shift-color($warning, $table-bg-scale),
+ 'danger': shift-color($danger, $table-bg-scale),
+ 'light': $light,
+ 'dark': $dark,
+) !default;
+// scss-docs-end table-loop
+
+// Buttons
+//
+// For each of MDB's buttons, define text, background, and border color.
+
+// scss-docs-start btn-variables
+$btn-padding-top: 0.625rem !default;
+$btn-padding-bottom: 0.5rem !default;
+$btn-padding-x: 1.5rem !default;
+$btn-font-size: 0.75rem !default;
+$btn-line-height: 1.5 !default;
+
+$btn-padding-top-lg: 0.75rem !default;
+$btn-padding-bottom-lg: 0.6875rem !default;
+$btn-padding-x-lg: 1.6875rem !default;
+$btn-font-size-lg: 0.875rem !default;
+$btn-line-height-lg: 1.6 !default;
+
+$btn-padding-top-sm: 0.375rem !default;
+$btn-padding-bottom-sm: 0.3125rem !default;
+$btn-padding-x-sm: 1rem !default;
+$btn-font-size-sm: 0.75rem !default;
+$btn-line-height-sm: 1.5 !default;
+
+$btn-border-width: $border-width-alternate !default;
+
+$btn-font-weight: $font-weight-medium !default;
+$btn-box-shadow: 0 4px 9px -4px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.35) !default;
+$btn-hover-box-shadow: 0 8px 9px -4px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.15),
+ 0 4px 18px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.1) !default;
+$btn-focus-box-shadow: $btn-hover-box-shadow !default;
+$btn-active-box-shadow: $btn-focus-box-shadow !default;
+
+$btn-link-color: $link-color !default;
+$btn-link-hover-color: $link-hover-color !default;
+$btn-link-focus-color: $link-focus-color !default;
+$btn-link-active-color: $link-active-color !default;
+$btn-link-disabled-color: $gray-500 !default;
+$btn-link-hover-bg: hsl(0, 0%, 96%) !default;
+
+// Allows for customizing button radius independently from global border radius
+$btn-border-radius: $border-radius !default;
+
+$btn-hover-bg-shade-amount: 5% !default;
+$btn-focus-bg-shade-amount: 5% !default;
+$btn-active-bg-shade-amount: 10% !default;
+
+$btn-hover-bg-tint-amount: 5% !default;
+$btn-focus-bg-tint-amount: 5% !default;
+$btn-active-bg-tint-amount: 10% !default;
+
+$btn-secondary-bg-tint-amount: 85% !default;
+$btn-secondary-color-shade-amount: 30% !default;
+$btn-light-color-tint-amount: 30% !default;
+
+$btn-outline-hover-bg-tint-amount: 95% !default;
+$btn-outline-focus-bg-tint-amount: 95% !default;
+$btn-outline-active-bg-tint-amount: 95% !default;
+
+$btn-outline-hover-bg-shade-amount: 20% !default;
+$btn-outline-focus-bg-shade-amount: 20% !default;
+$btn-outline-active-bg-shade-amount: 20% !default;
+
+$btn-contextual-box-shadow: 0 4px 9px -4px !default;
+$btn-contextual-box-shadow-state-first-part: 0 8px 9px -4px !default;
+$btn-contextual-box-shadow-state-second-part: 0 4px 18px 0 !default;
+
+$btn-outline-padding-top: 0.5rem !default;
+$btn-outline-padding-bottom: 0.375rem !default;
+$btn-outline-padding-x: 1.375rem !default;
+$btn-outline-border-width: $border-width-alternate !default;
+$btn-outline-line-height: 1.5 !default;
+
+$btn-outline-padding-top-lg: 0.625rem !default;
+$btn-outline-padding-bottom-lg: 0.5625rem !default;
+$btn-outline-padding-x-lg: 1.5625rem !default;
+$btn-outline-font-size-lg: 0.875rem !default;
+$btn-outline-line-height-lg: 1.6 !default;
+
+$btn-outline-padding-top-sm: 0.25rem !default;
+$btn-outline-padding-bottom-sm: 0.1875rem !default;
+$btn-outline-padding-x-sm: 0.875rem !default;
+$btn-outline-font-size-sm: 0.75rem !default;
+$btn-outline-line-height-sm: 1.5 !default;
+
+$btn-rounded-border-radius: 10rem !default;
+
+$btn-floating-border-radius: 50% !default;
+
+$btn-floating-width: 2.3125rem !default;
+$btn-floating-height: 2.3125rem !default;
+$btn-floating-icon-width: 2.3125rem !default;
+
+$btn-floating-width-lg: 2.8125rem !default;
+$btn-floating-height-lg: 2.8125rem !default;
+$btn-floating-icon-width-lg: 2.8125rem !default;
+
+$btn-floating-width-sm: 1.8125rem !default;
+$btn-floating-height-sm: 1.8125rem !default;
+$btn-floating-icon-width-sm: 1.8125rem !default;
+
+$btn-outline-floating-icon-width: 2.0625rem !default;
+$btn-outline-floating-icon-width-lg: 2.5625rem !default;
+$btn-outline-floating-icon-width-sm: 1.5625rem !default;
+
+$btn-floating-icon-line-height: 2.3125rem !default;
+$btn-floating-icon-line-height-lg: 2.8125rem !default;
+$btn-floating-icon-line-height-sm: 1.8125rem !default;
+
+$btn-outline-floating-icon-line-height: 2.0625rem !default;
+$btn-outline-floating-icon-line-height-lg: 2.5625rem !default;
+$btn-outline-floating-icon-line-height-sm: 1.5625rem !default;
+
+$fixed-action-btn-right: 2.1875rem !default;
+$fixed-action-btn-bottom: 2.1875rem !default;
+$fixed-action-btn-padding-top: 0.9375rem !default;
+$fixed-action-btn-padding-bottom: 1.25rem !default;
+$fixed-action-btn-padding-x: 1.25rem !default;
+$fixed-action-btn-li-margin-bottom: 1.5rem !default;
+
+$btn-block-margin-top: 0.5rem !default;
+// scss-docs-end btn-variables
+
+// Ripple
+
+// scss-docs-start ripple-variables
+$ripple-wave-cubicBezier: cubic-bezier(0, 0, 0.15, 1) !default;
+$ripple-wave-border-radius: 50% !default;
+$ripple-wave-opacity: 0.5 !default;
+$ripple-wave-transform: scale(0) !default;
+$ripple-wave-z-index: 999 !default;
+$ripple-wave-active-transform: scale(1) !default;
+// scss-docs-end ripple-variables
+
+// scss-docs-start ripple-colors-map
+$ripple: (
+ 'primary': $primary,
+ 'secondary': #e3ebf7,
+ 'success': $success,
+ 'danger': $danger,
+ 'warning': $warning,
+ 'info': $info,
+ 'light': $light,
+ 'dark': $dark,
+) !default;
+// scss-docs-end ripple-colors-map
+
+// Button group
+//
+// Material styling for group of buttons
+
+// scss-docs-start btn-group-variables
+$btn-group-border-radius: calc(4px - 1px) !default;
+$btn-group-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
+ border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !default;
+// scss-docs-end btn-group-variables
+
+// Forms
+//
+// Material styling for outline forms border, label, size
+
+// scss-docs-start input-variables
+$input-padding-top: 0.32rem !default;
+$input-padding-bottom: $input-padding-top !default;
+$input-padding-left: 0.75rem !default;
+$input-padding-right: $input-padding-left !default;
+$input-transition: all 0.2s linear !default;
+$input-color: var(--#{$prefix}surface-color) !default;
+$input-placeholder-color: rgba(var(--#{$prefix}surface-color-rgb), 0.8) !default;
+
+$form-label-left: 0.75rem !default;
+$form-label-padding-top: 0.37rem !default;
+$form-label-transition: all 0.2s ease-out !default;
+$form-label-color: var(--#{$prefix}form-control-label-color) !default;
+
+$input-focus-active-label-transform: translateY(-1rem) translateY(0.1rem) scale(0.8) !default;
+$input-focus-label-color: $primary !default;
+$input-focus-border-width: 0.125rem !default;
+$input-focus-border-color: $primary !default;
+$input-disabled-background-color: var(--#{$prefix}form-control-disabled-bg) !default;
+
+$input-font-size-lg: 1rem !default;
+$input-line-height-lg: 2.15 !default;
+$input-focus-active-label-transform-lg: translateY(-1.25rem) translateY(0.1rem) scale(0.8) !default;
+
+$input-padding-top-sm: 0.32rem !default;
+$input-padding-bottom-sm: 0.32rem !default;
+$input-font-size-sm: 0.775rem !default;
+$input-line-height-sm: 1.5 !default;
+$input-focus-active-label-transform-sm: translateY(-0.85rem) translateY(0.1rem) scale(0.8) !default;
+
+$form-notch-div-border-color: var(--#{$prefix}form-control-border-color) !default;
+$form-notch-leading-width: 0.5rem !default;
+$form-notch-leading-border-radius: 0.25rem !default;
+$form-notch-middle-max-width: 1rem !default;
+$form-notch-trailing-border-radius: $form-notch-leading-border-radius !default;
+
+$form-label-padding-top-lg: 0.7rem !default;
+$form-label-padding-top-sm: 0.33rem !default;
+$form-label-font-size-sm: 0.775rem !default;
+
+$form-white-input-color: $white !default;
+$form-white-label-color: $gray-50 !default;
+$form-white-notch-div-border-color: $gray-50 !default;
+$form-white-input-focus-label-color: $white !default;
+$form-white-input-focus-border-color: $white !default;
+$form-white-placeholder-color: rgba(255, 255, 255, 0.7) !default;
+$form-white-disabled-bgc: rgba(255, 255, 255, 0.45) !default;
+
+$form-check-input-margin-left: 1.79rem !default;
+$form-check-input-radio-margin-left: 1.85rem !default;
+$form-check-input-width-md: 1.125rem !default;
+$form-check-input-height: $form-check-input-width-md !default;
+$form-check-input-background-color: var(--#{$prefix}body-bg) !default;
+$form-check-input-border-width: 0.125rem !default;
+$form-check-input-border-color: var(--#{$prefix}form-control-border-color) !default;
+
+$form-check-input-before-box-shadow: 0px 0px 0px 13px transparent !default;
+$form-check-input-before-border-radius: 50% !default;
+$form-check-input-before-width: 0.875rem !default;
+$form-check-input-before-height: $form-check-input-before-width !default;
+$form-check-input-before-transform: scale(0) !default;
+
+$form-check-input-hover-before-opacity: 0.04 !default;
+$form-check-input-hover-before-box-shadow: 0px 0px 0px 13px
+ rgba(var(--#{$prefix}box-shadow-color-rgb), 0.6) !default;
+
+$form-check-input-focus-border-color: $form-check-input-border-color !default;
+$form-check-input-focus-transition: border-color 0.2s !default;
+
+$form-check-input-focus-before-opacity: 0.12 !default;
+$form-check-input-focus-before-box-shadow: $form-check-input-hover-before-box-shadow !default;
+$form-check-input-focus-before-transform: scale(1) !default;
+$form-check-input-focus-before-transition: box-shadow 0.2s, transform 0.2s !default;
+
+$form-check-input-checked-border-color: $primary !default;
+$form-check-input-checked-before-opacity: 0.16 !default;
+$form-check-input-checked-focus-border-color: $primary !default;
+$form-check-input-checked-focus-before-box-shadow: 0px 0px 0px 13px $primary !default;
+$form-check-input-checked-focus-before-transform: $form-check-input-focus-before-transform !default;
+$form-check-input-checked-focus-before-transition: $form-check-input-focus-before-transition !default;
+
+$form-check-input-checkbox-border-radius: 0.25rem !default;
+$form-check-input-checkbox-focus-after-width: 0.875rem !default;
+$form-check-input-checkbox-focus-after-height: $form-check-input-checkbox-focus-after-width !default;
+$form-check-input-checkbox-focus-after-background-color: var(--#{$prefix}body-bg) !default;
+$form-check-input-checkbox-checked-background-color: $primary !default;
+$form-check-input-checkbox-checked-focus-background-color: $primary !default;
+
+$form-check-input-checkbox-checked-after-transform: rotate(45deg) !default;
+$form-check-input-checkbox-checked-after-border-width: 0.125rem !default;
+$form-check-input-checkbox-checked-after-border-color: $white !default;
+$form-check-input-checkbox-checked-after-width: 0.375rem !default;
+$form-check-input-checkbox-checked-after-height: 0.8125rem !default;
+$form-check-input-checkbox-checked-after-margin-left: 0.25rem !default;
+$form-check-input-checkbox-checked-after-margin-top: -1px !default;
+
+$form-check-input-indeterminate-focus-before-box-shadow: 0px 0px 0px 13px $primary !default;
+$form-check-input-indeterminate-checked-background-color: $primary !default;
+$form-check-input-indeterminate-checked-after-transform: rotate(90deg) !default;
+$form-check-input-indeterminate-checked-after-border-width: 0.125rem !default;
+$form-check-input-indeterminate-checked-after-border-color: $white !default;
+$form-check-input-indeterminate-checked-after-width: 0.125rem !default;
+$form-check-input-indeterminate-checked-after-height: 0.875rem !default;
+$form-check-input-indeterminate-checked-after-margin-left: 0.375rem !default;
+$form-check-input-indeterminate-focus-background-color: $primary !default;
+$form-check-input-indeterminate-focus-border-color: $primary !default;
+$form-check-input-indeterminate-border-color: $gray-600 !default;
+
+$form-check-input-radio-border-radius: 50% !default;
+$form-check-input-radio-width: 1.25rem !default;
+$form-check-input-radio-height: $form-check-input-radio-width !default;
+
+$form-check-input-radio-before-width: 1rem !default;
+$form-check-input-radio-before-height: $form-check-input-radio-before-width !default;
+
+$form-check-input-radio-after-width: $form-check-input-radio-before-width !default;
+$form-check-input-radio-after-height: $form-check-input-radio-after-width !default;
+$form-check-input-radio-after-border-radius: $form-check-input-radio-border-radius !default;
+$form-check-input-radio-after-background-color: var(--#{$prefix}body-bg) !default;
+
+$form-check-input-radio-checked-background-color: var(--#{$prefix}body-bg) !default;
+$form-check-input-radio-checked-after-border-radius: $form-check-input-radio-border-radius !default;
+$form-check-input-radio-checked-after-width: 0.625rem !default;
+$form-check-input-radio-checked-after-height: $form-check-input-radio-checked-after-width !default;
+$form-check-input-radio-checked-after-border-color: $primary !default;
+$form-check-input-radio-checked-after-background-color: $primary !default;
+$form-check-input-radio-checked-after-margin-top: 0.1875rem !default;
+$form-check-input-radio-checked-after-margin-left: 0.1875rem !default;
+$form-check-input-radio-checked-after-transition: border-color !default;
+$form-check-input-radio-checked-focus-background-color: var(--#{$prefix}body-bg) !default;
+
+$form-switch-padding-left: 1.85rem !default;
+$form-switch-form-check-input-border-radius: 0.4375rem !default;
+$form-switch-form-check-input-width: 2rem !default;
+$form-switch-form-check-input-height: 0.875rem !default;
+$form-switch-form-check-input-background-color: rgba(
+ var(--#{$prefix}emphasis-color-rgb),
+ 0.25
+) !default;
+$form-switch-form-check-input-margin-top: 0.35rem !default;
+$form-switch-form-check-input-margin-right: 0.7rem !default;
+
+$form-switch-form-check-input-after-border-radius: 50% !default;
+$form-switch-form-check-input-after-width: 1.25rem !default;
+$form-switch-form-check-input-after-height: $form-switch-form-check-input-after-width !default;
+$form-switch-form-check-input-after-background-color: var(--#{$prefix}surface-bg) !default;
+$form-switch-form-check-input-after-margin-top: -0.1875rem !default;
+$form-switch-form-check-input-after-box-shadow: $box-shadow-2 !default;
+$form-switch-form-check-input-after-transition: background-color 0.2s, transform 0.2s !default;
+
+$form-switch-form-check-input-focus-before-box-shadow: 3px -1px 0px 13px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.6) !default;
+$form-switch-form-check-input-focus-before-transform: scale(1) !default;
+$form-switch-form-check-input-focus-before-transition: box-shadow 0.2s, transform 0.2s !default;
+
+$form-switch-form-check-input-focus-after-border-radius: $form-switch-form-check-input-after-border-radius !default;
+$form-switch-form-check-input-focus-after-width: 1.25rem !default;
+$form-switch-form-check-input-focus-after-height: $form-switch-form-check-input-focus-after-width !default;
+
+$form-switch-form-check-input-checked-focus-before-margin-left: 1.0625rem !default;
+$form-switch-form-check-input-checked-focus-before-box-shadow: 3px -1px 0px 13px $primary !default;
+$form-switch-form-check-input-checked-focus-before-transform: scale(1) !default;
+$form-switch-form-check-input-checked-focus-before-transition: box-shadow 0.2s, transform 0.2s !default;
+
+$form-switch-form-check-input-checked-checkbox-after-border-radius: 50% !default;
+$form-switch-form-check-input-checked-checkbox-after-width: 1.25rem !default;
+$form-switch-form-check-input-checked-checkbox-after-height: 1.25rem !default;
+$form-switch-form-check-input-checked-checkbox-after-background-color: $primary !default;
+$form-switch-form-check-input-checked-checkbox-after-margin-top: -3px !default;
+$form-switch-form-check-input-checked-checkbox-after-margin-left: 1.0625rem !default;
+$form-switch-form-check-input-checked-checkbox-after-box-shadow: 0 3px 1px -2px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.2),
+ 0 2px 2px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.14),
+ 0 1px 5px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.12) !default;
+$form-switch-form-check-input-checked-checkbox-after-transition: background-color 0.2s,
+ transform 0.2s !default;
+
+$form-file-height: calc(1.79rem + 0.33rem + 2px) !default;
+
+$form-file-label-focus-border-color: $primary !default;
+$form-file-label-focus-transition: all 0.2s linear !default;
+$form-file-label-focus-text-border-width: 2px 1px 2px 2px !default;
+$form-file-label-focus-button-border-width: 2px 2px 2px 1px !default;
+$form-file-label-disabled-background-color: #e9ecef !default;
+$form-file-label-height: $form-file-height !default;
+$form-file-label-border-color: #bdbdbd !default;
+$form-file-label-border-radius: 0.2rem !default;
+
+$form-file-text-padding-y: 0.33rem !default;
+$form-file-text-border-color: #bdbdbd !default;
+
+$form-file-button-line-height: 1.5 !default;
+$form-file-button-background-color: #fff !default;
+$form-file-button-border-color: $form-file-text-border-color !default;
+
+$form-file-sm-height: calc(1.33rem + 0.33rem + 2px) !default;
+$form-file-sm-font-size: 0.775rem !default;
+$form-file-sm-line-height: 1.5 !default;
+$form-file-sm-padding-y: $form-file-text-padding-y !default;
+
+$form-file-lg-height: calc(2.315rem + 0.33rem + 2px) !default;
+$form-file-lg-font-size: 1rem !default;
+$form-file-lg-line-height: 2.15 !default;
+$form-file-lg-padding-y: $form-file-text-padding-y !default;
+
+$form-select-transition: all 0.2s linear !default;
+$form-select-focus-border-color: $primary !default;
+$form-select-focus-border-box-shadow: inset 0px 0px 0px 1px $primary !default;
+
+$form-range-webkit-slider-thumb-margin-top: -6px !default;
+$form-range-webkit-slider-runnable-track-height: 4px !default;
+
+$input-group-height: calc(1.75rem + 0.33rem + 2px) !default;
+$input-group-min-height: $input-group-height !default;
+$input-group-padding-y: 0.27rem !default;
+$input-group-transition: all 0.2s linear !default;
+
+$input-group-focus-transition: $input-group-transition !default;
+$input-group-focus-border-color: $primary !default;
+$input-group-focus-box-shadow: inset 0 0 0 1px $primary !default;
+
+$input-group-text-padding-y: 0.26rem !default;
+
+$input-group-lg-height: calc(2.315rem + 0.33rem + 2px) !default;
+$input-group-lg-font-size: 1rem !default;
+$input-group-lg-padding-y: 0.33rem !default;
+$input-group-lg-text-font-size: $input-group-lg-font-size !default;
+
+$input-group-sm-height: calc(1.33rem + 0.33rem + 2px) !default;
+$input-group-sm-min-height: $input-group-sm-height !default;
+$input-group-sm-font-size: 0.775rem !default;
+$input-group-sm-padding-y: $input-group-lg-padding-y !default;
+$input-group-sm-text-font-size: $input-group-sm-font-size !default;
+$input-group-sm-text-line-height: 1.5 !default;
+
+$input-group-form-outline-border-left-width: 1px !default;
+$input-group-form-outline-border-left-color: #bdbdbd !default;
+// scss-docs-end input-variables
+
+// Form validation
+
+// scss-docs-start form-feedback-variables
+$form-feedback-valid-tooltip-color: $white !default;
+$form-feedback-valid-color: $success !default;
+$form-feedback-invalid-color: $danger !default;
+// scss-docs-end form-feedback-variables
+
+// scss-docs-start form-validation-states
+$form-validation-states-mdb: (
+ 'valid': (
+ 'color': $form-feedback-valid-color,
+ ),
+ 'invalid': (
+ 'color': $form-feedback-invalid-color,
+ ),
+) !default;
+// scss-docs-end form-validation-states
+
+// Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+
+// scss-docs-start zindex-stack
+$fixed-action-button-zindex: 1030 !default;
+$zindex-toast: 1060 !default;
+$alert-zindex: 1070 !default;
+$zindex-popover: 1080 !default;
+$zindex-tooltip: 1090 !default;
+// scss-docs-end zindex-stack
+
+// Navs
+
+// scss-docs-start nav-variables
+$nav-tabs-link-border-bottom-width: 2px !default;
+$nav-tabs-link-font-weight: 500 !default;
+$nav-tabs-link-font-size: 12px !default;
+$nav-tabs-link-padding-top: 17px !default;
+$nav-tabs-link-padding-bottom: 16px !default;
+$nav-tabs-link-padding-x: 29px !default;
+$nav-tabs-link-hover-background-color: var(--#{$prefix}highlight-bg-color) !default;
+$nav-tabs-link-active-color: $primary !default;
+$nav-tabs-link-active-border-color: $primary !default;
+
+$nav-pills-margin: 0.5rem !default;
+$nav-pills-link-border-radius: 0.25rem !default;
+$nav-pills-link-font-size: $nav-tabs-link-font-size !default;
+$nav-pills-link-padding-top: $nav-tabs-link-padding-top !default;
+$nav-pills-link-padding-bottom: $nav-tabs-link-padding-bottom !default;
+$nav-pills-link-padding-x: $nav-tabs-link-padding-x !default;
+$nav-pills-link-line-height: 1 !default;
+$nav-pills-link-background-color: $nav-tabs-link-hover-background-color !default;
+$nav-pills-link-font-weight: $nav-tabs-link-font-weight !default;
+
+$nav-tabs-link-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.55) !default;
+$nav-tabs-link-hover-bgc: var(--#{$prefix}highlight-bg-color) !default;
+
+$nav-pills-link-color: $nav-tabs-link-color !default;
+$nav-pills-link-hover-bg: $nav-tabs-link-hover-bgc !default;
+$nav-pills-link-active-bg: var(--#{$prefix}primary-bg-subtle) !default;
+$nav-pills-link-active-color: var(--#{$prefix}primary-text-emphasis) !default;
+// scss-docs-end nav-variables
+
+// Navbar
+
+// scss-docs-start navbar-variables
+$navbar-box-shadow: 0 4px 12px 0 rgba(var(--#{$prefix}box-shadow-color-rgb), 0.07),
+ 0 2px 4px rgba(var(--#{$prefix}box-shadow-color-rgb), 0.05) !default;
+$navbar-padding-top: 0.5625rem !default;
+$navbar-brand-img-margin-right: 0.25rem !default;
+// scss-docs-end navbar-variables
+
+// Dropdowns
+//
+// Dropdown menu container and contents.
+
+// scss-docs-start dropdown-variables
+$dropdown-color: var(--#{$prefix}surface-color) !default;
+$dropdown-bg: var(--#{$prefix}surface-bg) !default;
+$dropdown-link-color: $dropdown-color !default;
+$dropdown-header-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.55) !default;
+
+$dropdown-margin-top: 0.125rem !default;
+$dropdown-box-shadow: $box-shadow-4 !default;
+$dropdown-font-size: 0.875rem !default;
+$dropdown-item-border-radius: 0.5rem !default;
+$dropdown-item-padding-y: 0.5rem !default;
+$dropdown-item-padding-x: 1rem !default;
+$dropdown-border-radius: 0.5rem !default;
+$dropdown-divider-bg: var(--#{$prefix}divider-color) !default;
+$dropdown-divider-border-top-width: 2px;
+
+$dropdown-state-color: var(--#{$prefix}surface-color) !default;
+$dropdown-state-background-color: var(--#{$prefix}highlight-bg-color) !default;
+$dropdown-link-disabled-color: rgba(var(--#{$prefix}surface-color-rgb), 0.5) !default;
+
+$dropdown-menu-animated-animation-duration: 0.55s !default;
+$dropdown-menu-animated-animation-timing-function: ease !default;
+
+$dropdown-animation-duration: 1s !default;
+// scss-docs-end dropdown-variables
+
+// Pagination
+
+// scss-docs-start pagination-variables
+$pagination-font-size: 0.9rem !default;
+$pagination-color: var(--#{$prefix}body-color) !default;
+$pagination-transition: all 0.3s linear !default;
+$pagination-border-radius: 0.25rem !default;
+
+$pagination-hover-color: var(--#{$prefix}body-color) !default;
+$pagination-hover-bg: var(--#{$prefix}highlight-bg-color) !default;
+$pagination-active-transition: all 0.2s linear !default;
+
+$pagination-circle-border-radius: 50% !default;
+$pagination-circle-padding-x: 0.841rem !default;
+
+$pagination-circle-lg-padding-left: 1.399414rem !default;
+$pagination-circle-lg-padding-right: 1.399415rem !default;
+
+$pagination-circle-sm-padding-left: 0.696rem !default;
+$pagination-circle-sm-padding-right: 0.688rem !default;
+
+$pagination-focus-bg: var(--#{$prefix}highlight-bg-color) !default;
+
+$pagination-active-bg: var(--#{$prefix}primary-bg-subtle) !default;
+$pagination-active-color: var(--#{$prefix}primary-text-emphasis) !default;
+$pagination-active-font-weight: 500 !default;
+
+$pagination-disabled-color: rgba(var(--#{$prefix}body-color-rgb), 0.55) !default;
+$pagination-disabled-bg: transparent !default;
+// scss-docs-end pagination-variables
+
+// Placeholders
+
+// scss-docs-start placeholders
+$placeholder-opacity-max: 0.5 !default;
+$placeholder-opacity-min: 0.2 !default;
+// scss-docs-end placeholders
+
+// Cards
+
+// scss-docs-start card-variables
+$card-spacer-y: 1.5rem !default;
+$card-spacer-x: 1.5rem !default;
+$card-box-shadow: $box-shadow-4 !default;
+$card-cap-bg: rgba(255, 255, 255, 0) !default;
+$card-footer-background-color: rgba(255, 255, 255, 0) !default;
+$card-header-border-width: 2px !default;
+$card-header-border-color: var(--#{$prefix}divider-color) !default;
+$card-footer-border-width: 2px !default;
+$card-footer-border-color: var(--#{$prefix}divider-color) !default;
+$card-border-radius: 0.5rem !default;
+$card-img-overlay-padding: 1.5rem !default;
+$card-bg: var(--#{$prefix}surface-bg) !default;
+$card-border-color: rgba(0, 0, 0, 0.175);
+// scss-docs-end card-variables
+
+// Accordion
+
+// scss-docs-start accordion-variables
+$accordion-padding-y: 1.15rem !default;
+$accordion-padding-x: 1.5rem !default;
+$accordion-border-radius: $border-radius-lg !default;
+$accordion-button-color: var(--#{$prefix}surface-color) !default;
+$accordion-button-active-bg: var(--#{$prefix}surface-bg) !default;
+$accordion-button-active-color: $primary !default;
+$accordion-button-focus-box-shadow: inset 0 -1px 0 var(--#{$prefix}border-color) !default;
+$accordion-color: var(--#{$prefix}surface-color) !default;
+$accordion-flush-button-box-shadow: inset 0 -2px 0 var(--#{$prefix}divider-color) !default;
+$accordion-flush-item-border-bottom: 2px solid var(--#{$prefix}divider-color) !default;
+$accordion-borderless-button-border-radius: 0.5rem !default;
+$accordion-borderless-button-bgc: var(--#{$prefix}primary-bg-subtle) !default;
+$accordion-borderless-button-color: var(--#{$prefix}primary-text-emphasis) !default;
+$accordion-icon-color: var(--#{$prefix}surface-color) !default;
+$accordion-icon-active-color: $primary !default;
+// scss-docs-end accordion-variables
+
+// Tooltips
+
+// scss-docs-start tooltip-variables
+$tooltip-color: var(--#{$prefix}surface-inverted-color) !default;
+$tooltip-padding-y: 6px !default;
+$tooltip-padding-x: 16px !default;
+$tooltip-font-size: 14px !default;
+$tooltip-bg: var(--#{$prefix}surface-inverted-bg) !default;
+$tooltip-border-radius: 0.25rem !default;
+// scss-docs-end tooltip-feedback-variables
+
+// Popovers
+
+// scss-docs-start popover-variables
+$popover-box-shadow: $box-shadow-2 !default;
+$popover-border-radius: $border-radius-lg !default;
+$popover-border-bottom-width: $border-width-alternate !default;
+$popover-border-width: 1px !default;
+$popover-border-color: var(--#{$prefix}divider-color) !default;
+$popover-header-bg: var(--#{$prefix}surface-bg) !default;
+$popover-bg: var(--#{$prefix}surface-bg) !default;
+$popover-header-color: var(--#{$prefix}surface-color) !default;
+$popover-body-color: var(--#{$prefix}surface-color) !default;
+
+// scss-docs-end popover-variables
+
+// Toasts
+
+// scss-docs-start toast-variables
+$toast-box-shadow: $box-shadow-4 !default;
+$toast-border-radius: $border-radius-lg !default;
+$toast-border-bottom-width: $border-width-alternate !default;
+$toast-background-color: var(--#{$prefix}surface-bg) !default;
+$toast-padding-x: 1rem !default;
+$toast-padding-y: 0.65rem !default;
+$toast-header-background-color: var(--#{$prefix}surface-bg) !default;
+$toast-btn-close-width: 1.3em !default;
+$toast-btn-close-mr: -0.375rem !default;
+$toast-btn-close-ml: 0.75rem !default;
+// scss-docs-end toast-variables
+
+// Badges
+
+// scss-docs-start badge-variables
+$badge-border-radius: 0.27rem !default;
+
+$badge-dot-border-radius: 4.5px !default;
+$badge-dot-height: 9px !default;
+$badge-dot-width: $badge-dot-height !default;
+$badge-dot-margin-left: -0.3125rem !default;
+
+$badge-notification-font-size: 0.6rem !default;
+$badge-notification-margin-top: -0.1rem !default;
+$badge-notification-margin-left: -0.5rem !default;
+$badge-notification-padding-y: 0.2em !default;
+$badge-notification-padding-x: 0.45em !default;
+// scss-docs-end badge-variables
+
+// Modals
+
+// scss-docs-start modal-variables
+$modal-box-shadow: $box-shadow-4 !default;
+$modal-content-border-radius: $border-radius-lg !default;
+$modal-header-border-color: var(--#{$prefix}divider-color) !default;
+$modal-footer-border-color: var(--#{$prefix}divider-color) !default;
+$modal-header-border-width: $border-width-alternate !default;
+$modal-footer-border-width: $border-width-alternate !default;
+$modal-content-color: var(--#{$prefix}surface-color) !default;
+$modal-content-bg: var(--#{$prefix}surface-bg) !default;
+// scss-docs-end modal-variables
+
+// Alerts
+
+// scss-docs-start alert-variables
+$alert-padding-y: 1.25rem !default;
+$alert-padding-x: 1.5rem !default;
+$alert-border-radius: $border-radius-lg !default;
+// scss-docs-end alert-variables
+
+// Progress bars
+
+// scss-docs-start progress-variables
+$progress-height: 4px !default;
+$progress-circular-size: 48px !default;
+$progress-circular-bar-width: 4px !default;
+$progress-bar-color: color-contrast($primary) !default;
+// scss-docs-end progress-variables
+
+// Range
+
+// scss-docs-start range-variables
+$range-thumb-height: 30px !default;
+$range-thumb-width: 30px !default;
+$range-thumb-top: -35px !default;
+$range-thumb-background: $primary !default;
+$range-thumb-active-background: var(--#{$prefix}primary-bg-subtle) !default;
+$range-thumb-margin-left: -15px !default;
+$range-thumb-border-radius: 50% 50% 50% 0 !default;
+$range-thumb-transform: scale(0) !default;
+$range-thumb-transition: transform 0.2s ease-in-out !default;
+$range-thumb-value-font-size: 12px !default;
+$range-thumb-value-line-height: 30px !default;
+$range-thumb-value-color: color-contrast($primary) !default;
+$range-thumb-value-font-weight: 500 !default;
+// override default bootstrap values for form range
+$form-range-thumb-bg: $range-thumb-background !default;
+$form-range-thumb-disabled-bg: var(--#{$prefix}form-control-disabled-bg) !default;
+// scss-docs-end range-variables
+
+// List group
+
+// scss-docs-start list-group-variables
+$list-group-border-radius: $border-radius-lg !default;
+$list-group-item-padding-x: 1.5rem !default;
+$list-group-item-transition-time: 0.5s !default;
+$list-group-bg: transparent !default;
+$list-group-disabled-color: rgba(var(--#{$prefix}body-color-rgb), 0.5);
+
+$list-group-light-item-py: 1rem !default;
+$list-group-light-item-border: 2px solid var(--#{$prefix}divider-color) !default;
+$list-group-light-item-border-width: 2px !default;
+$list-group-light-active-border-radius: 0.5rem !default;
+$list-group-light-active-bg: var(--#{$prefix}primary-bg-subtle) !default;
+$list-group-light-active-color: var(--#{$prefix}primary-text-emphasis) !default;
+
+$list-group-small-item-py: 0.5rem !default;
+// scss-docs-end list-group-variables
+
+// Images
+
+// scss-docs-start images-variables
+$image-hover-overlay-transition: all 0.3s ease-in-out !default;
+
+$image-hover-zoom-transition: all 0.3s linear !default;
+$image-hover-zoom-transform: scale(1.1) !default;
+
+$image-hover-shadow-transition: $image-hover-overlay-transition !default;
+$image-hover-shadow-box-shadow: $box-shadow-4-strong !default;
+$image-hover-shadow-box-shadow-soft: $box-shadow-5 !default;
+// scss-docs-end images-variables
+
+// Breadcrumbs
+
+// scss-docs-start breadcrumb-variables
+$breadcrumb-divider-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.55) !default;
+$breadcrumb-active-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.55) !default;
+$breadcrumb-item-color: $breadcrumb-active-color !default;
+$breadcrumb-item-transition: color 0.15s ease-in-out !default;
+$breadcrumb-item-hover-color: rgba(var(--#{$prefix}emphasis-color-rgb), 0.7) !default;
+$breadcrumb-item-before-color: $breadcrumb-divider-color !default;
+// scss-docs-end breadcrumb-variables
+
+// Carousel
+
+// scss-docs-start carousel-variables
+$carousel-control-prev-icon-bg: none !default;
+$carousel-control-next-icon-bg: none !default;
+$carousel-control-icon-font-size: 1.7rem !default;
+// scss-docs-end carousel-variables
+
+// Scrollspy
+
+// scss-docs-start scrollspy-variables
+$scrollspy-menu-sidebar-font-size: 0.8rem !default;
+$scrollspy-menu-sidebar-color: var(--#{$prefix}body-color) !default;
+$scrollspy-menu-sidebar-line-height: 1.1rem !default;
+$scrollspy-menu-sidebar-padding-x: 5px !default;
+$scrollspy-menu-sidebar-font-weight: 400 !default;
+$scrollspy-menu-sidebar-transition: all 0.2s ease-in-out !default;
+$scrollspy-menu-sidebar-margin-y: 3px !default;
+
+$scrollspy-menu-sidebar-active-color: $primary !default;
+$scrollspy-menu-sidebar-active-font-weight: 600 !default;
+$scrollspy-menu-sidebar-active-border-width: 0.125rem !default;
+$scrollspy-menu-sidebar-active-border-color: $primary !default;
+
+$scrollspy-collapsible-nav-transition-time: 0.5s !default;
+// scss-docs-end scrollspy-variables
+
+// Dividers
+
+// scss-docs-start divider-variables
+$divider-height: $border-width-alternate !default;
+$divider-width: $border-width-alternate !default;
+$divider-bg: var(--#{$prefix}divider-color) !default;
+$divider-opacity: 1 !default;
+
+$divider-blurry-bg-image: linear-gradient(
+ 90deg,
+ transparent,
+ var(--#{$prefix}divider-blurry-color),
+ transparent
+) !default;
+$divider-blurry-height: 1px !default;
+$divider-blurry-opacity: 0.25 !default;
+
+$divider-blurry-vr-bg-image: linear-gradient(
+ 180deg,
+ transparent,
+ var(--#{$prefix}divider-blurry-color),
+ transparent
+) !default;
+$divider-blurry-vr-width: 1px !default;
+// scss-docs-end divider-variables
+
+// Global MDB light theme variables
+
+// scss-docs-start mdb-global-light-theme-variables
+$surface-color: $gray-800 !default;
+$surface-bg: $white !default;
+$surface-inverted-color: $white !default;
+$surface-inverted-bg: #6d6d6d !default;
+$divider-color: $gray-100 !default;
+$divider-blurry-color: hsl(0, 0%, 40%) !default;
+$highlight-bg-color: $gray-200 !default;
+$scrollbar-rail-bg: $gray-200 !default;
+$scrollbar-thumb-bg: $gray-500 !default;
+$picker-header-bg: $primary !default;
+$timepicker-clock-face-bg: var(--#{$prefix}secondary-bg) !default;
+$sidenav-backdrop-opacity: 0.1 !default;
+$form-control-border-color: $gray-400 !default;
+$form-control-label-color: $gray-600 !default;
+$form-control-disabled-bg: $gray-300 !default;
+$box-shadow-color: $black !default;
+$stepper-mobile-bg: $gray-50 !default;
+// scss-docs-start mdb-global-light-theme-variables
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-check.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-check.scss
new file mode 100644
index 00000000..f985c635
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-check.scss
@@ -0,0 +1,265 @@
+//
+// Material styles for check / radio / switch
+//
+
+.form-check {
+ // Additional MDB Angular styles
+ position: relative;
+ // Additional MDB Angular styles
+ min-height: 1.5rem;
+}
+
+.form-check-input {
+ position: relative;
+ width: $form-check-input-width-md;
+ height: $form-check-input-height;
+ background-color: $form-check-input-background-color;
+ border: $form-check-input-border-width solid $form-check-input-border-color;
+
+ &:before {
+ content: '';
+ position: absolute;
+ box-shadow: $form-check-input-before-box-shadow;
+ border-radius: $form-check-input-before-border-radius;
+ width: $form-check-input-before-width;
+ height: $form-check-input-before-height;
+ background-color: transparent;
+ opacity: 0;
+ pointer-events: none;
+ transform: $form-check-input-before-transform;
+ }
+
+ &:hover {
+ cursor: pointer;
+
+ &:before {
+ opacity: $form-check-input-hover-before-opacity;
+ box-shadow: $form-check-input-hover-before-box-shadow;
+ }
+ }
+
+ &:focus {
+ box-shadow: none;
+ border-color: $form-check-input-focus-border-color;
+ transition: $form-check-input-focus-transition;
+
+ &:before {
+ opacity: $form-check-input-focus-before-opacity;
+ box-shadow: $form-check-input-focus-before-box-shadow;
+ transform: $form-check-input-focus-before-transform;
+ transition: $form-check-input-focus-before-transition;
+ }
+ }
+
+ &:checked {
+ border-color: $form-check-input-checked-border-color;
+
+ &:before {
+ opacity: $form-check-input-checked-before-opacity;
+ }
+
+ &:after {
+ content: '';
+ position: absolute;
+ }
+
+ &:focus {
+ border-color: $form-check-input-checked-focus-border-color;
+
+ &:before {
+ box-shadow: $form-check-input-checked-focus-before-box-shadow;
+ transform: $form-check-input-checked-focus-before-transform;
+ transition: $form-check-input-checked-focus-before-transition;
+ }
+ }
+ }
+
+ &:indeterminate {
+ &:focus {
+ &:before {
+ box-shadow: $form-check-input-indeterminate-focus-before-box-shadow;
+ }
+ }
+ }
+
+ &[type='checkbox'] {
+ border-radius: $form-check-input-checkbox-border-radius;
+ margin-top: 0.19em;
+ margin-right: 6px;
+
+ &:focus {
+ &:after {
+ content: '';
+ position: absolute;
+ width: $form-check-input-checkbox-focus-after-width;
+ height: $form-check-input-checkbox-focus-after-height;
+ z-index: 1;
+ display: block;
+ border-radius: 0;
+ background-color: $form-check-input-checkbox-focus-after-background-color;
+ }
+ }
+
+ &:checked {
+ background-image: none;
+ background-color: $form-check-input-checkbox-checked-background-color;
+
+ &:after {
+ display: block;
+ transform: $form-check-input-checkbox-checked-after-transform #{'/*!rtl:ignore*/'};
+ border-width: $form-check-input-checkbox-checked-after-border-width;
+ border-color: $form-check-input-checkbox-checked-after-border-color;
+ width: $form-check-input-checkbox-checked-after-width;
+ height: $form-check-input-checkbox-checked-after-height;
+ border-style: solid;
+ border-top: 0;
+ border-left: 0 #{'/*!rtl:ignore*/'};
+ margin-left: $form-check-input-checkbox-checked-after-margin-left;
+ margin-top: $form-check-input-checkbox-checked-after-margin-top;
+ background-color: transparent;
+ }
+
+ &:focus {
+ background-color: $form-check-input-checkbox-checked-focus-background-color;
+ }
+ }
+
+ &:indeterminate {
+ border-color: $form-check-input-indeterminate-focus-border-color;
+ }
+ }
+
+ &[type='radio'] {
+ border-radius: $form-check-input-radio-border-radius;
+ width: $form-check-input-radio-width;
+ height: $form-check-input-radio-height;
+ margin-top: 0.125em;
+ margin-right: 4px;
+
+ &:before {
+ width: $form-check-input-radio-before-width;
+ height: $form-check-input-radio-before-height;
+ }
+
+ &:after {
+ content: '';
+ position: absolute;
+ width: $form-check-input-radio-after-width;
+ height: $form-check-input-radio-after-height;
+ z-index: 1;
+ display: block;
+ border-radius: $form-check-input-radio-after-border-radius;
+ background-color: $form-check-input-radio-after-background-color;
+ }
+
+ &:checked {
+ background-image: none;
+ background-color: $form-check-input-radio-checked-background-color;
+
+ &:after {
+ border-radius: $form-check-input-radio-checked-after-border-radius;
+ width: $form-check-input-radio-checked-after-width;
+ height: $form-check-input-radio-checked-after-height;
+ border-color: $form-check-input-radio-checked-after-border-color;
+ background-color: $form-check-input-radio-checked-after-background-color;
+ transition: $form-check-input-radio-checked-after-transition;
+ transform: translate(-50%, -50%);
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ }
+
+ &:focus {
+ background-color: $form-check-input-radio-checked-focus-background-color;
+ }
+ }
+ }
+}
+
+.form-check-label {
+ padding-left: 0.15rem;
+ &:hover {
+ cursor: pointer;
+ }
+}
+
+//
+// Switch
+//
+
+.form-switch {
+ .form-check-input {
+ background-image: none;
+ border-width: 0;
+ border-radius: $form-switch-form-check-input-border-radius;
+ width: $form-switch-form-check-input-width;
+ height: $form-switch-form-check-input-height;
+ background-color: $form-switch-form-check-input-background-color;
+ margin-top: 0.3em;
+ margin-right: 8px;
+
+ &:after {
+ content: '';
+ position: absolute;
+ border: none;
+ z-index: 2;
+ border-radius: $form-switch-form-check-input-after-border-radius;
+ width: $form-switch-form-check-input-after-width;
+ height: $form-switch-form-check-input-after-height;
+ background-color: $form-switch-form-check-input-after-background-color;
+ margin-top: $form-switch-form-check-input-after-margin-top;
+ box-shadow: $form-switch-form-check-input-after-box-shadow;
+ transition: $form-switch-form-check-input-after-transition;
+ }
+
+ &:focus {
+ background-image: none;
+
+ &:before {
+ box-shadow: $form-switch-form-check-input-focus-before-box-shadow;
+ transform: $form-switch-form-check-input-focus-before-transform;
+ transition: $form-switch-form-check-input-focus-before-transition;
+ }
+
+ &:after {
+ border-radius: $form-switch-form-check-input-focus-after-border-radius;
+ width: $form-switch-form-check-input-focus-after-width;
+ height: $form-switch-form-check-input-focus-after-height;
+ }
+ }
+
+ &:checked {
+ background-image: none;
+
+ &:focus {
+ background-image: none;
+
+ &:before {
+ margin-left: $form-switch-form-check-input-checked-focus-before-margin-left;
+ box-shadow: $form-switch-form-check-input-checked-focus-before-box-shadow;
+ transform: $form-switch-form-check-input-checked-focus-before-transform;
+ transition: $form-switch-form-check-input-checked-focus-before-transition;
+ }
+ }
+
+ &[type='checkbox'] {
+ background-image: none;
+
+ &:after {
+ content: '';
+ position: absolute;
+ border: none;
+ z-index: 2;
+ border-radius: $form-switch-form-check-input-checked-checkbox-after-border-radius;
+ width: $form-switch-form-check-input-checked-checkbox-after-width;
+ height: $form-switch-form-check-input-checked-checkbox-after-height;
+ background-color: $form-switch-form-check-input-checked-checkbox-after-background-color;
+ margin-top: $form-switch-form-check-input-checked-checkbox-after-margin-top;
+ margin-left: $form-switch-form-check-input-checked-checkbox-after-margin-left;
+ box-shadow: $form-switch-form-check-input-checked-checkbox-after-box-shadow;
+ transition: $form-switch-form-check-input-checked-checkbox-after-transition;
+ }
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-control.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-control.scss
new file mode 100644
index 00000000..233c4956
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-control.scss
@@ -0,0 +1,411 @@
+//
+// Material styles for form control - form outline
+//
+
+.form-control {
+ min-height: auto;
+ padding-top: 4px;
+ padding-bottom: 3.28px;
+ transition: all 0.1s linear;
+ box-shadow: none;
+
+ &:focus {
+ box-shadow: none;
+ transition: all 0.1s linear;
+ border-color: $primary;
+ box-shadow: inset 0px 0px 0px 1px $primary;
+ }
+ &.form-control-sm {
+ font-size: 0.775rem;
+ line-height: 1.5;
+ }
+ &.form-control-lg {
+ line-height: 2.15;
+ border-radius: 0.25rem;
+ }
+}
+
+// Additional MDB Angular styles
+.select {
+ ~ .form-label {
+ position: absolute;
+ top: 0;
+ left: $form-label-left;
+ padding-top: $form-label-padding-top;
+ pointer-events: none;
+ transform-origin: 0 0;
+ transition: $form-label-transition;
+ color: $form-label-color;
+ margin-bottom: 0;
+ }
+ ~ .form-notch {
+ display: flex;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ max-width: 100%;
+ height: 100%;
+ text-align: left;
+ pointer-events: none;
+ div {
+ pointer-events: none;
+ border: $border-width solid;
+ border-color: $form-notch-div-border-color;
+ box-sizing: border-box;
+ background: transparent;
+ transition: $input-transition;
+ }
+ .form-notch-leading {
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: $form-notch-leading-width;
+ border-right: none;
+ border-radius: $form-notch-leading-border-radius 0 0 $form-notch-leading-border-radius;
+ }
+ .form-notch-middle {
+ flex: 0 0 auto;
+ width: auto;
+ max-width: calc(100% - #{$form-notch-middle-max-width});
+ height: 100%;
+ border-right: none;
+ border-left: none;
+ }
+ .form-notch-trailing {
+ flex-grow: 1;
+ height: 100%;
+ border-left: none;
+ border-radius: 0 $form-notch-trailing-border-radius $form-notch-trailing-border-radius 0;
+ }
+ }
+ &.form-control:not(.placeholder-active)::placeholder {
+ opacity: 0;
+ }
+ & .form-control:focus,
+ &.active {
+ &::placeholder {
+ opacity: 1;
+ }
+ }
+ & .form-control:focus {
+ box-shadow: none !important;
+ }
+ // &:focus ~ .form-label,
+ &.active ~ .form-label {
+ transform: $input-focus-active-label-transform;
+ }
+ & .form-control:focus ~ .form-label {
+ color: $input-focus-label-color;
+ }
+ & .form-control:focus ~ .form-notch .form-notch-middle,
+ & .form-control.active ~ .form-notch .form-notch-middle {
+ border-right: none;
+ border-left: none;
+ border-top: none;
+ }
+ &:focus ~ .form-notch .form-notch-middle {
+ border-color: $input-focus-border-color;
+ box-shadow: 0 1px 0 0 $input-focus-border-color;
+ border-top: 1px solid transparent;
+ }
+ &:focus ~ .form-notch .form-notch-leading,
+ &.active ~ .form-notch .form-notch-leading {
+ border-right: none;
+ }
+ &:focus ~ .form-notch .form-notch-leading {
+ border-color: $input-focus-border-color;
+ box-shadow: -1px 0 0 0 $input-focus-border-color, 0 1px 0 0 $input-focus-border-color,
+ 0 -1px 0 0 $input-focus-border-color;
+ }
+ &:focus ~ .form-notch .form-notch-trailing,
+ &.active ~ .form-notch .form-notch-trailing {
+ border-left: none;
+ }
+ &:focus ~ .form-notch .form-notch-trailing {
+ border-color: $input-focus-border-color;
+ box-shadow: 1px 0 0 0 $input-focus-border-color, 0 -1px 0 0 $input-focus-border-color,
+ 0 1px 0 0 $input-focus-border-color;
+ }
+ &:disabled,
+ &.disabled,
+ &[readonly] {
+ background-color: $input-disabled-background-color;
+ }
+ &.form-control-lg {
+ font-size: $input-font-size-lg;
+ line-height: $input-line-height-lg;
+ // padding-left: $input-padding-left-lg;
+ // padding-right: $input-padding-right-lg;
+ ~ .form-label {
+ padding-top: $form-label-padding-top-lg;
+ }
+ &:focus ~ .form-label,
+ &.active ~ .form-label {
+ transform: $input-focus-active-label-transform-lg;
+ }
+ }
+ &.form-control-sm {
+ // padding-left: $input-padding-left-sm;
+ // padding-right: $input-padding-right-sm;
+ padding-top: $input-padding-top-sm;
+ padding-bottom: $input-padding-bottom-sm;
+ font-size: $input-font-size-sm;
+ line-height: $input-line-height-sm;
+ ~ .form-label {
+ padding-top: $form-label-padding-top-sm;
+ font-size: $form-label-font-size-sm;
+ }
+ &:focus ~ .form-label,
+ &.active ~ .form-label {
+ transform: $input-focus-active-label-transform-sm;
+ }
+ }
+}
+// Additional MDB Angular styles
+
+.form-outline {
+ position: relative;
+ // width: 100%; // This change introduces big layout differences
+ // and should be treated as a breaking change.
+ // It should be revised and checked.
+
+ .form-helper {
+ width: 100%;
+ position: absolute;
+ font-size: 0.875em;
+ color: #757575;
+ .form-counter {
+ text-align: right;
+ }
+ }
+
+ .trailing {
+ position: absolute;
+ right: 10px;
+ left: initial;
+ top: 50%;
+ transform: translateY(-50%);
+ pointer-events: none;
+ color: var(--#{$prefix}surface-color);
+ }
+
+ .form-icon-trailing {
+ padding-right: 2rem !important;
+ }
+
+ .form-control {
+ min-height: auto;
+ padding-top: $input-padding-top;
+ padding-bottom: $input-padding-bottom;
+ padding-left: $input-padding-left;
+ padding-right: $input-padding-right;
+ border: 0;
+ background: transparent;
+ transition: $input-transition;
+ ~ .form-label {
+ position: absolute;
+ top: 0;
+ max-width: 90%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ left: $form-label-left;
+ padding-top: $form-label-padding-top;
+ pointer-events: none;
+ transform-origin: 0 0;
+ transition: $form-label-transition;
+ color: $form-label-color;
+ margin-bottom: 0;
+ }
+ ~ .form-notch {
+ display: flex;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ max-width: 100%;
+ height: 100%;
+ text-align: left;
+ pointer-events: none;
+ div {
+ pointer-events: none;
+ border: $border-width solid;
+ border-color: $form-notch-div-border-color;
+ box-sizing: border-box;
+ background: transparent;
+ transition: $input-transition;
+ }
+ .form-notch-leading {
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: $form-notch-leading-width;
+ border-right: none;
+ border-radius: $form-notch-leading-border-radius 0 0 $form-notch-leading-border-radius;
+ }
+ .form-notch-middle {
+ flex: 0 0 auto;
+ width: auto;
+ max-width: calc(100% - #{$form-notch-middle-max-width});
+ height: 100%;
+ border-right: none;
+ border-left: none;
+ }
+ .form-notch-trailing {
+ flex-grow: 1;
+ height: 100%;
+ border-left: none;
+ border-radius: 0 $form-notch-trailing-border-radius $form-notch-trailing-border-radius 0;
+ }
+ }
+ &:not(.placeholder-active)::placeholder {
+ opacity: 0;
+ }
+ &:focus,
+ &.active {
+ &::placeholder {
+ opacity: 1;
+ }
+ }
+ &:focus {
+ box-shadow: none !important;
+ }
+ &:focus ~ .form-label,
+ &.active ~ .form-label {
+ transform: $input-focus-active-label-transform;
+ }
+ &:focus ~ .form-label {
+ color: $input-focus-label-color;
+ }
+ &:focus ~ .form-notch .form-notch-middle,
+ &.active ~ .form-notch .form-notch-middle {
+ border-right: none;
+ border-left: none;
+ border-top: 1px solid transparent;
+ }
+ &:focus ~ .form-notch .form-notch-middle {
+ border-color: $input-focus-border-color;
+ box-shadow: 0 1px 0 0 $input-focus-border-color;
+ border-top: 1px solid transparent;
+ }
+ &:focus ~ .form-notch .form-notch-leading,
+ &.active ~ .form-notch .form-notch-leading {
+ border-right: none;
+ }
+ &:focus ~ .form-notch .form-notch-leading {
+ border-color: $input-focus-border-color;
+ box-shadow: -1px 0 0 0 $input-focus-border-color, 0 1px 0 0 $input-focus-border-color,
+ 0 -1px 0 0 $input-focus-border-color;
+ }
+ &:focus ~ .form-notch .form-notch-trailing,
+ &.active ~ .form-notch .form-notch-trailing {
+ border-left: none;
+ }
+ &:focus ~ .form-notch .form-notch-trailing {
+ border-color: $input-focus-border-color;
+ box-shadow: 1px 0 0 0 $input-focus-border-color, 0 -1px 0 0 $input-focus-border-color,
+ 0 1px 0 0 $input-focus-border-color;
+ }
+ &:disabled,
+ &.disabled,
+ &[readonly] {
+ background-color: $input-disabled-background-color;
+ }
+
+ &:disabled,
+ &.disabled,
+ &[readonly] {
+ ~ .timepicker-toggle-button,
+ ~ .datepicker-toggle-button,
+ ~ .datetimepicker-toggle-button,
+ ~ .select-arrow,
+ ~ .trailing {
+ color: rgba(var(--#{$prefix}surface-color-rgb), 0.5);
+ }
+ }
+
+ &.form-control-lg {
+ font-size: $input-font-size-lg;
+ line-height: $input-line-height-lg;
+ // padding-left: $input-padding-left-lg;
+ // padding-right: $input-padding-right-lg;
+ ~ .form-label {
+ padding-top: $form-label-padding-top-lg;
+ }
+ &:focus ~ .form-label,
+ &.active ~ .form-label {
+ transform: $input-focus-active-label-transform-lg;
+ }
+ }
+ &.form-control-sm {
+ // padding-left: $input-padding-left-sm;
+ // padding-right: $input-padding-right-sm;
+ padding-top: $input-padding-top-sm;
+ padding-bottom: $input-padding-bottom-sm;
+ font-size: $input-font-size-sm;
+ line-height: $input-line-height-sm;
+ ~ .form-label {
+ padding-top: $form-label-padding-top-sm;
+ font-size: $form-label-font-size-sm;
+ }
+ &:focus ~ .form-label,
+ &.active ~ .form-label {
+ transform: $input-focus-active-label-transform-sm;
+ }
+ }
+ }
+
+ // Additional MDB Angular styles
+ .select-lg ~ .form-label {
+ padding-top: $form-label-padding-top-lg;
+ }
+
+ .select-sm ~ .form-label {
+ padding-top: $form-label-padding-top-sm;
+ font-size: $form-label-font-size-sm;
+ }
+ // Additional MDB Angular styles
+
+ &.form-white {
+ .form-control {
+ color: $form-white-input-color;
+ ~ .form-label {
+ color: $form-white-label-color;
+ }
+ ~ .form-notch {
+ div {
+ border-color: $form-white-notch-div-border-color;
+ }
+ }
+ &:focus ~ .form-label {
+ color: $form-white-input-focus-label-color;
+ }
+ &:focus ~ .form-notch .form-notch-middle {
+ border-color: $form-white-input-focus-border-color;
+ box-shadow: 0 1px 0 0 $form-white-input-focus-border-color;
+ border-top: 1px solid transparent;
+ }
+ &:focus ~ .form-notch .form-notch-leading {
+ border-color: $form-white-input-focus-border-color;
+ box-shadow: -1px 0 0 0 $form-white-input-focus-border-color,
+ 0 1px 0 0 $form-white-input-focus-border-color,
+ 0 -1px 0 0 $form-white-input-focus-border-color;
+ }
+ &:focus ~ .form-notch .form-notch-trailing {
+ border-color: $form-white-input-focus-border-color;
+ box-shadow: 1px 0 0 0 $form-white-input-focus-border-color,
+ 0 -1px 0 0 $form-white-input-focus-border-color,
+ 0 1px 0 0 $form-white-input-focus-border-color;
+ }
+ &::placeholder {
+ color: $form-white-placeholder-color;
+ }
+ &:disabled,
+ &.disabled,
+ &[readonly] {
+ background-color: $form-white-disabled-bgc;
+ }
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-file.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-file.scss
new file mode 100644
index 00000000..52c9fd24
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-file.scss
@@ -0,0 +1,21 @@
+.form-control {
+ &[type='file'] {
+ border-color: var(--#{$prefix}form-control-border-color);
+ &::-webkit-file-upload-button {
+ background-color: transparent;
+ }
+ &:disabled {
+ background-color: var(--#{$prefix}form-control-disabled-bg);
+ color: rgba(var(--#{$prefix}surface-color-rgb), 0.5);
+
+ &::file-selector-button {
+ color: rgba(var(--#{$prefix}surface-color-rgb), 0.5);
+ }
+ }
+ }
+ &:hover {
+ &:not(:disabled):not([readonly])::-webkit-file-upload-button {
+ background-color: transparent;
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-range.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-range.scss
new file mode 100644
index 00000000..bb4339ed
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-range.scss
@@ -0,0 +1,49 @@
+// Range
+//
+// Style range inputs the same across browsers. Vendor-specific rules for pseudo
+// elements cannot be mixed. As such, there are no shared styles for focus or
+// active states on prefixed selectors.
+
+.form-range {
+ &:focus {
+ box-shadow: none;
+
+ // Pseudo-elements must be split across multiple rulesets to have an effect.
+ // No box-shadow() mixin for focus accessibility.
+ &::-webkit-slider-thumb {
+ box-shadow: none;
+ }
+ &::-moz-range-thumb {
+ box-shadow: none;
+ }
+ &::-ms-thumb {
+ box-shadow: none;
+ }
+ }
+
+ &::-moz-focus-outer {
+ border: 0;
+ }
+
+ &::-webkit-slider-thumb {
+ margin-top: $form-range-webkit-slider-thumb-margin-top; // Webkit specific
+ box-shadow: none;
+ appearance: none;
+ }
+
+ &::-webkit-slider-runnable-track {
+ height: $form-range-webkit-slider-runnable-track-height;
+ border-radius: 0;
+ box-shadow: none;
+ background-color: var(--#{$prefix}secondary-bg);
+ }
+
+ &::-moz-range-thumb {
+ box-shadow: none;
+ appearance: none;
+ }
+
+ &::-moz-range-track {
+ box-shadow: none;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-select.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-select.scss
new file mode 100644
index 00000000..50890dce
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_form-select.scss
@@ -0,0 +1,19 @@
+// Select
+
+.select-input {
+ &.form-control {
+ &[readonly]:not([disabled]) {
+ background-color: transparent;
+ }
+ }
+}
+
+// .form-select {
+// transition: $form-select-transition;
+// }
+
+// .form-select:focus {
+// border-color: $form-select-focus-border-color;
+// outline: 0;
+// box-shadow: $form-select-focus-border-box-shadow;
+// }
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_input-group.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_input-group.scss
new file mode 100644
index 00000000..d82604fc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_input-group.scss
@@ -0,0 +1,141 @@
+//
+// Base styles
+//
+
+.input-group {
+ flex-wrap: nowrap;
+
+ > .form-control {
+ min-height: $input-group-min-height;
+ height: $input-group-height;
+ padding-top: $input-group-padding-y;
+ padding-bottom: $input-group-padding-y;
+ transition: $input-group-transition;
+
+ &:focus {
+ transition: $input-group-focus-transition;
+ border-color: $input-group-focus-border-color;
+ outline: 0;
+ box-shadow: $input-group-focus-box-shadow;
+ }
+ }
+}
+
+.input-group-text {
+ background-color: transparent;
+ padding-top: $input-group-text-padding-y;
+ padding-bottom: $input-group-text-padding-y;
+ > .form-check-input[type='checkbox'] {
+ margin-left: 1px;
+ margin-right: 1px;
+ }
+ > .form-check-input[type='radio'] {
+ margin-right: 0;
+ }
+}
+
+.input-group-lg {
+ > .form-control {
+ height: $input-group-lg-height;
+ font-size: $input-group-lg-font-size;
+ padding-top: $input-group-lg-padding-y;
+ padding-bottom: $input-group-lg-padding-y;
+ }
+
+ .input-group-text {
+ font-size: $input-group-lg-text-font-size;
+ }
+}
+
+.input-group-sm {
+ > .form-control {
+ min-height: $input-group-sm-min-height;
+ height: $input-group-sm-height;
+ font-size: $input-group-sm-font-size;
+ padding-top: $input-group-sm-padding-y;
+ padding-bottom: $input-group-sm-padding-y;
+ }
+
+ .input-group-text {
+ font-size: $input-group-sm-text-font-size;
+ line-height: $input-group-sm-text-line-height;
+ }
+}
+
+.input-group {
+ &.form-outline {
+ .input-group-text {
+ border-left: 0;
+ }
+ input + .input-group-text {
+ border: 0;
+ border-left: $input-group-form-outline-border-left-width solid
+ $input-group-form-outline-border-left-color;
+ }
+ }
+ .form-outline:not(:first-child),
+ .form-outline:not(:first-child) .form-notch-leading {
+ border-top-left-radius: 0 !important;
+ border-bottom-left-radius: 0 !important;
+ }
+ .form-outline:not(:last-child),
+ .form-outline:not(:last-child) .form-notch-trailing {
+ border-top-right-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+ }
+}
+
+.input-group > [class*='btn-outline-'] + [class*='btn-outline-'] {
+ border-left: 0;
+}
+
+.input-group > .btn[class*='btn-outline-'] {
+ padding-top: 0.47rem;
+}
+
+.input-group > .btn {
+ padding-top: 0.59rem;
+}
+
+.input-group {
+ &.input-group-lg {
+ .input-group-text {
+ height: $input-group-lg-height;
+ }
+ }
+}
+
+.input-group {
+ .input-group-text {
+ height: $input-group-height;
+ }
+ .btn {
+ line-height: 1;
+ }
+}
+
+.input-group {
+ &.input-group-sm {
+ .input-group-text {
+ height: $input-group-sm-height;
+ }
+ }
+}
+
+// In angular we have a different HTML code structure for dropdown.
+// Dropdown has to be inside a div, this requires border radius style
+// inheritance and height adjustments for dropdown toggler.
+
+.input-group {
+ .dropdown .dropdown-toggle {
+ height: 100%;
+ }
+ .dropdown:not(:last-child) .dropdown-toggle {
+ border-top-right-radius: inherit;
+ border-bottom-right-radius: inherit;
+ }
+ .dropdown:not(:first-child) .dropdown-toggle {
+ border-top-left-radius: inherit;
+ border-bottom-left-radius: inherit;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/forms/_validation.scss b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_validation.scss
new file mode 100644
index 00000000..34a9453d
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/forms/_validation.scss
@@ -0,0 +1,149 @@
+.error-message,
+.success-message {
+ position: absolute;
+ top: 40px;
+ left: 0;
+ font-size: 0.875rem;
+}
+
+.form-check .error-message,
+.form-check .success-message {
+ top: 29px;
+ left: 26px;
+}
+
+textarea ~ .error-message,
+textarea ~ .success-message {
+ top: unset;
+ bottom: -26px;
+}
+
+.error-message {
+ color: $danger;
+}
+
+.success-message {
+ color: $success;
+}
+
+.form-outline .validate-success.ng-valid.ng-dirty,
+.form-outline .validate-success.ng-valid.ng-touched {
+ ~ .form-label {
+ color: $success;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $success;
+ }
+ &:focus ~ .form-notch .form-notch-middle,
+ &.active ~ .form-notch .form-notch-middle {
+ border-top: 1px solid transparent;
+ }
+ &:focus ~ .form-notch .form-notch-middle {
+ box-shadow: 0 1px 0 0 $success;
+ }
+ &:focus ~ .form-notch .form-notch-leading {
+ box-shadow: -1px 0 0 0 $success, 0 1px 0 0 $success, 0 -1px 0 0 $success;
+ }
+ &:focus ~ .form-notch .form-notch-trailing {
+ box-shadow: 1px 0 0 0 $success, 0 -1px 0 0 $success, 0 1px 0 0 $success;
+ }
+}
+
+.form-outline .validate-error.ng-invalid.ng-dirty,
+.form-outline .validate-error.ng-invalid.ng-touched {
+ ~ .form-label {
+ color: $danger;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $danger;
+ }
+ &:focus ~ .form-notch .form-notch-middle,
+ &.active ~ .form-notch .form-notch-middle {
+ border-top: 1px solid transparent;
+ }
+ &:focus ~ .form-notch .form-notch-middle {
+ box-shadow: 0 1px 0 0 $danger;
+ }
+ &:focus ~ .form-notch .form-notch-leading {
+ box-shadow: -1px 0 0 0 $danger, 0 1px 0 0 $danger, 0 -1px 0 0 $danger;
+ }
+ &:focus ~ .form-notch .form-notch-trailing {
+ box-shadow: 1px 0 0 0 $danger, 0 -1px 0 0 $danger, 0 1px 0 0 $danger;
+ }
+}
+
+.select.validate-success.ng-valid.ng-dirty,
+.select.validate-success.ng-valid.ng-touched {
+ ~ .form-label {
+ color: $success;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $success;
+ }
+ &.focused ~ .form-notch .form-notch-middle,
+ &.active ~ .form-notch .form-notch-middle {
+ border-top: 1px solid transparent;
+ }
+ &.focused ~ .form-notch .form-notch-middle {
+ box-shadow: 0 1px 0 0 $success;
+ }
+ &.focused ~ .form-notch .form-notch-leading {
+ box-shadow: -1px 0 0 0 $success, 0 1px 0 0 $success, 0 -1px 0 0 $success;
+ }
+ &.focused ~ .form-notch .form-notch-trailing {
+ box-shadow: 1px 0 0 0 $success, 0 -1px 0 0 $success, 0 1px 0 0 $success;
+ }
+}
+
+.select.validate-error.ng-invalid.ng-dirty,
+.select.validate-error.ng-invalid.ng-touched {
+ ~ .form-label {
+ color: $danger;
+ }
+ ~ .form-notch .form-notch-leading,
+ ~ .form-notch .form-notch-middle,
+ ~ .form-notch .form-notch-trailing {
+ border-color: $danger;
+ }
+ &.focused ~ .form-notch .form-notch-middle,
+ &.active ~ .form-notch .form-notch-middle {
+ border-top: 1px solid transparent;
+ }
+ &.focused ~ .form-notch .form-notch-middle {
+ box-shadow: 0 1px 0 0 $danger;
+ }
+ &.focused ~ .form-notch .form-notch-leading {
+ box-shadow: -1px 0 0 0 $danger, 0 1px 0 0 $danger, 0 -1px 0 0 $danger;
+ }
+ &.focused ~ .form-notch .form-notch-trailing {
+ box-shadow: 1px 0 0 0 $danger, 0 -1px 0 0 $danger, 0 1px 0 0 $danger;
+ }
+}
+
+.form-check .validate-success.ng-valid.ng-dirty,
+.form-check .validate-success.ng-valid.ng-touched {
+ border-color: $success;
+
+ &:checked {
+ background-color: $success;
+ }
+
+ ~ .form-check-label {
+ color: $success;
+ }
+}
+
+.form-check .validate-error.ng-invalid.ng-dirty,
+.form-check .validate-error.ng-invalid.ng-touched {
+ border-color: $danger;
+
+ ~ .form-check-label {
+ color: $danger;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_buttons.scss b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_buttons.scss
new file mode 100644
index 00000000..9289f969
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_buttons.scss
@@ -0,0 +1,101 @@
+// Button variants
+//
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+
+// scss-docs-start btn-variant-mixin
+@mixin button-variant(
+ $background,
+ $border,
+ $color: color-contrast($background),
+ $shadow: $btn-contextual-box-shadow
+ if(
+ $color == $color-contrast-light,
+ shade-color($background, $btn-hover-bg-shade-amount),
+ tint-color($background, $btn-hover-bg-tint-amount)
+ ),
+ $hover-background:
+ if(
+ $color == $color-contrast-light,
+ shade-color($background, $btn-hover-bg-shade-amount),
+ tint-color($background, $btn-hover-bg-tint-amount)
+ ),
+ $hover-border:
+ if(
+ $color == $color-contrast-light,
+ shade-color($border, $btn-hover-border-shade-amount),
+ tint-color($border, $btn-hover-border-tint-amount)
+ ),
+ $hover-color: $color,
+ $focus-background:
+ if(
+ $color == $color-contrast-light,
+ shade-color($background, $btn-focus-bg-shade-amount),
+ tint-color($background, $btn-focus-bg-tint-amount)
+ ),
+ $focus-color: $color,
+ $active-border:
+ if(
+ $color == $color-contrast-light,
+ shade-color($border, $btn-active-border-shade-amount),
+ tint-color($border, $btn-active-border-tint-amount)
+ ),
+ $active-background:
+ if(
+ $color == $color-contrast-light,
+ shade-color($background, $btn-active-bg-shade-amount),
+ tint-color($background, $btn-active-bg-tint-amount)
+ ),
+ $active-color: $color
+) {
+ --#{$prefix}btn-bg: #{$background};
+ --#{$prefix}btn-color: #{$color};
+ --#{$prefix}btn-box-shadow: #{$shadow};
+ --#{$prefix}btn-hover-bg: #{$hover-background};
+ --#{$prefix}btn-hover-color: #{$hover-color};
+ --#{$prefix}btn-focus-bg: #{$focus-background};
+ --#{$prefix}btn-focus-color: #{$focus-color};
+ --#{$prefix}btn-active-bg: #{$active-background};
+ --#{$prefix}btn-active-color: #{$active-color};
+}
+// scss-docs-end btn-variant-mixin
+
+// scss-docs-start btn-outline-variant-mixin
+@mixin button-outline-variant(
+ $color,
+ $hover-color,
+ $focus-color,
+ $active-color,
+ $hover-background: tint-color($color, $btn-outline-hover-bg-tint-amount),
+ $focus-background: tint-color($color, $btn-outline-focus-bg-tint-amount),
+ $active-background: tint-color($color, $btn-outline-active-bg-tint-amount)
+) {
+ --#{$prefix}btn-bg: transparent;
+ --#{$prefix}btn-color: #{$color};
+ --#{$prefix}btn-hover-bg: #{$hover-background};
+ --#{$prefix}btn-hover-color: #{$hover-color};
+ --#{$prefix}btn-focus-bg: #{$focus-background};
+ --#{$prefix}btn-focus-color: #{$focus-color};
+ --#{$prefix}btn-active-bg: #{$active-background};
+ --#{$prefix}btn-active-color: #{$active-color};
+}
+// scss-docs-end btn-outline-variant-mixin
+
+// scss-docs-start btn-size-mixin
+@mixin button-size($padding-top, $padding-bottom, $padding-x, $font-size, $line-height) {
+ --#{$prefix}btn-padding-top: #{$padding-top};
+ --#{$prefix}btn-padding-bottom: #{$padding-bottom};
+ --#{$prefix}btn-padding-x: #{$padding-x};
+ @include rfs($font-size, --#{$prefix}btn-font-size);
+ --#{$prefix}btn-line-height: #{$line-height};
+}
+// scss-docs-end btn-size-mixin
+
+// Button size variant outline
+@mixin button-outline-size($padding-top, $padding-bottom, $padding-x, $font-size, $line-height) {
+ --#{$prefix}btn-padding-top: #{$padding-top};
+ --#{$prefix}btn-padding-bottom: #{$padding-bottom};
+ --#{$prefix}btn-padding-x: #{$padding-x};
+ @include rfs($font-size, --#{$prefix}btn-font-size);
+ --#{$prefix}btn-line-height: #{$line-height};
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_color-mode.scss b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_color-mode.scss
new file mode 100644
index 00000000..f5993d1e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_color-mode.scss
@@ -0,0 +1,21 @@
+// scss-docs-start color-mode-mixin
+@mixin color-mode($mode: light, $root: false) {
+ @if $color-mode-type == 'media-query' {
+ @if $root == true {
+ @media (prefers-color-scheme: $mode) {
+ :root {
+ @content;
+ }
+ }
+ } @else {
+ @media (prefers-color-scheme: $mode) {
+ @content;
+ }
+ }
+ } @else {
+ [data-mdb-theme='#{$mode}'] {
+ @content;
+ }
+ }
+}
+// scss-docs-end color-mode-mixin
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_ripple.scss b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_ripple.scss
new file mode 100644
index 00000000..144c7dd9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_ripple.scss
@@ -0,0 +1,28 @@
+@mixin ripple-variant($color_value) {
+ $gradient: rgba(
+ $color: $color_value,
+ $alpha: 0.2,
+ )
+ 0,
+ rgba(
+ $color: $color_value,
+ $alpha: 0.3,
+ )
+ 40%,
+ rgba(
+ $color: $color_value,
+ $alpha: 0.4,
+ )
+ 50%,
+ rgba(
+ $color: $color_value,
+ $alpha: 0.5,
+ )
+ 60%,
+ rgba(
+ $color: $color_value,
+ $alpha: 0,
+ )
+ 70%;
+ background-image: radial-gradient(circle, $gradient);
+}
diff --git a/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_table-variants.scss b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_table-variants.scss
new file mode 100644
index 00000000..dabd297a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/free/mixins/_table-variants.scss
@@ -0,0 +1,9 @@
+// Deprecated
+
+// scss-docs-start table-variant
+// @mixin table-variant-mdb($state, $background) {
+// .table-#{$state} {
+// background-color: $background;
+// }
+// }
+// scss-docs-end table-variant
diff --git a/projects/mdb-angular-ui-kit/assets/scss/mdb.core.scss b/projects/mdb-angular-ui-kit/assets/scss/mdb.core.scss
new file mode 100644
index 00000000..12f6b9bf
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/mdb.core.scss
@@ -0,0 +1,32 @@
+// CORE FUNCTIONS
+@import './bootstrap/functions';
+@import './free/functions';
+
+// CORE VARIABLES
+@import './free/variables';
+@import './bootstrap/variables';
+@import './bootstrap/maps';
+
+// BOOTSTRAP CORE
+@import './bootstrap/mixins';
+@import './bootstrap/utilities';
+
+// BOOTSTRAP CORE COMPONENTS
+@import './bootstrap/root';
+@import './bootstrap/reboot';
+@import './bootstrap/type';
+@import './bootstrap/containers';
+@import './bootstrap/grid';
+
+// Utilities
+@import './bootstrap/utilities/api';
+
+// MDB CORE
+@import './free/mixins';
+@import './free/utilities';
+
+// MDB CORE COMPONENTS
+@import './free/root';
+@import './free/reboot';
+@import './free/colors';
+@import './free/shadows';
diff --git a/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss b/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss
new file mode 100644
index 00000000..99c486d6
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/mdb.free.scss
@@ -0,0 +1,110 @@
+// CORE FUNCTIONS
+@import './bootstrap/functions';
+@import './free/functions';
+
+// CORE VARIABLES
+@import './free/variables';
+@import './free/variables-dark';
+@import './bootstrap/variables';
+@import './bootstrap/variables-dark';
+@import './bootstrap/maps';
+
+// BOOTSTRAP CORE
+@import './bootstrap/mixins';
+@import './bootstrap/utilities';
+
+// BOOTSTRAP CORE COMPONENTS
+@import './bootstrap/root';
+@import './bootstrap/reboot';
+@import './bootstrap/type';
+@import './bootstrap/images';
+@import './bootstrap/containers';
+@import './bootstrap/grid';
+
+// BOOTSTRAP COMPONENTS
+@import './bootstrap/tables';
+@import './bootstrap/forms';
+@import './bootstrap/buttons';
+@import './bootstrap/transitions';
+@import './bootstrap/dropdown';
+@import './bootstrap/button-group';
+@import './bootstrap/nav';
+@import './bootstrap/navbar';
+@import './bootstrap/card';
+@import './bootstrap/breadcrumb';
+@import './bootstrap/pagination';
+@import './bootstrap/badge';
+@import './bootstrap/alert';
+@import './bootstrap/accordion';
+@import './bootstrap/progress';
+@import './bootstrap/placeholders';
+@import './bootstrap/list-group';
+@import './bootstrap/close';
+@import './bootstrap/toasts';
+@import './bootstrap/modal';
+@import './bootstrap/popover';
+@import './bootstrap/carousel';
+@import './bootstrap/spinners';
+@import './bootstrap/offcanvas';
+@import './bootstrap/tooltip';
+
+// Helpers
+@import './bootstrap/helpers';
+
+// Utilities
+@import './free/utilities';
+@import './bootstrap/utilities/api';
+
+// MDB CORE
+@import './free/mixins';
+@import './free/utilities';
+
+// MDB CORE COMPONENTS
+@import './free/root';
+@import './free/reboot';
+@import './free/type';
+@import './free/colors';
+@import './free/shadows';
+@import './free/flag';
+@import './free/images';
+
+// MDB ANGULAR OVERLAY Z-INDEX
+@import './free/overlay';
+
+// MDB FORMS
+@import './free/forms/form-control';
+@import './free/forms/form-select';
+@import './free/forms/form-check';
+@import './free/forms/form-file';
+@import './free/forms/input-group';
+@import './free/forms/validation';
+@import './free/forms/form-range';
+
+// MDB COMPONENTS
+@import './free/tables';
+@import './free/buttons';
+@import './free/deprecated';
+@import './free/dropdown';
+@import './free/button-group';
+@import './free/nav';
+@import './free/navbar';
+@import './free/card';
+@import './free/breadcrumb';
+@import './free/pagination';
+@import './free/badge';
+@import './free/alert';
+@import './free/progress';
+@import './free/list-group';
+@import './free/close';
+@import './free/modal';
+@import './free/toasts';
+@import './free/tooltip';
+@import './free/popover';
+@import './free/scrollspy';
+@import './free/ripple';
+@import './free/range';
+@import './free/accordion';
+@import './free/carousel';
+
+@import '@angular/cdk/overlay-prebuilt.css';
+@import '@angular/cdk/text-field-prebuilt.css';
diff --git a/projects/mdb-angular-ui-kit/assets/scss/mdb.scss b/projects/mdb-angular-ui-kit/assets/scss/mdb.scss
new file mode 100644
index 00000000..91e3a5a9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/assets/scss/mdb.scss
@@ -0,0 +1,110 @@
+// CORE FUNCTIONS
+@import './bootstrap/functions';
+@import './free/functions';
+
+// CORE VARIABLES
+@import './free/variables';
+@import './free/variables-dark';
+@import './bootstrap/variables';
+@import './bootstrap/variables-dark';
+@import './bootstrap/maps';
+
+// BOOTSTRAP CORE
+@import './bootstrap/mixins';
+@import './bootstrap/utilities';
+
+// BOOTSTRAP CORE COMPONENTS
+@import './bootstrap/root';
+@import './bootstrap/reboot';
+@import './bootstrap/type';
+@import './bootstrap/images';
+@import './bootstrap/containers';
+@import './bootstrap/grid';
+
+// BOOTSTRAP COMPONENTS
+@import './bootstrap/tables';
+@import './bootstrap/forms';
+@import './bootstrap/buttons';
+@import './bootstrap/transitions';
+@import './bootstrap/dropdown';
+@import './bootstrap/button-group';
+@import './bootstrap/nav';
+@import './bootstrap/navbar';
+@import './bootstrap/card';
+@import './bootstrap/breadcrumb';
+@import './bootstrap/pagination';
+@import './bootstrap/badge';
+@import './bootstrap/alert';
+@import './bootstrap/accordion';
+@import './bootstrap/progress';
+@import './bootstrap/placeholders';
+@import './bootstrap/list-group';
+@import './bootstrap/close';
+@import './bootstrap/toasts';
+@import './bootstrap/modal';
+@import './bootstrap/popover';
+@import './bootstrap/carousel';
+@import './bootstrap/spinners';
+@import './bootstrap/offcanvas';
+@import './bootstrap/tooltip';
+
+// Helpers
+@import './bootstrap/helpers';
+
+// Utilities
+@import './free/utilities';
+@import './bootstrap/utilities/api';
+
+// MDB CORE
+@import './free/mixins';
+
+// MDB CORE COMPONENTS
+@import './free/root';
+@import './free/reboot';
+@import './free/type';
+@import './free/colors';
+@import './free/shadows';
+@import './free/flag';
+@import './free/images';
+
+// MDB ANGULAR OVERLAY Z-INDEX
+@import './free/overlay';
+
+// MDB FORMS
+@import './free/forms/form-control';
+@import './free/forms/form-select';
+@import './free/forms/form-check';
+@import './free/forms/form-file';
+@import './free/forms/input-group';
+@import './free/forms/validation';
+@import './free/forms/form-range';
+
+// MDB COMPONENTS
+@import './free/tables';
+@import './free/buttons';
+@import './free/deprecated';
+@import './free/dropdown';
+@import './free/button-group';
+@import './free/nav';
+@import './free/navbar';
+@import './free/card';
+@import './free/breadcrumb';
+@import './free/pagination';
+@import './free/badge';
+@import './free/alert';
+@import './free/progress';
+@import './free/list-group';
+@import './free/close';
+@import './free/modal';
+@import './free/toasts';
+@import './free/tooltip';
+@import './free/popover';
+@import './free/scrollspy';
+@import './free/ripple';
+@import './free/range';
+@import './free/accordion';
+@import './free/carousel';
+
+
+@import '@angular/cdk/overlay-prebuilt.css';
+@import '@angular/cdk/text-field-prebuilt.css';
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel-item.component.ts b/projects/mdb-angular-ui-kit/carousel/carousel-item.component.ts
new file mode 100644
index 00000000..3950e52f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel-item.component.ts
@@ -0,0 +1,26 @@
+import { Component, ElementRef, HostBinding, Input } from '@angular/core';
+
+@Component({
+ selector: 'mdb-carousel-item',
+ template: ' ',
+ standalone: false,
+})
+export class MdbCarouselItemComponent {
+ @Input() interval: number | null = null;
+
+ @HostBinding('class.carousel-item')
+ carouselItem = true;
+
+ @HostBinding('class.active') active = false;
+
+ @HostBinding('class.carousel-item-next') next = false;
+ @HostBinding('class.carousel-item-prev') prev = false;
+ @HostBinding('class.carousel-item-start') start = false;
+ @HostBinding('class.carousel-item-end') end = false;
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ constructor(private _elementRef: ElementRef) {}
+}
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.component.html b/projects/mdb-angular-ui-kit/carousel/carousel.component.html
new file mode 100644
index 00000000..fbd79a1b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.component.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ Previous
+
+
+
+ Next
+
+
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.component.ts b/projects/mdb-angular-ui-kit/carousel/carousel.component.ts
new file mode 100644
index 00000000..8bb45cc4
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.component.ts
@@ -0,0 +1,395 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import {
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ContentChildren,
+ ElementRef,
+ EventEmitter,
+ HostBinding,
+ HostListener,
+ Input,
+ OnDestroy,
+ Output,
+ QueryList,
+} from '@angular/core';
+import { fromEvent, Subject } from 'rxjs';
+import { take, takeUntil } from 'rxjs/operators';
+import { MdbCarouselItemComponent } from './carousel-item.component';
+
+export enum Direction {
+ UNKNOWN,
+ NEXT,
+ PREV,
+}
+
+@Component({
+ selector: 'mdb-carousel',
+ templateUrl: './carousel.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: false,
+})
+export class MdbCarouselComponent implements AfterViewInit, OnDestroy {
+ @ContentChildren(MdbCarouselItemComponent) _items: QueryList;
+ get items(): MdbCarouselItemComponent[] {
+ return this._items && this._items.toArray();
+ }
+
+ @Input() animation: 'slide' | 'fade' = 'slide';
+
+ @Input()
+ get controls(): boolean {
+ return this._controls;
+ }
+ set controls(value: boolean) {
+ this._controls = coerceBooleanProperty(value);
+ }
+ private _controls = false;
+
+ @Input()
+ get dark(): boolean {
+ return this._dark;
+ }
+ set dark(value: boolean) {
+ this._dark = coerceBooleanProperty(value);
+ }
+ private _dark = false;
+
+ @Input()
+ get indicators(): boolean {
+ return this._indicators;
+ }
+ set indicators(value: boolean) {
+ this._indicators = coerceBooleanProperty(value);
+ }
+ private _indicators = false;
+
+ @Input()
+ get ride(): boolean {
+ return this._ride;
+ }
+ set ride(value: boolean) {
+ this._ride = coerceBooleanProperty(value);
+ }
+ private _ride = true;
+
+ @Input()
+ get interval(): number {
+ return this._interval;
+ }
+ set interval(value: number) {
+ this._interval = value;
+
+ if (this.items) {
+ this._restartInterval();
+ }
+ }
+ private _interval = 5000;
+
+ @Input() keyboard = true;
+ @Input() pause = true;
+ @Input() wrap = true;
+
+ @Output() slide: EventEmitter = new EventEmitter();
+ @Output() slideChange: EventEmitter = new EventEmitter();
+
+ get activeSlide(): number {
+ return this._activeSlide;
+ }
+
+ set activeSlide(index: number) {
+ if (this.items.length && this._activeSlide !== index) {
+ this._activeSlide = index;
+ this._restartInterval();
+ }
+ }
+ private _activeSlide = 0;
+
+ private _lastInterval: any;
+ private _isPlaying = false;
+ private _isSliding = false;
+
+ private readonly _destroy$: Subject = new Subject();
+
+ @HostListener('mouseenter')
+ onMouseEnter(): void {
+ if (this.pause && this._isPlaying) {
+ this.stop();
+ }
+ }
+
+ @HostListener('mouseleave')
+ onMouseLeave(): void {
+ if (this.pause && !this._isPlaying) {
+ this.play();
+ }
+ }
+
+ @HostBinding('class.d-block') display = true;
+
+ constructor(private _elementRef: ElementRef, private _cdRef: ChangeDetectorRef) {}
+
+ ngAfterViewInit(): void {
+ Promise.resolve().then(() => {
+ this._setActiveSlide(this._activeSlide);
+
+ if (this.interval > 0 && this.ride) {
+ this.play();
+ }
+ this._cdRef.markForCheck();
+ });
+
+ if (this.keyboard) {
+ fromEvent(this._elementRef.nativeElement, 'keydown')
+ .pipe(takeUntil(this._destroy$))
+ .subscribe((event: KeyboardEvent) => {
+ if (event.key === 'ArrowRight') {
+ this.next();
+ } else if (event.key === 'ArrowLeft') {
+ this.prev();
+ }
+ });
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _setActiveSlide(index: number): void {
+ const currentSlide = this.items[this._activeSlide];
+ currentSlide.active = false;
+
+ const newSlide = this.items[index];
+ newSlide.active = true;
+ this._activeSlide = index;
+ }
+
+ private _restartInterval(): void {
+ this._resetInterval();
+ const activeElement = this.items[this.activeSlide];
+ const interval = activeElement.interval ? activeElement.interval : this.interval;
+
+ if (!isNaN(interval) && interval > 0) {
+ this._lastInterval = setInterval(() => {
+ const nInterval = +interval;
+ if (this._isPlaying && !isNaN(nInterval) && nInterval > 0) {
+ this.next();
+ this._cdRef.markForCheck();
+ } else {
+ this.stop();
+ }
+ }, interval);
+ }
+ }
+
+ private _resetInterval(): void {
+ if (this._lastInterval) {
+ clearInterval(this._lastInterval);
+ this._lastInterval = null;
+ }
+ }
+
+ play(): void {
+ if (!this._isPlaying) {
+ this._isPlaying = true;
+ this._restartInterval();
+ }
+ }
+
+ stop(): void {
+ if (this._isPlaying) {
+ this._isPlaying = false;
+ this._resetInterval();
+ }
+ }
+
+ to(index: number): void {
+ if (index > this.items.length - 1 || index < 0) {
+ return;
+ }
+
+ if (this.activeSlide === index) {
+ this.stop();
+ this.play();
+ return;
+ }
+
+ const direction = index > this.activeSlide ? Direction.NEXT : Direction.PREV;
+
+ this._animateSlides(direction, this.activeSlide, index);
+ this.activeSlide = index;
+ }
+
+ next(): void {
+ if (!this._isSliding) {
+ this._slide(Direction.NEXT);
+ }
+ }
+
+ prev(): void {
+ if (!this._isSliding) {
+ this._slide(Direction.PREV);
+ }
+ }
+
+ private _slide(direction: Direction): void {
+ const isFirst = this._activeSlide === 0;
+ const isLast = this._activeSlide === this.items.length - 1;
+
+ if (!this.wrap) {
+ if ((direction === Direction.NEXT && isLast) || (direction === Direction.PREV && isFirst)) {
+ return;
+ }
+ }
+
+ const newSlideIndex = this._getNewSlideIndex(direction);
+
+ this._animateSlides(direction, this.activeSlide, newSlideIndex);
+ this.activeSlide = newSlideIndex;
+
+ this.slide.emit();
+ }
+
+ private _animateSlides(direction: Direction, currentIndex: number, nextIndex: number): void {
+ const currentItem = this.items[currentIndex];
+ const nextItem = this.items[nextIndex];
+ const currentEl = currentItem.host;
+ const nextEl = nextItem.host;
+
+ this._isSliding = true;
+
+ if (this._isPlaying) {
+ this.stop();
+ }
+
+ if (direction === Direction.NEXT) {
+ nextItem.next = true;
+
+ setTimeout(() => {
+ this._reflow(nextEl);
+ currentItem.start = true;
+ nextItem.start = true;
+ this._cdRef.markForCheck();
+ }, 0);
+
+ const transitionDuration = 600;
+
+ fromEvent(currentEl, 'transitionend')
+ .pipe(take(1))
+ .subscribe(() => {
+ nextItem.next = false;
+ nextItem.start = false;
+ nextItem.active = true;
+
+ currentItem.active = false;
+ currentItem.start = false;
+ currentItem.next = false;
+
+ this.slideChange.emit();
+ this._isSliding = false;
+ });
+
+ this._emulateTransitionEnd(currentEl, transitionDuration);
+ } else if (direction === Direction.PREV) {
+ nextItem.prev = true;
+
+ setTimeout(() => {
+ this._reflow(nextEl);
+ currentItem.end = true;
+ nextItem.end = true;
+ this._cdRef.markForCheck();
+ }, 0);
+
+ const transitionDuration = 600;
+
+ fromEvent(currentEl, 'transitionend')
+ .pipe(take(1))
+ .subscribe(() => {
+ nextItem.prev = false;
+ nextItem.end = false;
+ nextItem.active = true;
+
+ currentItem.active = false;
+ currentItem.end = false;
+ currentItem.prev = false;
+
+ this.slideChange.emit();
+ this._isSliding = false;
+ });
+
+ this._emulateTransitionEnd(currentEl, transitionDuration);
+ }
+
+ if (!this._isPlaying && this.interval > 0) {
+ this.play();
+ }
+ }
+
+ private _reflow(element: HTMLElement): number {
+ return element.offsetHeight;
+ }
+
+ private _emulateTransitionEnd(element: HTMLElement, duration: number): void {
+ let eventEmitted = false;
+ const durationPadding = 5;
+ const emulatedDuration = duration + durationPadding;
+
+ fromEvent(element, 'transitionend')
+ .pipe(take(1))
+ .subscribe(() => {
+ eventEmitted = true;
+ });
+
+ setTimeout(() => {
+ if (!eventEmitted) {
+ element.dispatchEvent(new Event('transitionend'));
+ }
+ }, emulatedDuration);
+ }
+
+ private _getNewSlideIndex(direction: Direction): number {
+ let newSlideIndex: number;
+
+ if (direction === Direction.NEXT) {
+ newSlideIndex = this._getNextSlideIndex();
+ }
+
+ if (direction === Direction.PREV) {
+ newSlideIndex = this._getPrevSlideIndex();
+ }
+
+ return newSlideIndex;
+ }
+
+ private _getNextSlideIndex(): number {
+ const isLast = this._activeSlide === this.items.length - 1;
+
+ if (!isLast) {
+ return this._activeSlide + 1;
+ } else if (this.wrap && isLast) {
+ return 0;
+ } else {
+ return this._activeSlide;
+ }
+ }
+
+ private _getPrevSlideIndex(): number {
+ const isFirst = this._activeSlide === 0;
+
+ if (!isFirst) {
+ return this._activeSlide - 1;
+ } else if (this.wrap && isFirst) {
+ return this.items.length - 1;
+ } else {
+ return this._activeSlide;
+ }
+ }
+
+ static ngAcceptInputType_controls: BooleanInput;
+ static ngAcceptInputType_dark: BooleanInput;
+ static ngAcceptInputType_indicators: BooleanInput;
+ static ngAcceptInputType_ride: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.module.ts b/projects/mdb-angular-ui-kit/carousel/carousel.module.ts
new file mode 100644
index 00000000..67a70093
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { MdbCarouselComponent } from './carousel.component';
+import { MdbCarouselItemComponent } from './carousel-item.component';
+
+@NgModule({
+ declarations: [MdbCarouselComponent, MdbCarouselItemComponent],
+ exports: [MdbCarouselComponent, MdbCarouselItemComponent],
+ imports: [CommonModule],
+})
+export class MdbCarouselModule {}
diff --git a/projects/mdb-angular-ui-kit/carousel/carousel.spec.ts b/projects/mdb-angular-ui-kit/carousel/carousel.spec.ts
new file mode 100644
index 00000000..7e7f5786
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/carousel.spec.ts
@@ -0,0 +1,218 @@
+import { Component, ViewChild } from '@angular/core';
+import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
+import { MdbCarouselComponent } from './carousel.component';
+import { MdbCarouselModule } from './carousel.module';
+
+const carouselTemplate = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+@Component({
+ template: carouselTemplate,
+ standalone: false,
+})
+export class CarouselTestComponent {
+ @ViewChild(MdbCarouselComponent, { static: true }) carousel: MdbCarouselComponent;
+ controls = false;
+ indicators = false;
+ wrap = true;
+ dark = false;
+ animation = 'slide';
+}
+
+describe('MDB Carousel', () => {
+ let fixture: ComponentFixture;
+ let component: CarouselTestComponent;
+ let carousel: MdbCarouselComponent;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [CarouselTestComponent],
+ imports: [MdbCarouselModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(CarouselTestComponent);
+ component = fixture.componentInstance;
+ carousel = component.carousel;
+
+ fixture.detectChanges();
+ });
+
+ it('should set first slide as active by default', fakeAsync(() => {
+ flush();
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ expect(items[0].classList.contains('active')).toBe(true);
+ }));
+
+ it('should show indicators if indicators input is set to true', () => {
+ component.indicators = true;
+ fixture.detectChanges();
+ const indicators = fixture.nativeElement.querySelectorAll('.carousel-indicators');
+ expect(indicators).toBeDefined();
+ });
+
+ it('should show controls if controls input is set to true', () => {
+ component.controls = true;
+ fixture.detectChanges();
+ const prevArrow = fixture.nativeElement.querySelector('.carousel-control-prev');
+ const nextArrow = fixture.nativeElement.querySelector('.carousel-control-next');
+ expect(prevArrow).toBeDefined();
+ expect(nextArrow).toBeDefined();
+ });
+
+ it('should add carousel-fade class if animation type is set to fade', () => {
+ component.animation = 'fade';
+ fixture.detectChanges();
+ const carouselEl = fixture.nativeElement.querySelector('.carousel');
+ expect(carouselEl.classList.contains('carousel-fade')).toBe(true);
+ });
+
+ it('should add carousel-dark class if dark input is set to true', () => {
+ component.dark = true;
+ fixture.detectChanges();
+ const carouselEl = fixture.nativeElement.querySelector('.carousel');
+ expect(carouselEl.classList.contains('carousel-dark')).toBe(true);
+ });
+
+ it('should set corresponding indicator as active', fakeAsync(() => {
+ component.indicators = true;
+ fixture.detectChanges();
+ const indicators = fixture.nativeElement.querySelectorAll('.carousel-indicators > button');
+ expect(indicators[0].classList.contains('active')).toBe(true);
+ }));
+
+ it('should change active slide on indicator click', fakeAsync(() => {
+ component.indicators = true;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const indicators = fixture.nativeElement.querySelectorAll('.carousel-indicators > button');
+ expect(indicators[0].classList.contains('active')).toBe(true);
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ indicators[1].click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(indicators[1].classList.contains('active')).toBe(true);
+ expect(items[1].classList.contains('active')).toBe(true);
+ }));
+
+ it('should change slide on previous arrow click', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = true;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const prevArrow = fixture.nativeElement.querySelector('.carousel-control-prev');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ prevArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+
+ prevArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[1].classList.contains('active')).toBe(true);
+ }));
+
+ it('should change slide on next arrow click', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = true;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const nextArrow = fixture.nativeElement.querySelector('.carousel-control-next');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[1].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+ }));
+
+ it('should not go to previous slide if first slide is active and wrap option is disabled', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = false;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const prevArrow = fixture.nativeElement.querySelector('.carousel-control-prev');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ prevArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[0].classList.contains('active')).toBe(true);
+ expect(items[2].classList.contains('active')).toBe(false);
+ }));
+
+ it('should not go to next slide if last slide is active and wrap option is disabled', fakeAsync(() => {
+ component.controls = true;
+ component.wrap = false;
+ fixture.detectChanges();
+ const items = fixture.nativeElement.querySelectorAll('.carousel-item');
+ const nextArrow = fixture.nativeElement.querySelector('.carousel-control-next');
+ expect(items[0].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[1].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+
+ nextArrow.click();
+ tick(1000);
+ fixture.detectChanges();
+
+ expect(items[2].classList.contains('active')).toBe(true);
+ expect(items[0].classList.contains('active')).toBe(false);
+ }));
+});
diff --git a/projects/mdb-angular-ui-kit/carousel/index.ts b/projects/mdb-angular-ui-kit/carousel/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/carousel/ng-package.json b/projects/mdb-angular-ui-kit/carousel/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/carousel/public_api.ts b/projects/mdb-angular-ui-kit/carousel/public_api.ts
new file mode 100644
index 00000000..0c661600
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/carousel/public_api.ts
@@ -0,0 +1,3 @@
+export { MdbCarouselComponent } from './carousel.component';
+export { MdbCarouselItemComponent } from './carousel-item.component';
+export { MdbCarouselModule } from './carousel.module';
diff --git a/projects/mdb-angular-ui-kit/checkbox/checkbox.directive.spec.ts b/projects/mdb-angular-ui-kit/checkbox/checkbox.directive.spec.ts
new file mode 100644
index 00000000..c143cc9e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/checkbox/checkbox.directive.spec.ts
@@ -0,0 +1,196 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Component, DebugElement } from '@angular/core';
+import { MdbCheckboxModule } from './index';
+import { By } from '@angular/platform-browser';
+import { UntypedFormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+describe('MDB Checkbox', () => {
+ let checkbox: BasicCheckboxComponent;
+ let fixture: ComponentFixture;
+ let input: HTMLInputElement;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ BasicCheckboxComponent,
+ CheckboxWithNgModelComponent,
+ CheckboxWithFormControlComponent,
+ ],
+ imports: [MdbCheckboxModule, FormsModule, ReactiveFormsModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(BasicCheckboxComponent);
+ fixture.detectChanges();
+ checkbox = fixture.componentInstance;
+ input = fixture.nativeElement.querySelector('input');
+ });
+
+ it('should be checked if checked input is set to true', () => {
+ checkbox.checked = true;
+ fixture.detectChanges();
+ expect(input.checked).toBe(true);
+ });
+
+ it('should be unchecked if checked input is set to false', () => {
+ checkbox.checked = false;
+ fixture.detectChanges();
+ expect(input.checked).toBe(false);
+ });
+
+ it('should be disabled if disabled input is set to true', () => {
+ checkbox.disabled = true;
+ fixture.detectChanges();
+ expect(input.disabled).toBe(true);
+ });
+
+ it('should be enabled if disabled input is set to false', () => {
+ checkbox.disabled = false;
+ fixture.detectChanges();
+ expect(input.disabled).toBe(false);
+ });
+
+ it('should toggle checked state when clicked', () => {
+ checkbox.checked = false;
+ fixture.detectChanges();
+ input.click();
+ fixture.detectChanges();
+ expect(input.checked).toBe(true);
+
+ input.click();
+ fixture.detectChanges();
+ expect(input.checked).toBe(false);
+ });
+
+ it('should not toggle checked state if element is disabled', () => {
+ checkbox.checked = false;
+ checkbox.disabled = true;
+ fixture.detectChanges();
+
+ expect(input.checked).toBe(false);
+
+ input.click();
+ fixture.detectChanges();
+
+ expect(input.checked).toBe(false);
+ });
+
+ describe('Checkbox with ngModel', () => {
+ let checkbox: CheckboxWithNgModelComponent;
+ let fixture: ComponentFixture;
+ let input: HTMLInputElement;
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CheckboxWithNgModelComponent);
+ fixture.detectChanges();
+ checkbox = fixture.componentInstance;
+ input = fixture.nativeElement.querySelector('input');
+ });
+
+ // it('should use value from ngModel to set default checked state', () => {
+ // checkbox.checked = true;
+ // fixture.detectChanges();
+
+ // expect(input.checked).toBe(true);
+ // });
+
+ it('should update ngModel value when checked state change', () => {
+ checkbox.checked = false;
+ fixture.detectChanges();
+
+ input.click();
+ fixture.detectChanges();
+
+ expect(checkbox.checked).toBe(true);
+ });
+ });
+
+ describe('Checkbox with form control', () => {
+ let checkbox: CheckboxWithFormControlComponent;
+ let fixture: ComponentFixture;
+ let input: HTMLInputElement;
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CheckboxWithFormControlComponent);
+ fixture.detectChanges();
+ checkbox = fixture.componentInstance;
+ input = fixture.nativeElement.querySelector('input');
+ });
+
+ it('should use value from form control to set default checked state', () => {
+ expect(input.checked).toBe(false);
+
+ checkbox.control.setValue(true);
+ fixture.detectChanges();
+
+ expect(input.checked).toBe(true);
+ });
+
+ it('should update form control value when checked state change', () => {
+ expect(checkbox.control.value).toBe(false);
+ expect(input.checked).toBe(false);
+
+ input.click();
+ fixture.detectChanges();
+
+ expect(input.checked).toBe(true);
+ expect(checkbox.control.value).toBe(true);
+ });
+
+ it('should disable input when form control disable method is used', () => {
+ expect(input.disabled).toBe(false);
+
+ checkbox.control.disable();
+ fixture.detectChanges();
+
+ expect(input.disabled).toBe(true);
+ });
+ });
+});
+
+const basicTemplate = `
+
+
+ Default checkbox
+
+`;
+
+@Component({
+ template: basicTemplate,
+ standalone: false,
+})
+class BasicCheckboxComponent {
+ checked = false;
+ disabled = false;
+}
+
+const ngModelTemplate = `
+
+
+ Default checkbox
+
+`;
+
+@Component({
+ template: ngModelTemplate,
+ standalone: false,
+})
+class CheckboxWithNgModelComponent {
+ checked = false;
+ disabled = false;
+}
+
+const formControlTemplate = `
+
+
+ Default checkbox
+
+`;
+
+@Component({
+ template: formControlTemplate,
+ standalone: false,
+})
+class CheckboxWithFormControlComponent {
+ control = new UntypedFormControl(false);
+}
diff --git a/projects/mdb-angular-ui-kit/checkbox/checkbox.directive.ts b/projects/mdb-angular-ui-kit/checkbox/checkbox.directive.ts
new file mode 100644
index 00000000..e30717fd
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/checkbox/checkbox.directive.ts
@@ -0,0 +1,125 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import {
+ EventEmitter,
+ forwardRef,
+ Input,
+ Output,
+ Directive,
+ HostBinding,
+ HostListener,
+} from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+
+export const MDB_CHECKBOX_VALUE_ACCESSOR: any = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => MdbCheckboxDirective),
+ multi: true,
+};
+
+export class MdbCheckboxChange {
+ element: MdbCheckboxDirective;
+ checked: boolean;
+}
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbCheckbox]',
+ providers: [MDB_CHECKBOX_VALUE_ACCESSOR],
+ standalone: false,
+})
+export class MdbCheckboxDirective {
+ @Input('checked')
+ get checked(): boolean {
+ return this._checked;
+ }
+ set checked(value: boolean) {
+ this._checked = coerceBooleanProperty(value);
+ }
+ private _checked = false;
+
+ @Input('value')
+ get value(): any {
+ return this._value;
+ }
+ set value(value: any) {
+ this._value = value;
+ }
+ private _value: any = null;
+
+ @Input('disabled')
+ get disabled(): boolean {
+ return this._disabled;
+ }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ }
+ private _disabled = false;
+
+ @Output() checkboxChange: EventEmitter = new EventEmitter();
+
+ @HostBinding('disabled')
+ get isDisabled(): boolean {
+ return this._disabled;
+ }
+
+ @HostBinding('checked')
+ get isChecked(): boolean {
+ return this._checked;
+ }
+
+ @HostListener('click')
+ onCheckboxClick(): void {
+ this.toggle();
+ }
+
+ @HostListener('blur')
+ onBlur(): void {
+ this.onTouched();
+ }
+
+ constructor() {}
+
+ get changeEvent(): MdbCheckboxChange {
+ const newChangeEvent = new MdbCheckboxChange();
+ newChangeEvent.element = this;
+ newChangeEvent.checked = this.checked;
+ return newChangeEvent;
+ }
+
+ toggle(): void {
+ if (this.disabled) {
+ return;
+ }
+ this._checked = !this._checked;
+ this.onChange(this.checked);
+ this.onCheckboxChange();
+ }
+
+ onCheckboxChange(): void {
+ this.checkboxChange.emit(this.changeEvent);
+ }
+
+ // Control Value Accessor Methods
+ onChange = (_: any) => {};
+ onTouched = () => {};
+
+ writeValue(value: any): void {
+ this.value = value;
+ this.checked = !!value;
+ }
+
+ registerOnChange(fn: (_: any) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ static ngAcceptInputType_checked: BooleanInput;
+ static ngAcceptInputType_disabled: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/checkbox/checkbox.module.ts b/projects/mdb-angular-ui-kit/checkbox/checkbox.module.ts
new file mode 100644
index 00000000..5268ccc6
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/checkbox/checkbox.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { MdbCheckboxDirective } from './checkbox.directive';
+
+@NgModule({
+ declarations: [MdbCheckboxDirective],
+ exports: [MdbCheckboxDirective],
+ imports: [CommonModule, FormsModule],
+})
+export class MdbCheckboxModule {}
diff --git a/projects/mdb-angular-ui-kit/checkbox/index.ts b/projects/mdb-angular-ui-kit/checkbox/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/checkbox/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/checkbox/ng-package.json b/projects/mdb-angular-ui-kit/checkbox/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/checkbox/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/checkbox/public_api.ts b/projects/mdb-angular-ui-kit/checkbox/public_api.ts
new file mode 100644
index 00000000..fcbb1357
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/checkbox/public_api.ts
@@ -0,0 +1,6 @@
+export {
+ MdbCheckboxDirective,
+ MdbCheckboxChange,
+ MDB_CHECKBOX_VALUE_ACCESSOR,
+} from './checkbox.directive';
+export { MdbCheckboxModule } from './checkbox.module';
diff --git a/projects/mdb-angular-ui-kit/collapse/collapse.directive.spec.ts b/projects/mdb-angular-ui-kit/collapse/collapse.directive.spec.ts
new file mode 100644
index 00000000..95189697
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/collapse/collapse.directive.spec.ts
@@ -0,0 +1,112 @@
+import { Component, ViewChild } from '@angular/core';
+import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
+import { MdbCollapseDirective } from '.';
+import { MdbCollapseModule } from './collapse.module';
+
+const template = `
+ Button
+
+ Collapse directive content
+
+`;
+
+@Component({
+ selector: 'mdb-collapse-test',
+ template,
+ standalone: false,
+})
+class TestCollapseComponent {
+ @ViewChild('collapse') collapse: MdbCollapseDirective;
+
+ collapsed = true;
+}
+
+describe('MDB Collapse', () => {
+ const ANIMATION_TIME = 355;
+ let fixture: ComponentFixture;
+ let element: any;
+ let component: any;
+ let collapse: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [TestCollapseComponent],
+ imports: [MdbCollapseModule],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestCollapseComponent);
+ fixture.detectChanges();
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ collapse = fixture.nativeElement.querySelector('.collapse');
+ });
+
+ it('should have content collapsed by default', () => {
+ expect(collapse.classList.contains('show')).toBe(false);
+ });
+
+ it('should be expanded if collapsed input is set to false', fakeAsync(() => {
+ component.collapsed = false;
+ fixture.detectChanges();
+
+ tick(ANIMATION_TIME);
+ flush();
+ fixture.detectChanges();
+
+ expect(collapse.classList).toContain('show');
+ }));
+
+ it('should allow toggling component by clicking on another element', fakeAsync(() => {
+ const button = fixture.nativeElement.querySelector('button');
+
+ expect(collapse.classList).not.toContain('show');
+
+ button.click();
+ fixture.detectChanges();
+
+ tick(ANIMATION_TIME);
+ flush();
+ fixture.detectChanges();
+
+ expect(collapse.classList).toContain('show');
+
+ button.click();
+ fixture.detectChanges();
+
+ tick(ANIMATION_TIME);
+ flush();
+ fixture.detectChanges();
+
+ expect(collapse.classList).not.toContain('show');
+ }));
+
+ it('should emit events on collapse and expand', fakeAsync(() => {
+ const button = fixture.nativeElement.querySelector('button');
+ const showSpy = jest.spyOn(component.collapse.collapseShow, 'emit');
+ const shownSpy = jest.spyOn(component.collapse.collapseShown, 'emit');
+ const hideSpy = jest.spyOn(component.collapse.collapseHide, 'emit');
+ const hiddenSpy = jest.spyOn(component.collapse.collapseHidden, 'emit');
+
+ button.click();
+ fixture.detectChanges();
+
+ expect(showSpy).toHaveBeenCalled();
+
+ tick(ANIMATION_TIME);
+ flush();
+ fixture.detectChanges();
+
+ expect(shownSpy).toHaveBeenCalled();
+
+ button.click();
+ fixture.detectChanges();
+
+ expect(hideSpy).toHaveBeenCalled();
+
+ tick(ANIMATION_TIME);
+ flush();
+ fixture.detectChanges();
+
+ expect(hiddenSpy).toHaveBeenCalled();
+ }));
+});
diff --git a/projects/mdb-angular-ui-kit/collapse/collapse.directive.ts b/projects/mdb-angular-ui-kit/collapse/collapse.directive.ts
new file mode 100644
index 00000000..5a67952a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/collapse/collapse.directive.ts
@@ -0,0 +1,149 @@
+import {
+ Directive,
+ ElementRef,
+ EventEmitter,
+ HostBinding,
+ Input,
+ Output,
+ Renderer2,
+} from '@angular/core';
+import { fromEvent } from 'rxjs';
+import { take } from 'rxjs/operators';
+
+const TRANSITION_TIME = 350;
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbCollapse]',
+ exportAs: 'mdbCollapse',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbCollapseDirective {
+ constructor(private _elementRef: ElementRef, private _renderer: Renderer2) {}
+
+ @HostBinding('class.collapse') collapseClass = true;
+
+ @Output() collapseShow: EventEmitter = new EventEmitter();
+ @Output() collapseShown: EventEmitter = new EventEmitter();
+ @Output() collapseHide: EventEmitter = new EventEmitter();
+ @Output() collapseHidden: EventEmitter = new EventEmitter();
+
+ @Input()
+ set collapsed(collapsed: boolean) {
+ if (collapsed !== this._collapsed) {
+ collapsed ? this.hide() : this.show();
+ this._collapsed = collapsed;
+ }
+ }
+ get collapsed(): boolean {
+ return this._collapsed;
+ }
+ private _collapsed = true;
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ private _isTransitioning = false;
+
+ show(): void {
+ if (this._isTransitioning || !this.collapsed) {
+ return;
+ }
+
+ this.collapseShow.emit(this);
+
+ this._renderer.removeClass(this.host, 'collapse');
+ this._renderer.addClass(this.host, 'collapsing');
+
+ this._renderer.setStyle(this.host, 'height', '0px');
+
+ this._isTransitioning = true;
+
+ const scrollHeight = this.host.scrollHeight;
+
+ fromEvent(this.host, 'transitionend')
+ .pipe(take(1))
+ .subscribe(() => {
+ this._isTransitioning = false;
+ this.collapsed = false;
+ this._renderer.removeClass(this.host, 'collapsing');
+ this._renderer.addClass(this.host, 'collapse');
+ this._renderer.addClass(this.host, 'show');
+
+ this._renderer.removeStyle(this.host, 'height');
+
+ this.collapseShown.emit(this);
+ });
+
+ this._emulateTransitionEnd(this.host, TRANSITION_TIME);
+
+ this._renderer.setStyle(this.host, 'height', `${scrollHeight}px`);
+ }
+
+ hide(): void {
+ if (this._isTransitioning || this.collapsed) {
+ return;
+ }
+
+ this.collapseHide.emit(this);
+
+ const hostHeight = this.host.getBoundingClientRect().height;
+
+ this._renderer.setStyle(this.host, 'height', `${hostHeight}px`);
+
+ this._reflow(this.host);
+
+ this._renderer.addClass(this.host, 'collapsing');
+ this._renderer.removeClass(this.host, 'collapse');
+ this._renderer.removeClass(this.host, 'show');
+
+ this._isTransitioning = true;
+
+ fromEvent(this.host, 'transitionend')
+ .pipe(take(1))
+ .subscribe(() => {
+ this._renderer.removeClass(this.host, 'collapsing');
+ this._renderer.addClass(this.host, 'collapse');
+ this._isTransitioning = false;
+ this.collapsed = true;
+
+ this.collapseHidden.emit(this);
+ });
+
+ this._renderer.removeStyle(this.host, 'height');
+ this._emulateTransitionEnd(this.host, TRANSITION_TIME);
+ }
+
+ toggle(): void {
+ if (this._isTransitioning) {
+ return;
+ }
+
+ this.collapsed = !this.collapsed;
+ this.collapsed ? this.hide() : this.show();
+ }
+
+ private _reflow(element: HTMLElement): number {
+ return element.offsetHeight;
+ }
+
+ private _emulateTransitionEnd(element: HTMLElement, duration: number): void {
+ let eventEmitted = false;
+ const durationPadding = 5;
+ const emulatedDuration = duration + durationPadding;
+
+ fromEvent(element, 'transitionend')
+ .pipe(take(1))
+ .subscribe(() => {
+ eventEmitted = true;
+ });
+
+ setTimeout(() => {
+ if (!eventEmitted) {
+ element.dispatchEvent(new Event('transitionend'));
+ }
+ }, emulatedDuration);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/collapse/collapse.module.ts b/projects/mdb-angular-ui-kit/collapse/collapse.module.ts
new file mode 100755
index 00000000..e5e5b1c9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/collapse/collapse.module.ts
@@ -0,0 +1,8 @@
+import { MdbCollapseDirective } from './collapse.directive';
+import { NgModule } from '@angular/core';
+
+@NgModule({
+ declarations: [MdbCollapseDirective],
+ exports: [MdbCollapseDirective],
+})
+export class MdbCollapseModule {}
diff --git a/projects/mdb-angular-ui-kit/collapse/index.ts b/projects/mdb-angular-ui-kit/collapse/index.ts
new file mode 100755
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/collapse/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/collapse/ng-package.json b/projects/mdb-angular-ui-kit/collapse/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/collapse/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/collapse/public_api.ts b/projects/mdb-angular-ui-kit/collapse/public_api.ts
new file mode 100644
index 00000000..a9c97388
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/collapse/public_api.ts
@@ -0,0 +1,2 @@
+export { MdbCollapseDirective } from './collapse.directive';
+export { MdbCollapseModule } from './collapse.module';
diff --git a/projects/mdb-angular-ui-kit/dropdown/dropdown-menu.directive.ts b/projects/mdb-angular-ui-kit/dropdown/dropdown-menu.directive.ts
new file mode 100644
index 00000000..7ee58e7a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/dropdown-menu.directive.ts
@@ -0,0 +1,53 @@
+import { Directive, ElementRef, EventEmitter, Input, Output, Renderer2 } from '@angular/core';
+
+export type MdbDropdownMenuPositionClass = 'dropdown-menu-end';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbDropdownMenu]',
+ exportAs: 'mdbDropdownMenu',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbDropdownMenuDirective {
+ constructor(public elementRef: ElementRef, private _renderer: Renderer2) {}
+ @Output() menuPositionClassChanged: EventEmitter = new EventEmitter();
+
+ @Input()
+ get menuPositionClass(): string {
+ return this._menuPositionClass;
+ }
+
+ set menuPositionClass(newClass: string) {
+ const host = this.elementRef.nativeElement;
+ const isSameClass = host.classList.contains(newClass);
+ if (this._menuPositionClass !== newClass && !isSameClass) {
+ const menuPositionClasses = [
+ 'dropdown-menu-start',
+ 'dropdown-menu-sm-start',
+ 'dropdown-menu-md-start',
+ 'dropdown-menu-lg-start',
+ 'dropdown-menu-xl-start',
+ 'dropdown-menu-xxl-start',
+ 'dropdown-menu-xxl-start',
+ 'dropdown-menu-xxl-start',
+ 'dropdown-menu-end',
+ 'dropdown-menu-sm-end',
+ 'dropdown-menu-md-end',
+ 'dropdown-menu-lg-end',
+ 'dropdown-menu-xl-end',
+ 'dropdown-menu-xxl-end',
+ 'dropdown-menu-xxl-end',
+ 'dropdown-menu-xxl-end',
+ ];
+
+ menuPositionClasses.forEach((className) => {
+ this._renderer.removeClass(host, className);
+ });
+ this._renderer.addClass(host, newClass);
+
+ this.menuPositionClassChanged.emit(this.menuPositionClass);
+ }
+ }
+ private _menuPositionClass: string;
+}
diff --git a/projects/mdb-angular-ui-kit/dropdown/dropdown-toggle.directive.ts b/projects/mdb-angular-ui-kit/dropdown/dropdown-toggle.directive.ts
new file mode 100644
index 00000000..99b17219
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/dropdown-toggle.directive.ts
@@ -0,0 +1,10 @@
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[mdbDropdownToggle]',
+ exportAs: 'mdbDropdownToggle',
+ standalone: false,
+})
+export class MdbDropdownToggleDirective {
+ constructor() {}
+}
diff --git a/projects/mdb-angular-ui-kit/dropdown/dropdown.component.html b/projects/mdb-angular-ui-kit/dropdown/dropdown.component.html
new file mode 100644
index 00000000..1d270978
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/dropdown.component.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/projects/mdb-angular-ui-kit/dropdown/dropdown.directive.spec.ts b/projects/mdb-angular-ui-kit/dropdown/dropdown.directive.spec.ts
new file mode 100644
index 00000000..64a97517
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/dropdown.directive.spec.ts
@@ -0,0 +1,371 @@
+import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick } from '@angular/core/testing';
+import { Component } from '@angular/core';
+import { MdbDropdownMenuDirective, MdbDropdownModule } from './index';
+import { MdbDropdownDirective } from './index';
+import { By } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { OverlayContainer } from '@angular/cdk/overlay';
+import { first } from 'rxjs';
+
+describe('MDB Dropdown', () => {
+ let fixture: ComponentFixture;
+ let testComponent: TestDropdownComponent;
+ let directive: MdbDropdownDirective;
+ let overlayContainer: OverlayContainer;
+ let overlayContainerElement: HTMLElement;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [MdbDropdownModule, NoopAnimationsModule],
+ declarations: [TestDropdownComponent],
+ teardown: { destroyAfterEach: false },
+ });
+
+ inject([OverlayContainer], (container: OverlayContainer) => {
+ overlayContainer = container;
+ overlayContainerElement = container.getContainerElement();
+ })();
+
+ fixture = TestBed.createComponent(TestDropdownComponent);
+ testComponent = fixture.componentInstance;
+ directive = fixture.debugElement
+ .query(By.directive(MdbDropdownDirective))
+ .injector.get(MdbDropdownDirective);
+ fixture.detectChanges();
+ });
+
+ afterEach(inject([OverlayContainer], (currentOverlayContainer: OverlayContainer) => {
+ currentOverlayContainer.ngOnDestroy();
+ overlayContainer.ngOnDestroy();
+ }));
+
+ describe('Opening and closing', () => {
+ it('should open and close dropdown on click', fakeAsync(() => {
+ jest.spyOn(directive, 'show');
+ jest.spyOn(directive, 'hide');
+
+ const buttonEl: HTMLButtonElement = fixture.nativeElement.querySelector('.dropdown-toggle');
+
+ buttonEl.click();
+ fixture.detectChanges();
+
+ expect(directive.show).toHaveBeenCalled();
+ expect(overlayContainerElement.textContent).toContain('Action');
+
+ buttonEl.click();
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(directive.hide).toHaveBeenCalled();
+ expect(overlayContainerElement.textContent).toEqual('');
+ }));
+
+ it('should close dropdown on outside click', fakeAsync(() => {
+ directive.show();
+ fixture.detectChanges();
+
+ document.body.click();
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toEqual('');
+ }));
+
+ it('should close dropdown on dropdown item click', fakeAsync(() => {
+ directive.show();
+ fixture.detectChanges();
+
+ const item: HTMLElement = document.querySelector('.dropdown-item');
+
+ item.click();
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toEqual('');
+ }));
+ });
+
+ describe('Accessibility', () => {
+ it('should update aria-expanded attribute on dropdown open and close', fakeAsync(() => {
+ const buttonEl: HTMLButtonElement = fixture.nativeElement.querySelector('.dropdown-toggle');
+
+ buttonEl.click();
+ fixture.detectChanges();
+
+ expect(buttonEl.getAttribute('aria-expanded')).toBe('true');
+
+ buttonEl.click();
+ fixture.detectChanges();
+ flush();
+
+ expect(buttonEl.getAttribute('aria-expanded')).toContain('false');
+ }));
+ });
+
+ describe('Keyboard navigation', () => {
+ it('should correctly focus dropdown items when ArrowUp or ArrowDown key is used', () => {
+ directive.show();
+ fixture.detectChanges();
+
+ const menu = document.querySelector('.dropdown-menu');
+ const items = menu.querySelectorAll('.dropdown-item');
+
+ document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
+ fixture.detectChanges();
+
+ expect(document.activeElement).toBe(items[0]);
+
+ document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
+ fixture.detectChanges();
+
+ expect(document.activeElement).toBe(items[1]);
+
+ document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
+ fixture.detectChanges();
+
+ expect(document.activeElement).toBe(items[0]);
+ });
+
+ it('should focus last option if ArrowUp is used and no item is selected', () => {
+ directive.show();
+ fixture.detectChanges();
+
+ const menu = document.querySelector('.dropdown-menu');
+ const items = menu.querySelectorAll('.dropdown-item');
+
+ document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
+ fixture.detectChanges();
+
+ expect(document.activeElement).toBe(items[items.length - 1]);
+ });
+
+ it('should close dropdown on ESC keyup', fakeAsync(() => {
+ directive.show();
+ fixture.detectChanges();
+
+ document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toEqual('');
+ }));
+ });
+
+ describe('Inputs', () => {
+ it('should not close dropdown on ESC keyup if closeOnEsc input is set to false', fakeAsync(() => {
+ testComponent.closeOnEsc = false;
+ directive.show();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+
+ document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+ }));
+
+ it('should not close dropdown on dropdown item click if closeOnItemClick input is set to false', fakeAsync(() => {
+ testComponent.closeOnItemClick = false;
+ directive.show();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+
+ const item: HTMLElement = document.querySelector('.dropdown-item');
+
+ item.click();
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+ }));
+
+ it('should not close dropdown on outside click if closeOnOutsideClick input is set to false', fakeAsync(() => {
+ testComponent.closeOnOutsideClick = false;
+ directive.show();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+
+ document.body.click();
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+ }));
+
+ it('should apply appropriate transform style when offset input is set', () => {
+ testComponent.offset = 43;
+ fixture.detectChanges();
+
+ directive.show();
+ fixture.detectChanges();
+
+ let overlayPane: HTMLDivElement = overlayContainerElement.querySelector('.cdk-overlay-pane');
+ expect(overlayPane.style.transform).toBe('translateY(43px)');
+ });
+
+ it('should apply appropriate class when positionClass input is set', () => {
+ expect(directive.host.classList).toContain('dropdown');
+ expect(directive.host.classList).not.toContain('dropup');
+
+ directive.positionClass = 'dropup';
+
+ expect(directive.host.classList).not.toContain('dropdown');
+ expect(directive.host.classList).toContain('dropup');
+ });
+
+ it('should apply appropriate class when menuPositionClass input is set', () => {
+ const dropdownMenuDirective: MdbDropdownMenuDirective = directive._dropdownMenu;
+ expect(dropdownMenuDirective.elementRef.nativeElement.classList).toContain(
+ 'dropdown-menu-start'
+ );
+ expect(dropdownMenuDirective.elementRef.nativeElement.classList).not.toContain(
+ 'dropdown-menu-end'
+ );
+
+ dropdownMenuDirective.menuPositionClass = 'dropdown-menu-end';
+
+ expect(dropdownMenuDirective.elementRef.nativeElement.classList).not.toContain(
+ 'dropdown-menu-start'
+ );
+ expect(dropdownMenuDirective.elementRef.nativeElement.classList).toContain(
+ 'dropdown-menu-end'
+ );
+ });
+ });
+
+ describe('Outputs', () => {
+ it('should emit events on show and hide', fakeAsync(() => {
+ let showDropdown: MdbDropdownDirective | undefined;
+ let shownDropdown: MdbDropdownDirective | undefined;
+ let hideDropdown: MdbDropdownDirective | undefined;
+ let hiddenDropdown: MdbDropdownDirective | undefined;
+
+ const showSpy = jest.spyOn(directive.dropdownShow, 'emit');
+ const shownSpy = jest.spyOn(directive.dropdownShown, 'emit');
+ const hideSpy = jest.spyOn(directive.dropdownHide, 'emit');
+ const hiddenSpy = jest.spyOn(directive.dropdownHidden, 'emit');
+
+ directive.dropdownShow.pipe(first()).subscribe((event) => (showDropdown = event));
+ directive.dropdownShown.pipe(first()).subscribe((event) => (shownDropdown = event));
+ directive.dropdownHide.pipe(first()).subscribe((event) => (hideDropdown = event));
+ directive.dropdownHidden.pipe(first()).subscribe((event) => (hiddenDropdown = event));
+
+ directive.show();
+ fixture.detectChanges();
+
+ expect(showSpy).toHaveBeenCalledTimes(1);
+ expect(showDropdown).toEqual(directive);
+
+ tick();
+
+ expect(shownSpy).toHaveBeenCalledTimes(1);
+ expect(shownDropdown).toEqual(directive);
+
+ directive.hide();
+ fixture.detectChanges();
+
+ expect(hideSpy).toHaveBeenCalledTimes(1);
+ expect(hideDropdown).toEqual(directive);
+
+ tick();
+
+ expect(hiddenSpy).toHaveBeenCalledTimes(1);
+ expect(hiddenDropdown).toEqual(directive);
+ }));
+ });
+
+ describe('Public methods', () => {
+ it('should show dropdown when show method is called', fakeAsync(() => {
+ expect(overlayContainerElement.textContent).not.toContain('Action');
+
+ directive.show();
+ fixture.detectChanges();
+ flush();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+ }));
+
+ it('should hide dropdown when hide method is called', fakeAsync(() => {
+ expect(overlayContainerElement.textContent).not.toContain('Action');
+
+ directive.show();
+ fixture.detectChanges();
+ flush();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+
+ directive.hide();
+ fixture.detectChanges();
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toEqual('');
+ }));
+
+ it('should toggle dropdown when toggle method is called', fakeAsync(() => {
+ expect(overlayContainerElement.textContent).not.toContain('Action');
+
+ directive.toggle();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toContain('Action');
+
+ directive.toggle();
+ fixture.detectChanges();
+ flush();
+ fixture.detectChanges();
+
+ expect(overlayContainerElement.textContent).toEqual('');
+ }));
+ });
+});
+
+@Component({
+ selector: 'mdb-dropdown-test',
+ template: `
+
+ Dropdown button
+
+
+ `,
+ standalone: false,
+})
+class TestDropdownComponent {
+ closeOnOutsideClick = true;
+ closeOnItemClick = true;
+ closeOnEsc = true;
+ offset = 0;
+}
diff --git a/projects/mdb-angular-ui-kit/dropdown/dropdown.directive.ts b/projects/mdb-angular-ui-kit/dropdown/dropdown.directive.ts
new file mode 100644
index 00000000..77c082e9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/dropdown.directive.ts
@@ -0,0 +1,462 @@
+import {
+ AfterContentInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ EventEmitter,
+ Input,
+ OnDestroy,
+ Output,
+ Renderer2,
+ TemplateRef,
+ ViewChild,
+ ViewContainerRef,
+ booleanAttribute,
+ numberAttribute,
+} from '@angular/core';
+import {
+ ConnectedPosition,
+ FlexibleConnectedPositionStrategy,
+ Overlay,
+ OverlayConfig,
+ OverlayPositionBuilder,
+ OverlayRef,
+} from '@angular/cdk/overlay';
+import { TemplatePortal } from '@angular/cdk/portal';
+import { fromEvent, Observable, Subject } from 'rxjs';
+import { filter, takeUntil } from 'rxjs/operators';
+import { ContentChild } from '@angular/core';
+import { MdbDropdownToggleDirective } from './dropdown-toggle.directive';
+import { MdbDropdownMenuDirective } from './dropdown-menu.directive';
+import { animate, state, style, transition, trigger, AnimationEvent } from '@angular/animations';
+import { BreakpointObserver } from '@angular/cdk/layout';
+
+export type MdbDropdownPositionClass = 'dropdown' | 'dropup' | 'dropstart' | 'dropend';
+
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: '[mdbDropdown]',
+ templateUrl: 'dropdown.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ animations: [
+ trigger('fade', [
+ state('visible', style({ opacity: 1 })),
+ state('hidden', style({ opacity: 0 })),
+ transition('visible => hidden', animate('150ms linear')),
+ transition('hidden => visible', [style({ opacity: 0 }), animate('150ms linear')]),
+ ]),
+ ],
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbDropdownDirective implements OnDestroy, AfterContentInit {
+ @ViewChild('dropdownTemplate') _template: TemplateRef;
+ @ContentChild(MdbDropdownToggleDirective, { read: ElementRef }) _dropdownToggle: ElementRef;
+ @ContentChild(MdbDropdownMenuDirective) _dropdownMenu: MdbDropdownMenuDirective;
+
+ @Input({ transform: booleanAttribute }) animation = true;
+ @Input({ transform: booleanAttribute }) closeOnEsc = true;
+ @Input({ transform: booleanAttribute }) closeOnItemClick = true;
+ @Input({ transform: booleanAttribute }) closeOnOutsideClick = true;
+ @Input({ transform: numberAttribute }) offset = 0;
+ @Input()
+ get positionClass(): MdbDropdownPositionClass {
+ return this._positionClass;
+ }
+ set positionClass(newClass: MdbDropdownPositionClass) {
+ const isSameClass = this.host.classList.contains(newClass);
+ if (this._positionClass !== newClass && !isSameClass) {
+ const positionClasses = ['dropdown', 'dropup', 'dropstart', 'dropend'];
+ positionClasses.forEach((className) => {
+ this._renderer.removeClass(this.host, className);
+ });
+ this._renderer.addClass(this.host, newClass);
+ }
+ this._updateOverlay();
+ }
+ private _positionClass: MdbDropdownPositionClass;
+ @Input({ transform: booleanAttribute }) withPush = false;
+
+ @Output() dropdownShow: EventEmitter = new EventEmitter();
+ @Output() dropdownShown: EventEmitter = new EventEmitter();
+ @Output() dropdownHide: EventEmitter = new EventEmitter();
+ @Output() dropdownHidden: EventEmitter = new EventEmitter();
+
+ private _overlayRef: OverlayRef;
+ private _portal: TemplatePortal;
+ private _open = false;
+ private _isDropUp: boolean;
+ private _isDropStart: boolean;
+ private _isDropEnd: boolean;
+ private _isDropdownMenuEnd: boolean;
+ private _xPosition: string;
+ private _breakpoints: any;
+ private _mousedownTarget: HTMLElement | null = null;
+
+ readonly _destroy$: Subject = new Subject();
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ _breakpointSubscription: any;
+ _animationState = 'hidden';
+
+ constructor(
+ private _overlay: Overlay,
+ private _overlayPositionBuilder: OverlayPositionBuilder,
+ private _elementRef: ElementRef,
+ private _vcr: ViewContainerRef,
+ private _breakpointObserver: BreakpointObserver,
+ private _cdRef: ChangeDetectorRef,
+ private _renderer: Renderer2
+ ) {
+ this._breakpoints = {
+ isSm: this._breakpointObserver.isMatched('(min-width: 576px)'),
+ isMd: this._breakpointObserver.isMatched('(min-width: 768px)'),
+ isLg: this._breakpointObserver.isMatched('(min-width: 992px)'),
+ isXl: this._breakpointObserver.isMatched('(min-width: 1200px)'),
+ isXxl: this._breakpointObserver.isMatched('(min-width: 1400px)'),
+ };
+ }
+
+ ngAfterContentInit(): void {
+ this._bindDropdownToggleClick();
+ this._listenToMenuPositionClassChange();
+ }
+
+ ngOnDestroy(): void {
+ if (this._overlayRef) {
+ this._overlayRef.detach();
+ this._overlayRef.dispose();
+ }
+
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _bindDropdownToggleClick(): void {
+ fromEvent(this._dropdownToggle.nativeElement, 'click')
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.toggle());
+ }
+
+ private _listenToMenuPositionClassChange(): void {
+ this._dropdownMenu.menuPositionClassChanged
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this._updateOverlay());
+ }
+
+ private _updateOverlay() {
+ this._overlayRef?.updatePositionStrategy(this._createPositionStrategy());
+ }
+
+ private _createOverlayConfig(): OverlayConfig {
+ return new OverlayConfig({
+ hasBackdrop: false,
+ scrollStrategy: this._overlay.scrollStrategies.reposition(),
+ positionStrategy: this._createPositionStrategy(),
+ });
+ }
+
+ private _createOverlay(): void {
+ this._overlayRef = this._overlay.create(this._createOverlayConfig());
+ }
+
+ private _createPositionStrategy(): FlexibleConnectedPositionStrategy {
+ const positionStrategy = this._overlayPositionBuilder
+ .flexibleConnectedTo(this._dropdownToggle)
+ .withPositions(this._getPosition())
+ .withFlexibleDimensions(false)
+ .withPush(this.withPush);
+
+ return positionStrategy;
+ }
+
+ private _getPosition(): ConnectedPosition[] {
+ this._isDropUp = this.host.classList.contains('dropup');
+ this._isDropStart = this.host.classList.contains('dropstart');
+ this._isDropEnd = this.host.classList.contains('dropend');
+ this._isDropdownMenuEnd =
+ this._dropdownMenu.elementRef.nativeElement.classList.contains('dropdown-menu-end');
+ this._xPosition = this._isDropdownMenuEnd ? 'end' : 'start';
+
+ const regex = new RegExp(/dropdown-menu-(sm|md|lg|xl|xxl)-(start|end)/, 'g');
+
+ const responsiveClass = this._dropdownMenu.elementRef.nativeElement.className.match(regex);
+
+ if (responsiveClass) {
+ this._subscribeBrakpoints();
+
+ const positionRegex = new RegExp(/start|end/, 'g');
+ const breakpointRegex = new RegExp(/(sm|md|lg|xl|xxl)/, 'g');
+
+ const dropdownPosition = positionRegex.exec(responsiveClass)[0];
+ const breakpoint = breakpointRegex.exec(responsiveClass)[0];
+
+ switch (true) {
+ case breakpoint === 'xxl' && this._breakpoints.isXxl:
+ this._xPosition = dropdownPosition;
+ break;
+ case breakpoint === 'xl' && this._breakpoints.isXl:
+ this._xPosition = dropdownPosition;
+ break;
+ case breakpoint === 'lg' && this._breakpoints.isLg:
+ this._xPosition = dropdownPosition;
+ break;
+ case breakpoint === 'md' && this._breakpoints.isMd:
+ this._xPosition = dropdownPosition;
+ break;
+ case breakpoint === 'sm' && this._breakpoints.isSm:
+ this._xPosition = dropdownPosition;
+ break;
+ default:
+ break;
+ }
+ }
+
+ let position;
+
+ const positionDropup = {
+ originX: this._xPosition,
+ originY: 'top',
+ overlayX: this._xPosition,
+ overlayY: 'bottom',
+ offsetY: -this.offset,
+ };
+
+ const positionDropdown = {
+ originX: this._xPosition,
+ originY: 'bottom',
+ overlayX: this._xPosition,
+ overlayY: 'top',
+ offsetY: this.offset,
+ };
+
+ const positionDropstart = {
+ originX: 'start',
+ originY: 'top',
+ overlayX: 'end',
+ overlayY: 'top',
+ offsetX: this.offset,
+ };
+
+ const positionDropend = {
+ originX: 'end',
+ originY: 'top',
+ overlayX: 'start',
+ overlayY: 'top',
+ offsetX: -this.offset,
+ };
+
+ switch (true) {
+ case this._isDropEnd:
+ position = [positionDropend, positionDropstart];
+ break;
+ case this._isDropStart:
+ position = [positionDropstart, positionDropend];
+ break;
+ case this._isDropUp:
+ position = [positionDropup, positionDropdown];
+ break;
+ default:
+ position = [positionDropdown, positionDropup];
+ break;
+ }
+
+ return position;
+ }
+
+ private _listenToEscKeyup(overlayRef: OverlayRef): Observable {
+ return fromEvent(document, 'keyup').pipe(
+ filter((event: KeyboardEvent) => event.key === 'Escape'),
+ takeUntil(overlayRef.detachments())
+ );
+ }
+
+ private _listenToMousedown(overlayRef: OverlayRef): Observable {
+ return fromEvent(document, 'mousedown').pipe(takeUntil(overlayRef.detachments()));
+ }
+
+ private _listenToClick(overlayRef: OverlayRef, origin: HTMLElement): Observable {
+ return fromEvent(document, 'click').pipe(
+ filter((event: MouseEvent) => {
+ const target = event.target as HTMLElement;
+ const isInsideMenu = this._dropdownMenu.elementRef.nativeElement.contains(target);
+ const notTogglerIcon = !this._dropdownToggle.nativeElement.contains(target);
+ const notCustomContent =
+ !isInsideMenu || (target.classList && target.classList.contains('dropdown-item'));
+ const notOrigin = target !== origin;
+ return notOrigin && notTogglerIcon && notCustomContent;
+ }),
+ takeUntil(overlayRef.detachments())
+ );
+ }
+
+ public onAnimationEnd(event: AnimationEvent): void {
+ if (event.fromState === 'visible' && event.toState === 'hidden') {
+ this._overlayRef.detach();
+ this._open = false;
+
+ this._renderer.setAttribute(this._dropdownToggle.nativeElement, 'aria-expanded', 'false');
+
+ this.dropdownHidden.emit(this);
+ }
+
+ if (event.fromState === 'hidden' && event.toState === 'visible') {
+ this.dropdownShown.emit(this);
+ }
+ }
+
+ private _subscribeBrakpoints(): void {
+ const brakpoints = [
+ '(min-width: 576px)',
+ '(min-width: 768px)',
+ '(min-width: 992px)',
+ '(min-width: 1200px)',
+ '(min-width: 1400px)',
+ ];
+
+ this._breakpointSubscription = this._breakpointObserver
+ .observe(brakpoints)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe((result) => {
+ Object.keys(this._breakpoints).forEach((key, index) => {
+ const brakpointValue = brakpoints[index];
+ const newBreakpoint = result.breakpoints[brakpointValue];
+ const isBreakpointChanged = newBreakpoint !== this._breakpoints[key];
+
+ if (!isBreakpointChanged) {
+ return;
+ }
+
+ this._breakpoints[key] = newBreakpoint;
+
+ if (this._open) {
+ this._updateOverlay();
+ }
+ });
+ });
+ }
+
+ show(): void {
+ this._cdRef.markForCheck();
+
+ if (this._open) {
+ return;
+ }
+
+ if (!this._overlayRef) {
+ this._createOverlay();
+ }
+
+ this._portal = new TemplatePortal(this._template, this._vcr);
+
+ this.dropdownShow.emit(this);
+
+ this._open = true;
+
+ this._renderer.setAttribute(this._dropdownToggle.nativeElement, 'aria-expanded', 'true');
+
+ this._overlayRef.attach(this._portal);
+
+ this._listenToEscKeyup(this._overlayRef).subscribe((isEsc) => {
+ if (isEsc && this.closeOnEsc) {
+ this.hide();
+ }
+ });
+
+ this._overlayRef
+ .keydownEvents()
+ .pipe(takeUntil(this._overlayRef.detachments()))
+ .subscribe((event: KeyboardEvent) => {
+ this._handleKeyboardNavigation(event);
+ });
+
+ this._listenToMousedown(this._overlayRef).subscribe((event) => {
+ this._mousedownTarget = event.target as HTMLElement;
+ });
+
+ this._listenToClick(this._overlayRef, this._dropdownToggle.nativeElement).subscribe((event) => {
+ const target = event.target as HTMLElement;
+ const isDropdownItem = target.classList && target.classList.contains('dropdown-item');
+
+ const isOnMousedownDropdownMenu = this._dropdownMenu.elementRef.nativeElement.contains(
+ this._mousedownTarget
+ );
+
+ this._mousedownTarget = null;
+
+ if (this.closeOnItemClick && isDropdownItem) {
+ this.hide();
+ return;
+ }
+ if (this.closeOnOutsideClick && !isDropdownItem && !isOnMousedownDropdownMenu) {
+ this.hide();
+ return;
+ }
+ });
+
+ this._animationState = 'visible';
+ }
+
+ private _handleKeyboardNavigation(event: KeyboardEvent) {
+ const items: HTMLElement[] = Array.from(
+ this._dropdownMenu.elementRef.nativeElement.querySelectorAll('.dropdown-item')
+ );
+ const key = event.key;
+ const activeElement = this._dropdownMenu.elementRef.nativeElement.ownerDocument.activeElement;
+
+ if (items.length === 0) {
+ return;
+ }
+
+ let index = items.indexOf(activeElement);
+
+ switch (key) {
+ case 'ArrowDown':
+ event.preventDefault();
+
+ index = Math.min(index + 1, items.length - 1);
+ break;
+ case 'ArrowUp':
+ event.preventDefault();
+
+ if (index === -1) {
+ index = items.length - 1;
+ break;
+ }
+ index = Math.max(index - 1, 0);
+ break;
+ }
+
+ const nextActiveElement: HTMLElement = items[index];
+
+ if (nextActiveElement) {
+ nextActiveElement.focus();
+ }
+ }
+
+ hide(): void {
+ this._cdRef.markForCheck();
+
+ if (!this._open) {
+ return;
+ }
+
+ this.dropdownHide.emit(this);
+
+ this._animationState = 'hidden';
+ }
+
+ toggle(): void {
+ this._cdRef.markForCheck();
+
+ if (this._open) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/dropdown/dropdown.module.ts b/projects/mdb-angular-ui-kit/dropdown/dropdown.module.ts
new file mode 100644
index 00000000..75211ac9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/dropdown.module.ts
@@ -0,0 +1,12 @@
+import { MdbDropdownDirective } from './dropdown.directive';
+import { MdbDropdownToggleDirective } from './dropdown-toggle.directive';
+import { MdbDropdownMenuDirective } from './dropdown-menu.directive';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { OverlayModule } from '@angular/cdk/overlay';
+@NgModule({
+ imports: [CommonModule, OverlayModule],
+ declarations: [MdbDropdownDirective, MdbDropdownToggleDirective, MdbDropdownMenuDirective],
+ exports: [MdbDropdownDirective, MdbDropdownToggleDirective, MdbDropdownMenuDirective],
+})
+export class MdbDropdownModule {}
diff --git a/projects/mdb-angular-ui-kit/dropdown/index.ts b/projects/mdb-angular-ui-kit/dropdown/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/dropdown/ng-package.json b/projects/mdb-angular-ui-kit/dropdown/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/dropdown/public_api.ts b/projects/mdb-angular-ui-kit/dropdown/public_api.ts
new file mode 100644
index 00000000..2677a6fd
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/dropdown/public_api.ts
@@ -0,0 +1,4 @@
+export { MdbDropdownDirective, MdbDropdownPositionClass } from './dropdown.directive';
+export { MdbDropdownToggleDirective } from './dropdown-toggle.directive';
+export { MdbDropdownMenuDirective } from './dropdown-menu.directive';
+export { MdbDropdownModule } from './dropdown.module';
diff --git a/projects/mdb-angular-ui-kit/forms/form-control.component.html b/projects/mdb-angular-ui-kit/forms/form-control.component.html
new file mode 100644
index 00000000..81f48f48
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/form-control.component.html
@@ -0,0 +1,6 @@
+
+
diff --git a/projects/mdb-angular-ui-kit/forms/form-control.component.ts b/projects/mdb-angular-ui-kit/forms/form-control.component.ts
new file mode 100644
index 00000000..f81ef2aa
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/form-control.component.ts
@@ -0,0 +1,149 @@
+import {
+ Component,
+ ChangeDetectionStrategy,
+ HostBinding,
+ ViewChild,
+ ContentChild,
+ ElementRef,
+ AfterContentInit,
+ Renderer2,
+ OnDestroy,
+ NgZone,
+ AfterContentChecked,
+} from '@angular/core';
+import { MdbAbstractFormControl } from './form-control';
+import { MdbLabelDirective } from './label.directive';
+import { ContentObserver } from '@angular/cdk/observers';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+@Component({
+ selector: 'mdb-form-control',
+ templateUrl: './form-control.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: false,
+})
+export class MdbFormControlComponent implements AfterContentInit, AfterContentChecked, OnDestroy {
+ @ViewChild('notchLeading', { static: true }) _notchLeading: ElementRef;
+ @ViewChild('notchMiddle', { static: true }) _notchMiddle: ElementRef;
+ @ContentChild(MdbAbstractFormControl, { static: true }) _formControl: MdbAbstractFormControl;
+ @ContentChild(MdbLabelDirective, { static: false, read: ElementRef }) _label: ElementRef;
+
+ @HostBinding('class.form-outline') outline = true;
+ @HostBinding('class.d-block') display = true;
+
+ get input(): HTMLInputElement {
+ return this._formControl.input;
+ }
+
+ constructor(
+ private _renderer: Renderer2,
+ private _contentObserver: ContentObserver,
+ private _elementRef: ElementRef,
+ private _ngZone: NgZone
+ ) {}
+
+ readonly _destroy$: Subject = new Subject();
+
+ private _notchLeadingLength = 9;
+ private _labelMarginLeft = 0;
+ private _labelGapPadding = 8;
+ private _labelScale = 0.8;
+ private _recalculateGapWhenVisible = false;
+
+ private _previousLabel: ElementRef | null = null;
+
+ ngAfterContentInit(): void {
+ if (this.hasLabel) {
+ setTimeout(() => {
+ this._updateBorderGap();
+ }, 0);
+ this._previousLabel = this._label;
+ } else {
+ this._renderer.addClass(this.input, 'placeholder-active');
+ }
+ this._updateLabelActiveState();
+
+ if (this.hasLabel) {
+ this._contentObserver
+ .observe(this._label.nativeElement)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => {
+ this._updateBorderGap();
+ });
+ }
+
+ this._formControl.stateChanges.pipe(takeUntil(this._destroy$)).subscribe(() => {
+ this._updateLabelActiveState();
+ if (this.hasLabel) {
+ this._updateBorderGap();
+ }
+ });
+
+ this._ngZone.runOutsideAngular(() => {
+ this._ngZone.onStable.pipe(takeUntil(this._destroy$)).subscribe(() => {
+ if (this.hasLabel && this._recalculateGapWhenVisible) {
+ this._updateBorderGap();
+ }
+ });
+ });
+ }
+
+ ngAfterContentChecked(): void {
+ if (!this._previousLabel && this.hasLabel) {
+ setTimeout(() => this._updateBorderGap());
+ }
+ this._previousLabel = this._label;
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.unsubscribe();
+ }
+
+ get hasLabel(): boolean {
+ return !!this._label;
+ }
+
+ private _getLabelWidth(): number {
+ return this._label.nativeElement.clientWidth * this._labelScale + this._labelGapPadding;
+ }
+
+ private _updateBorderGap(): void {
+ // Element is in DOM but is not visible, we need to recalculate the gap when element
+ // is displayed. This problem may occur in components such as tabs where content of
+ // inactive tabs has display:none styles
+
+ if (this._isHidden()) {
+ this._recalculateGapWhenVisible = true;
+ return;
+ }
+
+ const notchLeadingWidth = `${this._labelMarginLeft + this._notchLeadingLength}px`;
+ const notchMiddleWidth = `${this._getLabelWidth()}px`;
+
+ this._notchLeading.nativeElement.style.width = notchLeadingWidth;
+ this._notchMiddle.nativeElement.style.width = notchMiddleWidth;
+ this._label.nativeElement.style.marginLeft = `${this._labelMarginLeft}px`;
+
+ this._recalculateGapWhenVisible = false;
+ }
+
+ private _updateLabelActiveState(): void {
+ if (this._isLabelActive()) {
+ this._renderer.addClass(this.input, 'active');
+ } else {
+ this._renderer.removeClass(this.input, 'active');
+ }
+ }
+
+ private _isLabelActive(): boolean {
+ return this._formControl && this._formControl.labelActive;
+ }
+
+ private _isHidden(): boolean {
+ const el = this._elementRef.nativeElement;
+
+ return !el.offsetHeight && !el.offsetWidth;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/forms/form-control.spec.ts b/projects/mdb-angular-ui-kit/forms/form-control.spec.ts
new file mode 100644
index 00000000..5ea204d3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/form-control.spec.ts
@@ -0,0 +1,125 @@
+import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing';
+import { Component, DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { MdbFormsModule } from './index';
+import { MdbFormControlComponent } from './form-control.component';
+
+describe('MDB Form Control', () => {
+ let fixture: ComponentFixture;
+ let wrapper: DebugElement;
+ let input: DebugElement;
+ const labelGapPadding = 8;
+ const labelScale = 0.8;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [BasicFormControlComponent, WithoutLabelComponent, DynamicLabelComponent],
+ imports: [MdbFormsModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(BasicFormControlComponent);
+ fixture.detectChanges();
+ wrapper = fixture.debugElement.query(By.directive(MdbFormControlComponent));
+ input = fixture.debugElement.query(By.css('input'));
+ });
+
+ beforeAll(() => {
+ Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 20 });
+ Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 20 });
+ Object.defineProperty(HTMLElement.prototype, 'clientWidth', { configurable: true, value: 20 });
+ });
+
+ it('should add outline class to the wrapper element', () => {
+ fixture.detectChanges();
+ expect(wrapper.nativeElement.classList.contains('form-outline')).toBe(true);
+ });
+
+ it('should toggle input active class on value change', () => {
+ input.nativeElement.value = 'Test';
+ fixture.detectChanges();
+ expect(input.nativeElement.classList.contains('active')).toBe(true);
+ });
+
+ it('should set placeholder-active class on input if label is not defined', () => {
+ const fixture = TestBed.createComponent(WithoutLabelComponent);
+ fixture.detectChanges();
+
+ const input = fixture.nativeElement.querySelector('input');
+
+ expect(input.classList).toContain('placeholder-active');
+ });
+
+ it('should set top border gap on component init if label is defined', fakeAsync(() => {
+ const fixture = TestBed.createComponent(BasicFormControlComponent);
+ fixture.detectChanges();
+
+ flush();
+ fixture.detectChanges();
+ const labelWidth = fixture.nativeElement.querySelector('label').clientWidth;
+ const middleNotch = fixture.nativeElement.querySelector('.form-notch-middle');
+ const expectedBorderGap = labelWidth * labelScale + labelGapPadding + 'px';
+
+ expect(middleNotch.style.width).toEqual(expectedBorderGap);
+ }));
+
+ it('should update border gap when label is dynamically rendered with *ngIf', fakeAsync(() => {
+ const fixture = TestBed.createComponent(DynamicLabelComponent);
+ fixture.detectChanges();
+
+ let label = fixture.nativeElement.querySelector('label');
+ let middleNotch = fixture.nativeElement.querySelector('.form-notch-middle');
+ expect(label).toBeNull();
+ expect(middleNotch.style.width).toBe('');
+
+ fixture.componentInstance.showLabel = true;
+ fixture.detectChanges();
+ flush();
+ fixture.detectChanges();
+
+ label = fixture.nativeElement.querySelector('label');
+ middleNotch = fixture.nativeElement.querySelector('.form-notch-middle');
+ expect(label).not.toBeNull();
+ const expectedBorderGap = label.clientWidth * labelScale + labelGapPadding + 'px';
+ expect(middleNotch.style.width).toEqual(expectedBorderGap);
+ }));
+});
+
+const dynamicLabelTemplate = `
+
+
+ Example label
+
+`;
+@Component({
+ template: dynamicLabelTemplate,
+ standalone: false,
+})
+class DynamicLabelComponent {
+ showLabel = false;
+}
+
+const basicTemplate = `
+
+
+ Example label
+
+`;
+
+@Component({
+ template: basicTemplate,
+ standalone: false,
+})
+class BasicFormControlComponent {}
+
+const withoutLabelTemplate = `
+
+
+
+`;
+
+@Component({
+ template: withoutLabelTemplate,
+ standalone: false,
+})
+class WithoutLabelComponent {}
diff --git a/projects/mdb-angular-ui-kit/forms/form-control.ts b/projects/mdb-angular-ui-kit/forms/form-control.ts
new file mode 100644
index 00000000..a60ad746
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/form-control.ts
@@ -0,0 +1,10 @@
+import { Observable } from 'rxjs';
+import { Directive } from '@angular/core';
+
+@Directive()
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export abstract class MdbAbstractFormControl {
+ readonly stateChanges: Observable;
+ readonly input: HTMLInputElement;
+ readonly labelActive: boolean;
+}
diff --git a/projects/mdb-angular-ui-kit/forms/forms.module.ts b/projects/mdb-angular-ui-kit/forms/forms.module.ts
new file mode 100644
index 00000000..69d9a85e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/forms.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { MdbFormControlComponent } from './form-control.component';
+import { MdbInputDirective } from './input.directive';
+import { MdbLabelDirective } from './label.directive';
+
+@NgModule({
+ declarations: [MdbFormControlComponent, MdbInputDirective, MdbLabelDirective],
+ exports: [MdbFormControlComponent, MdbInputDirective, MdbLabelDirective],
+ imports: [CommonModule, FormsModule],
+})
+export class MdbFormsModule {}
diff --git a/projects/mdb-angular-ui-kit/forms/index.ts b/projects/mdb-angular-ui-kit/forms/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/forms/input.directive.ts b/projects/mdb-angular-ui-kit/forms/input.directive.ts
new file mode 100644
index 00000000..bdc0280f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/input.directive.ts
@@ -0,0 +1,169 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import {
+ AfterViewInit,
+ DestroyRef,
+ Directive,
+ DoCheck,
+ ElementRef,
+ HostBinding,
+ HostListener,
+ Input,
+ OnDestroy,
+ Optional,
+ Renderer2,
+ Self,
+} from '@angular/core';
+import { NgControl } from '@angular/forms';
+import { Subject } from 'rxjs';
+import { MdbAbstractFormControl } from './form-control';
+import { AutofillEvent, AutofillMonitor } from '@angular/cdk/text-field';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbInput]',
+ exportAs: 'mdbInput',
+ providers: [{ provide: MdbAbstractFormControl, useExisting: MdbInputDirective }],
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbInputDirective
+ implements MdbAbstractFormControl, DoCheck, AfterViewInit, OnDestroy
+{
+ constructor(
+ private _elementRef: ElementRef,
+ private _renderer: Renderer2,
+ @Optional() @Self() private _ngControl: NgControl,
+ private _autofill: AutofillMonitor,
+ private _destroyRef: DestroyRef
+ ) {}
+
+ readonly stateChanges: Subject = new Subject();
+
+ private _focused = false;
+ private _autofilled = false;
+ private _color = '';
+
+ ngAfterViewInit() {
+ if (typeof getComputedStyle === 'function') {
+ this._color = getComputedStyle(this._elementRef.nativeElement).color;
+
+ if (this._hasTypeInterferingPlaceholder()) {
+ this._updateTextColorForDateType();
+ }
+ }
+
+ this._autofill.monitor(this.input).subscribe((event: AutofillEvent) => {
+ this._autofilled = event.isAutofilled;
+ this.stateChanges.next();
+ });
+
+ this.stateChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(() => {
+ if (this._hasTypeInterferingPlaceholder()) {
+ this._updateTextColorForDateType();
+ }
+ });
+ }
+
+ private _currentNativeValue: any;
+
+ @HostBinding('disabled')
+ @Input('disabled')
+ get disabled(): boolean {
+ if (this._ngControl && this._ngControl.disabled !== null) {
+ return this._ngControl.disabled;
+ }
+ return this._disabled;
+ }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ }
+ private _disabled = false;
+
+ @Input('readonly')
+ get readonly(): boolean {
+ return this._readonly;
+ }
+ set readonly(value: boolean) {
+ if (value) {
+ this._renderer.setAttribute(this._elementRef.nativeElement, 'readonly', '');
+ } else {
+ this._renderer.removeAttribute(this._elementRef.nativeElement, 'readonly');
+ }
+ this._readonly = coerceBooleanProperty(value);
+ }
+ private _readonly = false;
+
+ @Input()
+ get value(): string {
+ return this._elementRef.nativeElement.value;
+ }
+ set value(value: string) {
+ if (value !== this.value) {
+ this._elementRef.nativeElement.value = value;
+ this._value = value;
+ this.stateChanges.next();
+ }
+ }
+ private _value: any;
+
+ private _updateTextColorForDateType() {
+ const actualColor = getComputedStyle(this._elementRef.nativeElement).color;
+ this._color = actualColor !== 'rgba(0, 0, 0, 0)' ? actualColor : this._color;
+
+ const color = this.labelActive ? this._color : `transparent`;
+
+ this._renderer.setStyle(this._elementRef.nativeElement, 'color', color);
+ }
+
+ @HostListener('focus')
+ _onFocus(): void {
+ this._focused = true;
+ this.stateChanges.next();
+ }
+
+ @HostListener('blur')
+ _onBlur(): void {
+ this._focused = false;
+ this.stateChanges.next();
+ }
+
+ ngDoCheck(): void {
+ const value = this._elementRef.nativeElement.value;
+ if (this._currentNativeValue !== value) {
+ this._currentNativeValue = value;
+ this.stateChanges.next();
+ }
+ }
+
+ get hasValue(): boolean {
+ return this._elementRef.nativeElement.value !== '';
+ }
+
+ get focused(): boolean {
+ return this._focused;
+ }
+
+ get autofilled(): boolean {
+ return this._autofilled;
+ }
+
+ get input(): HTMLInputElement {
+ return this._elementRef.nativeElement;
+ }
+
+ get labelActive(): boolean {
+ return this.focused || this.hasValue || this.autofilled;
+ }
+
+ private _hasTypeInterferingPlaceholder() {
+ const typesArray = ['date', 'datetime-local', 'time', 'month', 'week'];
+ return typesArray.includes(this._elementRef.nativeElement.type);
+ }
+
+ static ngAcceptInputType_disabled: BooleanInput;
+ static ngAcceptInputType_readonly: BooleanInput;
+
+ ngOnDestroy(): void {
+ this._autofill.stopMonitoring(this.input);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/forms/input.spec.ts b/projects/mdb-angular-ui-kit/forms/input.spec.ts
new file mode 100644
index 00000000..0193f64d
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/input.spec.ts
@@ -0,0 +1,48 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Component, DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { MdbFormsModule } from './index';
+
+describe('MDB Checkbox', () => {
+ let component: BasicInputComponent;
+ let fixture: ComponentFixture;
+ let input: DebugElement;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [BasicInputComponent],
+ imports: [MdbFormsModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(BasicInputComponent);
+ component = fixture.componentInstance;
+ input = fixture.debugElement.query(By.css('input'));
+ });
+
+ it('Should be disabled if disabled input is set to true', () => {
+ component.disabled = true;
+ fixture.detectChanges();
+ expect(input.nativeElement.disabled).toBe(true);
+ });
+
+ it('Should be readonly if readonly input is set to true', () => {
+ component.readonly = true;
+ fixture.detectChanges();
+ expect(input.nativeElement.hasAttribute('readonly')).toBe(true);
+ });
+});
+
+const basicTemplate = `
+
+`;
+
+@Component({
+ selector: 'mdb-input-test',
+ template: basicTemplate,
+ standalone: false,
+})
+class BasicInputComponent {
+ disabled = false;
+ readonly = false;
+}
diff --git a/projects/mdb-angular-ui-kit/forms/label.directive.ts b/projects/mdb-angular-ui-kit/forms/label.directive.ts
new file mode 100644
index 00000000..c277cda2
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/label.directive.ts
@@ -0,0 +1,12 @@
+import { Directive, ElementRef } from '@angular/core';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbLabel]',
+ exportAs: 'mdbLabel',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbLabelDirective {
+ constructor() {}
+}
diff --git a/projects/mdb-angular-ui-kit/forms/ng-package.json b/projects/mdb-angular-ui-kit/forms/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/forms/public_api.ts b/projects/mdb-angular-ui-kit/forms/public_api.ts
new file mode 100644
index 00000000..4fb2cf87
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/forms/public_api.ts
@@ -0,0 +1,5 @@
+export { MdbFormControlComponent } from './form-control.component';
+export { MdbInputDirective } from './input.directive';
+export { MdbLabelDirective } from './label.directive';
+export { MdbFormsModule } from './forms.module';
+export { MdbAbstractFormControl } from './form-control';
diff --git a/projects/mdb-angular-ui-kit/index.ts b/projects/mdb-angular-ui-kit/index.ts
new file mode 100644
index 00000000..ff8b4c56
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/index.ts
@@ -0,0 +1 @@
+export default {};
diff --git a/projects/mdb-angular-ui-kit/jest.config.js b/projects/mdb-angular-ui-kit/jest.config.js
new file mode 100644
index 00000000..32df13e3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/jest.config.js
@@ -0,0 +1,10 @@
+const baseConfig = require('../../jest.config');
+
+module.exports = {
+ ...baseConfig,
+ globals: {
+ 'ts-jest': {
+ tsConfig: '/projects/mdb-angular-ui-kit/tsconfig.spec.json',
+ },
+ },
+};
diff --git a/projects/mdb-angular-ui-kit/modal/index.ts b/projects/mdb-angular-ui-kit/modal/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/modal/modal-config.ts b/projects/mdb-angular-ui-kit/modal/modal-config.ts
new file mode 100644
index 00000000..a8cf3deb
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal-config.ts
@@ -0,0 +1,14 @@
+import { ViewContainerRef } from '@angular/core';
+
+export class MdbModalConfig {
+ animation? = true;
+ backdrop? = true;
+ ignoreBackdropClick? = false;
+ keyboard? = true;
+ modalClass? = '';
+ containerClass? = '';
+ viewContainerRef?: ViewContainerRef;
+ data?: D | null = null;
+ nonInvasive? = false;
+ focusElementSelector? = '';
+}
diff --git a/projects/mdb-angular-ui-kit/modal/modal-container.component.html b/projects/mdb-angular-ui-kit/modal/modal-container.component.html
new file mode 100644
index 00000000..c5c752e1
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal-container.component.html
@@ -0,0 +1,9 @@
+
diff --git a/projects/mdb-angular-ui-kit/modal/modal-container.component.ts b/projects/mdb-angular-ui-kit/modal/modal-container.component.ts
new file mode 100644
index 00000000..4ba207b6
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal-container.component.ts
@@ -0,0 +1,338 @@
+import { CdkPortalOutlet, ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
+import {
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ Component,
+ ComponentRef,
+ ElementRef,
+ EmbeddedViewRef,
+ HostBinding,
+ HostListener,
+ Inject,
+ NgZone,
+ OnDestroy,
+ OnInit,
+ Renderer2,
+ ViewChild,
+ DOCUMENT,
+} from '@angular/core';
+import { MdbModalConfig } from './modal-config';
+import { ConfigurableFocusTrapFactory, ConfigurableFocusTrap } from '@angular/cdk/a11y';
+import { fromEvent, Subject } from 'rxjs';
+import { filter, takeUntil } from 'rxjs/operators';
+
+// width below which, according to css rules, modal position changes - modal gets position relative instead of absolute.
+const MODAL_CSS_BREAKPOINT = 992;
+const MODAL_OPEN_CLASS = 'modal-open';
+const NON_INVASIVE_CLASS = 'modal-non-invasive-open';
+const NON_INVASIVE_SHOW_CLASS = 'modal-non-invasive-show';
+
+@Component({
+ selector: 'mdb-modal-container',
+ templateUrl: 'modal-container.component.html',
+ changeDetection: ChangeDetectionStrategy.Default,
+ standalone: false,
+})
+export class MdbModalContainerComponent implements OnInit, AfterViewInit, OnDestroy {
+ @ViewChild(CdkPortalOutlet, { static: true }) _portalOutlet: CdkPortalOutlet;
+ @ViewChild('dialog', { static: true }) modalDialog: ElementRef;
+ @ViewChild('content', { static: true }) modalContent: ElementRef;
+
+ readonly _destroy$: Subject = new Subject();
+ readonly backdropClick$: Subject = new Subject();
+
+ _config: MdbModalConfig;
+
+ BACKDROP_TRANSITION = 150;
+ MODAL_TRANSITION = 200;
+ NON_INVASIVE_TRANSITION = 450;
+
+ private _previouslyFocusedElement: HTMLElement;
+ private _focusTrap: ConfigurableFocusTrap;
+
+ @HostBinding('class.modal') modal = true;
+ @HostBinding('class.fade')
+ get hasAnimation(): boolean {
+ return this._config.animation;
+ }
+
+ @HostListener('window:resize', ['$event'])
+ onWindowResize() {
+ this._ngZone.runOutsideAngular(() => {
+ if (this._config.nonInvasive) {
+ this._handleWindowResize();
+ }
+ });
+ }
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ private _isScrollable = false;
+ private _isBottomRight = false;
+ private _isBottomLeft = false;
+ private _isTopRight = false;
+ private _isTopLeft = false;
+ private _isSideTopModal = false;
+ private _isSideBottomModal = false;
+ private _isSideModal = false;
+ private _isModalBottom = false;
+ private _modalContentRect: null | DOMRectReadOnly;
+ private _modalContentComputedStyles: null | CSSStyleDeclaration;
+ private _modalDialogComputedStyles: null | CSSStyleDeclaration;
+ private _topOffset = 0;
+ private _leftOffset = 0;
+ private _rightOffset = 0;
+ private _bottomOffset = 0;
+
+ constructor(
+ @Inject(DOCUMENT) private _document,
+ public _elementRef: ElementRef,
+ private _renderer: Renderer2,
+ private _focusTrapFactory: ConfigurableFocusTrapFactory,
+ private _ngZone: NgZone
+ ) {}
+
+ ngOnInit(): void {
+ this._updateContainerClass();
+ this._renderer.setStyle(this.host, 'display', 'block');
+
+ if (!this._config.nonInvasive) {
+ this._focusTrap = this._focusTrapFactory.create(this.host);
+ this._previouslyFocusedElement = this._document.activeElement as HTMLElement;
+ }
+ const focusElement =
+ this._config.focusElementSelector &&
+ (this.modalContent.nativeElement.querySelector(
+ this._config.focusElementSelector
+ ) as HTMLElement);
+ if (this._config.animation) {
+ setTimeout(() => {
+ this._renderer.addClass(this.host, 'show');
+ if (focusElement) {
+ setTimeout(() => {
+ focusElement.focus();
+ }, this.MODAL_TRANSITION);
+ } else {
+ setTimeout(() => {
+ this._focusTrap?.focusInitialElementWhenReady();
+ }, this.MODAL_TRANSITION);
+ }
+ }, this.BACKDROP_TRANSITION);
+ } else if (focusElement) {
+ focusElement.focus();
+ } else {
+ this._focusTrap?.focusInitialElementWhenReady();
+ }
+ }
+
+ ngAfterViewInit(): void {
+ const widthWithVerticalScroll = this._document.body.offsetWidth;
+ this._renderer.addClass(this._document.body, MODAL_OPEN_CLASS);
+
+ if (this._config.nonInvasive) {
+ this._renderer.addClass(this._document.body, NON_INVASIVE_CLASS);
+ setTimeout(() => {
+ this._onNonInvasiveModalShown();
+ }, this.NON_INVASIVE_TRANSITION);
+ }
+
+ if (!this._config.nonInvasive) {
+ this._renderer.setStyle(this._document.body, 'overflow', 'hidden');
+ }
+
+ const widthWithoutVerticalScroll = this._document.body.offsetWidth;
+
+ if (!this._config.nonInvasive) {
+ this._renderer.setStyle(
+ this._document.body,
+ 'padding-right',
+ `${widthWithoutVerticalScroll - widthWithVerticalScroll}px`
+ );
+ }
+
+ if (!this._config.ignoreBackdropClick && !this._config.nonInvasive) {
+ fromEvent(this.host, 'mousedown')
+ .pipe(
+ filter((event: MouseEvent) => {
+ const target = event.target as HTMLElement;
+ const dialog = this.modalDialog.nativeElement;
+ const notDialog = target !== dialog;
+ const notDialogContent = !dialog.contains(target);
+ return notDialog && notDialogContent;
+ }),
+ takeUntil(this._destroy$)
+ )
+ .subscribe((event: MouseEvent) => {
+ this.backdropClick$.next(event);
+ });
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._previouslyFocusedElement?.focus();
+ this._focusTrap?.destroy();
+
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _updateContainerClass(): void {
+ if (
+ this._config.containerClass === '' ||
+ (this._config.containerClass.length && this._config.containerClass.length === 0)
+ ) {
+ return;
+ }
+
+ const containerClasses = this._config.containerClass.split(' ');
+
+ containerClasses.forEach((containerClass) => {
+ this._renderer.addClass(this.host, containerClass);
+ });
+ }
+
+ private _onNonInvasiveModalShown() {
+ this._isScrollable = this._config.modalClass.includes('modal-dialog-scrollable');
+ this._isBottomRight = this._config.modalClass.includes('modal-bottom-right');
+ this._isBottomLeft = this._config.modalClass.includes('modal-bottom-left');
+ this._isTopRight = this._config.modalClass.includes('modal-top-right');
+ this._isTopLeft = this._config.modalClass.includes('modal-top-left');
+ this._isModalBottom = this._config.modalClass.includes('modal-bottom');
+ this._isSideTopModal = this._isTopLeft || this._isTopRight;
+ this._isSideBottomModal = this._isBottomLeft || this._isBottomRight;
+ this._isSideModal = this._isSideTopModal || this._isSideBottomModal;
+ this._modalContentRect = this.modalContent.nativeElement.getBoundingClientRect();
+ this._modalContentComputedStyles = window.getComputedStyle(this.modalContent.nativeElement);
+ this._modalDialogComputedStyles = window.getComputedStyle(this.modalDialog.nativeElement);
+ this._topOffset = parseInt(this._modalDialogComputedStyles.top, 0);
+ this._leftOffset = parseInt(this._modalDialogComputedStyles.left, 0);
+ this._rightOffset = parseInt(this._modalDialogComputedStyles.right, 0);
+ this._bottomOffset = parseInt(this._modalDialogComputedStyles.bottom, 0);
+
+ this._renderer.addClass(this.host, NON_INVASIVE_SHOW_CLASS);
+ this._setNonInvasiveStyles();
+ }
+
+ private _setNonInvasiveStyles(leftOffset = 0, topOffset = 0) {
+ const isAboveBreakpoint = window.innerWidth >= MODAL_CSS_BREAKPOINT;
+ this._renderer.setStyle(this.host, 'left', `${this._modalContentRect.left + leftOffset}px`);
+ this._renderer.setStyle(this.host, 'width', this._modalContentComputedStyles.width);
+
+ if (!this._isScrollable) {
+ // If the modal content is not long enough to require scroll shrink the modal wrapper to
+ // the height of modal content so other elements on site are clickable outside modal
+ this._renderer.setStyle(this.host, 'height', this._modalContentComputedStyles.height);
+ this._renderer.setStyle(this.host, 'display', '');
+ }
+
+ if (isAboveBreakpoint) {
+ if (this._isSideBottomModal || this._isModalBottom) {
+ // Force modal to correct bottom placement. It's needed because modal host has position
+ // fixed and fixed height.
+ this._renderer.setStyle(this.host, 'top', `${this._modalContentRect.top + topOffset}px`);
+ }
+
+ if (this._isSideModal) {
+ // Enable horizontal scrolling when the content is wider than the modal's fixed width
+ this._renderer.setStyle(this.host, 'overflowX', 'auto');
+ }
+ }
+
+ if (!isAboveBreakpoint) {
+ this.host.style.height = '';
+ }
+ }
+
+ _onNonInvasiveModalHidden() {
+ this._renderer.removeClass(this.host, NON_INVASIVE_SHOW_CLASS);
+ this._resetNonInvasiveStyles();
+ this._removeNonInvasiveClass();
+ }
+
+ private _resetNonInvasiveStyles() {
+ this._renderer.setStyle(this.host, 'left', '');
+ this._renderer.setStyle(this.host, 'top', '');
+ this._renderer.setStyle(this.host, 'height', '');
+ this._renderer.setStyle(this.host, 'width', '');
+
+ if (!this._isScrollable) {
+ this._renderer.setStyle(this.host, 'display', '');
+ }
+
+ if (this._isSideModal) {
+ this._renderer.setStyle(this.host, 'overflowX', '');
+ }
+ }
+
+ private _removeNonInvasiveClass() {
+ const isOtherModalOpen = this._document.body.querySelector(
+ '.modal.show.modal-non-invasive-show'
+ );
+ if (!isOtherModalOpen) {
+ this._renderer.removeClass(this._document.body, NON_INVASIVE_CLASS);
+ } else {
+ this._renderer.addClass(this._document.body, MODAL_OPEN_CLASS);
+ }
+ }
+
+ private _handleWindowResize() {
+ const modalContent = this.host.querySelector('.modal-content');
+ this._resetNonInvasiveStyles();
+
+ this._modalContentRect = modalContent.getBoundingClientRect();
+ this._modalContentComputedStyles = window.getComputedStyle(modalContent);
+
+ if (this._isSideTopModal || this._isSideBottomModal) {
+ let sideOffset = 0;
+ let topOffset = 0;
+ if (this._isBottomRight || this._isBottomLeft) {
+ topOffset = -this._bottomOffset;
+ }
+ if (this._isBottomRight || this._isTopRight) {
+ sideOffset = -this._rightOffset;
+ }
+ if (this._isBottomLeft || this._isTopLeft) {
+ sideOffset = this._leftOffset;
+ }
+
+ this._setNonInvasiveStyles(sideOffset, topOffset);
+ } else {
+ this._setNonInvasiveStyles();
+ }
+ }
+
+ _close(): void {
+ if (this._config.animation) {
+ this._renderer.removeClass(this.host, 'show');
+ }
+
+ // Pause iframe/video when closing modal
+ const iframeElements = Array.from(this.host.querySelectorAll('iframe'));
+ const videoElements = Array.from(this.host.querySelectorAll('video'));
+
+ iframeElements.forEach((iframe: HTMLIFrameElement) => {
+ const srcAttribute: any = iframe.getAttribute('src');
+ this._renderer.setAttribute(iframe, 'src', srcAttribute);
+ });
+
+ videoElements.forEach((video: HTMLVideoElement) => {
+ video.pause();
+ });
+ }
+
+ _restoreScrollbar(): void {
+ this._renderer.removeClass(this._document.body, MODAL_OPEN_CLASS);
+ this._renderer.removeStyle(this._document.body, 'overflow');
+ this._renderer.removeStyle(this._document.body, 'padding-right');
+ }
+
+ attachComponentPortal(portal: ComponentPortal): ComponentRef {
+ return this._portalOutlet.attachComponentPortal(portal);
+ }
+
+ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef {
+ return this._portalOutlet.attachTemplatePortal(portal);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/modal/modal-ref.ts b/projects/mdb-angular-ui-kit/modal/modal-ref.ts
new file mode 100644
index 00000000..8293199a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal-ref.ts
@@ -0,0 +1,27 @@
+import { OverlayRef } from '@angular/cdk/overlay';
+import { Observable, Subject } from 'rxjs';
+import { MdbModalContainerComponent } from './modal-container.component';
+
+export class MdbModalRef {
+ constructor(protected _overlayRef: OverlayRef, private _container: MdbModalContainerComponent) {}
+
+ component: T;
+
+ private readonly onClose$: Subject = new Subject();
+ readonly onClose: Observable = this.onClose$.asObservable();
+
+ close(message?: any): void {
+ this._container._close();
+
+ setTimeout(() => {
+ if (this._container._config.nonInvasive) {
+ this._container._onNonInvasiveModalHidden();
+ }
+ this._container._restoreScrollbar();
+ this.onClose$.next(message);
+ this.onClose$.complete();
+ this._overlayRef.detach();
+ this._overlayRef.dispose();
+ }, this._container.MODAL_TRANSITION);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/modal/modal.module.ts b/projects/mdb-angular-ui-kit/modal/modal.module.ts
new file mode 100644
index 00000000..8775961b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal.module.ts
@@ -0,0 +1,14 @@
+import { CommonModule } from '@angular/common';
+import { OverlayModule } from '@angular/cdk/overlay';
+import { PortalModule } from '@angular/cdk/portal';
+import { NgModule } from '@angular/core';
+import { MdbModalContainerComponent } from './modal-container.component';
+import { MdbModalService } from './modal.service';
+
+@NgModule({
+ imports: [CommonModule, OverlayModule, PortalModule],
+ exports: [MdbModalContainerComponent],
+ declarations: [MdbModalContainerComponent],
+ providers: [MdbModalService],
+})
+export class MdbModalModule {}
diff --git a/projects/mdb-angular-ui-kit/modal/modal.service.ts b/projects/mdb-angular-ui-kit/modal/modal.service.ts
new file mode 100644
index 00000000..0b28823e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal.service.ts
@@ -0,0 +1,144 @@
+import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
+import { ComponentPortal, ComponentType, TemplatePortal } from '@angular/cdk/portal';
+
+import {
+ Inject,
+ Injectable,
+ Injector,
+ StaticProvider,
+ TemplateRef,
+ ChangeDetectorRef,
+ DOCUMENT,
+} from '@angular/core';
+import { fromEvent } from 'rxjs';
+import { filter, take } from 'rxjs/operators';
+import { MdbModalConfig } from './modal-config';
+import { MdbModalContainerComponent } from './modal-container.component';
+import { MdbModalRef } from './modal-ref';
+
+@Injectable()
+export class MdbModalService {
+ constructor(
+ @Inject(DOCUMENT) private _document,
+ private _overlay: Overlay,
+ private _injector: Injector
+ ) {}
+
+ open(
+ componentOrTemplateRef: ComponentType | TemplateRef,
+ config?: MdbModalConfig
+ ): MdbModalRef {
+ const defaultConfig = new MdbModalConfig();
+ config = config ? Object.assign(defaultConfig, config) : defaultConfig;
+
+ const overlayRef = this._createOverlay(config);
+ const container = this._createContainer(overlayRef, config);
+ const modalRef = this._createContent(componentOrTemplateRef, container, overlayRef, config);
+
+ this._registerListeners(modalRef, config, container);
+
+ return modalRef;
+ }
+
+ private _createOverlay(config: MdbModalConfig): OverlayRef {
+ const overlayConfig = this._getOverlayConfig(config);
+ return this._overlay.create(overlayConfig);
+ }
+
+ private _getOverlayConfig(modalConfig: MdbModalConfig): OverlayConfig {
+ const config = new OverlayConfig({
+ positionStrategy: this._overlay.position().global(),
+ scrollStrategy: this._overlay.scrollStrategies.noop(),
+ hasBackdrop: modalConfig.nonInvasive ? false : modalConfig.backdrop,
+ backdropClass: 'mdb-backdrop',
+ });
+
+ return config;
+ }
+
+ private _createContainer(
+ overlayRef: OverlayRef,
+ config: MdbModalConfig
+ ): MdbModalContainerComponent {
+ const portal = new ComponentPortal(MdbModalContainerComponent, null, this._injector);
+ const containerRef = overlayRef.attach(portal);
+ containerRef.instance._config = config;
+
+ containerRef.changeDetectorRef.detectChanges();
+
+ return containerRef.instance;
+ }
+
+ private _createContent(
+ componentOrTemplate: ComponentType | TemplateRef,
+ container: MdbModalContainerComponent,
+ overlayRef: OverlayRef,
+ config: MdbModalConfig
+ ): MdbModalRef {
+ const modalRef: MdbModalRef = new MdbModalRef(overlayRef, container);
+
+ if (componentOrTemplate instanceof TemplateRef) {
+ container.attachTemplatePortal(
+ new TemplatePortal(componentOrTemplate, null, {
+ $implicit: config.data,
+ modalRef,
+ } as any)
+ );
+ } else {
+ const injector = this._createInjector(config, modalRef, container);
+ const contentRef = container.attachComponentPortal(
+ new ComponentPortal(componentOrTemplate, config.viewContainerRef, injector)
+ );
+
+ if (config.data) {
+ Object.assign(contentRef.instance, { ...config.data });
+ }
+
+ modalRef.component = contentRef.instance;
+ }
+
+ return modalRef;
+ }
+
+ private _createInjector(
+ config: MdbModalConfig,
+ modalRef: MdbModalRef,
+ container: MdbModalContainerComponent
+ ): Injector {
+ const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
+
+ // The dialog container should be provided as the dialog container and the dialog's
+ // content are created out of the same `ViewContainerRef` and as such, are siblings
+ // for injector purposes. To allow the hierarchy that is expected, the dialog
+ // container is explicitly provided in the injector.
+ const providers: StaticProvider[] = [
+ { provide: MdbModalContainerComponent, useValue: container },
+ { provide: MdbModalRef, useValue: modalRef },
+ ];
+
+ return Injector.create({ parent: userInjector || this._injector, providers });
+ }
+
+ private _registerListeners(
+ modalRef: MdbModalRef,
+ config: MdbModalConfig,
+ container: MdbModalContainerComponent
+ ): void {
+ container.backdropClick$.pipe(take(1)).subscribe(() => {
+ modalRef.close();
+ });
+
+ if (config.keyboard) {
+ fromEvent(container._elementRef.nativeElement, 'keydown')
+ .pipe(
+ filter((event: KeyboardEvent) => {
+ return event.key === 'Escape';
+ }),
+ take(1)
+ )
+ .subscribe(() => {
+ modalRef.close();
+ });
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/modal/modal.spec.ts b/projects/mdb-angular-ui-kit/modal/modal.spec.ts
new file mode 100644
index 00000000..7f67a9c0
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/modal.spec.ts
@@ -0,0 +1,324 @@
+import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick } from '@angular/core/testing';
+import { OverlayContainer } from '@angular/cdk/overlay';
+
+import { MdbModalModule } from './modal.module';
+import { MdbModalService } from './modal.service';
+import { Component, NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+@Component({
+ template: `
+
+
+
+ `,
+ providers: [MdbModalService],
+ standalone: false,
+})
+class BasicModalComponent {
+ constructor(public modal: MdbModalService) {}
+
+ mainView = true;
+
+ setView(isMain) {
+ this.mainView = isMain;
+ }
+}
+
+@NgModule({
+ declarations: [BasicModalComponent],
+ imports: [BrowserModule],
+})
+class TestModalModule {}
+
+describe('MDB Modal', () => {
+ let modal: MdbModalService;
+ let overlayContainer: OverlayContainer;
+ let overlayContainerElement: HTMLElement;
+ let fixture: ComponentFixture;
+
+ beforeEach(fakeAsync(() => {
+ const module = TestBed.configureTestingModule({
+ imports: [MdbModalModule, TestModalModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ TestBed.compileComponents();
+ fixture = module.createComponent(BasicModalComponent);
+ }));
+
+ beforeEach(inject(
+ [MdbModalService, OverlayContainer],
+ (mdbModal: MdbModalService, oc: OverlayContainer) => {
+ modal = mdbModal;
+ overlayContainer = oc;
+ overlayContainerElement = oc.getContainerElement();
+ }
+ ));
+
+ afterEach(() => {
+ overlayContainer.ngOnDestroy();
+ });
+
+ it('should open a modal with a specified component', () => {
+ modal.open(BasicModalComponent);
+
+ expect(overlayContainerElement.textContent).toContain('Modal title');
+
+ const modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ expect(modalContainer).not.toBe(null);
+ });
+
+ it('should correctly add container classes', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ containerClass: 'top',
+ });
+
+ fixture.detectChanges();
+ tick(350);
+
+ const modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ expect(modalContainer.classList.contains('top')).toBe(true);
+ }));
+
+ it('should correctly add modal classes', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ modalClass: 'modal-top-right',
+ });
+
+ fixture.detectChanges();
+ tick(350);
+
+ const modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ const modalDialog = modalContainer.querySelector('.modal-dialog');
+ expect(modalDialog.classList.contains('modal-top-right')).toBe(true);
+ }));
+
+ it('should close the modal on backdrop click', fakeAsync(() => {
+ modal.open(BasicModalComponent);
+
+ fixture.detectChanges();
+ tick(350);
+
+ let modalContainer = overlayContainerElement.querySelector(
+ 'mdb-modal-container'
+ ) as HTMLElement;
+ expect(modalContainer).not.toBe(null);
+
+ const event = new MouseEvent('mousedown', { clientX: 0, clientY: 0 });
+ modalContainer.dispatchEvent(event);
+
+ fixture.detectChanges();
+ tick(700);
+
+ modalContainer = overlayContainerElement.querySelector('mdb-modal-container') as HTMLElement;
+
+ expect(modalContainer).toBe(null);
+ }));
+
+ it('should not close the modal on mousedown inside modal, move mouse outside modal and mouseup', fakeAsync(() => {
+ modal.open(BasicModalComponent);
+
+ fixture.detectChanges();
+ tick(350);
+
+ let modalContainer = overlayContainerElement.querySelector(
+ 'mdb-modal-container'
+ ) as HTMLElement;
+ let modalContent = overlayContainerElement.querySelector('.modal-content') as HTMLElement;
+
+ expect(modalContainer).not.toBe(null);
+ expect(modalContent).not.toBe(null);
+
+ const mousedownEvent = new MouseEvent('mousedown', { clientX: 0, clientY: 0 });
+ const mouseupEvent = new MouseEvent('mouseup', { clientX: 0, clientY: 0 });
+
+ modalContent.dispatchEvent(mousedownEvent);
+ modalContainer.dispatchEvent(mouseupEvent);
+
+ fixture.detectChanges();
+ tick(700);
+
+ modalContainer = overlayContainerElement.querySelector('mdb-modal-container') as HTMLElement;
+ modalContent = overlayContainerElement.querySelector('.modal-content') as HTMLElement;
+
+ expect(modalContent).not.toBe(null);
+ expect(modalContainer).not.toBe(null);
+ }));
+
+ it('should not close the modal on backdrop click if ignoreBackdropClick is set to true', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ ignoreBackdropClick: true,
+ });
+
+ fixture.detectChanges();
+ tick(350);
+
+ let modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ expect(modalContainer).not.toBe(null);
+
+ const event = new MouseEvent('mousedown');
+ modalContainer.dispatchEvent(event);
+
+ fixture.detectChanges();
+ tick(700);
+
+ modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+
+ expect(modalContainer).not.toBe(null);
+ }));
+
+ it('should close on escape press if keyboard option is true', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ keyboard: true,
+ });
+
+ fixture.detectChanges();
+ tick(350);
+
+ let modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ expect(modalContainer).not.toBe(null);
+
+ const event = new KeyboardEvent('keydown', { key: 'Escape' });
+ modalContainer.dispatchEvent(event);
+
+ fixture.detectChanges();
+ tick(700);
+
+ modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+
+ expect(modalContainer).toBe(null);
+ }));
+
+ it('should not close on escape press if keyboard option is false', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ keyboard: false,
+ });
+
+ let modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ expect(modalContainer).not.toBe(null);
+
+ const event = new KeyboardEvent('keydown', { key: 'Escape' });
+ modalContainer.dispatchEvent(event);
+
+ fixture.detectChanges();
+ tick(700);
+
+ modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+
+ expect(modalContainer).not.toBe(null);
+ }));
+
+ it('should not close when click on btn inside modal', fakeAsync(() => {
+ modal.open(BasicModalComponent);
+
+ fixture.detectChanges();
+ tick(700);
+
+ let modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ let mainView = modalContainer.querySelector('#main-view');
+ let notMainView = modalContainer.querySelector('#not-main-view');
+ let mainViewToggler = modalContainer.querySelector('#main-view-toggler');
+ let notMainViewToggler = modalContainer.querySelector('#not-main-view-toggler');
+
+ expect(modalContainer).not.toBe(null);
+ expect(mainView).not.toBe(null);
+ expect(notMainView).toBe(null);
+ expect(mainViewToggler).not.toBe(null);
+ expect(notMainViewToggler).toBe(null);
+
+ mainViewToggler.dispatchEvent(new MouseEvent('click'));
+
+ fixture.detectChanges();
+ tick(700);
+
+ modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ mainView = modalContainer.querySelector('#main-view');
+ notMainView = modalContainer.querySelector('#not-main-view');
+ mainViewToggler = modalContainer.querySelector('#main-view-toggler');
+ notMainViewToggler = modalContainer.querySelector('#not-main-view-toggler');
+
+ expect(modalContainer).not.toBe(null);
+ expect(mainView).toBe(null);
+ expect(notMainView).not.toBe(null);
+ expect(mainViewToggler).toBe(null);
+ expect(notMainViewToggler).not.toBe(null);
+ }));
+});
+
+describe('MDB Non-invasive Modal', () => {
+ let modal: MdbModalService;
+ let overlayContainer: OverlayContainer;
+ let overlayContainerElement: HTMLElement;
+ let fixture: ComponentFixture;
+
+ beforeEach(fakeAsync(() => {
+ const module = TestBed.configureTestingModule({
+ imports: [MdbModalModule, TestModalModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ TestBed.compileComponents();
+ fixture = module.createComponent(BasicModalComponent);
+ }));
+
+ beforeEach(inject(
+ [MdbModalService, OverlayContainer],
+ (mdbModal: MdbModalService, oc: OverlayContainer) => {
+ modal = mdbModal;
+ overlayContainer = oc;
+ overlayContainerElement = oc.getContainerElement();
+ }
+ ));
+
+ afterEach(() => {
+ overlayContainer.ngOnDestroy();
+ });
+
+ it('should add non-invasive class', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ nonInvasive: true,
+ });
+
+ fixture.detectChanges();
+ flush();
+
+ fixture.detectChanges();
+ tick(350);
+
+ const body = document.body;
+ const modalContainer = overlayContainerElement.querySelector('mdb-modal-container');
+ expect(body.classList.contains('modal-non-invasive-open')).toBe(true);
+ expect(modalContainer.classList.contains('modal-non-invasive-show')).toBe(true);
+ }));
+
+ it('should not apply padding-right style to document body', fakeAsync(() => {
+ modal.open(BasicModalComponent, {
+ nonInvasive: true,
+ });
+
+ fixture.detectChanges();
+ flush();
+
+ fixture.detectChanges();
+ tick(350);
+
+ const body = document.body;
+ expect(body.style.paddingRight).toBe('0px');
+ }));
+});
diff --git a/projects/mdb-angular-ui-kit/modal/ng-package.json b/projects/mdb-angular-ui-kit/modal/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/modal/public_api.ts b/projects/mdb-angular-ui-kit/modal/public_api.ts
new file mode 100644
index 00000000..58191971
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/modal/public_api.ts
@@ -0,0 +1,5 @@
+export { MdbModalConfig } from './modal-config';
+export { MdbModalRef } from './modal-ref';
+export { MdbModalContainerComponent } from './modal-container.component';
+export { MdbModalService } from './modal.service';
+export { MdbModalModule } from './modal.module';
diff --git a/projects/mdb-angular-ui-kit/ng-package.json b/projects/mdb-angular-ui-kit/ng-package.json
new file mode 100644
index 00000000..fc74550a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ng-package.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
+ "dest": "../../dist/mdb-angular-ui-kit",
+ "lib": {
+ "entryFile": "index.ts"
+ },
+ "assets": ["./assets/**/*", "./CHANGELOG.md", "./License.pdf"]
+}
diff --git a/projects/mdb-angular-ui-kit/package.json b/projects/mdb-angular-ui-kit/package.json
new file mode 100644
index 00000000..0fb8a0de
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "mdb-angular-ui-kit",
+ "repository": "https://github.com/mdbootstrap/mdb-angular-ui-kit",
+ "homepage": "https://mdbootstrap.com/docs/b5/angular/",
+ "author": "MDBootstrap",
+ "license": "MIT",
+ "version": "9.0.0",
+ "peerDependencies": {
+ "@angular/common": "^20.0.0",
+ "@angular/core": "^20.0.0",
+ "@angular/animations": "^20.0.0",
+ "@angular/forms": "^20.0.0",
+ "@angular/cdk": "^20.0.0"
+ },
+ "schematics": "./schematics/collection.json",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "exports": {
+ "./assets/scss/mdb.scss": {
+ "sass": "./assets/scss/mdb.scss"
+ }
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/popover/index.ts b/projects/mdb-angular-ui-kit/popover/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/popover/ng-package.json b/projects/mdb-angular-ui-kit/popover/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/popover/popover.component.html b/projects/mdb-angular-ui-kit/popover/popover.component.html
new file mode 100644
index 00000000..99d4df76
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/popover.component.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ {{ content }}
+
+
diff --git a/projects/mdb-angular-ui-kit/popover/popover.component.ts b/projects/mdb-angular-ui-kit/popover/popover.component.ts
new file mode 100644
index 00000000..c3bdfab0
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/popover.component.ts
@@ -0,0 +1,50 @@
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ Input,
+ TemplateRef,
+ booleanAttribute,
+} from '@angular/core';
+import { trigger, style, animate, transition, state, AnimationEvent } from '@angular/animations';
+import { Subject } from 'rxjs';
+@Component({
+ selector: 'mdb-popover',
+ templateUrl: 'popover.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ animations: [
+ trigger('fade', [
+ state('visible', style({ opacity: 1 })),
+ state('hidden', style({ opacity: 0 })),
+ transition('visible <=> hidden', animate('150ms linear')),
+ transition(':enter', animate('150ms linear')),
+ ]),
+ ],
+ standalone: false,
+})
+export class MdbPopoverComponent {
+ @Input({ transform: booleanAttribute }) animation: boolean;
+ @Input() content: string | TemplateRef;
+ @Input() context: any;
+ @Input() title: string;
+
+ get isContentTemplate(): boolean {
+ return this.content instanceof TemplateRef;
+ }
+
+ readonly _hidden: Subject = new Subject();
+
+ animationState = 'hidden';
+
+ constructor(private _cdRef: ChangeDetectorRef) {}
+
+ markForCheck(): void {
+ this._cdRef.markForCheck();
+ }
+
+ onAnimationEnd(event: AnimationEvent): void {
+ if (event.toState === 'hidden') {
+ this._hidden.next();
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/popover/popover.directive.spec.ts b/projects/mdb-angular-ui-kit/popover/popover.directive.spec.ts
new file mode 100644
index 00000000..a7f091ed
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/popover.directive.spec.ts
@@ -0,0 +1,241 @@
+import { ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing';
+import { Component } from '@angular/core';
+import { MdbPopoverModule } from './index';
+import { MdbPopoverDirective } from './popover.directive';
+import { By } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+describe('MDB Popover', () => {
+ describe('after init', () => {
+ let fixture: ComponentFixture;
+ let element: any;
+ let component: any;
+ let directive: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [MdbPopoverModule, NoopAnimationsModule],
+ declarations: [TestPopoverComponent],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestPopoverComponent);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+ });
+
+ it('should create the component', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should open tooltip after mouseenter and close after mouseout', () => {
+ fixture.detectChanges();
+
+ directive = fixture.debugElement
+ .query(By.directive(MdbPopoverDirective))
+ .injector.get(MdbPopoverDirective) as MdbPopoverDirective;
+
+ const onOpen = jest.spyOn(directive, 'show');
+ const onClose = jest.spyOn(directive, 'hide');
+
+ const buttonEl = element.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('mouseenter'));
+ fixture.detectChanges();
+
+ expect(directive.show).toHaveBeenCalled();
+
+ directive._open = true;
+ buttonEl.dispatchEvent(new Event('mouseleave'));
+ fixture.detectChanges();
+
+ expect(directive.hide).toHaveBeenCalled();
+ });
+
+ it('should set popover header and title', () => {
+ jest.useFakeTimers();
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('mouseenter'));
+ jest.runAllTimers();
+
+ fixture.detectChanges();
+ const popoverContent = document.querySelector('.popover-body').textContent;
+ const popoverTitle = document.querySelector('.popover-header').textContent;
+
+ expect(popoverContent).toMatch(component.testMdbPopover);
+ expect(popoverTitle).toMatch(component.testMdbPopoverTitle);
+ });
+
+ it('should set placement', () => {
+ jest.useFakeTimers();
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('mouseenter'));
+ jest.runAllTimers();
+
+ fixture.detectChanges();
+ directive = fixture.debugElement
+ .query(By.directive(MdbPopoverDirective))
+ .injector.get(MdbPopoverDirective) as MdbPopoverDirective;
+
+ const placement = directive._overlayRef._config.positionStrategy._lastPosition.originY;
+ expect(placement).toMatch('top');
+ });
+ });
+
+ describe('onInit', () => {
+ it('should open/close tooltip after click', () => {
+ let fixture: ComponentFixture;
+ let directive: any;
+ let component: any;
+ let element: any;
+
+ TestBed.configureTestingModule({
+ imports: [MdbPopoverModule, NoopAnimationsModule],
+ declarations: [TestPopoverComponent2],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestPopoverComponent2);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+
+ directive = fixture.debugElement
+ .query(By.directive(MdbPopoverDirective))
+ .injector.get(MdbPopoverDirective) as MdbPopoverDirective;
+
+ const onOpen = jest.spyOn(directive, 'show');
+ const onClose = jest.spyOn(directive, 'hide');
+
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+
+ expect(directive.show).toHaveBeenCalled();
+
+ directive._open = true;
+ buttonEl.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+
+ expect(directive.hide).toHaveBeenCalled();
+ });
+
+ it('should prevent open', () => {
+ let fixture: ComponentFixture;
+ let directive: any;
+ let component: any;
+ let element: any;
+
+ TestBed.configureTestingModule({
+ imports: [MdbPopoverModule, NoopAnimationsModule],
+ declarations: [TestPopoverComponent3],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestPopoverComponent3);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+
+ directive = fixture.debugElement
+ .query(By.directive(MdbPopoverDirective))
+ .injector.get(MdbPopoverDirective) as MdbPopoverDirective;
+
+ const onOpen = jest.spyOn(directive, 'show');
+
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+
+ expect(directive.show).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('custom template', () => {
+ let fixture: ComponentFixture;
+ let element: any;
+ let component: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [MdbPopoverModule, NoopAnimationsModule],
+ declarations: [TestPopoverComponent4],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestPopoverComponent4);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+ });
+
+ it('should set custom content with context data', fakeAsync(() => {
+ const buttonEl = element.querySelector('button');
+ buttonEl.click();
+ fixture.detectChanges();
+ flush();
+ const popoverBody = document.querySelector('.popover-body');
+ expect(popoverBody.textContent).toBe('Current user: John Doe');
+ }));
+ });
+});
+
+@Component({
+ selector: 'mdb-test-tooltip',
+ template: `
+ MDB Button
+ `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestPopoverComponent {
+ testTrigger = 'hover';
+ testMdbPopover = 'popoverTitle';
+ testMdbPopoverTitle = 'popoverTitle';
+ testPlacement = 'top';
+ testDisabled = false;
+}
+
+@Component({
+ selector: 'mdb-test-popover2',
+ template: ` MDB Button `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestPopoverComponent2 {}
+
+@Component({
+ selector: 'mdb-test-popover2',
+ template: `
+ MDB Button
+ `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestPopoverComponent3 {}
+
+@Component({
+ selector: 'mdb-test-popover4',
+ template: `
+ Click to toggle popover
+
+ Current user: {{ person.name }} {{ person.surname }} `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestPopoverComponent4 {}
diff --git a/projects/mdb-angular-ui-kit/popover/popover.directive.ts b/projects/mdb-angular-ui-kit/popover/popover.directive.ts
new file mode 100644
index 00000000..e76db104
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/popover.directive.ts
@@ -0,0 +1,243 @@
+import {
+ ComponentRef,
+ Directive,
+ ElementRef,
+ EventEmitter,
+ Input,
+ OnDestroy,
+ OnInit,
+ Output,
+ TemplateRef,
+ booleanAttribute,
+ numberAttribute,
+} from '@angular/core';
+import {
+ ConnectedPosition,
+ Overlay,
+ OverlayConfig,
+ OverlayPositionBuilder,
+ OverlayRef,
+} from '@angular/cdk/overlay';
+import { ComponentPortal } from '@angular/cdk/portal';
+import { MdbPopoverComponent } from './popover.component';
+import { fromEvent, Subject } from 'rxjs';
+import { first, takeUntil } from 'rxjs/operators';
+import { MdbPopoverPosition } from './popover.types';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbPopover]',
+ exportAs: 'mdbPopover',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbPopoverDirective implements OnInit, OnDestroy {
+ @Input({ transform: booleanAttribute }) animation = true;
+ @Input({ transform: numberAttribute }) delayHide = 0;
+ @Input({ transform: numberAttribute }) delayShow = 0;
+ @Input() mdbPopover: TemplateRef | string = '';
+ @Input() mdbPopoverData: any;
+ @Input() mdbPopoverTitle = '';
+ @Input({ transform: numberAttribute }) offset = 4;
+ @Input() placement: MdbPopoverPosition = 'top';
+ @Input({ transform: booleanAttribute }) popoverDisabled = false;
+ @Input() trigger = 'click';
+
+ @Output() popoverShow = new EventEmitter();
+ @Output() popoverShown = new EventEmitter();
+ @Output() popoverHide = new EventEmitter();
+ @Output() popoverHidden = new EventEmitter();
+
+ private _overlayRef: OverlayRef;
+ private _tooltipRef: ComponentRef;
+ private _open = false;
+ private _showTimeout: any = 0;
+ private _hideTimeout: any = 0;
+
+ readonly _destroy$: Subject = new Subject();
+
+ constructor(
+ private _overlay: Overlay,
+ private _overlayPositionBuilder: OverlayPositionBuilder,
+ private _elementRef: ElementRef
+ ) {}
+
+ ngOnInit(): void {
+ if (this.popoverDisabled || (this.mdbPopover === '' && this.mdbPopoverTitle === '')) {
+ return;
+ }
+
+ this._bindTriggerEvents();
+ }
+
+ ngOnDestroy(): void {
+ if (this._open) {
+ this.hide();
+ }
+
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _bindTriggerEvents(): void {
+ const triggers = this.trigger.split(' ');
+
+ triggers.forEach((trigger) => {
+ if (trigger === 'click') {
+ fromEvent(this._elementRef.nativeElement, trigger)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.toggle());
+ } else if (trigger !== 'manual') {
+ const evIn = trigger === 'hover' ? 'mouseenter' : 'focusin';
+ const evOut = trigger === 'hover' ? 'mouseleave' : 'focusout';
+
+ fromEvent(this._elementRef.nativeElement, evIn)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.show());
+ fromEvent(this._elementRef.nativeElement, evOut)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.hide());
+ }
+ });
+ }
+
+ private _createOverlayConfig(): OverlayConfig {
+ const positionStrategy = this._overlayPositionBuilder
+ .flexibleConnectedTo(this._elementRef)
+ .withPositions(this._getPosition())
+ .withPush(false);
+ const overlayConfig = new OverlayConfig({
+ hasBackdrop: false,
+ scrollStrategy: this._overlay.scrollStrategies.reposition(),
+ positionStrategy,
+ });
+
+ return overlayConfig;
+ }
+
+ private _createOverlay(): void {
+ this._overlayRef = this._overlay.create(this._createOverlayConfig());
+ }
+
+ private _getPosition(): ConnectedPosition[] {
+ let position;
+
+ const positionTop = {
+ originX: 'center',
+ originY: 'top',
+ overlayX: 'center',
+ overlayY: 'bottom',
+ offsetY: -this.offset,
+ };
+
+ const positionBottom = {
+ originX: 'center',
+ originY: 'bottom',
+ overlayX: 'center',
+ overlayY: 'top',
+ offsetY: this.offset,
+ };
+
+ const positionRight = {
+ originX: 'end',
+ originY: 'center',
+ overlayX: 'start',
+ overlayY: 'center',
+ offsetX: this.offset,
+ };
+
+ const positionLeft = {
+ originX: 'start',
+ originY: 'center',
+ overlayX: 'end',
+ overlayY: 'center',
+ offsetX: -this.offset,
+ };
+
+ switch (this.placement) {
+ case 'top':
+ position = [positionTop, positionBottom];
+ break;
+ case 'bottom':
+ position = [positionBottom, positionTop];
+ break;
+ case 'left':
+ position = [positionLeft, positionRight, positionTop, positionBottom];
+ break;
+ case 'right':
+ position = [positionRight, positionLeft, positionTop, positionBottom];
+ break;
+ default:
+ break;
+ }
+
+ return position;
+ }
+
+ show(): void {
+ if (this._hideTimeout) {
+ this._overlayRef.detach();
+ clearTimeout(this._hideTimeout);
+ this._hideTimeout = null;
+ }
+
+ this._createOverlay();
+
+ if (this._hideTimeout) {
+ clearTimeout(this._hideTimeout);
+ this._hideTimeout = null;
+ }
+
+ this._showTimeout = setTimeout(() => {
+ const tooltipPortal = new ComponentPortal(MdbPopoverComponent);
+
+ this.popoverShow.emit(this);
+ this._open = true;
+
+ this._tooltipRef = this._overlayRef.attach(tooltipPortal);
+
+ this._tooltipRef.instance.content = this.mdbPopover;
+ this._tooltipRef.instance.title = this.mdbPopoverTitle;
+ this._tooltipRef.instance.animation = this.animation;
+ this._tooltipRef.instance.context = this.mdbPopoverData;
+ this._tooltipRef.instance.animationState = 'visible';
+ this._tooltipRef.instance.markForCheck();
+
+ this.popoverShown.emit(this);
+ }, this.delayShow);
+ }
+
+ hide(): void {
+ if (this._showTimeout) {
+ clearTimeout(this._showTimeout);
+ this._showTimeout = null;
+ } else {
+ return;
+ }
+
+ this._hideTimeout = setTimeout(() => {
+ this.popoverHide.emit(this);
+ if (!this._tooltipRef) {
+ this._overlayRef.detach();
+ this._open = false;
+ this.popoverHidden.emit(this);
+ } else {
+ this._tooltipRef.instance._hidden.pipe(first()).subscribe(() => {
+ this._overlayRef.detach();
+ this._open = false;
+ this.popoverHidden.emit(this);
+ });
+ this._tooltipRef.instance.animationState = 'hidden';
+ this._tooltipRef.instance.markForCheck();
+ }
+ }, this.delayHide);
+ }
+
+ toggle(): void {
+ if (this._open) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/popover/popover.module.ts b/projects/mdb-angular-ui-kit/popover/popover.module.ts
new file mode 100644
index 00000000..e5315adc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/popover.module.ts
@@ -0,0 +1,12 @@
+import { MdbPopoverDirective } from './popover.directive';
+import { NgModule } from '@angular/core';
+import { OverlayModule } from '@angular/cdk/overlay';
+import { CommonModule } from '@angular/common';
+import { MdbPopoverComponent } from './popover.component';
+
+@NgModule({
+ imports: [CommonModule, OverlayModule],
+ declarations: [MdbPopoverDirective, MdbPopoverComponent],
+ exports: [MdbPopoverDirective, MdbPopoverComponent],
+})
+export class MdbPopoverModule {}
diff --git a/projects/mdb-angular-ui-kit/popover/popover.types.ts b/projects/mdb-angular-ui-kit/popover/popover.types.ts
new file mode 100644
index 00000000..4648f6bf
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/popover.types.ts
@@ -0,0 +1 @@
+export type MdbPopoverPosition = 'top' | 'right' | 'bottom' | 'left';
diff --git a/projects/mdb-angular-ui-kit/popover/public_api.ts b/projects/mdb-angular-ui-kit/popover/public_api.ts
new file mode 100644
index 00000000..946952a3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/popover/public_api.ts
@@ -0,0 +1,4 @@
+export { MdbPopoverDirective } from './popover.directive';
+export { MdbPopoverModule } from './popover.module';
+export { MdbPopoverPosition } from './popover.types';
+export { MdbPopoverComponent } from './popover.component';
diff --git a/projects/mdb-angular-ui-kit/radio/index.ts b/projects/mdb-angular-ui-kit/radio/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/radio/ng-package.json b/projects/mdb-angular-ui-kit/radio/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/radio/public_api.ts b/projects/mdb-angular-ui-kit/radio/public_api.ts
new file mode 100644
index 00000000..67dd43dd
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/public_api.ts
@@ -0,0 +1,3 @@
+export { MdbRadioDirective } from './radio-button.directive';
+export { MdbRadioGroupDirective, MDB_RADIO_GROUP_VALUE_ACCESSOR } from './radio-group.directive';
+export { MdbRadioModule } from './radio.module';
diff --git a/projects/mdb-angular-ui-kit/radio/radio-button.directive.ts b/projects/mdb-angular-ui-kit/radio/radio-button.directive.ts
new file mode 100644
index 00000000..a311fcf9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/radio-button.directive.ts
@@ -0,0 +1,77 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { Directive, HostBinding, Input } from '@angular/core';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbRadio]',
+ standalone: false,
+})
+export class MdbRadioDirective {
+ @Input()
+ get name(): string {
+ return this._name;
+ }
+ set name(value: string) {
+ this._name = value;
+ }
+ private _name: string;
+
+ @Input('checked')
+ get checked(): boolean {
+ return this._checked;
+ }
+ set checked(value: boolean) {
+ this._checked = coerceBooleanProperty(value);
+ }
+ private _checked = false;
+
+ @Input('value')
+ get value(): any {
+ return this._value;
+ }
+ set value(value: any) {
+ this._value = value;
+ }
+ private _value: any = null;
+
+ @Input('disabled')
+ get disabled(): boolean {
+ return this._disabled;
+ }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ }
+ private _disabled = false;
+
+ @HostBinding('disabled')
+ get isDisabled(): boolean {
+ return this._disabled;
+ }
+
+ @HostBinding('checked')
+ get isChecked(): boolean {
+ return this._checked;
+ }
+
+ @HostBinding('attr.name')
+ get nameAttr(): string {
+ return this.name;
+ }
+
+ constructor() {}
+
+ _updateName(value: string): void {
+ this._name = value;
+ }
+
+ _updateChecked(value: boolean): void {
+ this._checked = value;
+ }
+
+ _updateDisabledState(value: boolean): void {
+ this._disabled = value;
+ }
+
+ static ngAcceptInputType_checked: BooleanInput;
+ static ngAcceptInputType_disabled: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/radio/radio-group.directive.spec.ts b/projects/mdb-angular-ui-kit/radio/radio-group.directive.spec.ts
new file mode 100644
index 00000000..95dce45a
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/radio-group.directive.spec.ts
@@ -0,0 +1,76 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Component } from '@angular/core';
+import { MdbRadioModule } from './index';
+
+describe('MDB Checkbox', () => {
+ let component: BasicRadioGroupComponent;
+ let fixture: ComponentFixture;
+ let nativeElement: HTMLElement;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [BasicRadioGroupComponent],
+ imports: [MdbRadioModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(BasicRadioGroupComponent);
+ component = fixture.componentInstance;
+ nativeElement = fixture.elementRef.nativeElement;
+ });
+
+ it('Should disable all inputs if the group is disabled', () => {
+ component.disabled = true;
+ fixture.detectChanges();
+ const inputs = nativeElement.querySelectorAll('input');
+
+ expect(inputs[0].disabled).toBe(true);
+ expect(inputs[1].disabled).toBe(true);
+ });
+
+ it('Should set inputs name to the group name', () => {
+ component.name = 'test name';
+ fixture.detectChanges();
+ const inputs = nativeElement.querySelectorAll('input');
+
+ expect(inputs[0].getAttribute('name')).toBe('test name');
+ expect(inputs[1].getAttribute('name')).toBe('test name');
+ });
+});
+
+const basicTemplate = `
+
+`;
+
+@Component({
+ selector: 'mdb-radio-group-test',
+ template: basicTemplate,
+ standalone: false,
+})
+class BasicRadioGroupComponent {
+ checked = false;
+ name = 'mdb-radio-group';
+ disabled = false;
+}
diff --git a/projects/mdb-angular-ui-kit/radio/radio-group.directive.ts b/projects/mdb-angular-ui-kit/radio/radio-group.directive.ts
new file mode 100644
index 00000000..fb1bf8fd
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/radio-group.directive.ts
@@ -0,0 +1,128 @@
+import {
+ AfterContentInit,
+ ContentChildren,
+ Directive,
+ forwardRef,
+ Input,
+ OnDestroy,
+ QueryList,
+} from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { from, Subject } from 'rxjs';
+import { startWith, switchMap, takeUntil } from 'rxjs/operators';
+import { MdbRadioDirective } from './radio-button.directive';
+
+export const MDB_RADIO_GROUP_VALUE_ACCESSOR: any = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => MdbRadioGroupDirective),
+ multi: true,
+};
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbRadioGroup]',
+ providers: [MDB_RADIO_GROUP_VALUE_ACCESSOR],
+ standalone: false,
+})
+export class MdbRadioGroupDirective implements ControlValueAccessor, AfterContentInit, OnDestroy {
+ @ContentChildren(MdbRadioDirective, { descendants: true }) radios: QueryList;
+
+ @Input()
+ get value(): any {
+ return this._value;
+ }
+ set value(value: any) {
+ this._value = value;
+ if (this.radios) {
+ this._updateChecked();
+ }
+ }
+ private _value: any;
+
+ @Input()
+ get name(): string {
+ return this._name;
+ }
+ set name(name: string) {
+ this._name = name;
+ if (this.radios) {
+ this._updateNames();
+ }
+ }
+ private _name: string;
+
+ @Input()
+ get disabled(): boolean {
+ return this._disabled;
+ }
+ set disabled(disabled: boolean) {
+ this._disabled = disabled;
+
+ if (this.radios) {
+ this._updateDisabled();
+ }
+ }
+ private _disabled = false;
+
+ private _destroy$ = new Subject();
+
+ onChange = (_: any) => {};
+ onTouched = () => {};
+
+ ngAfterContentInit(): void {
+ this._updateNames();
+ this._updateDisabled();
+
+ this.radios.changes
+ .pipe(
+ startWith(this.radios),
+ switchMap((radios: QueryList) => from(Promise.resolve(radios))),
+ takeUntil(this._destroy$)
+ )
+ .subscribe(() => this._updateRadiosState());
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _updateRadiosState(): void {
+ this._updateNames();
+ this._updateChecked();
+ this._updateDisabled();
+ }
+
+ private _updateNames(): void {
+ this.radios.forEach((radio: MdbRadioDirective) => radio._updateName(this.name));
+ }
+
+ private _updateChecked(): void {
+ this.radios.forEach((radio: MdbRadioDirective) => {
+ const isChecked = radio.value === this._value;
+ radio._updateChecked(isChecked);
+ });
+ }
+
+ private _updateDisabled(): void {
+ this.radios.forEach((radio: MdbRadioDirective) => radio._updateDisabledState(this._disabled));
+ }
+
+ // Control value accessor methods
+ registerOnChange(fn: (value: any) => any): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: () => any): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this._disabled = isDisabled;
+ this._updateDisabled();
+ }
+
+ writeValue(value: any): void {
+ this.value = value;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/radio/radio.directive.spec.ts b/projects/mdb-angular-ui-kit/radio/radio.directive.spec.ts
new file mode 100644
index 00000000..6ba75e5c
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/radio.directive.spec.ts
@@ -0,0 +1,73 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Component } from '@angular/core';
+import { MdbRadioModule } from './index';
+
+describe('MDB Checkbox', () => {
+ let component: BasicRadioComponent;
+ let fixture: ComponentFixture;
+ let nativeElement: HTMLElement;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [BasicRadioComponent],
+ imports: [MdbRadioModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(BasicRadioComponent);
+ component = fixture.componentInstance;
+ nativeElement = fixture.elementRef.nativeElement;
+ });
+
+ it('Should disable input element if disabled is set to true', () => {
+ component.disabled = true;
+ fixture.detectChanges();
+ const input = nativeElement.querySelector('input');
+
+ expect(input.hasAttribute('disabled')).toBe(true);
+ });
+
+ it('Should correctly update name attribute', () => {
+ component.name = 'test name';
+ fixture.detectChanges();
+
+ const input = nativeElement.querySelector('input');
+
+ expect(input.getAttribute('name')).toBe('test name');
+ });
+
+ it('Should correctly update input checked state', () => {
+ component.checked = true;
+ fixture.detectChanges();
+
+ const input = nativeElement.querySelector('input');
+
+ expect(input.checked).toBe(true);
+ });
+});
+
+const basicTemplate = `
+
+
+ Default radio
+
+`;
+
+@Component({
+ selector: 'mdb-radio-test',
+ template: basicTemplate,
+ standalone: false,
+})
+class BasicRadioComponent {
+ checked = false;
+ name = 'mdb-radio';
+ disabled = false;
+}
diff --git a/projects/mdb-angular-ui-kit/radio/radio.module.ts b/projects/mdb-angular-ui-kit/radio/radio.module.ts
new file mode 100644
index 00000000..a265ecd4
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/radio/radio.module.ts
@@ -0,0 +1,12 @@
+import { MdbRadioDirective } from './radio-button.directive';
+import { MdbRadioGroupDirective } from './radio-group.directive';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+@NgModule({
+ declarations: [MdbRadioDirective, MdbRadioGroupDirective],
+ exports: [MdbRadioDirective, MdbRadioGroupDirective],
+ imports: [CommonModule, FormsModule],
+})
+export class MdbRadioModule {}
diff --git a/projects/mdb-angular-ui-kit/range/index.ts b/projects/mdb-angular-ui-kit/range/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/range/ng-package.json b/projects/mdb-angular-ui-kit/range/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/range/public_api.ts b/projects/mdb-angular-ui-kit/range/public_api.ts
new file mode 100644
index 00000000..6d843762
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/public_api.ts
@@ -0,0 +1,2 @@
+export { MdbRangeModule } from './range.module';
+export { MdbRangeComponent } from './range.component';
diff --git a/projects/mdb-angular-ui-kit/range/range.component.html b/projects/mdb-angular-ui-kit/range/range.component.html
new file mode 100644
index 00000000..9672cce9
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/range.component.html
@@ -0,0 +1,27 @@
+{{ label }}
+
+
+
+ {{ value }}
+
+
diff --git a/projects/mdb-angular-ui-kit/range/range.component.spec.ts b/projects/mdb-angular-ui-kit/range/range.component.spec.ts
new file mode 100644
index 00000000..99d20f5d
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/range.component.spec.ts
@@ -0,0 +1,101 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
+import { MdbRangeModule } from './range.module';
+import { MdbRangeComponent } from './range.component';
+import { UntypedFormControl, ReactiveFormsModule } from '@angular/forms';
+import { By } from '@angular/platform-browser';
+
+const template = `
+
+`;
+
+@Component({
+ selector: 'mdb-range-test',
+ template,
+ standalone: false,
+})
+class TestRangeComponent implements OnInit {
+ @ViewChild(MdbRangeComponent) _range: MdbRangeComponent;
+
+ rangeControl = new UntypedFormControl(50);
+ ngOnInit(): void {
+ this.rangeControl.valueChanges.subscribe((val) => console.log(val));
+ }
+}
+
+describe('MDB Range', () => {
+ let fixture: ComponentFixture;
+ let component: any;
+ let mdbRange: any;
+ let thumb: any;
+ let valueThumb: any;
+ let input: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [TestRangeComponent],
+ imports: [MdbRangeModule, ReactiveFormsModule],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestRangeComponent);
+ fixture.detectChanges();
+ component = fixture.componentInstance;
+ mdbRange = fixture.debugElement.query(By.css('mdb-range'));
+ thumb = fixture.debugElement.query(By.css('.thumb'));
+ valueThumb = fixture.debugElement.query(By.css('.thumb-value'));
+ input = fixture.debugElement.query(By.css('input'));
+ });
+
+ it('should show thumb on mousedown and hide on mauseup', fakeAsync(() => {
+ expect(thumb.nativeElement.classList.contains('thumb-active')).toBe(false);
+
+ input.nativeElement.dispatchEvent(new MouseEvent('mousedown'));
+
+ fixture.detectChanges();
+ flush();
+
+ expect(thumb.nativeElement.classList.contains('thumb-active')).toBe(true);
+
+ input.nativeElement.dispatchEvent(new MouseEvent('mouseup'));
+
+ fixture.detectChanges();
+ flush();
+
+ expect(thumb.nativeElement.classList.contains('thumb-active')).toBe(false);
+ }));
+
+ it('should show input value', () => {
+ fixture.detectChanges();
+
+ expect(document.querySelector('.thumb')).not.toBe(null);
+ expect(valueThumb.nativeElement.textContent).toBe(input.nativeElement.value);
+ });
+
+ it('should update thumb value after input', () => {
+ input.nativeElement.value = 24;
+ input.nativeElement.dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+
+ expect(valueThumb.nativeElement.textContent).toBe('24');
+ });
+
+ it('should update value after set new FormControl', () => {
+ component.rangeControl = new UntypedFormControl(60);
+ fixture.detectChanges();
+
+ expect(valueThumb.nativeElement.textContent).toBe('60');
+ expect(input.nativeElement.value).toBe('60');
+ });
+
+ it('should update thumb position', fakeAsync(() => {
+ const initialThumbStyle = { ...component._range.thumbStyle };
+
+ component.rangeControl = new UntypedFormControl(70);
+
+ fixture.detectChanges();
+ flush();
+ const newThumbStyle = { ...component._range.thumbStyle };
+
+ expect(initialThumbStyle.left).not.toBe(newThumbStyle.left);
+ }));
+});
diff --git a/projects/mdb-angular-ui-kit/range/range.component.ts b/projects/mdb-angular-ui-kit/range/range.component.ts
new file mode 100644
index 00000000..b1f1f024
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/range.component.ts
@@ -0,0 +1,134 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import {
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ EventEmitter,
+ forwardRef,
+ HostListener,
+ Input,
+ Output,
+ ViewChild,
+} from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+export const RANGE_VALUE_ACCESOR: any = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => MdbRangeComponent),
+ multi: true,
+};
+@Component({
+ selector: 'mdb-range',
+ templateUrl: 'range.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [RANGE_VALUE_ACCESOR],
+ standalone: false,
+})
+export class MdbRangeComponent implements ControlValueAccessor, AfterViewInit {
+ @ViewChild('input') input: ElementRef;
+ @ViewChild('thumb') thumb: ElementRef;
+ @ViewChild('thumbValue') thumbValue: ElementRef;
+
+ @Input() id: string;
+ @Input() required: boolean;
+ @Input() name: string;
+ @Input() value: string;
+
+ @Input()
+ get disabled(): boolean {
+ return this._disabled;
+ }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ }
+ private _disabled: boolean;
+
+ @Input() label: string;
+ @Input() min = 0;
+ @Input() max = 100;
+ @Input() step: number;
+
+ @Input()
+ get default(): boolean {
+ return this._default;
+ }
+ set default(value: boolean) {
+ this._default = value;
+ }
+ private _default: boolean;
+
+ @Input() defaultRangeCounterClass: string;
+
+ @Output() rangeValueChange = new EventEmitter();
+
+ public visibility = false;
+ public thumbStyle: any;
+
+ @HostListener('change', ['$event']) onchange(event: any): void {
+ this.onChange(event.target.value);
+ }
+
+ @HostListener('input') onInput(): void {
+ this.rangeValueChange.emit({ value: this.value });
+ this.focusRangeInput();
+ }
+
+ constructor(private _cdRef: ChangeDetectorRef) {}
+
+ ngAfterViewInit(): void {
+ this.thumbPositionUpdate();
+ this._cdRef.detectChanges();
+ }
+
+ focusRangeInput(): void {
+ this.input.nativeElement.focus();
+ this.visibility = true;
+ }
+
+ blurRangeInput(): void {
+ this.input.nativeElement.blur();
+ this.visibility = false;
+ }
+
+ thumbPositionUpdate(): void {
+ const rangeInput = this.input.nativeElement;
+ const inputValue = rangeInput.value;
+ const minValue = rangeInput.min ? rangeInput.min : 0;
+ const maxValue = rangeInput.max ? rangeInput.max : 100;
+ const newValue = Number(((inputValue - minValue) * 100) / (maxValue - minValue));
+
+ this.value = inputValue;
+ this.thumbStyle = { left: `calc(${newValue}% + (${8 - newValue * 0.15}px))` };
+ }
+
+ // Control Value Accessor Methods
+ onChange = (_: any) => {};
+ onTouched = () => {};
+
+ writeValue(value: any): void {
+ this.value = value;
+
+ this._cdRef.markForCheck();
+
+ setTimeout(() => {
+ this.thumbPositionUpdate();
+ }, 0);
+ }
+
+ registerOnChange(fn: (_: any) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ static ngAcceptInputType_default: BooleanInput;
+ static ngAcceptInputType_disabled: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/range/range.module.ts b/projects/mdb-angular-ui-kit/range/range.module.ts
new file mode 100644
index 00000000..3347ea45
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/range/range.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { MdbRangeComponent } from './range.component';
+
+@NgModule({
+ imports: [CommonModule, FormsModule],
+ declarations: [MdbRangeComponent],
+ exports: [MdbRangeComponent],
+})
+export class MdbRangeModule {}
diff --git a/projects/mdb-angular-ui-kit/ripple/index.ts b/projects/mdb-angular-ui-kit/ripple/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/ripple/ng-package.json b/projects/mdb-angular-ui-kit/ripple/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/ripple/public_api.ts b/projects/mdb-angular-ui-kit/ripple/public_api.ts
new file mode 100644
index 00000000..bcab2457
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/public_api.ts
@@ -0,0 +1,2 @@
+export { MdbRippleDirective } from './ripple.directive';
+export { MdbRippleModule } from './ripple.module';
diff --git a/projects/mdb-angular-ui-kit/ripple/ripple-utils.spec.ts b/projects/mdb-angular-ui-kit/ripple/ripple-utils.spec.ts
new file mode 100644
index 00000000..a6c57d99
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/ripple-utils.spec.ts
@@ -0,0 +1,119 @@
+import { colorToRGB, durationToMsNumber, getDiameter } from './ripple-utils';
+
+describe('Ripple utils', () => {
+ const DEFAULT_RIPPLE_COLOR = [0, 0, 0];
+
+ it('should convert seconds string to miliseconds in number format', () => {
+ expect(durationToMsNumber('5s')).toEqual(5000);
+ });
+
+ it('should convert miliseconds string to miliseconds in number format', () => {
+ expect(durationToMsNumber('3000ms')).toEqual(3000);
+ });
+
+ it('should convert hex color to rgb', () => {
+ expect(colorToRGB('#c953d6')).toEqual([201, 83, 214]);
+ });
+
+ it('should convert short hex color to rgb', () => {
+ expect(colorToRGB('#fff')).toEqual([255, 255, 255]);
+ });
+
+ it('should convert rgba color to rgb', () => {
+ expect(colorToRGB('rgba(255,0,0,0.5)')).toEqual([255, 0, 0]);
+ });
+
+ it('should return default ripple color for transparent color', () => {
+ expect(colorToRGB('transparent')).toEqual(DEFAULT_RIPPLE_COLOR);
+ });
+
+ it('should return correct diameter for click in the first quadrant', () => {
+ const height = 50;
+ const width = 100;
+ const offsetX = 90;
+ const offsetY = 10;
+
+ const pythagorean = (sideA: number, sideB: number) => Math.sqrt(sideA ** 2 + sideB ** 2);
+
+ const getCorner = {
+ topLeft: pythagorean(offsetX, offsetY),
+ topRight: pythagorean(width - offsetX, offsetY),
+ bottomLeft: pythagorean(offsetX, height - offsetY),
+ bottomRight: pythagorean(width - offsetX, height - offsetY),
+ };
+
+ expect(getDiameter({ offsetX, offsetY, height, width })).toEqual(getCorner.bottomLeft * 2);
+ });
+
+ it('should return correct diameter for click in the second quadrant', () => {
+ const height = 50;
+ const width = 100;
+ const offsetX = 15;
+ const offsetY = 10;
+
+ const pythagorean = (sideA: number, sideB: number) => Math.sqrt(sideA ** 2 + sideB ** 2);
+
+ const getCorner = {
+ topLeft: pythagorean(offsetX, offsetY),
+ topRight: pythagorean(width - offsetX, offsetY),
+ bottomLeft: pythagorean(offsetX, height - offsetY),
+ bottomRight: pythagorean(width - offsetX, height - offsetY),
+ };
+
+ expect(getDiameter({ offsetX, offsetY, height, width })).toEqual(getCorner.bottomRight * 2);
+ });
+
+ it('should return correct diameter for click in the third quadrant', () => {
+ const height = 50;
+ const width = 100;
+ const offsetX = 15;
+ const offsetY = 90;
+
+ const pythagorean = (sideA: number, sideB: number) => Math.sqrt(sideA ** 2 + sideB ** 2);
+
+ const getCorner = {
+ topLeft: pythagorean(offsetX, offsetY),
+ topRight: pythagorean(width - offsetX, offsetY),
+ bottomLeft: pythagorean(offsetX, height - offsetY),
+ bottomRight: pythagorean(width - offsetX, height - offsetY),
+ };
+
+ expect(getDiameter({ offsetX, offsetY, height, width })).toEqual(getCorner.topRight * 2);
+ });
+
+ it('should return correct diameter for click in the fourth quadrant', () => {
+ const height = 50;
+ const width = 100;
+ const offsetX = 90;
+ const offsetY = 30;
+
+ const pythagorean = (sideA: number, sideB: number) => Math.sqrt(sideA ** 2 + sideB ** 2);
+
+ const getCorner = {
+ topLeft: pythagorean(offsetX, offsetY),
+ topRight: pythagorean(width - offsetX, offsetY),
+ bottomLeft: pythagorean(offsetX, height - offsetY),
+ bottomRight: pythagorean(width - offsetX, height - offsetY),
+ };
+
+ expect(getDiameter({ offsetX, offsetY, height, width })).toEqual(getCorner.topLeft * 2);
+ });
+
+ it('should return correct diameter for click in the center', () => {
+ const height = 50;
+ const width = 100;
+ const offsetX = 50;
+ const offsetY = 25;
+
+ const pythagorean = (sideA: number, sideB: number) => Math.sqrt(sideA ** 2 + sideB ** 2);
+
+ const getCorner = {
+ topLeft: pythagorean(offsetX, offsetY),
+ topRight: pythagorean(width - offsetX, offsetY),
+ bottomLeft: pythagorean(offsetX, height - offsetY),
+ bottomRight: pythagorean(width - offsetX, height - offsetY),
+ };
+
+ expect(getDiameter({ offsetX, offsetY, height, width })).toEqual(getCorner.topLeft * 2);
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/ripple/ripple-utils.ts b/projects/mdb-angular-ui-kit/ripple/ripple-utils.ts
new file mode 100644
index 00000000..bf148847
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/ripple-utils.ts
@@ -0,0 +1,100 @@
+const DEFAULT_RIPPLE_COLOR = [0, 0, 0];
+
+export function durationToMsNumber(time: string): number {
+ return Number(time.replace('ms', '').replace('s', '000'));
+}
+
+export function colorToRGB(color: any): number[] {
+ function hexToRgb(color: any): any {
+ const HEX_COLOR_LENGTH = 7;
+ const IS_SHORT_HEX = color.length < HEX_COLOR_LENGTH;
+ if (IS_SHORT_HEX) {
+ color = `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`;
+ }
+ return [
+ parseInt(color.substr(1, 2), 16),
+ parseInt(color.substr(3, 2), 16),
+ parseInt(color.substr(5, 2), 16),
+ ];
+ }
+
+ function namedColorsToRgba(color: any): any {
+ const tempElem = document.body.appendChild(document.createElement('fictum'));
+ const flag = 'rgb(1, 2, 3)';
+ tempElem.style.color = flag;
+ if (tempElem.style.color !== flag) {
+ return DEFAULT_RIPPLE_COLOR;
+ }
+ tempElem.style.color = color;
+ if (tempElem.style.color === flag || tempElem.style.color === '') {
+ return DEFAULT_RIPPLE_COLOR;
+ } // color parse failed
+ color = getComputedStyle(tempElem).color;
+ document.body.removeChild(tempElem);
+ return color;
+ }
+
+ function rgbaToRgb(color: any): any {
+ color = color.match(/[.\d]+/g).map((a) => +Number(a));
+ color.length = 3;
+ return color;
+ }
+
+ if (color.toLowerCase() === 'transparent') {
+ return DEFAULT_RIPPLE_COLOR;
+ }
+ if (color[0] === '#') {
+ return hexToRgb(color);
+ }
+ if (color.indexOf('rgb') === -1) {
+ color = namedColorsToRgba(color);
+ }
+ if (color.indexOf('rgb') === 0) {
+ return rgbaToRgb(color);
+ }
+
+ return DEFAULT_RIPPLE_COLOR;
+}
+
+export function getDiameter({
+ offsetX,
+ offsetY,
+ height,
+ width,
+}: {
+ [key: string]: number;
+}): number {
+ const top = offsetY <= height / 2;
+ const left = offsetX <= width / 2;
+ const pythagorean = (sideA: number, sideB: number) => Math.sqrt(sideA ** 2 + sideB ** 2);
+
+ const positionCenter = offsetY === height / 2 && offsetX === width / 2;
+ // mouse position on the quadrants of the coordinate system
+ const quadrant = {
+ first: top === true && left === false,
+ second: top === true && left === true,
+ third: top === false && left === true,
+ fourth: top === false && left === false,
+ };
+
+ const getCorner = {
+ topLeft: pythagorean(offsetX, offsetY),
+ topRight: pythagorean(width - offsetX, offsetY),
+ bottomLeft: pythagorean(offsetX, height - offsetY),
+ bottomRight: pythagorean(width - offsetX, height - offsetY),
+ };
+
+ let diameter = 0;
+
+ if (positionCenter || quadrant.fourth) {
+ diameter = getCorner.topLeft;
+ } else if (quadrant.third) {
+ diameter = getCorner.topRight;
+ } else if (quadrant.second) {
+ diameter = getCorner.bottomRight;
+ } else if (quadrant.first) {
+ diameter = getCorner.bottomLeft;
+ }
+
+ return diameter * 2;
+}
diff --git a/projects/mdb-angular-ui-kit/ripple/ripple.directive.spec.ts b/projects/mdb-angular-ui-kit/ripple/ripple.directive.spec.ts
new file mode 100644
index 00000000..6045bf52
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/ripple.directive.spec.ts
@@ -0,0 +1,138 @@
+import { Component } from '@angular/core';
+import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
+import { MdbRippleModule } from './ripple.module';
+
+const template = `
+
+ Button
+
+`;
+
+@Component({
+ selector: 'mdb-ripple-test',
+ template,
+ standalone: false,
+})
+class TestRippleComponent {
+ rippleCentered = true;
+ rippleColor = '';
+ rippleDuration = '1s';
+ rippleRadius = 100;
+ rippleUnbound = false;
+}
+
+describe('MDB Ripple', () => {
+ let fixture: ComponentFixture;
+ let component: any;
+ let button: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [TestRippleComponent],
+ imports: [MdbRippleModule],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestRippleComponent);
+ fixture.detectChanges();
+ component = fixture.componentInstance;
+ button = document.querySelector('.btn');
+ });
+
+ it('should set class ripple-surface on element', () => {
+ expect(button.classList.contains('ripple-surface')).toBe(true);
+ });
+
+ it('should create helper element on click', () => {
+ button.click();
+
+ fixture.detectChanges();
+ expect(button.children[0]).not.toBe(null);
+ });
+
+ it('should add class ripple-wave on helper element', () => {
+ button.click();
+
+ fixture.detectChanges();
+
+ expect(button.children[0]).not.toBe(null);
+ expect(button.children[0].classList.contains('ripple-wave')).toBe(true);
+ });
+
+ it('should set class ripple-surface-unbound on wrapper if rippleUnbound option is true', () => {
+ component.rippleUnbound = true;
+
+ fixture.detectChanges();
+
+ button.click();
+
+ fixture.detectChanges();
+ expect(button.classList.contains('ripple-surface-unbound')).toBe(true);
+ });
+
+ it('should remove helper after duration', fakeAsync(() => {
+ button.click();
+
+ fixture.detectChanges();
+
+ let helper = button.children[0];
+
+ expect(helper).not.toBe(null);
+
+ tick(1000);
+
+ helper = button.children[0];
+
+ expect(helper).toBe(undefined);
+ }));
+
+ it('should accept Bootstrap colors', () => {
+ fixture.componentInstance.rippleColor = 'primary';
+ fixture.detectChanges();
+
+ button.click();
+ fixture.detectChanges();
+
+ expect(button.classList).toContain('ripple-surface-primary');
+ });
+
+ it('should add new colors class and remove previous color classes', fakeAsync(() => {
+ fixture.componentInstance.rippleColor = 'primary';
+ fixture.detectChanges();
+
+ button.click();
+ fixture.detectChanges();
+
+ expect(button.classList).toContain('ripple-surface-primary');
+
+ fixture.componentInstance.rippleColor = 'secondary';
+ fixture.detectChanges();
+
+ button.click();
+ fixture.detectChanges();
+
+ flush();
+
+ expect(button.classList).not.toContain('ripple-surface-primary');
+ expect(button.classList).toContain('ripple-surface-secondary');
+ }));
+
+ it('should add ripple-surface-color class only if Bootstrap color type is used', () => {
+ const REGEXP_CLASS_COLOR = new RegExp(`${'ripple-surface'}-[a-z]+`, 'gi');
+
+ fixture.componentInstance.rippleColor = '#c953d6';
+ fixture.detectChanges();
+
+ button.click();
+ fixture.detectChanges();
+
+ expect(REGEXP_CLASS_COLOR.test(button.classList)).toBe(false);
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/ripple/ripple.directive.ts b/projects/mdb-angular-ui-kit/ripple/ripple.directive.ts
new file mode 100644
index 00000000..e9270fe0
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/ripple.directive.ts
@@ -0,0 +1,205 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { Directive, ElementRef, HostBinding, HostListener, Input, Renderer2 } from '@angular/core';
+import { colorToRGB, durationToMsNumber, getDiameter } from './ripple-utils';
+
+const TRANSITION_BREAK_OPACITY = 0.5;
+
+const GRADIENT =
+ 'rgba({{color}}, 0.2) 0, rgba({{color}}, 0.3) 40%, rgba({{color}}, 0.4) 50%, rgba({{color}}, 0.5) 60%, rgba({{color}}, 0) 70%';
+const BOOTSTRAP_COLORS = [
+ 'primary',
+ 'secondary',
+ 'success',
+ 'danger',
+ 'warning',
+ 'info',
+ 'light',
+ 'dark',
+];
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbRipple]',
+ exportAs: 'mdbRipple',
+ standalone: false,
+})
+export class MdbRippleDirective {
+ @Input()
+ get rippleCentered(): boolean {
+ return this._rippleCentered;
+ }
+ set rippleCentered(value: boolean) {
+ this._rippleCentered = coerceBooleanProperty(value);
+ }
+ private _rippleCentered = false;
+
+ @Input() rippleColor = '';
+ @Input() rippleDuration = '500ms';
+ @Input() rippleRadius = 0;
+
+ @Input()
+ get rippleUnbound(): boolean {
+ return this._rippleUnbound;
+ }
+ set rippleUnbound(value: boolean) {
+ this._rippleUnbound = coerceBooleanProperty(value);
+ }
+ private _rippleUnbound = false;
+
+ private _rippleInSpan = false;
+
+ private _rippleTimer = null;
+
+ constructor(private _elementRef: ElementRef, private _renderer: Renderer2) {}
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ @HostBinding('class.ripple-surface') ripple = true;
+
+ @HostListener('click', ['$event'])
+ _createRipple(event: any): void {
+ const { layerX, layerY } = event;
+ const offsetX = layerX;
+ const offsetY = layerY;
+ const height = this.host.offsetHeight;
+ const width = this.host.offsetWidth;
+ const duration = durationToMsNumber(this.rippleDuration);
+ const diameterOptions = {
+ offsetX: this.rippleCentered ? height / 2 : offsetX,
+ offsetY: this.rippleCentered ? width / 2 : offsetY,
+ height,
+ width,
+ };
+ const diameter = getDiameter(diameterOptions);
+ const radiusValue = this.rippleRadius || diameter / 2;
+
+ const opacity = {
+ delay: duration * TRANSITION_BREAK_OPACITY,
+ duration: duration - duration * TRANSITION_BREAK_OPACITY,
+ };
+
+ const styles = {
+ left: this.rippleCentered ? `${width / 2 - radiusValue}px` : `${offsetX - radiusValue}px`,
+ top: this.rippleCentered ? `${height / 2 - radiusValue}px` : `${offsetY - radiusValue}px`,
+ height: `${this.rippleRadius * 2 || diameter}px`,
+ width: `${this.rippleRadius * 2 || diameter}px`,
+ transitionDelay: `0s, ${opacity.delay}ms`,
+ transitionDuration: `${duration}ms, ${opacity.duration}ms`,
+ };
+
+ const rippleHTML = this._renderer.createElement('div');
+
+ if (this.host.tagName.toLowerCase() === 'input') {
+ this._createWrapperSpan();
+ }
+
+ this._createHTMLRipple(this.host, rippleHTML, styles);
+ this._removeHTMLRipple(rippleHTML, duration);
+ }
+
+ private _createWrapperSpan(): void {
+ const parent = this._renderer.parentNode(this.host);
+ this._rippleInSpan = true;
+ if (parent.tagName.toLowerCase() === 'span' && parent.classList.contains('ripple-surface')) {
+ this._elementRef.nativeElement = parent;
+ } else {
+ const wrapper = this._renderer.createElement('span');
+
+ this._renderer.addClass(wrapper, 'ripple-surface');
+ this._renderer.addClass(wrapper, 'input-wrapper');
+
+ this._renderer.setStyle(wrapper, 'border', 0);
+
+ const shadow = getComputedStyle(this.host).boxShadow;
+ this._renderer.setStyle(wrapper, 'box-shadow', shadow);
+
+ // Put element as child
+ parent.replaceChild(wrapper, this.host);
+ wrapper.appendChild(this.host);
+ this._elementRef.nativeElement = wrapper;
+ }
+ this.host.focus();
+ }
+
+ _removeWrapperSpan() {
+ const child = this.host.firstChild;
+ this.host.replaceWith(child);
+ this._elementRef.nativeElement = child;
+ this.host.focus();
+ this._rippleInSpan = false;
+ }
+
+ private _createHTMLRipple(wrapper: HTMLElement, ripple: HTMLElement, styles: any): void {
+ Object.keys(styles).forEach((property) => (ripple.style[property] = styles[property]));
+ this._renderer.addClass(ripple, 'ripple-wave');
+
+ if (this.rippleColor !== '') {
+ this._removeOldColorClasses(wrapper);
+ this._addColor(ripple, wrapper);
+ }
+
+ this._toggleUnbound(wrapper);
+ this._appendRipple(ripple, wrapper);
+ }
+
+ private _removeHTMLRipple(ripple: HTMLElement, duration: number): void {
+ if (this._rippleTimer) {
+ clearTimeout(this._rippleTimer);
+ this._rippleTimer = null;
+ }
+ this._rippleTimer = setTimeout(() => {
+ if (ripple) {
+ ripple.remove();
+ this.host.querySelectorAll('.ripple-wave').forEach((rippleEl) => {
+ rippleEl.remove();
+ });
+ if (this._rippleInSpan && this.host.classList.contains('input-wrapper')) {
+ this._removeWrapperSpan();
+ }
+ }
+ }, duration);
+ }
+
+ _appendRipple(target: HTMLElement, parent: HTMLElement): void {
+ const FIX_ADD_RIPPLE_EFFECT = 50; // delay for active animations
+ this._renderer.appendChild(parent, target);
+ setTimeout(() => {
+ this._renderer.addClass(target, 'active');
+ }, FIX_ADD_RIPPLE_EFFECT);
+ }
+
+ _toggleUnbound(target: HTMLElement): void {
+ if (this.rippleUnbound) {
+ this._renderer.addClass(target, 'ripple-surface-unbound');
+ } else {
+ this._renderer.removeClass(target, 'ripple-surface-unbound');
+ }
+ }
+
+ _addColor(target: HTMLElement, parent: HTMLElement): void {
+ const isBootstrapColor = BOOTSTRAP_COLORS.find(
+ (color) => color === this.rippleColor.toLowerCase()
+ );
+
+ if (isBootstrapColor) {
+ this._renderer.addClass(parent, `${'ripple-surface'}-${this.rippleColor.toLowerCase()}`);
+ } else {
+ const rgbValue = colorToRGB(this.rippleColor).join(',');
+ const gradientImage = GRADIENT.split('{{color}}').join(`${rgbValue}`);
+ target.style.backgroundImage = `radial-gradient(circle, ${gradientImage})`;
+ }
+ }
+
+ _removeOldColorClasses(target: HTMLElement): void {
+ const REGEXP_CLASS_COLOR = new RegExp(`${'ripple-surface'}-[a-z]+`, 'gi');
+ const PARENT_CLASSS_COLOR = target.classList.value.match(REGEXP_CLASS_COLOR) || [];
+ PARENT_CLASSS_COLOR.forEach((className) => {
+ this._renderer.removeClass(target, className);
+ });
+ }
+
+ static ngAcceptInputType_rippleCentered: BooleanInput;
+ static ngAcceptInputType_rippleUnbound: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/ripple/ripple.module.ts b/projects/mdb-angular-ui-kit/ripple/ripple.module.ts
new file mode 100644
index 00000000..3ff0a9d7
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/ripple/ripple.module.ts
@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { MdbRippleDirective } from './ripple.directive';
+
+@NgModule({
+ declarations: [MdbRippleDirective],
+ imports: [],
+ exports: [MdbRippleDirective],
+})
+export class MdbRippleModule {}
diff --git a/projects/mdb-angular-ui-kit/schematics/collection.json b/projects/mdb-angular-ui-kit/schematics/collection.json
new file mode 100644
index 00000000..c7e10c80
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/schematics/collection.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
+ "schematics": {
+ "ng-add": {
+ "description": "Add MDB Angular Ui Kit to the application.",
+ "factory": "./ng-add/index#ngAdd",
+ "schema": "./ng-add/schema.json"
+ },
+ "ng-add-mdb-setup": {
+ "description": "Setup MDB Angular.",
+ "factory": "./ng-add/mdb-setup",
+ "schema": "./ng-add/schema.json"
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/schematics/ng-add/index.ts b/projects/mdb-angular-ui-kit/schematics/ng-add/index.ts
new file mode 100644
index 00000000..ad1cda85
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/schematics/ng-add/index.ts
@@ -0,0 +1,29 @@
+import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
+import { RunSchematicTask, NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
+import { Schema } from './schema';
+import { addPackageToPackageJson } from './package';
+
+// Just return the tree
+export function ngAdd(options: Schema): Rule {
+ return (tree: Tree, context: SchematicContext) => {
+ const angularDependencyVersion = '^20.0.0';
+
+ addPackageToPackageJson(tree, '@angular/cdk', angularDependencyVersion);
+ addPackageToPackageJson(tree, '@angular/forms', angularDependencyVersion);
+ addPackageToPackageJson(tree, '@angular/animations', angularDependencyVersion);
+
+ if (options.fontAwesome) {
+ addPackageToPackageJson(tree, '@fortawesome/fontawesome-free', '^6.0.0');
+ }
+
+ if (options.charts) {
+ addPackageToPackageJson(tree, 'chart.js', '^3.1.1');
+ }
+
+ const installMainDependenciesTask = context.addTask(new NodePackageInstallTask());
+
+ context.addTask(new RunSchematicTask('ng-add-mdb-setup', options), [
+ installMainDependenciesTask,
+ ]);
+ };
+}
diff --git a/projects/mdb-angular-ui-kit/schematics/ng-add/mdb-setup.ts b/projects/mdb-angular-ui-kit/schematics/ng-add/mdb-setup.ts
new file mode 100644
index 00000000..056e62e2
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/schematics/ng-add/mdb-setup.ts
@@ -0,0 +1,238 @@
+import { SchematicContext, Tree, chain } from '@angular-devkit/schematics';
+import { getWorkspace } from '@schematics/angular/utility/workspace';
+import { ProjectType } from '@schematics/angular/utility/workspace-models';
+import {
+ getProjectMainFile,
+ addModuleImportToRootModule,
+ getProjectFromWorkspace,
+ getProjectIndexFiles,
+ appendHtmlElementToHead,
+ getProjectStyleFile,
+ isStandaloneApp,
+} from '@angular/cdk/schematics';
+import { addRootProvider } from '@schematics/angular/utility';
+import { Schema } from './schema';
+
+const mdbModules = [
+ { name: 'MdbAccordionModule', path: 'mdb-angular-ui-kit/accordion'},
+ { name: 'MdbCarouselModule', path: 'mdb-angular-ui-kit/carousel'},
+ { name: 'MdbCheckboxModule', path: 'mdb-angular-ui-kit/checkbox'},
+ { name: 'MdbCollapseModule', path: 'mdb-angular-ui-kit/collapse'},
+ { name: 'MdbDropdownModule', path: 'mdb-angular-ui-kit/dropdown'},
+ { name: 'MdbFormsModule', path: 'mdb-angular-ui-kit/forms'},
+ { name: 'MdbModalModule', path: 'mdb-angular-ui-kit/modal'},
+ { name: 'MdbPopoverModule', path: 'mdb-angular-ui-kit/popover'},
+ { name: 'MdbRadioModule', path: 'mdb-angular-ui-kit/radio'},
+ { name: 'MdbRangeModule', path: 'mdb-angular-ui-kit/range'},
+ { name: 'MdbRippleModule', path: 'mdb-angular-ui-kit/ripple'},
+ { name: 'MdbScrollspyModule', path: 'mdb-angular-ui-kit/scrollspy'},
+ { name: 'MdbTabsModule', path: 'mdb-angular-ui-kit/tabs'},
+ { name: 'MdbTooltipModule', path: 'mdb-angular-ui-kit/tooltip'},
+ { name: 'MdbValidationModule', path: 'mdb-angular-ui-kit/validation'},
+];
+
+// eslint-disable-next-line space-before-function-paren
+export default function (options: Schema): any {
+ return async (tree: Tree) => {
+ const workspace: any = await getWorkspace(tree);
+ const project = getProjectFromWorkspace(workspace, options.project);
+
+ if (project.extensions.projectType === ProjectType.Application) {
+ return chain([
+ addMdbModulesImports(options),
+ addAngularAnimationsModule(options),
+ addStylesImports(options),
+ addChartsToScripts(options),
+ addRobotoFontToIndexHtml(options),
+ updateAppComponentContent(),
+ ]);
+ }
+ return;
+ };
+}
+
+function addMdbModulesImports(options: Schema): any {
+ return async (tree: Tree) => {
+ const workspace: any = await getWorkspace(tree);
+ const project = getProjectFromWorkspace(workspace, options.project);
+ const mainFile = getProjectMainFile(project);
+
+ if (isStandaloneApp(tree, mainFile)) {
+ return;
+ }
+
+ if (options.modules) {
+ mdbModules.forEach((module) => {
+ addModuleImportToRootModule(tree, module.name, module.path, project);
+ });
+ }
+
+ return tree;
+ };
+}
+
+function addAngularAnimationsModule(options: Schema): any {
+ return () => {
+ return addRootProvider(options.project, ({ code, external }) => {
+ return code`${external('provideAnimations', '@angular/platform-browser/animations')}(${
+ options.animations ? '' : `'noop'`
+ })`;
+ });
+ };
+}
+
+function addRobotoFontToIndexHtml(options: Schema): any {
+ return async (tree: Tree, context: SchematicContext) => {
+ const fontUrl = 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,600&display=swap';
+ const workspace: any = await getWorkspace(tree);
+ const project: any = getProjectFromWorkspace(workspace, options.project);
+ const projectIndexFiles = getProjectIndexFiles(project);
+ const logger = context.logger;
+
+ if (options.robotoFont) {
+ if (!projectIndexFiles.length) {
+ logger.error('Index HTML not found');
+ logger.info('Add roboto font manually');
+ return;
+ }
+
+ projectIndexFiles.forEach((indexFile: any) => {
+ appendHtmlElementToHead(tree, indexFile, ` `);
+ });
+ }
+
+ return tree;
+ };
+}
+
+function addStylesImports(options: Schema): any {
+ return async (host: Tree, context: SchematicContext) => {
+ const workspace: any = await getWorkspace(host);
+ const project = getProjectFromWorkspace(workspace, options.project);
+ const logger = context.logger;
+ const styleFilePath = getProjectStyleFile(project);
+
+ if (!styleFilePath) {
+ logger.error(
+ `Could not find the default style file for this project. Please add styles imports manually`
+ );
+ return;
+ }
+
+ const buffer = host.read(styleFilePath);
+
+ if (!buffer) {
+ logger.error(
+ `Could not read the default style file for this project. Please add styles imports manually`
+ );
+ return;
+ }
+
+ const fileContent = buffer.toString();
+
+ let newContent: string;
+
+ if (options.fontAwesome) {
+ newContent =
+ `@import '@fortawesome/fontawesome-free/css/all.css';\n` +
+ `@import 'mdb-angular-ui-kit/assets/scss/mdb.scss';\n`;
+ } else {
+ newContent = `@import 'mdb-angular-ui-kit/assets/scss/mdb.scss';\n`;
+ }
+
+ if (fileContent.includes(newContent)) {
+ return;
+ }
+
+ const recorder = host.beginUpdate(styleFilePath);
+
+ recorder.insertLeft(fileContent.length, newContent);
+ host.commitUpdate(recorder);
+ };
+}
+
+function addChartsToScripts(options: Schema): any {
+ return async (host: Tree, context: SchematicContext) => {
+ const logger = context.logger;
+
+ const chartsPath = 'node_modules/chart.js/dist/chart.js';
+
+ if (options.charts) {
+ const angularJsonFile = host.read('angular.json');
+
+ if (angularJsonFile) {
+ const angularJsonFileObject = JSON.parse(angularJsonFile.toString('utf-8'));
+ const project = options.project
+ ? options.project
+ : Object.keys(angularJsonFileObject.projects)[0];
+ const projectObject = angularJsonFileObject.projects[project];
+ if (!projectObject.architect.build.options.scripts) {
+ projectObject.architect.build.options.scripts = [];
+ }
+ const scripts = projectObject.architect.build.options.scripts;
+
+ scripts.push(chartsPath);
+
+ host.overwrite('angular.json', JSON.stringify(angularJsonFileObject, null, 2));
+ } else {
+ logger.error('Failed to add charts script to angular.json');
+ }
+ }
+ };
+}
+
+function updateAppComponentContent(): any {
+ return async (host: Tree, context: SchematicContext) => {
+ const filePath = './src/app/app.html';
+ const logger = context.logger;
+ const buffer = host.read(filePath);
+
+ if (!buffer) {
+ logger.error('No buffer');
+ return;
+ }
+
+ const fileContent = buffer.toString();
+
+ const newContent =
+ `\n` +
+ `
\n` +
+ `
\n` +
+ `
\n` +
+ `
Thank you for using our product. We're glad you're with us. \n` +
+ `
MDB Team
\n` +
+ `
\n` +
+ ` PS. We'll be releasing "How to build your first project with MDB 5 Angular" tutorial soon.\n` +
+ `
\n` +
+ `
Join now \n` +
+ `
\n` +
+ `
\n` +
+ `
`;
+
+ const hasNewContent = fileContent.includes(newContent);
+ const hasDefaultContent =
+ fileContent.includes('Delete the template below') &&
+ fileContent.includes('to get started with your project!') &&
+ fileContent.includes('Congratulations! Your app is running.');
+
+ if (hasNewContent || !hasDefaultContent) {
+ return;
+ }
+
+ const recorder = host.beginUpdate(filePath);
+
+ recorder.remove(0, fileContent.length);
+ recorder.insertLeft(0, newContent);
+ host.commitUpdate(recorder);
+ };
+}
diff --git a/projects/mdb-angular-ui-kit/schematics/ng-add/package.ts b/projects/mdb-angular-ui-kit/schematics/ng-add/package.ts
new file mode 100644
index 00000000..3c30a87d
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/schematics/ng-add/package.ts
@@ -0,0 +1,28 @@
+import { Tree } from '@angular-devkit/schematics';
+
+function sortObjectByKeys(obj: any): any {
+ return Object.keys(obj)
+ .sort()
+ .reduce((result: any, key) => (result[key] = obj[key]) && result, {});
+}
+
+export function addPackageToPackageJson(tree: Tree, pkg: string, version: string): Tree {
+ if (tree.exists('package.json')) {
+ const packageJsonFile = tree.read('package.json');
+ const sourceText = packageJsonFile && packageJsonFile.toString('utf-8');
+ const json = sourceText && JSON.parse(sourceText);
+
+ if (!json.dependencies) {
+ json.dependencies = {};
+ }
+
+ if (!json.dependencies[pkg]) {
+ json.dependencies[pkg] = version;
+ json.dependencies = sortObjectByKeys(json.dependencies);
+ }
+
+ tree.overwrite('package.json', JSON.stringify(json, null, 2));
+ }
+
+ return tree;
+}
diff --git a/projects/mdb-angular-ui-kit/schematics/ng-add/schema.json b/projects/mdb-angular-ui-kit/schematics/ng-add/schema.json
new file mode 100644
index 00000000..937f7f60
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/schematics/ng-add/schema.json
@@ -0,0 +1,46 @@
+{
+ "$schema": "http://json-schema.org/schema",
+ "$id": "mdb-angular-ng-add",
+ "title": "MDB Angular ng-add schematic",
+ "type": "object",
+ "properties": {
+ "project": {
+ "type": "string",
+ "description": "Name of the project.",
+ "$default": {
+ "$source": "projectName"
+ }
+ },
+ "modules": {
+ "type": "boolean",
+ "default": true,
+ "description": "Whether to import all MDB modules.",
+ "x-prompt": "Import all MDB modules?"
+ },
+ "robotoFont": {
+ "type": "boolean",
+ "default": true,
+ "description": "Whether to add Roboto Font.",
+ "x-prompt": "Set up Roboto Font?"
+ },
+ "animations": {
+ "type": "boolean",
+ "default": true,
+ "description": "Whether to set up browser animations.",
+ "x-prompt": "Set up Angular browser animations?"
+ },
+ "fontAwesome": {
+ "type": "boolean",
+ "default": true,
+ "description": "Whether to install and configure Font Awesome.",
+ "x-prompt": "Set up Font Awesome?"
+ },
+ "charts": {
+ "type": "boolean",
+ "default": true,
+ "description": "Whether to install and configure Charts.",
+ "x-prompt": "Set up Charts?"
+ }
+ },
+ "required": []
+}
diff --git a/projects/mdb-angular-ui-kit/schematics/ng-add/schema.ts b/projects/mdb-angular-ui-kit/schematics/ng-add/schema.ts
new file mode 100644
index 00000000..783672fa
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/schematics/ng-add/schema.ts
@@ -0,0 +1,8 @@
+export interface Schema {
+ project: string;
+ modules: boolean;
+ robotoFont: boolean;
+ animations: boolean;
+ fontAwesome: boolean;
+ charts: boolean;
+}
diff --git a/projects/mdb-angular-ui-kit/scrollspy/index.ts b/projects/mdb-angular-ui-kit/scrollspy/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/scrollspy/ng-package.json b/projects/mdb-angular-ui-kit/scrollspy/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/scrollspy/public_api.ts b/projects/mdb-angular-ui-kit/scrollspy/public_api.ts
new file mode 100644
index 00000000..697543bb
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/public_api.ts
@@ -0,0 +1,6 @@
+export { MdbScrollspyDirective } from './scrollspy.directive';
+export { MdbScrollspyElementDirective } from './scrollspy-element.directive';
+export { MdbScrollspyWindowDirective } from './scrollspy-window.directive';
+export { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+export { MdbScrollspyService } from './scrollspy.service';
+export { MdbScrollspyModule } from './scrollspy.module';
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy-element.directive.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-element.directive.ts
new file mode 100644
index 00000000..0265f064
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-element.directive.ts
@@ -0,0 +1,101 @@
+import {
+ Directive,
+ ElementRef,
+ OnInit,
+ Renderer2,
+ NgZone,
+ Input,
+ AfterViewInit,
+ Inject,
+ DOCUMENT,
+} from '@angular/core';
+import { MdbScrollspyService } from './scrollspy.service';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbScrollspyElement]',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export class MdbScrollspyElementDirective implements OnInit, AfterViewInit {
+ private id: string;
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ @Input() container: HTMLElement;
+
+ @Input('mdbScrollspyElement')
+ get scrollSpyId(): string {
+ return this._scrollSpyId;
+ }
+ set scrollSpyId(newId: string) {
+ if (newId) {
+ this._scrollSpyId = newId;
+ }
+ }
+ private _scrollSpyId: string;
+
+ @Input() offset = 0;
+
+ constructor(
+ private _elementRef: ElementRef,
+ private renderer: Renderer2,
+ private ngZone: NgZone,
+ private scrollSpyService: MdbScrollspyService,
+ @Inject(DOCUMENT) private _document: any
+ ) {}
+
+ isElementInViewport(): boolean {
+ const scrollTop = this.container.scrollTop;
+ const elTop = this.host.offsetTop - this.offset;
+ const elHeight = this.host.offsetHeight;
+
+ return scrollTop >= elTop && scrollTop < elTop + elHeight;
+ }
+
+ updateActiveState(scrollSpyId: string, id: string): void {
+ if (this.isElementInViewport()) {
+ this.scrollSpyService.removeActiveLinks(scrollSpyId);
+ this.scrollSpyService.updateActiveState(scrollSpyId, id);
+ }
+ }
+
+ onScroll(): void {
+ this.updateActiveState(this.scrollSpyId, this.id);
+ }
+
+ listenToScroll(): void {
+ this.renderer.listen(this.container, 'scroll', () => {
+ this.onScroll();
+ });
+ }
+
+ ngOnInit(): void {
+ this.id = this.host.id;
+
+ if (!this.container) {
+ this.container = this._getClosestEl(this.host, '.scrollspy-container');
+ }
+
+ this.renderer.setStyle(this.container, 'position', 'relative');
+
+ this.ngZone.runOutsideAngular(this.listenToScroll.bind(this));
+ }
+
+ ngAfterViewInit(): void {
+ setTimeout(() => {
+ this.updateActiveState(this.scrollSpyId, this.id);
+ }, 0);
+ }
+
+ private _getClosestEl(el: any, selector: string): HTMLElement | null {
+ for (; el && el !== this._document; el = el.parentNode) {
+ if (el.matches && el.matches(selector)) {
+ return el;
+ }
+ }
+ return null;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy-link.directive.spec.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-link.directive.spec.ts
new file mode 100644
index 00000000..3b1e1e7d
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-link.directive.spec.ts
@@ -0,0 +1,28 @@
+import { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+
+describe('ScrollspyDirective', () => {
+ let scrollspyLink: MdbScrollspyLinkDirective;
+ const cdRefMock = {
+ detectChanges: jest.fn(),
+ };
+
+ beforeEach(() => {
+ scrollspyLink = new MdbScrollspyLinkDirective(cdRefMock as any, document);
+ });
+
+ it('should get section with id equal to link id', () => {
+ scrollspyLink.id = 'scrollspy';
+ const div = document.createElement('div');
+ div.setAttribute('id', scrollspyLink.id);
+ document.body.appendChild(div);
+
+ scrollspyLink.ngOnInit();
+
+ expect(scrollspyLink.section).toBe(div);
+ });
+
+ it('should activate change detection cycle', () => {
+ scrollspyLink.detectChanges();
+ expect(cdRefMock.detectChanges).toHaveBeenCalled();
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy-link.directive.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-link.directive.ts
new file mode 100644
index 00000000..61cfe605
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-link.directive.ts
@@ -0,0 +1,74 @@
+import {
+ Directive,
+ OnInit,
+ Input,
+ HostListener,
+ HostBinding,
+ ChangeDetectorRef,
+ Inject,
+ DOCUMENT,
+} from '@angular/core';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbScrollspyLink]',
+ standalone: false,
+})
+export class MdbScrollspyLinkDirective implements OnInit {
+ @Input()
+ get scrollIntoView(): boolean {
+ return this._scrollIntoView;
+ }
+ set scrollIntoView(value: boolean) {
+ this._scrollIntoView = value;
+ }
+ private _scrollIntoView = true;
+
+ get section(): HTMLElement {
+ return this._section;
+ }
+ set section(value: HTMLElement) {
+ if (value) {
+ this._section = value;
+ }
+ }
+ private _section: HTMLElement;
+ private _id: string;
+
+ constructor(private cdRef: ChangeDetectorRef, @Inject(DOCUMENT) private document: any) {}
+
+ @Input('mdbScrollspyLink')
+ get id(): string {
+ return this._id;
+ }
+ set id(newId: string) {
+ if (newId) {
+ this._id = newId;
+ }
+ }
+
+ @HostBinding('class.scrollspy-link')
+ scrollspyLink = true;
+
+ @HostBinding('class.active')
+ active = false;
+
+ @HostListener('click', [])
+ onClick(): void {
+ if (this.section && this.scrollIntoView === true) {
+ this.section.scrollIntoView();
+ }
+ }
+
+ detectChanges(): void {
+ this.cdRef.detectChanges();
+ }
+
+ assignSectionToId(): void {
+ this.section = this.document.documentElement.querySelector(`#${this.id}`);
+ }
+
+ ngOnInit(): void {
+ this.assignSectionToId();
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy-window.directive.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-window.directive.ts
new file mode 100644
index 00000000..179d99e5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy-window.directive.ts
@@ -0,0 +1,82 @@
+import {
+ Directive,
+ ElementRef,
+ OnInit,
+ Inject,
+ Renderer2,
+ NgZone,
+ Input,
+ AfterViewInit,
+ DOCUMENT,
+} from '@angular/core';
+
+import { MdbScrollspyService } from './scrollspy.service';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbScrollspyWindow]',
+ standalone: false,
+})
+export class MdbScrollspyWindowDirective implements OnInit, AfterViewInit {
+ private id: string;
+
+ @Input('mdbScrollspyWindow')
+ get scrollSpyId(): string {
+ return this._scrollSpyId;
+ }
+ set scrollSpyId(newId: string) {
+ if (newId) {
+ this._scrollSpyId = newId;
+ }
+ }
+ private _scrollSpyId: string;
+
+ @Input() offset = 0;
+
+ constructor(
+ @Inject(DOCUMENT) private document: any,
+ private el: ElementRef,
+ private renderer: Renderer2,
+ private ngZone: NgZone,
+ private scrollSpyService: MdbScrollspyService
+ ) {}
+
+ isElementInViewport(): boolean {
+ const scrollTop = this.document.documentElement.scrollTop || this.document.body.scrollTop;
+ const elHeight = this.el.nativeElement.offsetHeight;
+ const elTop = this.el.nativeElement.offsetTop - this.offset;
+ const elBottom = elTop + elHeight;
+
+ return scrollTop >= elTop && scrollTop <= elBottom;
+ }
+
+ updateActiveState(scrollSpyId: string, id: string): void {
+ if (this.isElementInViewport()) {
+ this.scrollSpyService.updateActiveState(scrollSpyId, id);
+ } else {
+ this.scrollSpyService.removeActiveState(scrollSpyId, id);
+ }
+ }
+
+ onScroll(): void {
+ this.updateActiveState(this.scrollSpyId, this.id);
+ }
+
+ listenToScroll(): void {
+ this.renderer.listen(window, 'scroll', () => {
+ this.onScroll();
+ });
+ }
+
+ ngOnInit(): void {
+ this.id = this.el.nativeElement.id;
+
+ this.ngZone.runOutsideAngular(this.listenToScroll.bind(this));
+ }
+
+ ngAfterViewInit(): void {
+ setTimeout(() => {
+ this.updateActiveState(this.scrollSpyId, this.id);
+ }, 0);
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy.directive.spec.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.directive.spec.ts
new file mode 100644
index 00000000..329406e0
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.directive.spec.ts
@@ -0,0 +1,37 @@
+import { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+import { MdbScrollspyDirective } from './scrollspy.directive';
+import { MdbScrollspyService } from './scrollspy.service';
+
+describe('ScrollspyDirective', () => {
+ let scrollspy: MdbScrollspyDirective;
+ let scrollspyService: MdbScrollspyService;
+ const cdRefMock = {
+ detectChanges: jest.fn(),
+ };
+
+ beforeEach(() => {
+ scrollspyService = new MdbScrollspyService();
+ // scrollspy = new MdbScrollspyDirective(scrollspyService);
+ });
+
+ it('should add new scrollspy to service after content init', () => {
+ // const spy = jest.spyOn(scrollspyService, 'addScrollspy');
+ // scrollspy.ngAfterContentInit();
+ // expect(spy).toHaveBeenCalled();
+ });
+
+ it('should remove scrollspy from service on destroy', () => {
+ // const spy = jest.spyOn(scrollspyService, 'removeScrollspy');
+ // scrollspy.ngOnDestroy();
+ // expect(spy).toHaveBeenCalled();
+ });
+
+ it('should emit activeLinkChange event when active link change', () => {
+ // const spy = jest.spyOn(scrollspy.activeLinkChange, 'emit');
+ // const document = DOCUMENT;
+ // const link = new MdbScrollspyLinkDirective(cdRefMock as any, document);
+ // scrollspy.ngOnInit();
+ // scrollspyService.setActiveLink(link);
+ // expect(spy).toHaveBeenCalled();
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy.directive.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.directive.ts
new file mode 100644
index 00000000..868557a7
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.directive.ts
@@ -0,0 +1,135 @@
+import {
+ AfterContentInit,
+ Component,
+ ContentChildren,
+ ElementRef,
+ EventEmitter,
+ Inject,
+ Input,
+ OnDestroy,
+ OnInit,
+ Output,
+ PLATFORM_ID,
+ QueryList,
+ Renderer2,
+} from '@angular/core';
+import { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+import { MdbScrollspyService } from './scrollspy.service';
+import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
+import { Subject, Subscription } from 'rxjs';
+import { coerceBooleanProperty } from '@angular/cdk/coercion';
+import { isPlatformBrowser } from '@angular/common';
+
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: '[mdbScrollspy]',
+ template: ' ',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbScrollspyDirective implements OnInit, AfterContentInit, OnDestroy {
+ @ContentChildren(MdbScrollspyLinkDirective, { descendants: true })
+ links: QueryList;
+
+ readonly _destroy$: Subject = new Subject();
+
+ @Input('mdbScrollspy')
+ get id(): string {
+ return this._id;
+ }
+
+ set id(newId: string) {
+ if (newId) {
+ this._id = newId;
+ }
+ }
+
+ private _id: string;
+
+ @Input()
+ get collapsible(): boolean {
+ return this._collapsible;
+ }
+ set collapsible(value: boolean) {
+ this._collapsible = coerceBooleanProperty(value);
+ }
+
+ private _collapsible = false;
+
+ private _isBrowser: boolean;
+
+ @Output() activeLinkChange: EventEmitter = new EventEmitter();
+
+ activeSub: Subscription;
+
+ constructor(
+ private scrollSpyService: MdbScrollspyService,
+ private _elementRef: ElementRef,
+ private _renderer: Renderer2,
+ @Inject(PLATFORM_ID) platformId: Object
+ ) {
+ this._isBrowser = isPlatformBrowser(platformId);
+ }
+
+ get host(): HTMLElement {
+ return this._elementRef.nativeElement;
+ }
+
+ collapsibleElementHeight = 0;
+
+ ngOnInit(): void {
+ if (this._isBrowser) {
+ this.collapsibleElementHeight = this.host.getBoundingClientRect().height;
+ }
+
+ this.activeSub = this.scrollSpyService.active$
+ .pipe(takeUntil(this._destroy$), distinctUntilChanged())
+ .subscribe((activeLink) => {
+ this.activeLinkChange.emit(activeLink);
+ if (this.collapsible) {
+ this.styleCollapsibleElement();
+ }
+ });
+ }
+
+ ngAfterContentInit(): void {
+ this.scrollSpyService.addScrollspy({ id: this.id, links: this.links });
+ }
+
+ ngOnDestroy(): void {
+ this.scrollSpyService.removeScrollspy(this.id);
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private styleCollapsibleElement(): void {
+ this._renderer.setStyle(this.host, 'overflow', 'hidden');
+ this._renderer.setStyle(this.host, 'transition', 'height 0.2s ease-in-out');
+ this._renderer.setStyle(this.host, 'flex-wrap', 'nowrap');
+
+ const hostSiblings = this.getAllSiblings(this.host);
+ const isAnySiblingActive = hostSiblings.some((element) => {
+ return element.classList.contains('active');
+ });
+
+ if (this.collapsible && isAnySiblingActive) {
+ this._renderer.setStyle(this.host, 'height', `${this.collapsibleElementHeight}px`);
+ } else if (this.collapsible && !isAnySiblingActive) {
+ this._renderer.setStyle(this.host, 'height', '0px');
+ }
+ }
+
+ private getAllSiblings(element: HTMLElement) {
+ let siblings = [];
+ if (!element.parentNode) {
+ return siblings;
+ }
+ let sibling = element.parentNode.firstElementChild;
+ do {
+ if (sibling != element) {
+ siblings.push(sibling);
+ }
+ } while ((sibling = sibling.nextElementSibling));
+ return siblings;
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy.module.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.module.ts
new file mode 100644
index 00000000..018f0a0b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.module.ts
@@ -0,0 +1,24 @@
+import { NgModule } from '@angular/core';
+
+import { MdbScrollspyDirective } from './scrollspy.directive';
+import { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+import { MdbScrollspyElementDirective } from './scrollspy-element.directive';
+import { MdbScrollspyService } from './scrollspy.service';
+import { MdbScrollspyWindowDirective } from './scrollspy-window.directive';
+
+@NgModule({
+ declarations: [
+ MdbScrollspyDirective,
+ MdbScrollspyLinkDirective,
+ MdbScrollspyElementDirective,
+ MdbScrollspyWindowDirective,
+ ],
+ exports: [
+ MdbScrollspyDirective,
+ MdbScrollspyLinkDirective,
+ MdbScrollspyElementDirective,
+ MdbScrollspyWindowDirective,
+ ],
+ providers: [MdbScrollspyService],
+})
+export class MdbScrollspyModule {}
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy.service.spec.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.service.spec.ts
new file mode 100644
index 00000000..35d5d912
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.service.spec.ts
@@ -0,0 +1,82 @@
+import { TestBed, inject } from '@angular/core/testing';
+import { QueryList } from '@angular/core';
+
+import { MdbScrollspyService } from './scrollspy.service';
+import { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+
+describe('ScrollspyService', () => {
+ let scrollspyService: MdbScrollspyService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [MdbScrollspyService],
+ teardown: { destroyAfterEach: false },
+ });
+
+ inject([MdbScrollspyService], (service: MdbScrollspyService) => {
+ scrollspyService = service;
+ })();
+ });
+
+ it('should add new scrollspy to the list', () => {
+ const links = new QueryList();
+ const id = 'test-scrollspy';
+ scrollspyService.addScrollspy({ id, links });
+
+ expect(scrollspyService.scrollSpys).toEqual([{ id: 'test-scrollspy', links }]);
+ });
+
+ it('should remove scrollspy from the list', () => {
+ const links = new QueryList();
+ const id = 'test-scrollspy';
+ scrollspyService.addScrollspy({ id, links });
+
+ expect(scrollspyService.scrollSpys).toEqual([{ id: 'test-scrollspy', links }]);
+
+ scrollspyService.removeScrollspy('test-scrollspy');
+
+ expect(scrollspyService.scrollSpys).toEqual([]);
+ });
+
+ it('should correctly set and remove link active state', () => {
+ const links = [
+ {
+ id: 'test-link',
+ active: false,
+ section: 'test-section',
+ detectChanges: () => {},
+ },
+ ];
+
+ scrollspyService.scrollSpys = [{ id: 'test-scrollspy', links }];
+
+ expect(links[0].active).toBe(false);
+
+ scrollspyService.updateActiveState('test-scrollspy', 'test-link');
+
+ expect(links[0].active).toBe(true);
+
+ scrollspyService.removeActiveState('test-scrollspy', 'test-link');
+ });
+
+ it('should correctly remove active state from all links in specific scrollspy', () => {
+ const links = [
+ {
+ id: 'test-link-1',
+ active: true,
+ section: 'test-section-1',
+ detectChanges: () => {},
+ },
+ {
+ id: 'test-link-2',
+ active: true,
+ section: 'test-section-2',
+ detectChanges: () => {},
+ },
+ ];
+
+ scrollspyService.scrollSpys = [{ id: 'test-scrollspy', links }];
+
+ scrollspyService.removeActiveLinks('test-scrollspy');
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/scrollspy/scrollspy.service.ts b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.service.ts
new file mode 100644
index 00000000..3ee523dc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/scrollspy/scrollspy.service.ts
@@ -0,0 +1,87 @@
+import { Injectable, QueryList } from '@angular/core';
+import { MdbScrollspyLinkDirective } from './scrollspy-link.directive';
+import { Subject, Observable } from 'rxjs';
+
+export interface MdbScrollspy {
+ id: string;
+ links: QueryList;
+}
+
+@Injectable()
+export class MdbScrollspyService {
+ scrollSpys: MdbScrollspy[] = [];
+
+ private activeSubject = new Subject();
+ active$: Observable = this.activeSubject;
+
+ addScrollspy(scrollSpy: MdbScrollspy): void {
+ this.scrollSpys.push(scrollSpy);
+ }
+
+ removeScrollspy(scrollSpyId: string): void {
+ const scrollSpyIndex = this.scrollSpys.findIndex((spy) => {
+ return spy.id === scrollSpyId;
+ });
+ this.scrollSpys.splice(scrollSpyIndex, 1);
+ }
+
+ updateActiveState(scrollSpyId: string, activeLinkId: string): void {
+ const scrollSpy = this.scrollSpys.find((spy) => {
+ return spy.id === scrollSpyId;
+ });
+
+ if (!scrollSpy) {
+ return;
+ }
+
+ const activeLink = scrollSpy.links.find((link) => {
+ return link.id === activeLinkId;
+ });
+
+ this.setActiveLink(activeLink);
+ }
+
+ removeActiveState(scrollSpyId: string, activeLinkId: string): void {
+ const scrollSpy = this.scrollSpys.find((spy) => {
+ return spy.id === scrollSpyId;
+ });
+
+ if (!scrollSpy) {
+ return;
+ }
+
+ const activeLink = scrollSpy.links.find((link) => {
+ return link.id === activeLinkId;
+ });
+
+ if (!activeLink) {
+ return;
+ }
+
+ activeLink.active = false;
+ activeLink.detectChanges();
+ }
+
+ setActiveLink(activeLink: MdbScrollspyLinkDirective | any): void {
+ if (activeLink) {
+ activeLink.active = true;
+ activeLink.detectChanges();
+ this.activeSubject.next(activeLink);
+ }
+ }
+
+ removeActiveLinks(scrollSpyId: string): void {
+ const scrollSpy: MdbScrollspy | undefined = this.scrollSpys.find((spy) => {
+ return spy.id === scrollSpyId;
+ });
+
+ if (!scrollSpy) {
+ return;
+ }
+
+ scrollSpy.links.forEach((link) => {
+ link.active = false;
+ link.detectChanges();
+ });
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/index.ts b/projects/mdb-angular-ui-kit/tabs/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/tabs/ng-package.json b/projects/mdb-angular-ui-kit/tabs/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/tabs/public_api.ts b/projects/mdb-angular-ui-kit/tabs/public_api.ts
new file mode 100644
index 00000000..5be2ae78
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/public_api.ts
@@ -0,0 +1,7 @@
+export { MdbTabComponent } from './tab.component';
+export { MdbTabContentDirective } from './tab-content.directive';
+export { MdbTabTitleDirective } from './tab-title.directive';
+export { MdbTabsComponent } from './tabs.component';
+export { MdbTabsModule } from './tabs.module';
+export { MdbTabPortalOutlet } from './tab-outlet.directive';
+export { MdbTabChange } from './tabs.component';
diff --git a/projects/mdb-angular-ui-kit/tabs/tab-content.directive.ts b/projects/mdb-angular-ui-kit/tabs/tab-content.directive.ts
new file mode 100644
index 00000000..f8e91973
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tab-content.directive.ts
@@ -0,0 +1,13 @@
+import { Directive, InjectionToken, TemplateRef } from '@angular/core';
+
+export const MDB_TAB_CONTENT = new InjectionToken('MdbTabContentDirective');
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbTabContent]',
+ providers: [{ provide: MDB_TAB_CONTENT, useExisting: MdbTabContentDirective }],
+ standalone: false,
+})
+export class MdbTabContentDirective {
+ constructor(public template: TemplateRef) {}
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/tab-outlet.directive.ts b/projects/mdb-angular-ui-kit/tabs/tab-outlet.directive.ts
new file mode 100644
index 00000000..fb7f1101
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tab-outlet.directive.ts
@@ -0,0 +1,50 @@
+import { CdkPortalOutlet } from '@angular/cdk/portal';
+
+import {
+ Directive,
+ Inject,
+ Input,
+ OnDestroy,
+ OnInit,
+ ViewContainerRef,
+ DOCUMENT,
+} from '@angular/core';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { MdbTabComponent } from './tab.component';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbTabPortalOutlet]',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export class MdbTabPortalOutlet extends CdkPortalOutlet implements OnInit, OnDestroy {
+ readonly _destroy$: Subject = new Subject();
+
+ @Input() tab: MdbTabComponent;
+
+ constructor(_vcr: ViewContainerRef, @Inject(DOCUMENT) _document: any) {
+ super(_vcr, _document);
+ }
+
+ ngOnInit(): void {
+ super.ngOnInit();
+
+ if ((this.tab.shouldAttach || this.tab.active) && !this.hasAttached()) {
+ this.attach(this.tab.content);
+ } else {
+ this.tab.activeStateChange$.pipe(takeUntil(this._destroy$)).subscribe((isActive) => {
+ if (isActive && !this.hasAttached()) {
+ this.attach(this.tab.content);
+ }
+ });
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ super.ngOnDestroy();
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/tab-title.directive.ts b/projects/mdb-angular-ui-kit/tabs/tab-title.directive.ts
new file mode 100644
index 00000000..a30be04b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tab-title.directive.ts
@@ -0,0 +1,13 @@
+import { Directive, InjectionToken, TemplateRef } from '@angular/core';
+
+export const MDB_TAB_TITLE = new InjectionToken('MdbTabTitleDirective');
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbTabTitle]',
+ providers: [{ provide: MDB_TAB_TITLE, useExisting: MdbTabTitleDirective }],
+ standalone: false,
+})
+export class MdbTabTitleDirective {
+ constructor(public template: TemplateRef) {}
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/tab.component.html b/projects/mdb-angular-ui-kit/tabs/tab.component.html
new file mode 100644
index 00000000..cd48c06b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tab.component.html
@@ -0,0 +1 @@
+
diff --git a/projects/mdb-angular-ui-kit/tabs/tab.component.ts b/projects/mdb-angular-ui-kit/tabs/tab.component.ts
new file mode 100644
index 00000000..581a8427
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tab.component.ts
@@ -0,0 +1,117 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { TemplatePortal } from '@angular/cdk/portal';
+import {
+ Component,
+ ContentChild,
+ Input,
+ OnInit,
+ TemplateRef,
+ ViewChild,
+ ViewContainerRef,
+} from '@angular/core';
+import { Subject } from 'rxjs';
+import { MDB_TAB_CONTENT } from './tab-content.directive';
+import { MDB_TAB_TITLE } from './tab-title.directive';
+
+const SHOW_TRANSITION_DELAY = 150; // Time of transition taken from styles
+const TRANSITION_PADDING = 5; // Value from standard added via executeAfterTransition function
+
+@Component({
+ selector: 'mdb-tab',
+ templateUrl: './tab.component.html',
+ standalone: false,
+})
+export class MdbTabComponent implements OnInit {
+ @ContentChild(MDB_TAB_CONTENT, { read: TemplateRef, static: true })
+ _lazyContent: TemplateRef;
+
+ @ContentChild(MDB_TAB_TITLE, { read: TemplateRef, static: true })
+ _titleContent: TemplateRef;
+
+ @ViewChild(TemplateRef, { static: true }) _content: TemplateRef;
+
+ readonly activeStateChange$: Subject = new Subject();
+
+ @Input()
+ get disabled(): boolean {
+ return this._disabled;
+ }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ }
+ private _disabled = false;
+
+ @Input()
+ get fade(): boolean {
+ return this._fade;
+ }
+ set fade(value: boolean) {
+ this._fade = coerceBooleanProperty(value);
+ }
+ private _fade = true;
+
+ @Input() title: string;
+
+ get content(): TemplatePortal | null {
+ return this._contentPortal;
+ }
+
+ get titleContent(): TemplatePortal | null {
+ return this._titlePortal;
+ }
+
+ get shouldAttach(): boolean {
+ return this._lazyContent === undefined;
+ }
+
+ private _contentPortal: TemplatePortal | null = null;
+ private _titlePortal: TemplatePortal | null = null;
+
+ get active(): boolean {
+ return this._active;
+ }
+
+ set active(value: boolean) {
+ this._active = coerceBooleanProperty(value);
+ this.activeStateChange$.next(value);
+ }
+
+ private _active = false;
+
+ get show(): boolean {
+ return this._show;
+ }
+
+ set show(value: boolean) {
+ // We use setTimeout to apply delay for setting show class to reproduce standard library where
+ // show class is applied after a delay to newly activated item via usage of _queueCallback and
+ // executeAfterTransition functions which introduce delay equal to transition time taken from
+ // element styles
+ setTimeout(() => {
+ this._show = coerceBooleanProperty(value);
+ }, SHOW_TRANSITION_DELAY + TRANSITION_PADDING);
+ }
+
+ private _show = true;
+
+ constructor(private _vcr: ViewContainerRef) {}
+
+ ngOnInit(): void {
+ this._createContentPortal();
+
+ if (this._titleContent) {
+ this._createTitlePortal();
+ }
+ }
+
+ private _createContentPortal(): void {
+ const content = this._lazyContent || this._content;
+ this._contentPortal = new TemplatePortal(content, this._vcr);
+ }
+
+ private _createTitlePortal(): void {
+ this._titlePortal = new TemplatePortal(this._titleContent, this._vcr);
+ }
+
+ static ngAcceptInputType_disabled: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/tabs.animations.ts b/projects/mdb-angular-ui-kit/tabs/tabs.animations.ts
new file mode 100644
index 00000000..c5504482
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tabs.animations.ts
@@ -0,0 +1,30 @@
+import {
+ animate,
+ AnimationTriggerMetadata,
+ keyframes,
+ style,
+ transition,
+ trigger,
+ animation,
+ useAnimation,
+} from '@angular/animations';
+
+export function fadeInAnimation(): AnimationTriggerMetadata {
+ return trigger('fadeIn', [
+ transition('0 => 1', [
+ useAnimation(
+ animation(
+ [
+ animate(
+ '500ms 0ms',
+ keyframes([style({ opacity: 0, offset: 0 }), style({ opacity: 1, offset: 1 })])
+ ),
+ ],
+ {
+ delay: 0,
+ }
+ )
+ ),
+ ]),
+ ]);
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/tabs.component.html b/projects/mdb-angular-ui-kit/tabs/tabs.component.html
new file mode 100644
index 00000000..85ef5ad3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tabs.component.html
@@ -0,0 +1,49 @@
+
+
+
diff --git a/projects/mdb-angular-ui-kit/tabs/tabs.component.ts b/projects/mdb-angular-ui-kit/tabs/tabs.component.ts
new file mode 100644
index 00000000..ed922aeb
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tabs.component.ts
@@ -0,0 +1,156 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import {
+ AfterContentInit,
+ Component,
+ ContentChildren,
+ EventEmitter,
+ HostBinding,
+ Input,
+ OnDestroy,
+ Output,
+ QueryList,
+} from '@angular/core';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { MdbTabComponent } from './tab.component';
+
+export class MdbTabChange {
+ index: number;
+ tab: MdbTabComponent;
+}
+
+@Component({
+ selector: 'mdb-tabs',
+ templateUrl: './tabs.component.html',
+ standalone: false,
+})
+export class MdbTabsComponent implements AfterContentInit, OnDestroy {
+ @ContentChildren(MdbTabComponent) tabs: QueryList;
+
+ readonly _destroy$: Subject = new Subject();
+
+ @Input()
+ get fill(): boolean {
+ return this._fill;
+ }
+ set fill(value: boolean) {
+ this._fill = coerceBooleanProperty(value);
+ }
+ private _fill = false;
+
+ @Input()
+ get justified(): boolean {
+ return this._justified;
+ }
+ set justified(value: boolean) {
+ this._justified = coerceBooleanProperty(value);
+ }
+ private _justified = false;
+
+ @Input()
+ get pills(): boolean {
+ return this._pills;
+ }
+ set pills(value: boolean) {
+ this._pills = coerceBooleanProperty(value);
+ }
+ private _pills = false;
+
+ @HostBinding('class.row')
+ @Input()
+ get vertical(): boolean {
+ return this._vertical;
+ }
+ set vertical(value: boolean) {
+ this._vertical = coerceBooleanProperty(value);
+ }
+ private _vertical = false;
+
+ @Input() navColumnClass = 'col-3';
+ @Input() contentColumnClass = 'col-9';
+
+ get navColClass(): string {
+ return this.vertical ? this.navColumnClass : '';
+ }
+
+ get contentColClass(): string {
+ return this.vertical ? this.contentColumnClass : '';
+ }
+
+ private _selectedIndex: number;
+
+ @Output() activeTabChange: EventEmitter = new EventEmitter();
+
+ constructor() {}
+
+ ngAfterContentInit(): void {
+ const firstActiveTabIndex = this.tabs.toArray().findIndex((tab) => !tab.disabled);
+
+ this.setActiveTab(firstActiveTabIndex);
+ this.tabs.changes.pipe(takeUntil(this._destroy$)).subscribe(() => {
+ const hasActiveTab = this.tabs.find((tab) => tab.active);
+
+ if (!hasActiveTab) {
+ const closestTabIndex = this._getClosestTabIndex(this._selectedIndex);
+
+ if (closestTabIndex !== -1) {
+ this.setActiveTab(closestTabIndex);
+ }
+ }
+ });
+ }
+
+ setActiveTab(index: number): void {
+ const activeTab = this.tabs.toArray()[index];
+
+ if (!activeTab || (activeTab && activeTab.disabled)) {
+ return;
+ }
+
+ this.tabs.forEach((tab) => (tab.active = tab === activeTab));
+ this.tabs.forEach((tab) => (tab.show = tab === activeTab));
+
+ this._selectedIndex = index;
+
+ const tabChangeEvent = this._getTabChangeEvent(index, activeTab);
+ this.activeTabChange.emit(tabChangeEvent);
+ }
+
+ private _getTabChangeEvent(index: number, tab: MdbTabComponent): MdbTabChange {
+ const event = new MdbTabChange();
+ event.index = index;
+ event.tab = tab;
+
+ return event;
+ }
+
+ private _getClosestTabIndex(index: number): number {
+ const tabs = this.tabs.toArray();
+ const tabsLength = tabs.length;
+ if (!tabsLength) {
+ return -1;
+ }
+
+ for (let i = 1; i <= tabsLength; i += 1) {
+ const prevIndex = index - i;
+ const nextIndex = index + i;
+ if (tabs[prevIndex] && !tabs[prevIndex].disabled) {
+ return prevIndex;
+ }
+ if (tabs[nextIndex] && !tabs[nextIndex].disabled) {
+ return nextIndex;
+ }
+ }
+ return -1;
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ static ngAcceptInputType_fill: BooleanInput;
+ static ngAcceptInputType_justified: BooleanInput;
+ static ngAcceptInputType_pills: BooleanInput;
+ static ngAcceptInputType_vertical: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/tabs/tabs.module.ts b/projects/mdb-angular-ui-kit/tabs/tabs.module.ts
new file mode 100644
index 00000000..679136a3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tabs.module.ts
@@ -0,0 +1,27 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MdbTabComponent } from './tab.component';
+import { MdbTabsComponent } from './tabs.component';
+import { PortalModule } from '@angular/cdk/portal';
+import { MdbTabContentDirective } from './tab-content.directive';
+import { MdbTabPortalOutlet } from './tab-outlet.directive';
+import { MdbTabTitleDirective } from './tab-title.directive';
+
+@NgModule({
+ declarations: [
+ MdbTabComponent,
+ MdbTabContentDirective,
+ MdbTabTitleDirective,
+ MdbTabPortalOutlet,
+ MdbTabsComponent,
+ ],
+ imports: [CommonModule, PortalModule],
+ exports: [
+ MdbTabComponent,
+ MdbTabContentDirective,
+ MdbTabTitleDirective,
+ MdbTabPortalOutlet,
+ MdbTabsComponent,
+ ],
+})
+export class MdbTabsModule {}
diff --git a/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts b/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts
new file mode 100644
index 00000000..3b9eb2af
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tabs/tabs.spec.ts
@@ -0,0 +1,287 @@
+import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core';
+import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { MdbTabComponent } from './tab.component';
+import { MdbTabsComponent } from './tabs.component';
+import { MdbTabsModule } from './tabs.module';
+
+const tabsTemplate = `
+
+ Tab 1 content
+ Tab 2 content
+ Tab 3 content
+
+
+ Tab 4
+
+ Tab content 4
+
+ Hidden tab content
+
+`;
+
+@Component({
+ template: tabsTemplate,
+ standalone: false,
+})
+export class TabsTestComponent {
+ pills = false;
+ fill = false;
+ justified = false;
+ vertical = false;
+ navColumnClass = 'col-3';
+ contentColumnClass = 'col-9';
+ firstTabDisabled = true;
+ secondTabDisabled = false;
+ thirdTabDisabled = false;
+ showHiddenTab = false;
+
+ @ViewChild(MdbTabsComponent) tabsComponent: MdbTabsComponent;
+ @ViewChildren(MdbTabComponent) tabComponents: QueryList;
+}
+
+describe('MDB Tabs', () => {
+ let fixture: ComponentFixture;
+ let component: TabsTestComponent;
+ let tabsComponent: MdbTabsComponent;
+
+ beforeEach(fakeAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [TabsTestComponent],
+ imports: [MdbTabsModule, NoopAnimationsModule],
+ teardown: { destroyAfterEach: false },
+ });
+
+ fixture = TestBed.createComponent(TabsTestComponent);
+ component = fixture.componentInstance;
+
+ fixture.detectChanges();
+ tick();
+ flush();
+
+ tabsComponent = component.tabsComponent;
+ }));
+
+ it('should activate first available tab', () => {
+ fixture.detectChanges();
+
+ const tabs = component.tabComponents.toArray();
+
+ expect(tabs[0].active).toBe(false);
+ expect(tabs[1].active).toBe(true);
+ });
+
+ it('should set show to true and apply show class on first available tab', () => {
+ fixture.detectChanges();
+
+ const tabs = component.tabComponents.toArray();
+ const tabPanes = fixture.debugElement.queryAll(By.css('.tab-pane'));
+
+ expect(tabs[0].show).toBe(false);
+ expect(tabs[1].show).toBe(true);
+ expect(tabs[2].show).toBe(false);
+ expect(tabPanes[0].nativeElement.classList.contains('show')).toBe(false);
+ expect(tabPanes[1].nativeElement.classList.contains('show')).toBe(true);
+ expect(tabPanes[2].nativeElement.classList.contains('show')).toBe(false);
+ });
+
+ it('should change active tab on tab button click', () => {
+ fixture.detectChanges();
+
+ const tabs = component.tabComponents.toArray();
+ const tabLinks = fixture.debugElement.queryAll(By.css('.nav-link'));
+
+ expect(tabs[1].active).toBe(true);
+
+ tabLinks[2].nativeElement.click();
+ fixture.detectChanges();
+
+ expect(tabs[2].active).toBe(true);
+ });
+
+ it('should apply show class after 155ms delay on tab button click', fakeAsync(() => {
+ fixture.detectChanges();
+ flush();
+
+ const tabs = component.tabComponents.toArray();
+ const tabLinks = fixture.debugElement.queryAll(By.css('.nav-link'));
+ const tabPanes = fixture.debugElement.queryAll(By.css('.tab-pane'));
+
+ expect(tabs[1].active).toBe(true);
+ expect(tabs[1].show).toBe(true);
+ expect(tabPanes[1].nativeElement.classList.contains('show')).toBe(true);
+ expect(tabs[2].active).toBe(false);
+ expect(tabs[2].show).toBe(false);
+ expect(tabPanes[2].nativeElement.classList.contains('show')).toBe(false);
+
+ tabLinks[2].nativeElement.click();
+ fixture.detectChanges();
+
+ expect(tabs[1].active).toBe(false);
+ expect(tabs[1].show).toBe(true);
+ expect(tabPanes[1].nativeElement.classList.contains('show')).toBe(true);
+ expect(tabs[2].active).toBe(true);
+ expect(tabs[2].show).toBe(false);
+ expect(tabPanes[2].nativeElement.classList.contains('show')).toBe(false);
+
+ tick(155);
+ fixture.detectChanges();
+
+ expect(tabs[1].active).toBe(false);
+ expect(tabs[1].show).toBe(false);
+ expect(tabPanes[1].nativeElement.classList.contains('show')).toBe(false);
+ expect(tabs[2].active).toBe(true);
+ expect(tabs[2].show).toBe(true);
+ expect(tabPanes[2].nativeElement.classList.contains('show')).toBe(true);
+ }));
+
+ it('should add active class to active tab link', () => {
+ fixture.detectChanges();
+
+ const tabLinks = fixture.debugElement.queryAll(By.css('.nav-link'));
+
+ expect(tabLinks[1].nativeElement.classList.contains('active')).toBe(true);
+
+ tabLinks[2].nativeElement.click();
+ fixture.detectChanges();
+
+ expect(tabLinks[2].nativeElement.classList.contains('active')).toBe(true);
+ });
+
+ it('should add disabled class to disabled tab link', () => {
+ fixture.detectChanges();
+
+ const tabLinks = fixture.debugElement.queryAll(By.css('.nav-link'));
+
+ expect(tabLinks[0].nativeElement.classList.contains('disabled')).toBe(true);
+ });
+
+ it('should add nav-pills class if pills input is set to true', () => {
+ component.pills = true;
+ fixture.detectChanges();
+
+ const tabNav = fixture.debugElement.query(By.css('.nav'));
+
+ expect(tabNav.nativeElement.classList.contains('nav-pills')).toBe(true);
+ });
+
+ it('should add nav-fill class if fill input is set to true', () => {
+ component.fill = true;
+ fixture.detectChanges();
+
+ const tabNav = fixture.debugElement.query(By.css('.nav'));
+
+ expect(tabNav.nativeElement.classList.contains('nav-fill')).toBe(true);
+ });
+
+ it('should add nav-justified class if justified input is set to true', () => {
+ component.justified = true;
+ fixture.detectChanges();
+
+ const tabNav = fixture.debugElement.query(By.css('.nav'));
+
+ expect(tabNav.nativeElement.classList.contains('nav-justified')).toBe(true);
+ });
+
+ it('should add flex-column and text-center classes if vertical input is set to true', () => {
+ component.vertical = true;
+ fixture.detectChanges();
+
+ const tabNav = fixture.debugElement.query(By.css('.nav'));
+
+ expect(tabNav.nativeElement.classList.contains('flex-column')).toBe(true);
+ expect(tabNav.nativeElement.classList.contains('text-center')).toBe(true);
+ });
+
+ it('should not set nav and content column classes on horizontal tabs', () => {
+ component.vertical = false;
+ fixture.detectChanges();
+
+ const tabNav = fixture.debugElement.query(By.css('.nav'));
+ const tabContent = fixture.debugElement.query(By.css('.tab-content'));
+
+ expect(tabNav.nativeElement.classList.contains('col-3')).toBe(false);
+ expect(tabContent.nativeElement.classList.contains('col-9')).toBe(false);
+ });
+
+ it('should correctly set and update nav and content column classes on vertical tabs', () => {
+ component.vertical = true;
+ fixture.detectChanges();
+
+ const tabNav = fixture.debugElement.query(By.css('.nav'));
+ const tabContent = fixture.debugElement.query(By.css('.tab-content'));
+
+ expect(tabNav.nativeElement.classList.contains('col-3')).toBe(true);
+ expect(tabContent.nativeElement.classList.contains('col-9')).toBe(true);
+
+ component.navColumnClass = 'col-6';
+ component.contentColumnClass = 'col-6';
+ fixture.detectChanges();
+
+ expect(tabNav.nativeElement.classList.contains('col-3')).toBe(false);
+ expect(tabContent.nativeElement.classList.contains('col-9')).toBe(false);
+ expect(tabNav.nativeElement.classList.contains('col-6')).toBe(true);
+ expect(tabContent.nativeElement.classList.contains('col-6')).toBe(true);
+ });
+
+ it('should not activate disabled tab programmaticaly', () => {
+ const tabs = component.tabComponents.toArray();
+
+ expect(tabs[0].disabled).toBe(true);
+ expect(tabs[0].active).toBe(false);
+
+ component.tabsComponent.setActiveTab(0);
+ fixture.detectChanges();
+
+ expect(tabs[0].active).toBe(false);
+ });
+
+ it('should not change current active tab when tab list is updated', () => {
+ const tabs = component.tabComponents.toArray();
+
+ expect(tabs[1].active).toBe(true);
+
+ component.showHiddenTab = true;
+ fixture.detectChanges();
+
+ expect(tabs[1].active).toBe(true);
+ });
+
+ it('should activate first available on tab list change if no tab is active', () => {
+ let tabs = component.tabComponents.toArray();
+
+ expect(tabs[1].active).toBe(true);
+
+ tabs[1].active = false;
+ component.secondTabDisabled = true;
+ fixture.detectChanges();
+
+ component.showHiddenTab = true;
+ fixture.detectChanges();
+
+ tabs = component.tabComponents.toArray();
+
+ expect(tabs[1].active).toBe(false);
+ expect(tabs[2].active).toBe(true);
+ });
+
+ it('should render custom title content when mdbTabTitle directive and ng-template is used', () => {
+ const span = fixture.nativeElement.querySelector('span');
+ const customTabIcon = span.querySelector('i');
+
+ expect(customTabIcon).toBeTruthy();
+ expect(span.textContent).toEqual('Tab 4');
+ });
+
+ it('should lazy load tab content if mdbTabContent directive and ng-template is used', () => {
+ const tabPanes = fixture.nativeElement.querySelectorAll('.tab-pane');
+
+ expect(tabPanes[3].textContent).toEqual('');
+
+ component.tabsComponent.setActiveTab(3);
+ fixture.detectChanges();
+
+ expect(tabPanes[3].textContent).toEqual('Tab content 4');
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/tooltip/index.ts b/projects/mdb-angular-ui-kit/tooltip/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/tooltip/ng-package.json b/projects/mdb-angular-ui-kit/tooltip/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/tooltip/public_api.ts b/projects/mdb-angular-ui-kit/tooltip/public_api.ts
new file mode 100644
index 00000000..8e4beb2b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/public_api.ts
@@ -0,0 +1,4 @@
+export { MdbTooltipDirective } from './tooltip.directive';
+export { MdbTooltipModule } from './tooltip.module';
+export { MdbTooltipPosition } from './tooltip.types';
+export { MdbTooltipComponent } from './tooltip.component';
diff --git a/projects/mdb-angular-ui-kit/tooltip/tooltip.component.html b/projects/mdb-angular-ui-kit/tooltip/tooltip.component.html
new file mode 100644
index 00000000..1b58b751
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/tooltip.component.html
@@ -0,0 +1,17 @@
+
+
+ {{ title }}
+
diff --git a/projects/mdb-angular-ui-kit/tooltip/tooltip.component.ts b/projects/mdb-angular-ui-kit/tooltip/tooltip.component.ts
new file mode 100644
index 00000000..2d09ecac
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/tooltip.component.ts
@@ -0,0 +1,46 @@
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ HostBinding,
+ Input,
+} from '@angular/core';
+import { trigger, style, animate, transition, state, AnimationEvent } from '@angular/animations';
+import { Subject } from 'rxjs';
+@Component({
+ selector: 'mdb-tooltip',
+ templateUrl: 'tooltip.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ animations: [
+ trigger('fade', [
+ state('visible', style({ opacity: 1 })),
+ state('hidden', style({ opacity: 0 })),
+ transition('visible => hidden', animate('150ms linear')),
+ transition(':enter', animate('150ms linear')),
+ ]),
+ ],
+ standalone: false,
+})
+export class MdbTooltipComponent {
+ @Input() title: string;
+ @Input() html: boolean;
+ @Input() animation: boolean;
+
+ @HostBinding('class.tooltip') tooltip = true;
+
+ readonly _hidden: Subject = new Subject();
+
+ animationState = 'hidden';
+
+ constructor(private _cdRef: ChangeDetectorRef) {}
+
+ markForCheck(): void {
+ this._cdRef.markForCheck();
+ }
+
+ onAnimationEnd(event: AnimationEvent): void {
+ if (event.toState === 'hidden') {
+ this._hidden.next();
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/tooltip/tooltip.directive.spec.ts b/projects/mdb-angular-ui-kit/tooltip/tooltip.directive.spec.ts
new file mode 100644
index 00000000..3e4c063b
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/tooltip.directive.spec.ts
@@ -0,0 +1,190 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Component } from '@angular/core';
+import { MdbTooltipModule } from './index';
+import { MdbTooltipDirective } from './tooltip.directive';
+import { By } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+describe('MDB Tooltip', () => {
+ describe('after init', () => {
+ let fixture: ComponentFixture;
+ let element: any;
+ let component: any;
+ let directive: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [MdbTooltipModule, NoopAnimationsModule],
+ declarations: [TestTooltipComponent],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestTooltipComponent);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+ });
+
+ it('should create the component', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should open tooltip after mouseenter and close after mouseout', () => {
+ fixture.detectChanges();
+
+ directive = fixture.debugElement
+ .query(By.directive(MdbTooltipDirective))
+ .injector.get(MdbTooltipDirective) as MdbTooltipDirective;
+
+ const onOpen = jest.spyOn(directive, 'show');
+ const onClose = jest.spyOn(directive, 'hide');
+
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('mouseenter'));
+ fixture.detectChanges();
+
+ expect(directive.show).toHaveBeenCalled();
+
+ directive.open = true;
+ buttonEl.dispatchEvent(new Event('mouseleave'));
+ fixture.detectChanges();
+
+ expect(directive.hide).toHaveBeenCalled();
+ });
+
+ it('should set tooltip title', () => {
+ jest.useFakeTimers();
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('mouseenter'));
+ jest.runAllTimers();
+
+ fixture.detectChanges();
+ const tooltip = document.querySelector('.tooltip-inner');
+ expect(tooltip.textContent).toMatch(component.testMdbTooltip);
+ });
+
+ it('should set placement', () => {
+ jest.useFakeTimers();
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('mouseenter'));
+ jest.runAllTimers();
+
+ fixture.detectChanges();
+ directive = fixture.debugElement
+ .query(By.directive(MdbTooltipDirective))
+ .injector.get(MdbTooltipDirective) as MdbTooltipDirective;
+
+ const placement = directive._overlayRef._config.positionStrategy._lastPosition.originY;
+ expect(placement).toMatch('top');
+ });
+ });
+
+ describe('onInit', () => {
+ it('should open/close tooltip after click', () => {
+ let fixture: ComponentFixture;
+ let directive: any;
+ let component: any;
+ let element: any;
+
+ TestBed.configureTestingModule({
+ imports: [MdbTooltipModule],
+ declarations: [TestTooltipComponent2],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestTooltipComponent2);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+
+ directive = fixture.debugElement
+ .query(By.directive(MdbTooltipDirective))
+ .injector.get(MdbTooltipDirective) as MdbTooltipDirective;
+
+ const onOpen = jest.spyOn(directive, 'show');
+ const onClose = jest.spyOn(directive, 'hide');
+
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+
+ expect(directive.show).toHaveBeenCalled();
+
+ directive._open = true;
+ buttonEl.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+
+ expect(directive.hide).toHaveBeenCalled();
+ });
+ });
+
+ it('should prevent open', () => {
+ let fixture: ComponentFixture;
+ let directive: any;
+ let component: any;
+ let element: any;
+
+ TestBed.configureTestingModule({
+ imports: [MdbTooltipModule],
+ declarations: [TestTooltipComponent3],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestTooltipComponent3);
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ fixture.detectChanges();
+
+ directive = fixture.debugElement
+ .query(By.directive(MdbTooltipDirective))
+ .injector.get(MdbTooltipDirective) as MdbTooltipDirective;
+
+ const onOpen = jest.spyOn(directive, 'show');
+
+ const buttonEl = fixture.nativeElement.querySelector('button');
+
+ buttonEl.dispatchEvent(new Event('click'));
+ fixture.detectChanges();
+
+ expect(directive.show).not.toHaveBeenCalled();
+ });
+});
+
+@Component({
+ selector: 'mdb-test-tooltip',
+ template: `
+ MDB Button
+ `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestTooltipComponent {
+ testTrigger = 'hover';
+ testMdbTooltip = 'tooltipTitle';
+ testPlacement = 'top';
+ testDisabled = false;
+}
+
+@Component({
+ selector: 'mdb-test-tooltip2',
+ template: ` MDB Button `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestTooltipComponent2 {}
+
+@Component({
+ selector: 'mdb-test-tooltip2',
+ template: `
+ MDB Button
+ `,
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+class TestTooltipComponent3 {}
diff --git a/projects/mdb-angular-ui-kit/tooltip/tooltip.directive.ts b/projects/mdb-angular-ui-kit/tooltip/tooltip.directive.ts
new file mode 100644
index 00000000..265e3bd3
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/tooltip.directive.ts
@@ -0,0 +1,235 @@
+import {
+ ComponentRef,
+ Directive,
+ ElementRef,
+ EventEmitter,
+ Input,
+ OnDestroy,
+ OnInit,
+ Output,
+} from '@angular/core';
+import {
+ ConnectedPosition,
+ Overlay,
+ OverlayConfig,
+ OverlayPositionBuilder,
+ OverlayRef,
+} from '@angular/cdk/overlay';
+import { ComponentPortal } from '@angular/cdk/portal';
+import { MdbTooltipComponent } from './tooltip.component';
+import { MdbTooltipPosition } from './tooltip.types';
+import { fromEvent, Subject } from 'rxjs';
+import { first, takeUntil } from 'rxjs/operators';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbTooltip]',
+ exportAs: 'mdbTooltip',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbTooltipDirective implements OnInit, OnDestroy {
+ @Input() mdbTooltip = '';
+ @Input() tooltipDisabled = false;
+ @Input() placement: MdbTooltipPosition = 'top';
+ @Input() html = false;
+ @Input() animation = true;
+ @Input() trigger = 'hover focus';
+ @Input() delayShow = 0;
+ @Input() delayHide = 0;
+ @Input() offset = 6;
+
+ @Output() tooltipShow: EventEmitter = new EventEmitter();
+ @Output() tooltipShown: EventEmitter = new EventEmitter();
+ @Output() tooltipHide: EventEmitter = new EventEmitter();
+ @Output() tooltipHidden: EventEmitter = new EventEmitter();
+
+ private _overlayRef: OverlayRef;
+ private _tooltipRef: ComponentRef;
+ private _open = false;
+ private _showTimeout: any = 0;
+ private _hideTimeout: any = 0;
+
+ readonly _destroy$: Subject = new Subject();
+
+ constructor(
+ private _overlay: Overlay,
+ private _overlayPositionBuilder: OverlayPositionBuilder,
+ private _elementRef: ElementRef
+ ) {}
+
+ ngOnInit(): void {
+ if (this.tooltipDisabled || this.mdbTooltip === '') {
+ return;
+ }
+
+ this._bindTriggerEvents();
+ }
+
+ ngOnDestroy(): void {
+ if (this._open || this._showTimeout) {
+ this.hide();
+ }
+
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+
+ private _bindTriggerEvents(): void {
+ const triggers = this.trigger.split(' ');
+
+ triggers.forEach((trigger) => {
+ if (trigger === 'click') {
+ fromEvent(this._elementRef.nativeElement, trigger)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.toggle());
+ } else if (trigger !== 'manual') {
+ const evIn = trigger === 'hover' ? 'mouseenter' : 'focusin';
+ const evOut = trigger === 'hover' ? 'mouseleave' : 'focusout';
+
+ fromEvent(this._elementRef.nativeElement, evIn)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.show());
+ fromEvent(this._elementRef.nativeElement, evOut)
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => this.hide());
+ }
+ });
+ }
+
+ private _createOverlayConfig(): OverlayConfig {
+ const positionStrategy = this._overlayPositionBuilder
+ .flexibleConnectedTo(this._elementRef)
+ .withPositions(this._getPosition());
+ const overlayConfig = new OverlayConfig({
+ hasBackdrop: false,
+ scrollStrategy: this._overlay.scrollStrategies.reposition(),
+ positionStrategy,
+ });
+
+ return overlayConfig;
+ }
+
+ private _createOverlay(): void {
+ this._overlayRef = this._overlay.create(this._createOverlayConfig());
+ }
+
+ private _getPosition(): ConnectedPosition[] {
+ let position;
+
+ const positionTop = {
+ originX: 'center',
+ originY: 'top',
+ overlayX: 'center',
+ overlayY: 'bottom',
+ offsetY: -this.offset,
+ };
+
+ const positionBottom = {
+ originX: 'center',
+ originY: 'bottom',
+ overlayX: 'center',
+ overlayY: 'top',
+ offsetY: this.offset,
+ };
+
+ const positionRight = {
+ originX: 'end',
+ originY: 'center',
+ overlayX: 'start',
+ overlayY: 'center',
+ offsetX: this.offset,
+ };
+
+ const positionLeft = {
+ originX: 'start',
+ originY: 'center',
+ overlayX: 'end',
+ overlayY: 'center',
+ offsetX: -this.offset,
+ };
+
+ switch (this.placement) {
+ case 'top':
+ position = [positionTop, positionBottom];
+ break;
+ case 'bottom':
+ position = [positionBottom, positionTop];
+ break;
+ case 'left':
+ position = [positionLeft, positionRight];
+ break;
+ case 'right':
+ position = [positionRight, positionLeft];
+ break;
+ default:
+ break;
+ }
+
+ return position;
+ }
+
+ show(): void {
+ if (this._hideTimeout || this._open) {
+ this._overlayRef.detach();
+ clearTimeout(this._hideTimeout);
+ this._hideTimeout = null;
+ }
+
+ this._createOverlay();
+
+ this._showTimeout = setTimeout(() => {
+ if (!this._overlayRef.hasAttached()) {
+ const tooltipPortal = new ComponentPortal(MdbTooltipComponent);
+
+ this.tooltipShow.emit(this);
+ this._open = true;
+
+ this._tooltipRef = this._overlayRef.attach(tooltipPortal);
+ this._tooltipRef.instance.title = this.mdbTooltip;
+ this._tooltipRef.instance.html = this.html;
+ this._tooltipRef.instance.animation = this.animation;
+ this._tooltipRef.instance.animationState = 'visible';
+
+ this._tooltipRef.instance.markForCheck();
+
+ this.tooltipShown.emit(this);
+ }
+ }, this.delayShow);
+ }
+
+ hide(): void {
+ if (this._showTimeout) {
+ clearTimeout(this._showTimeout);
+ this._showTimeout = null;
+ } else {
+ return;
+ }
+
+ this._hideTimeout = setTimeout(() => {
+ this.tooltipHide.emit(this);
+
+ if (!this._tooltipRef) {
+ this._overlayRef.detach();
+ this._open = false;
+ this.tooltipHidden.emit(this);
+ } else {
+ this._tooltipRef.instance._hidden.pipe(first()).subscribe(() => {
+ this._overlayRef.detach();
+ this._open = false;
+ this.tooltipHidden.emit(this);
+ });
+ this._tooltipRef.instance.animationState = 'hidden';
+ this._tooltipRef.instance.markForCheck();
+ }
+ }, this.delayHide);
+ }
+
+ toggle(): void {
+ if (this._open) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/tooltip/tooltip.module.ts b/projects/mdb-angular-ui-kit/tooltip/tooltip.module.ts
new file mode 100644
index 00000000..845b1bfc
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/tooltip.module.ts
@@ -0,0 +1,12 @@
+import { MdbTooltipDirective } from './tooltip.directive';
+import { NgModule } from '@angular/core';
+import { OverlayModule } from '@angular/cdk/overlay';
+import { CommonModule } from '@angular/common';
+import { MdbTooltipComponent } from './tooltip.component';
+
+@NgModule({
+ imports: [CommonModule, OverlayModule],
+ declarations: [MdbTooltipDirective, MdbTooltipComponent],
+ exports: [MdbTooltipDirective, MdbTooltipComponent],
+})
+export class MdbTooltipModule {}
diff --git a/projects/mdb-angular-ui-kit/tooltip/tooltip.types.ts b/projects/mdb-angular-ui-kit/tooltip/tooltip.types.ts
new file mode 100644
index 00000000..677da187
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tooltip/tooltip.types.ts
@@ -0,0 +1 @@
+export type MdbTooltipPosition = 'top' | 'right' | 'bottom' | 'left';
diff --git a/projects/mdb-angular-ui-kit/tsconfig.lib.json b/projects/mdb-angular-ui-kit/tsconfig.lib.json
new file mode 100644
index 00000000..ea356ef5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tsconfig.lib.json
@@ -0,0 +1,24 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/lib",
+ "declaration": true,
+ "declarationMap": true,
+ "inlineSources": true,
+ "types": [],
+ "lib": [
+ "dom",
+ "es2018"
+ ]
+ },
+ "angularCompilerOptions": {
+ "skipTemplateCodegen": true,
+ "strictMetadataEmit": true,
+ "enableResourceInlining": true
+ },
+ "exclude": [
+ "src/test.ts",
+ "**/*.spec.ts"
+ ]
+}
diff --git a/projects/mdb-angular-ui-kit/tsconfig.lib.prod.json b/projects/mdb-angular-ui-kit/tsconfig.lib.prod.json
new file mode 100644
index 00000000..06de549e
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tsconfig.lib.prod.json
@@ -0,0 +1,10 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.lib.json",
+ "compilerOptions": {
+ "declarationMap": false
+ },
+ "angularCompilerOptions": {
+ "compilationMode": "partial"
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/tsconfig.schematics.json b/projects/mdb-angular-ui-kit/tsconfig.schematics.json
new file mode 100644
index 00000000..ff47ee5f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tsconfig.schematics.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "lib": ["es2018", "dom"],
+ "declaration": true,
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "noEmitOnError": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitAny": true,
+ "noImplicitThis": true,
+ "noUnusedParameters": true,
+ "noUnusedLocals": true,
+ "rootDir": "schematics",
+ "outDir": "../../dist/mdb-angular-ui-kit/schematics",
+ "skipDefaultLibCheck": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strictNullChecks": true,
+ "target": "es6",
+ "types": ["jasmine", "node"]
+ },
+ "include": ["schematics/"],
+ "exclude": []
+}
diff --git a/projects/mdb-angular-ui-kit/tsconfig.spec.json b/projects/mdb-angular-ui-kit/tsconfig.spec.json
new file mode 100644
index 00000000..372750f4
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tsconfig.spec.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/spec",
+ "types": ["jest"],
+ "module": "commonjs",
+ "emitDecoratorMetadata": true,
+ "allowJs": true
+ },
+ "files": [],
+ "include": ["**/*.spec.ts", "**/*.d.ts"]
+}
diff --git a/projects/mdb-angular-ui-kit/tslint.json b/projects/mdb-angular-ui-kit/tslint.json
new file mode 100644
index 00000000..124133f8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/tslint.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../tslint.json",
+ "rules": {
+ "directive-selector": [
+ true,
+ "attribute",
+ "lib",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "lib",
+ "kebab-case"
+ ]
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/validation/error.directive.ts b/projects/mdb-angular-ui-kit/validation/error.directive.ts
new file mode 100644
index 00000000..ad7c17ea
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/error.directive.ts
@@ -0,0 +1,60 @@
+import {
+ Input,
+ HostBinding,
+ ElementRef,
+ Renderer2,
+ OnInit,
+ OnDestroy,
+ Component,
+} from '@angular/core';
+import { fromEvent, Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+let defaultIdNumber = 0;
+
+@Component({
+ selector: 'mdb-error',
+ template: ' ',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbErrorDirective implements OnInit, OnDestroy {
+ @Input() id = `mdb-error-${defaultIdNumber++}`;
+
+ @HostBinding('class.error-message') errorMsg = true;
+ @HostBinding('attr.id') messageId = this.id;
+
+ readonly _destroy$: Subject = new Subject();
+
+ constructor(private _elementRef: ElementRef, private renderer: Renderer2) {}
+
+ private _getClosestEl(el: any, selector: string): HTMLElement | null {
+ for (; el && el !== document; el = el.parentNode) {
+ if (el.matches && el.matches(selector)) {
+ return el;
+ }
+ }
+ return null;
+ }
+
+ ngOnInit(): void {
+ const textarea = this._getClosestEl(this._elementRef.nativeElement, 'textarea');
+
+ if (textarea) {
+ let height = textarea.offsetHeight + 4 + 'px';
+ this.renderer.setStyle(this._elementRef.nativeElement, 'top', height);
+
+ fromEvent(textarea, 'keyup')
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => {
+ height = textarea.offsetHeight + 4 + 'px';
+ this.renderer.setStyle(this._elementRef.nativeElement, 'top', height);
+ });
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/validation/index.ts b/projects/mdb-angular-ui-kit/validation/index.ts
new file mode 100644
index 00000000..4aaf8f92
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/index.ts
@@ -0,0 +1 @@
+export * from './public_api';
diff --git a/projects/mdb-angular-ui-kit/validation/ng-package.json b/projects/mdb-angular-ui-kit/validation/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/projects/mdb-angular-ui-kit/validation/public_api.ts b/projects/mdb-angular-ui-kit/validation/public_api.ts
new file mode 100644
index 00000000..5aa0ed87
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/public_api.ts
@@ -0,0 +1,4 @@
+export { MdbValidateDirective } from './validate.directive';
+export { MdbErrorDirective } from './error.directive';
+export { MdbSuccessDirective } from './success.directive';
+export { MdbValidationModule } from './validation.module';
diff --git a/projects/mdb-angular-ui-kit/validation/success.directive.ts b/projects/mdb-angular-ui-kit/validation/success.directive.ts
new file mode 100644
index 00000000..0eeeacc5
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/success.directive.ts
@@ -0,0 +1,60 @@
+import {
+ Input,
+ HostBinding,
+ ElementRef,
+ Renderer2,
+ OnInit,
+ OnDestroy,
+ Component,
+} from '@angular/core';
+import { fromEvent, Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+let defaultIdNumber = 0;
+
+@Component({
+ selector: 'mdb-success',
+ template: ' ',
+ standalone: false,
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class MdbSuccessDirective implements OnInit, OnDestroy {
+ @Input() id = `mdb-success-${defaultIdNumber++}`;
+
+ @HostBinding('class.success-message') successMsg = true;
+ @HostBinding('attr.id') messageId = this.id;
+
+ readonly _destroy$: Subject = new Subject();
+
+ constructor(private _elementRef: ElementRef, private renderer: Renderer2) {}
+
+ private _getClosestEl(el: any, selector: string): HTMLElement | null {
+ for (; el && el !== document; el = el.parentNode) {
+ if (el.matches && el.matches(selector)) {
+ return el;
+ }
+ }
+ return null;
+ }
+
+ ngOnInit(): void {
+ const textarea = this._getClosestEl(this._elementRef.nativeElement, 'textarea');
+
+ if (textarea) {
+ let height = textarea.offsetHeight + 4 + 'px';
+ this.renderer.setStyle(this._elementRef.nativeElement, 'top', height);
+
+ fromEvent(textarea, 'keyup')
+ .pipe(takeUntil(this._destroy$))
+ .subscribe(() => {
+ height = textarea.offsetHeight + 4 + 'px';
+ this.renderer.setStyle(this._elementRef.nativeElement, 'top', height);
+ });
+ }
+ }
+
+ ngOnDestroy(): void {
+ this._destroy$.next();
+ this._destroy$.complete();
+ }
+}
diff --git a/projects/mdb-angular-ui-kit/validation/validate.directive.spec.ts b/projects/mdb-angular-ui-kit/validation/validate.directive.spec.ts
new file mode 100644
index 00000000..eb24d105
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/validate.directive.spec.ts
@@ -0,0 +1,63 @@
+import { Component } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MdbValidationModule } from './validation.module';
+
+const template = `
+
+`;
+
+@Component({
+ selector: 'mdb-collapse-test',
+ template,
+ standalone: false,
+})
+class TestValidateComponent {
+ success = true;
+ error = true;
+}
+
+describe('MDB Collapse', () => {
+ let fixture: ComponentFixture;
+ let element: any;
+ let component: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [TestValidateComponent],
+ imports: [MdbValidationModule],
+ teardown: { destroyAfterEach: false },
+ });
+ fixture = TestBed.createComponent(TestValidateComponent);
+ fixture.detectChanges();
+ component = fixture.componentInstance;
+ element = fixture.nativeElement;
+ });
+
+ it('should add validation classes', () => {
+ const input = fixture.nativeElement.querySelector('.input');
+ expect(input.classList.contains('validate-success')).toBe(true);
+ expect(input.classList.contains('validate-error')).toBe(true);
+ });
+
+ it('should only add validate-success class if validateError is set to false', () => {
+ component.success = true;
+ component.error = false;
+ fixture.detectChanges();
+
+ const input = fixture.nativeElement.querySelector('.input');
+
+ expect(input.classList.contains('validate-success')).toBe(true);
+ expect(input.classList.contains('validate-error')).toBe(false);
+ });
+
+ it('should only add validate-error class if validateSuccess is set to false', () => {
+ component.success = false;
+ component.error = true;
+ fixture.detectChanges();
+
+ const input = fixture.nativeElement.querySelector('.input');
+
+ expect(input.classList.contains('validate-success')).toBe(false);
+ expect(input.classList.contains('validate-error')).toBe(true);
+ });
+});
diff --git a/projects/mdb-angular-ui-kit/validation/validate.directive.ts b/projects/mdb-angular-ui-kit/validation/validate.directive.ts
new file mode 100644
index 00000000..41d649b8
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/validate.directive.ts
@@ -0,0 +1,79 @@
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core';
+
+@Directive({
+ // eslint-disable-next-line @angular-eslint/directive-selector
+ selector: '[mdbValidate]',
+ standalone: false,
+})
+export class MdbValidateDirective implements OnInit {
+ private _validate = true;
+ private _validateSuccess = true;
+ private _validateError = true;
+
+ @Input()
+ get mdbValidate(): boolean {
+ return this._mdbValidate;
+ }
+ set mdbValidate(value: boolean) {
+ this._mdbValidate = coerceBooleanProperty(value);
+ }
+ private _mdbValidate: boolean;
+
+ @Input()
+ get validate(): boolean {
+ return this._validate;
+ }
+ set validate(value: boolean) {
+ this._validate = coerceBooleanProperty(value);
+ this.updateErrorClass();
+ this.updateSuccessClass();
+ }
+
+ @Input()
+ get validateSuccess(): boolean {
+ return this._validateSuccess;
+ }
+ set validateSuccess(value: boolean) {
+ this._validateSuccess = coerceBooleanProperty(value);
+ this.updateSuccessClass();
+ }
+
+ @Input()
+ get validateError(): boolean {
+ return this._validateError;
+ }
+ set validateError(value: boolean) {
+ this._validateError = coerceBooleanProperty(value);
+ this.updateErrorClass();
+ this.updateSuccessClass();
+ }
+
+ constructor(private renderer: Renderer2, private _elementRef: ElementRef) {}
+
+ updateSuccessClass(): void {
+ if (this.validate && this.validateSuccess) {
+ this.renderer.addClass(this._elementRef.nativeElement, 'validate-success');
+ } else {
+ this.renderer.removeClass(this._elementRef.nativeElement, 'validate-success');
+ }
+ }
+
+ updateErrorClass(): void {
+ if (this.validate && this.validateError) {
+ this.renderer.addClass(this._elementRef.nativeElement, 'validate-error');
+ } else {
+ this.renderer.removeClass(this._elementRef.nativeElement, 'validate-error');
+ }
+ }
+
+ ngOnInit(): void {
+ this.updateSuccessClass();
+ this.updateErrorClass();
+ }
+
+ static ngAcceptInputType_mdbValidate: BooleanInput;
+ static ngAcceptInputType_validate: BooleanInput;
+ static ngAcceptInputType_validateSuccess: BooleanInput;
+ static ngAcceptInputType_validateError: BooleanInput;
+}
diff --git a/projects/mdb-angular-ui-kit/validation/validation.module.ts b/projects/mdb-angular-ui-kit/validation/validation.module.ts
new file mode 100644
index 00000000..8c2c5f2f
--- /dev/null
+++ b/projects/mdb-angular-ui-kit/validation/validation.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MdbErrorDirective } from './error.directive';
+import { MdbSuccessDirective } from './success.directive';
+import { MdbValidateDirective } from './validate.directive';
+
+@NgModule({
+ imports: [CommonModule],
+ declarations: [MdbErrorDirective, MdbSuccessDirective, MdbValidateDirective],
+ exports: [MdbErrorDirective, MdbSuccessDirective, MdbValidateDirective],
+})
+export class MdbValidationModule {}
diff --git a/setup-jest.ts b/setup-jest.ts
new file mode 100644
index 00000000..eb5e4856
--- /dev/null
+++ b/setup-jest.ts
@@ -0,0 +1,30 @@
+import 'jest-preset-angular';
+
+/* global mocks for jsdom */
+const mock = () => {
+ let storage: { [key: string]: string } = {};
+ return {
+ getItem: (key: string) => (key in storage ? storage[key] : null),
+ setItem: (key: string, value: string) => (storage[key] = value || ''),
+ removeItem: (key: string) => delete storage[key],
+ clear: () => (storage = {}),
+ };
+};
+
+Object.defineProperty(window, 'localStorage', { value: mock() });
+Object.defineProperty(window, 'sessionStorage', { value: mock() });
+Object.defineProperty(window, 'getComputedStyle', {
+ value: () => ['-webkit-appearance'],
+});
+
+Object.defineProperty(document.body.style, 'transform', {
+ value: () => {
+ return {
+ enumerable: true,
+ configurable: true,
+ };
+ },
+});
+
+/* output shorter and more meaningful Zone error stack traces */
+// Error.stackTraceLimit = 2;
diff --git a/src/angular-bootstrap-md/.directory b/src/angular-bootstrap-md/.directory
deleted file mode 100755
index 43f29452..00000000
--- a/src/angular-bootstrap-md/.directory
+++ /dev/null
@@ -1,10 +0,0 @@
-[Dolphin]
-PreviewsShown=true
-SortOrder=1
-SortRole=modificationtime
-Timestamp=2018,4,20,11,20,20
-Version=4
-VisibleRoles=Icons_text,Icons_modificationtime
-
-[Settings]
-HiddenFilesShown=true
diff --git a/src/angular-bootstrap-md/README.md b/src/angular-bootstrap-md/README.md
deleted file mode 100755
index d0594d2f..00000000
--- a/src/angular-bootstrap-md/README.md
+++ /dev/null
@@ -1,177 +0,0 @@
-[](https://mdbootstrap.com/angular/)
-
-# Angular Bootstrap with Material Design
-
-[](https://badge.fury.io/js/angular-bootstrap-md)
-
-Built with Angular 5, Bootstrap 4 and TypeScript. CLI version available. Absolutely no jQuery.
-
-400+ material UI elements, 600+ material icons, 74 CSS animations, TypeScript modules, SASS files and many more.
-
-All fully responsive. All compatible with different browsers.
-
-__________
-
-# Demo:
-**Main demo**: https://mdbootstrap.com/angular/components/
-
-# Version:
-- Angular CLI 1.5.0 - 6.0.2
-- Angular 5.0.0 - 6.0.2
-
-# Quick start
-- Clone following repo:
-```javascript
-git clone https://github.com/mdbootstrap/Angular-Bootstrap-with-Material-Design.git .
-```
-note "." at the end. It will clone files directly into current folder.
-- Run `npm i`
-- Run `npm start`
-- Voilà! Open browser and visit http://localhost:4200
-
-Now you can navigate to our documentation (http://mdbootstrap.com/angular/), pick any component and place within your project.
-
-# How to install MDB via npm:
-- create new project `ng new project_name --style=scss`
-- `npm i angular-bootstrap-md --save`
-- to app.module.ts add
-```javascript
-import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
-import { MDBBootstrapModule } from 'angular-bootstrap-md';
-
-@NgModule({
- imports: [
- MDBBootstrapModule.forRoot()
- ],
- schemas: [ NO_ERRORS_SCHEMA ]
-});
-```
-- in angular-cli.json change:
-
-`"styleExt": "css"` to `"styleExt": "scss"`
-
-rename /src/styles.css to styles.scss
-
-- if you want to change styles in exisiting project you can use `ng set defaults.styleExt scss`
-
-- add below lines to angular-cli.json:
-```javascript
-"styles": [
- "../node_modules/font-awesome/scss/font-awesome.scss",
- "../node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
- "../node_modules/angular-bootstrap-md/scss/mdb-free.scss",
- "./styles.scss"
-],
-"scripts": [
- "../node_modules/chart.js/dist/Chart.js",
- "../node_modules/hammerjs/hammer.min.js"
-],
-```
-- install external libs
-```bash
-npm install -–save chart.js@2.5.0 font-awesome hammerjs
-```
-
-### Run server
-```bash
-ng serve --open
-```
-
-## Getting started:
-
-http://mdbootstrap.com/angular/getting-started/
-
-**5min Quick Start**: https://mdbootstrap.com/angular/5min-quickstart/
-
-# Additional tutorials:
-
-**MDB - Bootstrap tutorial**: https://mdbootstrap.com/bootstrap-tutorial/
-
-**MDB - Wordpress tutorial**: https://mdbootstrap.com/wordpress-tutorial/
-
-# PRO version:
-
-**Angular Bootstrap with Material Design PRO (from $79)**: https://mdbootstrap.com/product/angular-bootstrap-pro/
-
-## Documentation:
-**Huge, detailed documentation avilable online on**: http://mdbootstrap.com/angular/
-
-# Highlights:
-**Bootstrap 4**
-Up-to-date with the latest standards of Bootstrap 4 and all the best it has to offer.
-
-**Angular CLI**
-A command line interface handling all the tedious tasks for you out of the box.
-
-**Detailed documentation**
-Intuitive and user-friendly documentation, created with a copy-paste approach.
-
-**No jQuery**
-Writing you code with pure Angular is now quicker, easier, and cleaner.
-
-**TypeScript**
-Superset of JavaScript that compiles to clean JavaScript output.
-
-**Angular 6**
-Create smarter and faster Angular apps with the latest official Angular release.
-
-**Cross-browser compatibility**
-Works perfectly with Chrome, Firefox, IE, Safari, Opera and Microsoft Edge.
-
-**Frequent updates**
-Expect any bugs being fixed in a matter of days.
-
-**Active community**
-MDB is broadly used by professionals on multiple levels, who are ready to aid you.
-
-**Modularity**
-Use TypeScript modules to compile package adjusted yo your needs.
-
-**Useful helpers**
-Reduce the frequency of highly repetitive declarations in your CSS.
-
-**Technical support**
-Every day we help our users with their issues and problems.
-
-**SASS files**
-Thought-out .scss files come in a compile-ready form.
-
-**Flexbox**
-Full support of Flexbox layout system lets you forget about alignment issues.
-
-
-A big **thank you to all our users** who are working with us to improve the software. We wouldn't be where we are without you.
-
-
-# Useful Links:
-
-Getting started: https://mdbootstrap.com/angular-bootstrap-getting-started/
-
-5 min quick start: https://mdbootstrap.com/angular/5min-quickstart/
-
-Material Design + Bootstrap Tutorial: https://mdbootstrap.com/bootstrap-tutorial/
-
-Material Design + WordPress Tutorial: https://mdbootstrap.com/wordpress-tutorial/
-
-Freebies: https://mdbootstrap.com/freebies/
-
-Premium Templates: https://mdbootstrap.com/templates/
-
-Changelog: https://mdbootstrap.com/angular/changelog/
-
-# Social Media:
-
-Twitter: https://twitter.com/MDBootstrap
-
-Facebook: https://www.facebook.com/mdbootstrap
-
-Pinterest: https://pl.pinterest.com/mdbootstrap
-
-Google+: https://plus.google.com/u/0/b/107863090883699620484/+Mdbootstrap/posts
-
-Dribbble: https://dribbble.com/mdbootstrap
-
-LinkedIn: https://www.linkedin.com/company/material-design-for-bootstrap
-
-## Contact:
-office@mdbootstrap.com
diff --git a/src/angular-bootstrap-md/buttons/buttons.module.ts b/src/angular-bootstrap-md/buttons/buttons.module.ts
deleted file mode 100755
index bc805fa0..00000000
--- a/src/angular-bootstrap-md/buttons/buttons.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { NgModule, ModuleWithProviders } from '@angular/core';
-
-import { ButtonCheckboxDirective } from './checkbox.directive';
-import { ButtonRadioDirective } from './radio.directive';
-
-@NgModule({
- declarations: [ButtonCheckboxDirective, ButtonRadioDirective],
- exports: [ButtonCheckboxDirective, ButtonRadioDirective]
-})
-export class ButtonsModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: ButtonsModule, providers: []};
- }
-}
diff --git a/src/angular-bootstrap-md/buttons/checkbox.directive.ts b/src/angular-bootstrap-md/buttons/checkbox.directive.ts
deleted file mode 100755
index 9d0700c1..00000000
--- a/src/angular-bootstrap-md/buttons/checkbox.directive.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Directive, HostBinding, HostListener, Input, OnInit, forwardRef } from '@angular/core';
-import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
-
-// TODO: config: activeClass - Class to apply to the checked buttons
-
-export const CHECKBOX_CONTROL_VALUE_ACCESSOR: any = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => ButtonCheckboxDirective),
- multi: true
-};
-
-/**
-* Add checkbox functionality to any element
-*/
-@Directive({selector: '[mdbCheckbox]', providers: [CHECKBOX_CONTROL_VALUE_ACCESSOR]})
-export class ButtonCheckboxDirective implements ControlValueAccessor, OnInit {
- /** Truthy value, will be set to ngModel */
- @Input() public btnCheckboxTrue: any = true;
- /** Falsy value, will be set to ngModel */
- @Input() public btnCheckboxFalse: any = false;
-
- @HostBinding('class.active') public state = false;
-
- protected value: any;
- protected isDisabled: boolean;
-
- protected onChange: any = Function.prototype;
- protected onTouched: any = Function.prototype;
-
- // view -> model
- @HostListener('click')
- public onClick(): void {
- if (this.isDisabled) {
- return;
- }
-
- this.toggle(!this.state);
- this.onChange(this.value);
- }
-
- public ngOnInit(): any {
- this.toggle(this.trueValue === this.value);
- }
-
- protected get trueValue(): boolean {
- return typeof this.btnCheckboxTrue !== 'undefined'
- ? this.btnCheckboxTrue
- : true;
- }
-
- protected get falseValue(): boolean {
- return typeof this.btnCheckboxFalse !== 'undefined'
- ? this.btnCheckboxFalse
- : false;
- }
-
- public toggle(state: boolean): void {
- this.state = state;
- this.value = this.state ? this.trueValue : this.falseValue;
- }
-
- // ControlValueAccessor
- // model -> view
- public writeValue(value: any): void {
- this.state = this.trueValue === value;
- this.value = value ? this.trueValue : this.falseValue;
- }
-
- public setDisabledState(isDisabled: boolean): void {
- this.isDisabled = isDisabled;
- }
-
- public registerOnChange(fn: (_: any) => {}): void {
- this.onChange = fn;
- }
-
- public registerOnTouched(fn: () => {}): void {
- this.onTouched = fn;
- }
-}
diff --git a/src/angular-bootstrap-md/buttons/index.ts b/src/angular-bootstrap-md/buttons/index.ts
deleted file mode 100755
index 99b59e51..00000000
--- a/src/angular-bootstrap-md/buttons/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { ButtonsModule } from './buttons.module';
-export { ButtonRadioDirective } from './radio.directive';
-export { ButtonCheckboxDirective } from './checkbox.directive';
diff --git a/src/angular-bootstrap-md/buttons/licens.md b/src/angular-bootstrap-md/buttons/licens.md
deleted file mode 100755
index 68dc12fd..00000000
--- a/src/angular-bootstrap-md/buttons/licens.md
+++ /dev/null
@@ -1 +0,0 @@
-https://github.com/valor-software/ngx-bootstrap/blob/development/LICENSE
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/buttons/radio.directive.ts b/src/angular-bootstrap-md/buttons/radio.directive.ts
deleted file mode 100755
index 16888030..00000000
--- a/src/angular-bootstrap-md/buttons/radio.directive.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Directive, ElementRef, HostBinding, forwardRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core';
-import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
-
-export const RADIO_CONTROL_VALUE_ACCESSOR: any = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => ButtonRadioDirective),
- multi: true
-};
-
-/**
-* Create radio buttons or groups of buttons.
-* A value of a selected button is bound to a variable specified via ngModel.
-*/
-@Directive({ selector: '[mdbRadio]', providers: [RADIO_CONTROL_VALUE_ACCESSOR] })
-export class ButtonRadioDirective implements ControlValueAccessor, OnInit {
- public onChange: any = Function.prototype;
- public onTouched: any = Function.prototype;
-
- radioElementsArray: Array = [];
- /** Radio button value, will be set to `ngModel` */
- @Input() public mdbRadio: any;
- /** If `true` — radio button can be unchecked */
- @Input() public uncheckable: boolean;
- /** Current value of radio component or group */
- @Input() public value: any;
-
- protected el: ElementRef;
-
- @HostBinding('class.active')
- public get isActive(): boolean {
- return this.mdbRadio === this.value;
- }
-
- // @HostBinding('class.active')
- @HostListener('click', ['$event'])
- public onClick(event?: any): void {
- try {
- this.el.nativeElement.parentElement.childNodes.forEach(element => {
- this.radioElementsArray.push(element);
- });
- this.radioElementsArray.forEach(element => {
- this.renderer.removeClass(element, 'active');
- });
- this.renderer.addClass(event.target, 'active');
- } catch (error) {
-
- }
- if (this.el.nativeElement.attributes.disabled) {
- return;
- }
-
- if (this.uncheckable && this.mdbRadio === this.value) {
- this.value = undefined;
- } else {
- this.value = this.mdbRadio;
- }
-
- this.onTouched();
- this.onChange(this.value);
- }
-
- public constructor(el: ElementRef, private renderer: Renderer2) {
- this.el = el;
- }
-
- public ngOnInit(): void {
- this.uncheckable = typeof this.uncheckable !== 'undefined';
- }
-
- public onBlur(): void {
- this.onTouched();
- }
-
- // ControlValueAccessor
- // model -> view
- public writeValue(value: any): void {
- this.value = value;
- }
-
- public registerOnChange(fn: any): void {
- this.onChange = fn;
- }
-
- public registerOnTouched(fn: any): void {
- this.onTouched = fn;
- }
-}
diff --git a/src/angular-bootstrap-md/carousel/carousel.component.html b/src/angular-bootstrap-md/carousel/carousel.component.html
deleted file mode 100755
index 07b7a5fc..00000000
--- a/src/angular-bootstrap-md/carousel/carousel.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/carousel/carousel.component.ts b/src/angular-bootstrap-md/carousel/carousel.component.ts
deleted file mode 100755
index 24ec6f7d..00000000
--- a/src/angular-bootstrap-md/carousel/carousel.component.ts
+++ /dev/null
@@ -1,457 +0,0 @@
-import { Component, Input, OnDestroy, Output, EventEmitter, ElementRef, HostListener } from '@angular/core';
-
- import { isBs3 } from '../utils/ng2-bootstrap-config';
- import { LinkedList } from '../utils/linked-list.class';
- import { SlideComponent } from './slide.component';
- import { CarouselConfig } from './carousel.config';
-
- export enum Direction {UNKNOWN, NEXT, PREV}
-
-/**
-* Base element to create carousel
-*/
-@Component({
- selector: 'mdb-carousel',
- templateUrl: './carousel.component.html',
-})
-
-export class CarouselComponent implements OnDestroy {
- SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight' };
-
- protected _slides: LinkedList = new LinkedList();
- public get slides(): SlideComponent[] {
- return this._slides.toArray();
- }
-
- protected currentInterval: any;
- protected isPlaying: boolean;
- protected destroyed = false;
- // protected el: ElementRef = null;
- protected el: ElementRef | any = null;
- protected animationEnd = true;
-
- /** If `true` — carousel will not cycle continuously and will have hard stops (prevent looping) */
- @Input() public noWrap: boolean;
- /** If `true` — will disable pausing on carousel mouse hover */
- @Input() public noPause: boolean;
-
- @Input('isControls') public isControls: boolean = true;
- @Input() public keyboard: boolean;
-
- @Input('class') public class: String = '';
- @Input('type') public type: String = '';
- @Input('animation') public animation: String = '';
-
- // protected _currentActiveSlide: number;
- protected _currentActiveSlide: number | any ;
-
- /** Will be emitted when active slide has been changed. Part of two-way-bindable [(activeSlide)] property */
- @Output() public activeSlideChange: EventEmitter = new EventEmitter(false);
-
- /** Index of currently displayed slide(started for 0) */
- @Input()
- public set activeSlide(index: number) {
- if (this._slides.length && index !== this._currentActiveSlide) {
- this._select(index);
- }
- }
- public get activeSlide(): number {
- return this._currentActiveSlide;
- }
-
- protected _interval: number;
-
- public checkNavigation() {
- if (this.type === 'carousel-multi-item') {
- return false;
- }
- return true;
-
- }
-
- public checkDots() {
- if (this.type === 'carousel-thumbnails') {
- return false;
- }
-
-
- return true;
- }
-
- getImg(slide: any) {
- return slide.el.nativeElement.querySelector('img').src;
- }
-
- /**
- * Delay of item cycling in milliseconds. If false, carousel won't cycle automatically.
- */
- @Input()
- public get interval(): number {
- return this._interval;
- }
- public set interval(value: number) {
- this._interval = value;
- this.restartTimer();
- }
-
- public get isBs4(): boolean {
- return !isBs3();
- }
-
- public constructor(config: CarouselConfig, el: ElementRef) {
- Object.assign(this, config);
- this.el = el;
- }
-
- public ngOnDestroy(): void {
- this.destroyed = true;
- }
-
- /**
- * Adds new slide. If this slide is first in collection - set it as active and starts auto changing
- * @param slide
- */
- public addSlide(slide: SlideComponent): void {
- this._slides.add(slide);
- if (this._slides.length === 1) {
- this._currentActiveSlide = void 0;
- this.activeSlide = 0;
- this.play();
- }
- }
-
- /**
- * Removes specified slide. If this slide is active - will roll to another slide
- * @param slide
- */
- public removeSlide(slide: SlideComponent): void {
- const remIndex = this._slides.indexOf(slide);
-
- if (this._currentActiveSlide === remIndex) {
-
- // removing of active slide
- // let nextSlideIndex: number = void 0;
- let nextSlideIndex: number | any = void 0;
- if (this._slides.length > 1) {
- // if this slide last - will roll to first slide, if noWrap flag is FALSE or to previous, if noWrap is TRUE
- // in case, if this slide in middle of collection, index of next slide is same to removed
- nextSlideIndex = !this.isLast(remIndex) ? remIndex :
- this.noWrap ? remIndex - 1 : 0;
- }
- this._slides.remove(remIndex);
-
- // prevents exception with changing some value after checking
- setTimeout(() => {
- this._select(nextSlideIndex);
- }, 0);
- } else {
- this._slides.remove(remIndex);
- const currentSlideIndex = this.getCurrentSlideIndex();
- setTimeout(() => {
- // after removing, need to actualize index of current active slide
- this._currentActiveSlide = currentSlideIndex;
- this.activeSlideChange.emit(this._currentActiveSlide);
- }, 0);
-
- }
- }
- // Fixed problem while cannot swipe next / previous image while using HammerJS.
- swipe(action = this.SWIPE_ACTION.RIGHT) {
- if (action === this.SWIPE_ACTION.RIGHT) {
- this.previousSlide();
- }
-
- if (action === this.SWIPE_ACTION.LEFT) {
- this.nextSlide();
- }
- }
-
-
- /**
- * Rolling to next slide
- * @param force: {boolean} if true - will ignore noWrap flag
- */
- public nextSlide(force: boolean = false) {
- if (this.animation === 'slide') {
- this.pause();
- const direction = Direction.NEXT;
- this.slideAnimation(this.findNextSlideIndex(direction, force), direction);
- } else if (this.animation === 'fade') {
- this.pause();
- this.fadeAnimation(this.findNextSlideIndex(Direction.NEXT, force));
- } else {
- this.activeSlide = this.findNextSlideIndex(Direction.NEXT, force);
- }
-
- }
-
- /**
- * Rolling to previous slide
- * @param force: {boolean} if true - will ignore noWrap flag
- */
- public previousSlide(force: boolean = false): void {
- if (this.animation === 'slide') {
- this.pause();
- const direction = Direction.PREV;
- this.slideAnimation(this.findNextSlideIndex(direction, force), direction);
- } else if (this.animation === 'fade') {
- this.pause();
- this.fadeAnimation(this.findNextSlideIndex(Direction.PREV, force));
- } else {
- this.activeSlide = this.findNextSlideIndex(Direction.PREV, force);
- }
- }
-
- protected fadeAnimation(goToIndex: number) {
- //const currentSlide = this._slides.get(this._currentActiveSlide);
- const goToSlide = this._slides.get(goToIndex);
-
- if (this.animationEnd) {
- this.animationEnd = false;
-
- goToSlide.directionNext = true;
-
- setTimeout(() => {
- goToSlide.directionNext = false;
- this.animationEnd = true;
-
- this.activeSlide = goToIndex;
-
- this.play();
- }, 100);
- }
- }
-
- protected slideAnimation(goToIndex: number, direction: any) {
-
- const currentSlide = this._slides.get(this._currentActiveSlide);
- const goToSlide = this._slides.get(goToIndex);
-
- if (this.animationEnd) {
- if (direction === Direction.NEXT) {
- this.animationEnd = false;
- goToSlide.directionNext = true;
- setTimeout(() => {
- goToSlide.directionLeft = true;
- currentSlide.directionLeft = true;
- }, 100);
- }
-
- if (direction === Direction.PREV) {
- this.animationEnd = false;
-
- goToSlide.directionPrev = true;
- setTimeout(() => {
- goToSlide.directionRight = true;
- currentSlide.directionRight = true;
- }, 100);
- }
-
- setTimeout(() => {
- goToSlide.directionLeft = false;
- goToSlide.directionNext = false;
- currentSlide.directionLeft = false;
- currentSlide.directionNext = false;
- goToSlide.directionRight = false;
- goToSlide.directionPrev = false;
- currentSlide.directionRight = false;
- currentSlide.directionPrev = false;
-
- this.animationEnd = true;
-
- this.activeSlide = goToIndex;
-
- let directionName;
- if (direction === Direction.NEXT) {
- directionName = 'Next';
- } else if (direction === Direction.PREV) {
- directionName = 'Prev';
- }
-
- this.activeSlideChange.emit({'direction': directionName, 'relatedTarget': this.activeSlide});
- this.play();
- }, 700);
- }
- }
-
-
-
- /**
- * Rolling to specified slide
- * @param index: {number} index of slide, which must be shown
- */
- public selectSlide(index: number): void {
- this.pause();
- if (this.animation === 'slide') {
-
- if (this.activeSlide < index) {
- this.slideAnimation(index, Direction.NEXT);
- } else if (this.activeSlide > index) {
- this.slideAnimation(index, Direction.PREV);
- }
- } else if (this.animation === 'fade') {
- if (index !== this.activeSlide) {
- this.fadeAnimation(index);
- }
- }
- this.play();
- }
-
- /**
- * Starts a auto changing of slides
- */
- @HostListener('mouseleave') play() {
- if (!this.isPlaying) {
- this.isPlaying = true;
- this.restartTimer();
- }
- }
-
- /**
- * Stops a auto changing of slides
- */
- @HostListener('mouseenter') pause() {
- if (!this.noPause) {
- this.isPlaying = false;
- this.resetTimer();
- }
- }
-
- /**
- * Finds and returns index of currently displayed slide
- */
- public getCurrentSlideIndex(): number {
- return this._slides.findIndex((slide: SlideComponent) => slide.active);
- }
-
- /**
- * Defines, whether the specified index is last in collection
- */
- public isLast(index: number): boolean {
- return index + 1 >= this._slides.length;
- }
-
- /**
- * Defines next slide index, depending of direction
- * @param direction: Direction(UNKNOWN|PREV|NEXT)
- * @param force: {boolean} if TRUE - will ignore noWrap flag, else will return undefined if next slide require wrapping
-
- */
- private findNextSlideIndex(direction: Direction, force: boolean): any {
- let nextSlideIndex = 0;
-
- if (!force && (this.isLast(this.activeSlide) && direction !== Direction.PREV && this.noWrap)) {
- return void 0;
- }
-
- switch (direction) {
- case Direction.NEXT:
- // if this is last slide, not force, looping is disabled and need to going forward - select current slide, as a next
- nextSlideIndex = (!this.isLast(this._currentActiveSlide)) ? this._currentActiveSlide + 1 :
- (!force && this.noWrap ) ? this._currentActiveSlide : 0;
- break;
- case Direction.PREV:
- // if this is first slide, not force, looping is disabled and need to going backward - select current slide, as a next
- nextSlideIndex = (this._currentActiveSlide > 0) ? this._currentActiveSlide - 1 :
- (!force && this.noWrap ) ? this._currentActiveSlide : this._slides.length - 1;
- break;
- default:
- throw new Error('Unknown direction');
- }
- return nextSlideIndex;
- }
-
- /**
- * Sets a slide, which specified through index, as active
- * @param index
- */
- private _select(index: number): void {
- if (isNaN(index)) {
- this.pause();
- return;
- }
- const currentSlide = this._slides.get(this._currentActiveSlide);
- if (currentSlide) {
- currentSlide.active = false;
- }
- const nextSlide = this._slides.get(index);
- if (nextSlide) {
- this._currentActiveSlide = index;
- nextSlide.active = true;
- this.activeSlide = index;
- // this.activeSlideChange.emit(index);
- }
- }
-
- /**
- * Starts loop of auto changing of slides
- */
- private restartTimer(): any {
- this.resetTimer();
- const interval = +this.interval;
- if (!isNaN(interval) && interval > 0) {
- this.currentInterval = setInterval(
- () => {
- const nInterval = +this.interval;
- if (this.isPlaying && !isNaN(this.interval) && nInterval > 0 && this.slides.length) {
- this.nextSlide();
- } else {
- this.pause();
- }
- },
- interval);
- }
- }
-
- /**
- * Stops loop of auto changing of slides
- */
- private resetTimer(): void {
- if (this.currentInterval) {
- clearInterval(this.currentInterval);
- this.currentInterval = void 0;
- }
- }
-
- protected hasClass(el: any, className: any) {
- if (el.classList) {
- return el.classList.contains(className);
- } else {
- return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
- }
- }
-
- protected classAdd(el: any, className: any) {
- if (el.classList) {
- el.classList.add(className);
- } else if (!this.hasClass(el, className)) {
- el.className += ' ' + className;
- }
- }
-
- protected removeClass(el: any, className: any) {
- if (el.classList) {
- el.classList.remove(className);
- } else if (this.hasClass(el, className)) {
- const reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
- el.className = el.className.replace(reg, ' ');
- }
- }
-
- @HostListener('keyup', ['$event']) keyboardControl(event: KeyboardEvent) {
- if (this.keyboard) {
- if (event.keyCode === 39) {
- this.nextSlide();
- }
-
- if (event.keyCode === 37) {
- this.previousSlide();
- }
- }
-
- }
- @HostListener('click', ['$event']) focus() {
- this.el.nativeElement.focus();
- }
-
- }
diff --git a/src/angular-bootstrap-md/carousel/carousel.config.ts b/src/angular-bootstrap-md/carousel/carousel.config.ts
deleted file mode 100755
index 21fbe1ff..00000000
--- a/src/angular-bootstrap-md/carousel/carousel.config.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable()
-export class CarouselConfig {
- /** Default interval of auto changing of slides */
- public interval = 5000;
-
- /** Is loop of auto changing of slides can be paused */
- public noPause = false;
-
- /** Is slides can wrap from the last to the first slide */
- public noWrap = false;
-
- public keyboard = false;
-}
diff --git a/src/angular-bootstrap-md/carousel/carousel.module.ts b/src/angular-bootstrap-md/carousel/carousel.module.ts
deleted file mode 100755
index 777217cd..00000000
--- a/src/angular-bootstrap-md/carousel/carousel.module.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { NgModule, ModuleWithProviders } from '@angular/core';
-
-import { CarouselComponent } from './carousel.component';
-import { SlideComponent } from './slide.component';
-import { CarouselConfig } from './carousel.config';
-
-@NgModule({
- imports: [CommonModule],
- declarations: [SlideComponent, CarouselComponent],
- exports: [SlideComponent, CarouselComponent],
- providers: [CarouselConfig]
-})
-export class CarouselModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: CarouselModule, providers: []};
- }
-}
diff --git a/src/angular-bootstrap-md/carousel/index.ts b/src/angular-bootstrap-md/carousel/index.ts
deleted file mode 100755
index 322baeaa..00000000
--- a/src/angular-bootstrap-md/carousel/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { CarouselComponent } from './carousel.component';
-export { CarouselModule } from './carousel.module';
-export { SlideComponent } from './slide.component';
-export { CarouselConfig } from './carousel.config';
diff --git a/src/angular-bootstrap-md/carousel/licens.md b/src/angular-bootstrap-md/carousel/licens.md
deleted file mode 100755
index 68dc12fd..00000000
--- a/src/angular-bootstrap-md/carousel/licens.md
+++ /dev/null
@@ -1 +0,0 @@
-https://github.com/valor-software/ngx-bootstrap/blob/development/LICENSE
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/carousel/slide.component.ts b/src/angular-bootstrap-md/carousel/slide.component.ts
deleted file mode 100755
index c7dc53c5..00000000
--- a/src/angular-bootstrap-md/carousel/slide.component.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Component, HostBinding, OnDestroy, Input, OnInit, ElementRef } from '@angular/core';
-
-import { CarouselComponent } from './carousel.component';
-
-@Component({
- selector: 'mdb-slide',
- template: `
-
- `
-})
-export class SlideComponent implements OnInit, OnDestroy {
-
- /** Is current slide active */
- @HostBinding('class.active')
- @Input() public active: boolean;
- @HostBinding('class.animated') animated = false;
- @HostBinding('class.carousel-item-next') directionNext = false;
- @HostBinding('class.carousel-item-left') directionLeft = false;
- @HostBinding('class.carousel-item-prev') directionPrev = false;
- @HostBinding('class.carousel-item-right') directionRight = false;
- /** Wraps element by appropriate CSS classes */
- @HostBinding('class.carousel-item')
-
-
- /** Link to Parent(container-collection) component */
- protected carousel: CarouselComponent;
- // public el: ElementRef = null;
- public el: ElementRef | any = null;
-
- public constructor(carousel: CarouselComponent, el: ElementRef) {
- this.carousel = carousel;
- this.el = el;
- }
-
- /** Fires changes in container collection after adding a new slide instance */
- public ngOnInit(): void {
- this.carousel.addSlide(this);
- }
-
- /** Fires changes in container collection after removing of this slide instance */
- public ngOnDestroy(): void {
- this.carousel.removeSlide(this);
- }
-}
diff --git a/src/angular-bootstrap-md/changelog b/src/angular-bootstrap-md/changelog
deleted file mode 100644
index 80839b6b..00000000
--- a/src/angular-bootstrap-md/changelog
+++ /dev/null
@@ -1,206 +0,0 @@
-6.1.1
-*** NOTE: ***
-You may need to rename the modules you are using in your application. The list of modified modules is presented below.
-
-In 6.1.0 we have made it possible to import only those modules that you actually use in your application.
-If only individual modules are used, import them but do not import the MDBBootstrapModulesPro main module as this may cause errors.
-
-*** What's new: ***
- Source code delivered to customers is now compilated to .js instead of .ts,
- There's now possibility to import only those modules, which you need in your project. Full module list: https://mdbootstrap.com/angular/modules-list
- Changed names of few modules:
- * MDBChartsModule -> ChartsModule,
- * BsDropdownModule -> DropdownModule,
- * MDBTooltipModule -> TooltipModule,
- * SqueezeBoxModule -> AccordionModule,
- * Ng2CompleterModule -> AutocompleteModule,
- * MDBDatePickerModule -> DatepickerModule,
- * MDBUploaderModule -> FileInputModule,
- * MDBPageScrollModule -> SmoothscrollModule,
- * MdbStickyModule -> StickyContentModule,
- * MaterialRootModule -> PreloadersModule.
-
-*** Fixes: ***
- Fixed missing space in mdb-angular.scss file,
- Added close icon to Autocomplete,
- Fixed problem with prefilling Char Counter value,
- Fixed problem with overlapping label & error / success messages with mdbInputDirective,
- Fixed problem with focusing inputs through TAB key.
-
-
-6.0.2
-*** General: ***
- Changed Renderer to Renderer2.
-
-*** Fixes: ***
- Unnable to scroll in Sidenav,
- Fixed problems with skins,
- Fixed mobile view in DatePicker,
- Fixed problem with scrolling page to the top edge in DatePicker,
- Fixed problem with scrolling page to the top edge in TimePicker.
-
-
-6.0.1
-*** Fixes: ***
- Fix for dropdowns, dropup variation,
- Fix for fixed button when collapsing (need to pass $event in .toggle() method),
- Few fixes in scss
-
-6.0.0
-*** NOTE: Most of the components have been rebuilt. Please, check the ones you use in your project. ***
-
-*** General: ***
-
- Compatible with MDB 4.5.1,
- Compatible with Angular 6.0.0-rc.5, rxjs 6.0.0-uncanny-rc.7 and rxjs-compat 6.0.0-uncanny-rc.7,
- Compatible with Angular CLI ^1.7.1.
-
-*** NOTE 2: For proper using Angular 6 with MDB Angular 6 please check, if you're using rxjs in version at least 6.0.0-uncanny-rc.7 and rxjs-compat in version at least 6.0.0-uncanny-rc.7. Using older versions may produce errors and problems. ***
-
-*** Marked as deprecated: ***
-
- mdbActive,
- mdbInputValidate,
- mdbDeepDirective,
- mdbRippleRadius.
-
-mdbInputDirective has three of the above directives - mdbActive, mdbInputValidate and mdbDeepDirective.
-
-mdbActive is used by default and we do not anticipate that you will be able to disable it.
-
-mdbInputValidate is used by default, but the user can disable validation by using the [mdbValidate]="false" control on input which contains mdbInputDirective.
-
-mdbDeepDirective is used by default when used on a checkbox or radio. It is possible to disable it by using [focusCheckbox]="false" and [focusRadio]="false".
-
-mdbRippleRadius was changed to mdbWavesEffect. mdbRippleRadius will be deleted after next release.
-
-*** Fixes: ***
-
- Fix for z-index in few scenarios (tabs & datePicker), navbar and other,
- Fixed clearUnselected in Autocomplete,
- Fix for mdbActive in Autocomplete.
-
-*** Features: ***
-
- Added feature to close datePicker after selecting an date (closeAfterSelect: true) in datePicker options object.
-
-*** Docs: ***
-
- New category Forms,
- Autocomplete, Inputs, Input Validation, Forms moved from Components to Forms,
- Material Select moved from Advanced to Forms,
- Input Groups moved from Extended to Forms,
- Added instructions how to update Angular projects: https://mdbootstrap.com/getting-started/update-instructions/
- Added instructions how to create project with Angular Universal: https://mdbootstrap.com/getting-started/universal/
-
-*** Changed syntax: ***
-
- Cards,
- Flipping Cards,
- Intros,
- Sections,
- Inputs,
- Footers,
- Preloaders,
- Masks,
- Hover Effects,
- Accordion,
- Modals,
-
-5.2.3
-*** Compatibility with Angular Universal (Server-Side Rendering) ***. Read tutorial.
-Improved dropdown animation in Material Select,
-Fixed problem with dropdown transition in Material Select,
-"Not Found" message in Material Select will be visible only when filtering does not show results ,
-Fix for top / bottom arrow in Material Select,
-Added possibility to overwrite $image-path variable.
-
-5.2.2
-Hotfixes not included in 5.2.1 patch
-
-5.2.1
-Enhanced mdbActive directive - works with data prefilled by browser
-Sidenav issue fixed (skin is optional)
-Fixed with rounded buttons inside input group
-Fixed for dropdown not getting closed after click in other dropdown.
-Fix for line breaking label in checkboxes
-
-5.2.0
-Updated to Bootstrap 4 stable version
-Integration with .NET CORE - tutorial here
-Compatibility with Angular 5.2.1 and CLI 1.6.5
-noImplicitAny and strictNullChecks compatibility
-New validation added (number, tel, submit) + custom validation regex. Read tutorial.
-Configurable labels for DatePicker
-
-5.1.2
-Angular 5.2.1 and CLI 1.6.5 compatibility
-Extended docs for modals - auto focus on input
-Fixed validation for email and password
-Aria attributes added to datepicker
-Added filtering options to Material Select
-Support for IE+ and Edge for Double Navigation
-Extended DatePicker with new functionality:
-Important!
-By default date picker will load now +- 7 years starting from current year. It can be overwritten using minYear and maxYear parameters:
-
-import { Component } from '@angular/core';
-import { IMyOptions } from 'your_path_to/date-picker/index';
-
-@Component({
- selector: 'date-picker-component-example',
- templateUrl: 'toast.component.html'
-})
-
- export class DatePickerComponentExample {
- public myDatePickerOptions: IMyOptions = {
- minYear: 2015,
- maxYear: 2017
- };
-}
-
-5.1.1
-Double navigation - support for collapsed sidenav and navbar
-Extended documentation for Autocomplete
-Auto resize function for collapse added
-Mobile gestures carousel support added
-Added option to hide next / prev buttons in carousel
-Fix for: Module has no exported member 'MDBSpinningPreloader'
-Automatically set date in DatePicker using ngModel
-Fixed docs for File Upload
-
-5.1.0
-Angular 5.1.3 and CLI 1.6.3. compatibility
-Fixed issue with exceeding text tabs component
-Auto-close navbar after click into link (check documentation for more information)
-Fixed problem with error/success message on Firefox
-Fixed problems with selecting hour on mobile devices in Timepicker
-Extended configuration options of easy pie charts (check documentation for more information)
-Fixed for relative path in _skins.scss
-Fixed issue with date picker for Firefox
-Enhanced support for small screen resolution in Datepicker (<575 px)
-Fix for issue while using multiple MDB Autocomplete components
-
-5.0.5
-Added compatibility with noUnusedLocals
-
-5.0.4
-Added compatibility with noUnusedParameters
-
-5.0.3
-Fixed missing arrow in popovers
-
-5.0.2
-Fixed circular dependencies error.
-
-5.0.1
-Fixed for imgPath in .scss
-
-5.0.0
-Version 5.0.0 brings brings compatibility with new Angular 5.0 and CLI 1.5. Except for that new update include Bootstrap 4 Beta 2 assets.
-
-What's new in Bootstrap 4 beta-2:
-
-Restored `.offset-*` classes
-Switched Breadcrumbs from `float` to Flexbox
-Dropped support for Bower
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/charts/chart.directive.ts b/src/angular-bootstrap-md/charts/chart.directive.ts
deleted file mode 100755
index 908a74fc..00000000
--- a/src/angular-bootstrap-md/charts/chart.directive.ts
+++ /dev/null
@@ -1,280 +0,0 @@
-import {
- OnDestroy,
- OnInit,
- OnChanges,
- EventEmitter,
- ElementRef,
- Input,
- Output,
- SimpleChanges,
- Directive
-} from '@angular/core';
-
-import { Color } from './color.service';
-import { Colors } from './colors.service';
-
-import { isPlatformBrowser } from '@angular/common';
-import { PLATFORM_ID, Inject } from '@angular/core';
-declare var Chart: any;
-
-@Directive({ selector: 'canvas[mdbChart]', exportAs: 'mdb-base-chart' })
-export class BaseChartDirective implements OnDestroy, OnChanges, OnInit, Colors {
- public static defaultColors: Array = [
- [255, 99, 132],
- [54, 162, 235],
- [255, 206, 86],
- [231, 233, 237],
- [75, 192, 192],
- [151, 187, 205],
- [220, 220, 220],
- [247, 70, 74],
- [70, 191, 189],
- [253, 180, 92],
- [148, 159, 177],
- [77, 83, 96]
- ];
-
- @Input() public data: number[] | any[];
- @Input() public datasets: any[];
- @Input() public labels: Array = [];
- @Input() public options: any = {
- legend: { display: false }
- };
- @Input() public chartType: string;
- @Input() public colors: Array;
- @Input() public legend = false;
-
- @Output() public chartClick: EventEmitter = new EventEmitter();
- @Output() public chartHover: EventEmitter = new EventEmitter();
-
- public ctx: any;
- public chart: any;
- cvs: any;
- initFlag = false;
-
- element: ElementRef;
- isBrowser: any = false;
- public constructor(element: ElementRef, @Inject(PLATFORM_ID) platformId: string) {
- this.element = element;
- this.isBrowser = isPlatformBrowser(platformId);
- }
-
- public ngOnInit(): any {
- if (this.isBrowser) {
- this.ctx = this.element.nativeElement.getContext('2d');
- this.cvs = this.element.nativeElement;
- this.initFlag = true;
- if (this.data || this.datasets) {
- this.refresh();
- }
- }
- }
-
- public ngOnChanges(changes: SimpleChanges): void {
- if (this.initFlag) {
- // Check if the changes are in the data or datasets
- if (changes.hasOwnProperty('data') || changes.hasOwnProperty('datasets')) {
- if (changes['data']) {
- this.updateChartData(changes['data'].currentValue);
- } else {
- this.updateChartData(changes['datasets'].currentValue);
- }
-
- this.chart.update();
- } else {
- // otherwise rebuild the chart
- this.refresh();
- }
- }
- }
-
- public ngOnDestroy(): any {
- if (this.chart) {
- this.chart.destroy();
- this.chart = void 0;
- }
- }
-
- public getChartBuilder(ctx: any/*, data:Array, options:any*/): any {
- const datasets: any = this.getDatasets();
-
- const options: any = Object.assign({}, this.options);
- if (this.legend === false) {
- options.legend = { display: false };
- }
- // hock for onHover and onClick events
- options.hover = options.hover || {};
- if (!options.hover.onHover) {
- options.hover.onHover = (event: any, active: Array) => {
- if (active && active.length) {
- this.chartHover.emit({ event, active });
- }
- };
- }
-
- if (!options.onClick) {
- options.onClick = (event: any, active: Array) => {
- this.chartClick.emit({ event, active });
- };
- }
-
- const opts = {
- type: this.chartType,
- data: {
- labels: this.labels,
- datasets: datasets
- },
- options: options
- };
-
- return new Chart(ctx, opts);
- }
-
- private updateChartData(newDataValues: number[] | any[]): void {
- if (Array.isArray(newDataValues[0].data)) {
- this.chart.data.datasets.forEach((dataset: any, i: number) => {
- dataset.data = newDataValues[i].data;
-
- if (newDataValues[i].label) {
- dataset.label = newDataValues[i].label;
- }
- });
- } else {
- this.chart.data.datasets[0].data = newDataValues;
- }
- }
-
- private getDatasets(): any {
- let datasets: any = void 0;
- // in case if datasets is not provided, but data is present
- if (!this.datasets || !this.datasets.length && (this.data && this.data.length)) {
- if (Array.isArray(this.data[0])) {
- datasets = (this.data as Array).map((data: number[], index: number) => {
- return { data, label: this.labels[index] || `Label ${index}` };
- });
- } else {
- datasets = [{ data: this.data, label: `Label 0` }];
- }
- }
-
- if (this.datasets && this.datasets.length ||
- (datasets && datasets.length)) {
- datasets = (this.datasets || datasets)
- .map((elm: number, index: number) => {
- const newElm: any = Object.assign({}, elm);
- if (this.colors && this.colors.length) {
- Object.assign(newElm, this.colors[index]);
- } else {
- Object.assign(newElm, getColors(this.chartType, index, newElm.data.length));
- }
- return newElm;
- });
- }
-
- if (!datasets) {
- throw new Error(`ng-charts configuration error,
- data or datasets field are required to render char ${this.chartType}`);
- }
-
- return datasets;
- }
-
- private refresh(): any {
- this.ngOnDestroy();
- this.chart = this.getChartBuilder(this.ctx/*, data, this.options*/);
- }
-}
-
-function rgba(colour: Array, alpha: number): string {
- return 'rgba(' + colour.concat(alpha).join(',') + ')';
-}
-
-function getRandomInt(min: number, max: number): number {
- return Math.floor(Math.random() * (max - min + 1)) + min;
-}
-
-function formatLineColor(colors: Array): Color {
- return {
- backgroundColor: rgba(colors, 0.4),
- borderColor: rgba(colors, 1),
- pointBackgroundColor: rgba(colors, 1),
- pointBorderColor: '#fff',
- pointHoverBackgroundColor: '#fff',
- pointHoverBorderColor: rgba(colors, 0.8)
- };
-}
-
-function formatBarColor(colors: Array): Color {
- return {
- backgroundColor: rgba(colors, 0.6),
- borderColor: rgba(colors, 1),
- hoverBackgroundColor: rgba(colors, 0.8),
- hoverBorderColor: rgba(colors, 1)
- };
-}
-
-function formatPieColors(colors: Array): any {
- return {
- backgroundColor: colors.map((color: number[]) => rgba(color, 0.6)),
- borderColor: colors.map(() => '#fff'),
- pointBackgroundColor: colors.map((color: number[]) => rgba(color, 1)),
- pointBorderColor: colors.map(() => '#fff'),
- pointHoverBackgroundColor: colors.map((color: number[]) => rgba(color, 1)),
- pointHoverBorderColor: colors.map((color: number[]) => rgba(color, 1))
- };
-}
-
-function formatPolarAreaColors(colors: Array): Color {
- return {
- backgroundColor: colors.map((color: number[]) => rgba(color, 0.6)),
- borderColor: colors.map((color: number[]) => rgba(color, 1)),
- hoverBackgroundColor: colors.map((color: number[]) => rgba(color, 0.8)),
- hoverBorderColor: colors.map((color: number[]) => rgba(color, 1))
- };
-}
-
-function getRandomColor(): number[] {
- return [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];
-}
-
-/**
- * Generate colors for line|bar charts
- */
-function generateColor(index: number): number[] {
- return BaseChartDirective.defaultColors[index] || getRandomColor();
-}
-
-/**
- * Generate colors for pie|doughnut charts
- */
-function generateColors(count: number): Array {
- const colorsArr: Array = new Array(count);
- for (let i = 0; i < count; i++) {
- colorsArr[i] = BaseChartDirective.defaultColors[i] || getRandomColor();
- }
- return colorsArr;
-}
-
-/**
- * Generate colors by chart type
- */
-function getColors(chartType: string, index: number, count: number): any {
- if (chartType === 'pie' || chartType === 'doughnut') {
- return formatPieColors(generateColors(count));
- }
-
- if (chartType === 'polarArea') {
- return formatPolarAreaColors(generateColors(count));
- }
-
- if (chartType === 'line' || chartType === 'radar') {
- return formatLineColor(generateColor(index));
- }
-
- if (chartType === 'bar' || chartType === 'horizontalBar') {
- return formatBarColor(generateColor(index));
- }
- return generateColor(index);
-}
-
-
diff --git a/src/angular-bootstrap-md/charts/chart.module.ts b/src/angular-bootstrap-md/charts/chart.module.ts
deleted file mode 100755
index 9baf90d7..00000000
--- a/src/angular-bootstrap-md/charts/chart.module.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { NgModule } from '@angular/core';
-
-import { BaseChartDirective } from './chart.directive';
-
-@NgModule({
- declarations: [
- BaseChartDirective
- ],
- exports: [
- BaseChartDirective
- ],
- imports: []
-})
-export class ChartsModule {
-}
diff --git a/src/angular-bootstrap-md/charts/color.service.ts b/src/angular-bootstrap-md/charts/color.service.ts
deleted file mode 100755
index f6d02c62..00000000
--- a/src/angular-bootstrap-md/charts/color.service.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// private helper functions
-export interface Color {
- backgroundColor?: string | string[];
- borderWidth?: number | number[];
- borderColor?: string | string[];
- borderCapStyle?: string;
- borderDash?: number[];
- borderDashOffset?: number;
- borderJoinStyle?: string;
-
- pointBorderColor?: string | string[];
- pointBackgroundColor?: string | string[];
- pointBorderWidth?: number | number[];
-
- pointRadius?: number | number[];
- pointHoverRadius?: number | number[];
- pointHitRadius?: number | number[];
-
- pointHoverBackgroundColor?: string | string[];
- pointHoverBorderColor?: string | string[];
- pointHoverBorderWidth?: number | number[];
- pointStyle?: string | string[];
-
- hoverBackgroundColor?: string | string[];
- hoverBorderColor?: string | string[];
- hoverBorderWidth?: number;
-}
diff --git a/src/angular-bootstrap-md/charts/colors.service.ts b/src/angular-bootstrap-md/charts/colors.service.ts
deleted file mode 100755
index 0d150743..00000000
--- a/src/angular-bootstrap-md/charts/colors.service.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { Color } from './color.service';
-
-export interface Colors extends Color {
- data?: number[];
- label?: string;
-}
diff --git a/src/angular-bootstrap-md/charts/index.ts b/src/angular-bootstrap-md/charts/index.ts
deleted file mode 100755
index 94317ab9..00000000
--- a/src/angular-bootstrap-md/charts/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { BaseChartDirective } from './chart.directive';
-export { Color } from './color.service';
-export { Colors } from './colors.service';
-export { ChartsModule } from './chart.module';
diff --git a/src/angular-bootstrap-md/charts/licens.md b/src/angular-bootstrap-md/charts/licens.md
deleted file mode 100755
index a9cf4e94..00000000
--- a/src/angular-bootstrap-md/charts/licens.md
+++ /dev/null
@@ -1,5 +0,0 @@
-https://github.com/valor-software/ng2-charts/blob/development/LICENSE
-https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
-https://github.com/rendro/easy-pie-chart/blob/master/LICENSE
-https://www.npmjs.com/package/ng2modules-easypiechart
-
diff --git a/src/angular-bootstrap-md/collapse/collapse.directive.ts b/src/angular-bootstrap-md/collapse/collapse.directive.ts
deleted file mode 100755
index 1c1decff..00000000
--- a/src/angular-bootstrap-md/collapse/collapse.directive.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-// todo: add animations when https://github.com/angular/angular/issues/9947 solved
-import {
- Directive, ElementRef, EventEmitter, Input, OnInit, Output,
- Renderer2, AfterViewInit
-} from '@angular/core';
-
-
-@Directive({
- selector: '[mdbCollapse]',
- exportAs: 'bs-collapse',
- /* tslint:disable-next-line */
-})
-export class CollapseDirective implements OnInit, AfterViewInit {
-
- @Output('showBsCollapse') public showBsCollapse: EventEmitter = new EventEmitter();
- @Output('shownBsCollapse') public shownBsCollapse: EventEmitter = new EventEmitter();
- @Output('hideBsCollapse') public hideBsCollapse: EventEmitter = new EventEmitter();
- @Output('hiddenBsCollapse') public hiddenBsCollapse: EventEmitter = new EventEmitter();
-
-
- /** This event fires as soon as content collapses */
- @Output() public collapsed: EventEmitter = new EventEmitter();
- /** This event fires as soon as content becomes visible */
- @Output() public expanded: EventEmitter = new EventEmitter();
- // shown
- // @HostBinding('class.in')
- // @HostBinding('class.show')
- // @HostBinding('attr.aria-expanded')
- public isExpanded = true;
- // hidden
- // @HostBinding('attr.aria-hidden')
- public isCollapsed = false;
- // stale state
- // @HostBinding('class.collapse')
- public isCollapse = true;
- // animation state
- // @HostBinding('class.collapsing')
- public isCollapsing = false;
- public collapsing = false;
-
-
- @Input() public collapse: boolean;
- @Input() public animationTime = 500;
-
- public maxHeight: number;
-
-
- protected _el: ElementRef;
- protected _renderer: Renderer2;
-
- public constructor(_el: ElementRef, _renderer: Renderer2) {
- this._el = _el;
- this._renderer = _renderer;
- }
- ngOnInit() {
-
- this._el.nativeElement.classList.add('show');
-
- this._el.nativeElement.style.transition = this.animationTime + 'ms ease';
-
- if (!this.collapse) {
- this._el.nativeElement.classList.remove('show');
- this.hide();
- } else {
- this.show();
- }
-
- this.isExpanded = this.collapse;
- }
-
- ngAfterViewInit() {
- this.maxHeight = this._el.nativeElement.scrollHeight;
- }
-
- public resize(): void {
- const container = this._el.nativeElement;
- this.maxHeight = this._el.nativeElement.scrollHeight;
- this._renderer.setStyle(container, 'height', this.maxHeight + 'px');
- }
-
- /** allows to manually toggle content visibility */
- public toggle(event?: any): void {
- if (!this.collapsing) {
- if (this.isExpanded) {
- this.hide();
- } else {
- this.show();
- }
- }
- try {
- if (event.type === 'click') {
- this.maxHeight = event.target.parentElement.nextElementSibling.scrollHeight;
- } else if (event.type === 'mouseenter' || event.type === 'mouseleave') {
- this.maxHeight = event.target.nextElementSibling.scrollHeight;
- }
- } catch (error) { }
- }
-
-
- /** allows to manually hide content */
- public hide(): void {
- this.collapsing = true;
- this.hideBsCollapse.emit();
- this.isCollapse = false;
- this.isCollapsing = true;
-
- this.isExpanded = false;
- this.isCollapsed = true;
-
- const container = this._el.nativeElement;
-
- container.classList.remove('collapse');
- container.classList.remove('show');
- container.classList.add('collapsing');
-
- this._renderer.setStyle(container, 'height', '0px');
-
- setTimeout(() => {
- container.classList.remove('collapsing');
- container.classList.add('collapse');
- this.hiddenBsCollapse.emit();
- this.collapsing = false;
- }, this.animationTime);
- this.collapsed.emit(this);
- }
-
- /** allows to manually show collapsed content */
- public show(): void {
- if (!this.isExpanded) {
- this.collapsing = true;
- this.showBsCollapse.emit();
- this.isCollapse = false;
- this.isCollapsing = true;
-
- this.isExpanded = true;
- this.isCollapsed = false;
-
- const container = this._el.nativeElement;
-
- container.classList.remove('collapse');
- container.classList.add('collapsing');
-
- setTimeout(() => {
- this._renderer.setStyle(container, 'height', this.maxHeight + 'px');
- }, 10);
-
- setTimeout(() => {
- container.classList.remove('collapsing');
- container.classList.add('collapse');
- container.classList.add('show');
- this.shownBsCollapse.emit();
- this.collapsing = false;
- }, this.animationTime - (this.animationTime * 0.5));
- this.expanded.emit(this);
- }
- }
-}
diff --git a/src/angular-bootstrap-md/collapse/collapse.module.ts b/src/angular-bootstrap-md/collapse/collapse.module.ts
deleted file mode 100755
index 72da3066..00000000
--- a/src/angular-bootstrap-md/collapse/collapse.module.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { NgModule, ModuleWithProviders } from '@angular/core';
-import { CollapseDirective } from './collapse.directive';
-
-@NgModule({
- declarations: [CollapseDirective],
- exports: [CollapseDirective]
-})
-export class CollapseModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: CollapseModule, providers: []};
- }
-}
diff --git a/src/angular-bootstrap-md/collapse/index.ts b/src/angular-bootstrap-md/collapse/index.ts
deleted file mode 100755
index c10f14f7..00000000
--- a/src/angular-bootstrap-md/collapse/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { CollapseDirective } from './collapse.directive';
-export { CollapseModule } from './collapse.module';
diff --git a/src/angular-bootstrap-md/dropdown/dropdown-container.component.html b/src/angular-bootstrap-md/dropdown/dropdown-container.component.html
deleted file mode 100755
index 8841ad9f..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown-container.component.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/dropdown/dropdown-container.component.ts b/src/angular-bootstrap-md/dropdown/dropdown-container.component.ts
deleted file mode 100755
index da47cdec..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown-container.component.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { ChangeDetectionStrategy, Component, OnDestroy, HostBinding } from '@angular/core';
-import { BsDropdownState } from './dropdown.state';
-
-@Component({
- selector: 'mdb-dropdown-container',
- changeDetection: ChangeDetectionStrategy.OnPush,
- template: `
-
-
-
- `
-})
-export class BsDropdownContainerComponent implements OnDestroy {
- isOpen = false;
-
- @HostBinding('style.display') display = 'block';
- @HostBinding('style.position') position = 'absolute';
-
- get direction(): 'down' | 'up' {
- return this._state.direction;
- }
-
- private _subscription: any;
-
- constructor(private _state: BsDropdownState) {
- this._subscription = _state.isOpenChange.subscribe((value: boolean) => {
- this.isOpen = value;
- });
- }
-
- ngOnDestroy(): void {
- this._subscription.unsubscribe();
- }
-}
diff --git a/src/angular-bootstrap-md/dropdown/dropdown-menu.directive.ts b/src/angular-bootstrap-md/dropdown/dropdown-menu.directive.ts
deleted file mode 100755
index b8af437c..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown-menu.directive.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';
-import { BsDropdownState } from './dropdown.state';
-
-@Directive({
- selector: '[mdbDropdownMenu],[dropdownMenu]',
- exportAs: 'bs-dropdown-menu'
-})
-export class BsDropdownMenuDirective {
- constructor(_state: BsDropdownState,
- _viewContainer: ViewContainerRef,
- _templateRef: TemplateRef) {
- _state.resolveDropdownMenu({
- templateRef: _templateRef,
- viewContainer: _viewContainer
- });
- }
-}
diff --git a/src/angular-bootstrap-md/dropdown/dropdown-toggle.directive.ts b/src/angular-bootstrap-md/dropdown/dropdown-toggle.directive.ts
deleted file mode 100755
index 438395b1..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown-toggle.directive.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Directive, ElementRef, HostBinding, HostListener, OnDestroy } from '@angular/core';
-import { Subscription } from 'rxjs';
-
-import { BsDropdownState } from './dropdown.state';
-
-@Directive({
- selector: '[mdbDropdownToggle],[dropdownToggle]',
- exportAs: 'bs-dropdown-toggle'
-})
-export class BsDropdownToggleDirective implements OnDestroy {
- private _subscriptions: Subscription[] = [];
-
- @HostBinding('attr.aria-haspopup') ariaHaspopup = true;
- // @HostBinding('attr.disabled') isDisabled: boolean = null;
- @HostBinding('attr.disabled') isDisabled: boolean | any = null;
-
- // @HostBinding('class.active')
- @HostBinding('attr.aria-expanded') isOpen: boolean;
-
- @HostListener('click')
- onClick(): void {
- if (this.isDisabled) {
- return;
- }
- this._state.toggleClick.emit();
- }
-
- @HostListener('document:click', ['$event'])
- onDocumentClick(event: any): void {
- if (this._state.autoClose && event.button !== 2 &&
- !this._element.nativeElement.contains(event.target)) {
- this._state.toggleClick.emit(false);
- }
-}
-
-@HostListener('keyup.esc')
-onEsc(): void {
- if (this._state.autoClose) {
- this._state.toggleClick.emit(false);
- }
-}
-
-constructor(private _state: BsDropdownState,
- private _element: ElementRef) {
- // sync is open value with state
- this._subscriptions.push(this._state
- .isOpenChange.subscribe((value: boolean) => this.isOpen = value));
- // populate disabled state
- this._subscriptions.push(this._state
- .isDisabledChange
- // .subscribe((value: boolean) => this.isDisabled = value || null));
- .subscribe((value: boolean | any) => this.isDisabled = value || null));
-}
-
-ngOnDestroy(): void {
- for (const sub of this._subscriptions) {
- sub.unsubscribe();
- }
-}
-}
diff --git a/src/angular-bootstrap-md/dropdown/dropdown.config.ts b/src/angular-bootstrap-md/dropdown/dropdown.config.ts
deleted file mode 100755
index 51d4988b..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown.config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Injectable } from '@angular/core';
-
-/** Default dropdown configuration */
-@Injectable()
-export class BsDropdownConfig {
- /** default dropdown auto closing behavior */
- autoClose = true;
-}
diff --git a/src/angular-bootstrap-md/dropdown/dropdown.directive.ts b/src/angular-bootstrap-md/dropdown/dropdown.directive.ts
deleted file mode 100755
index 589c48bc..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown.directive.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-import {
- Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output, Renderer2,
- ViewContainerRef
-} from '@angular/core';
-import { Subscription } from 'rxjs';
-
-import { ComponentLoader } from '../utils/component-loader/component-loader.class';
-import { ComponentLoaderFactory } from '../utils/component-loader/component-loader.factory';
-import { BsDropdownConfig } from './dropdown.config';
-import { BsDropdownContainerComponent } from './dropdown-container.component';
-import { BsDropdownState } from './dropdown.state';
-import { BsComponentRef } from '../utils/component-loader/bs-component-ref.class';
-import { BsDropdownMenuDirective } from './dropdown-menu.directive';
-import { isBs3 } from '../utils/ng2-bootstrap-config';
-
-@Directive({
- selector: '[mdbDropdown],[dropdown]',
- exportAs: 'bs-dropdown',
- providers: [BsDropdownState]
-})
-export class BsDropdownDirective implements OnInit, OnDestroy {
-
- /**
- * Placement of a popover. Accepts: "top", "bottom", "left", "right"
- */
- @Input() placement: string;
- /**
- * Specifies events that should trigger. Supports a space separated list of
- * event names.
- */
- @Input() triggers: string;
- /**
- * A selector specifying the element the popover should be appended to.
- * Currently only supports "body".
- */
- @Input() container: string;
-
- /**
- * This attribute indicates that the dropdown should be opened upwards
- */
- @HostBinding('class.dropup')
- @Input() dropup: boolean;
-
- /**
- * Indicates that dropdown will be closed on item or document click,
- * and after pressing ESC
- */
- @Input() set autoClose(value: boolean) {
- if (typeof value === 'boolean') {
- this._state.autoClose = value;
- }
- };
-
- get autoClose(): boolean {
- return this._state.autoClose;
- }
-
- /**
- * Disables dropdown toggle and hides dropdown menu if opened
- */
- @Input() set isDisabled(value: boolean) {
- this._isDisabled = value;
- this._state.isDisabledChange.emit(value);
- if (value) {
- this.hide();
- }
- }
-
- get isDisabled(): boolean { return this._isDisabled; }
-
- /**
- * Returns whether or not the popover is currently being shown
- */
- @HostBinding('class.open')
- @HostBinding('class.show')
- @Input() get isOpen(): boolean {
- if (this._showInline) {
- return this._isInlineOpen;
- }
- return this._dropdown.isShown;
- }
-
- set isOpen(value: boolean) {
- if (value) {
- this.show();
- } else {
- this.hide();
- }
- }
-
- /**
- * Emits an event when isOpen change
- */
- @Output() isOpenChange: EventEmitter;
-
- /**
- * Emits an event when the popover is shown
- */
- @Output() onShown: EventEmitter;
-
- /**
- * Emits an event when the popover is hidden
- */
- @Output() onHidden: EventEmitter;
-
- get isBs4(): boolean {
- return !isBs3();
- }
- // todo: move to component loader
- _isInlineOpen = false;
- _showInline: boolean;
- _inlinedMenu: EmbeddedViewRef;
-
- _isDisabled: boolean;
- _dropdown: ComponentLoader;
- _subscriptions: Subscription[] = [];
- _isInited = false;
-
- constructor(private _elementRef: ElementRef,
- private _renderer: Renderer2,
- private _viewContainerRef: ViewContainerRef,
- private _cis: ComponentLoaderFactory,
- private _config: BsDropdownConfig,
- private _state: BsDropdownState) {
- // create dropdown component loader
- this._dropdown = this._cis
- .createLoader(this._elementRef, this._viewContainerRef, this._renderer)
- .provide({ provide: BsDropdownState, useValue: this._state });
-
- this.onShown = this._dropdown.onShown;
- this.onHidden = this._dropdown.onHidden;
- this.isOpenChange = this._state.isOpenChange;
-
- // set initial dropdown state from config
- this._state.autoClose = this._config.autoClose;
- }
-
- ngOnInit(): void {
- // fix: seems there are an issue with `routerLinkActive`
- // which result in duplicated call ngOnInit without call to ngOnDestroy
- // read more: https://github.com/valor-software/ngx-bootstrap/issues/1885
- if (this._isInited) { return; }
- this._isInited = true;
-
- this._showInline = !this.container;
-
- // attach DOM listeners
- this._dropdown.listen({
- triggers: this.triggers,
- show: () => this.show()
- });
-
- // toggle visibility on toggle element click
- this._subscriptions.push(this._state
- .toggleClick.subscribe((value: boolean) => this.toggle(value)));
-
- // hide dropdown if set disabled while opened
- this._subscriptions.push(this._state
- .isDisabledChange
- .subscribe((element: any) => {
- if (element === true) {
- this.hide();
- }
- }
- ));
-
- // attach dropdown menu inside of dropdown
- if (this._showInline) {
- this._state.dropdownMenu
- .then((dropdownMenu: BsComponentRef) => {
- this._inlinedMenu = dropdownMenu.viewContainer.createEmbeddedView(dropdownMenu.templateRef);
- });
- }
- }
-
- /**
- * Opens an element’s popover. This is considered a “manual” triggering of
- * the popover.
- */
- show(): void {
- if (this.isOpen || this.isDisabled) {
- return;
- }
-
- // material and dropup dropdown animation
- //const parent = this._elementRef.nativeElement.classList;
- const container = this._elementRef.nativeElement.lastElementChild;
-
- setTimeout(() => { container.classList.add('fadeInDropdown'); }, 200);
-
- if (this._showInline) {
- this._isInlineOpen = true;
- this.onShown.emit(true);
- this._state.isOpenChange.emit(true);
- return;
- }
- this._state.dropdownMenu
- .then((dropdownMenu) => {
- // check direction in which dropdown should be opened
- const _dropup = this.dropup === true ||
- (typeof this.dropup !== 'undefined' && this.dropup !== false);
- this._state.direction = _dropup ? 'up' : 'down';
- const _placement = this.placement ||
- (_dropup ? 'top left' : 'bottom left');
-
- // show dropdown
- this._dropdown
- .attach(BsDropdownContainerComponent)
- .to(this.container)
- .position({ attachment: _placement })
- .show({
- content: dropdownMenu.templateRef,
- placement: _placement
- });
-
- this._state.isOpenChange.emit(true);
-
- });
-
- }
-
- /**
- * Closes an element’s popover. This is considered a “manual” triggering of
- * the popover.
- */
- hide(): void {
- if (!this.isOpen) {
- return;
- }
-
- const parent = this._elementRef.nativeElement.classList;
- const container = this._elementRef.nativeElement.lastElementChild;
-
-
- if ((parent.value === 'dropdown open show') || (parent.value === 'btn-group dropup open show')) {
- container.classList.remove('fadeInDropdown');
-
- setTimeout(() => {
-
- if (this._showInline) {
- this._isInlineOpen = false;
- this.onHidden.emit(true);
- } else {
- this._dropdown.hide();
- }
-
- this._state.isOpenChange.emit(false);
-
- }, 560);
-
- } else {
-
- if (this._showInline) {
- this._isInlineOpen = false;
- this.onHidden.emit(true);
- } else {
- this._dropdown.hide();
- }
-
- this._state.isOpenChange.emit(false);
-
- }
-
- }
-
- /**
- * Toggles an element’s popover. This is considered a “manual” triggering of
- * the popover.
- */
- toggle(value?: boolean): void {
- if (this.isOpen || value === false) {
- return this.hide();
- }
-
- return this.show();
- }
-
- ngOnDestroy(): void {
- // clean up subscriptions and destroy dropdown
- for (const sub of this._subscriptions) {
- sub.unsubscribe();
- }
- this._dropdown.dispose();
- }
-}
diff --git a/src/angular-bootstrap-md/dropdown/dropdown.module.ts b/src/angular-bootstrap-md/dropdown/dropdown.module.ts
deleted file mode 100755
index 404ab15d..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown.module.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { ModuleWithProviders, NgModule } from '@angular/core';
-import { ComponentLoaderFactory } from '../utils/component-loader/component-loader.factory';
-
-import { PositioningService } from '../utils/positioning/positioning.service';
-import { BsDropdownContainerComponent } from './dropdown-container.component';
-import { BsDropdownMenuDirective } from './dropdown-menu.directive';
-import { BsDropdownToggleDirective } from './dropdown-toggle.directive';
-import { BsDropdownConfig } from './dropdown.config';
-
-import { BsDropdownDirective } from './dropdown.directive';
-import { BsDropdownState } from './dropdown.state';
-
-@NgModule({
- declarations: [
- BsDropdownMenuDirective,
- BsDropdownToggleDirective,
- BsDropdownContainerComponent,
- BsDropdownDirective
- ],
- exports: [
- BsDropdownMenuDirective,
- BsDropdownToggleDirective,
- BsDropdownDirective
- ],
- entryComponents: [BsDropdownContainerComponent]
-})
-export class DropdownModule {
- public static forRoot(config?: any): ModuleWithProviders {
- return {
- ngModule: DropdownModule, providers: [
- ComponentLoaderFactory,
- PositioningService,
- BsDropdownState,
- {provide: BsDropdownConfig, useValue: config ? config : {autoClose: true}}
- ]
- };
- };
-}
diff --git a/src/angular-bootstrap-md/dropdown/dropdown.state.ts b/src/angular-bootstrap-md/dropdown/dropdown.state.ts
deleted file mode 100755
index 403dc4e1..00000000
--- a/src/angular-bootstrap-md/dropdown/dropdown.state.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { EventEmitter, Injectable } from '@angular/core';
-import { BsComponentRef } from '../utils/component-loader/bs-component-ref.class';
-
-@Injectable()
-export class BsDropdownState {
- direction: 'down' | 'up' = 'down';
- autoClose: boolean;
- isOpenChange = new EventEmitter();
- isDisabledChange = new EventEmitter();
- toggleClick = new EventEmitter();
-
- /**
- * Content to be displayed as popover.
- */
- dropdownMenu: Promise>;
- resolveDropdownMenu: (componentRef: BsComponentRef) => void;
-
- constructor() {
- this.dropdownMenu = new Promise((resolve) => {
- this.resolveDropdownMenu = resolve;
- });
- }
- }
diff --git a/src/angular-bootstrap-md/dropdown/index.ts b/src/angular-bootstrap-md/dropdown/index.ts
deleted file mode 100755
index 64b15674..00000000
--- a/src/angular-bootstrap-md/dropdown/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export { BsDropdownDirective } from './dropdown.directive';
-export { BsDropdownMenuDirective } from './dropdown-menu.directive';
-export { BsDropdownToggleDirective } from './dropdown-toggle.directive';
-export { BsDropdownContainerComponent } from './dropdown-container.component';
-export { BsDropdownState } from './dropdown.state';
-export { BsDropdownConfig } from './dropdown.config';
-export { DropdownModule } from './dropdown.module';
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.eot b/src/angular-bootstrap-md/font/roboto/Roboto-Bold.eot
deleted file mode 100644
index b73776ee..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.eot and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.ttf b/src/angular-bootstrap-md/font/roboto/Roboto-Bold.ttf
deleted file mode 100644
index 68822caf..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.ttf and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.woff b/src/angular-bootstrap-md/font/roboto/Roboto-Bold.woff
deleted file mode 100644
index 1f75afdc..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.woff and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.woff2 b/src/angular-bootstrap-md/font/roboto/Roboto-Bold.woff2
deleted file mode 100644
index 350d1c3a..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Bold.woff2 and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Light.eot b/src/angular-bootstrap-md/font/roboto/Roboto-Light.eot
deleted file mode 100644
index 072cdc48..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Light.eot and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Light.ttf b/src/angular-bootstrap-md/font/roboto/Roboto-Light.ttf
deleted file mode 100644
index aa453407..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Light.ttf and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Light.woff b/src/angular-bootstrap-md/font/roboto/Roboto-Light.woff
deleted file mode 100644
index 3480c6c8..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Light.woff and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Light.woff2 b/src/angular-bootstrap-md/font/roboto/Roboto-Light.woff2
deleted file mode 100644
index 9a4d98c4..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Light.woff2 and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.eot b/src/angular-bootstrap-md/font/roboto/Roboto-Medium.eot
deleted file mode 100644
index f9ad9956..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.eot and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.ttf b/src/angular-bootstrap-md/font/roboto/Roboto-Medium.ttf
deleted file mode 100644
index a3c1a1f1..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.ttf and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.woff b/src/angular-bootstrap-md/font/roboto/Roboto-Medium.woff
deleted file mode 100644
index 1186773f..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.woff and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.woff2 b/src/angular-bootstrap-md/font/roboto/Roboto-Medium.woff2
deleted file mode 100644
index d10a5926..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Medium.woff2 and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.eot b/src/angular-bootstrap-md/font/roboto/Roboto-Regular.eot
deleted file mode 100644
index 9b5e8e41..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.eot and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.ttf b/src/angular-bootstrap-md/font/roboto/Roboto-Regular.ttf
deleted file mode 100644
index 0e58508a..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.ttf and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.woff b/src/angular-bootstrap-md/font/roboto/Roboto-Regular.woff
deleted file mode 100644
index f823258a..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.woff and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.woff2 b/src/angular-bootstrap-md/font/roboto/Roboto-Regular.woff2
deleted file mode 100644
index b7082ef3..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Regular.woff2 and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.eot b/src/angular-bootstrap-md/font/roboto/Roboto-Thin.eot
deleted file mode 100644
index 2284a3b3..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.eot and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.ttf b/src/angular-bootstrap-md/font/roboto/Roboto-Thin.ttf
deleted file mode 100644
index 8779333b..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.ttf and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.woff b/src/angular-bootstrap-md/font/roboto/Roboto-Thin.woff
deleted file mode 100644
index 2a98c1e4..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.woff and /dev/null differ
diff --git a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.woff2 b/src/angular-bootstrap-md/font/roboto/Roboto-Thin.woff2
deleted file mode 100644
index a38025a1..00000000
Binary files a/src/angular-bootstrap-md/font/roboto/Roboto-Thin.woff2 and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/lightbox/default-skin.png b/src/angular-bootstrap-md/img/lightbox/default-skin.png
deleted file mode 100644
index 441c502c..00000000
Binary files a/src/angular-bootstrap-md/img/lightbox/default-skin.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/lightbox/default-skin.svg b/src/angular-bootstrap-md/img/lightbox/default-skin.svg
deleted file mode 100644
index 9d5f0c6a..00000000
--- a/src/angular-bootstrap-md/img/lightbox/default-skin.svg
+++ /dev/null
@@ -1 +0,0 @@
-default-skin 2
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/img/lightbox/preloader.gif b/src/angular-bootstrap-md/img/lightbox/preloader.gif
deleted file mode 100644
index b8faa697..00000000
Binary files a/src/angular-bootstrap-md/img/lightbox/preloader.gif and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/01.png b/src/angular-bootstrap-md/img/overlays/01.png
deleted file mode 100644
index f9b60ee7..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/01.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/02.png b/src/angular-bootstrap-md/img/overlays/02.png
deleted file mode 100644
index acce7a66..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/02.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/03.png b/src/angular-bootstrap-md/img/overlays/03.png
deleted file mode 100644
index c11a355b..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/03.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/04.png b/src/angular-bootstrap-md/img/overlays/04.png
deleted file mode 100644
index 89b85338..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/04.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/04.png~HEAD b/src/angular-bootstrap-md/img/overlays/04.png~HEAD
deleted file mode 100644
index 89b85338..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/04.png~HEAD and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/04.png~ecacfc5c730e25dcad92fe8f4a80cba6c23a4b6b b/src/angular-bootstrap-md/img/overlays/04.png~ecacfc5c730e25dcad92fe8f4a80cba6c23a4b6b
deleted file mode 100644
index 89b85338..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/04.png~ecacfc5c730e25dcad92fe8f4a80cba6c23a4b6b and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/05.png b/src/angular-bootstrap-md/img/overlays/05.png
deleted file mode 100644
index 082bda8c..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/05.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/06.png b/src/angular-bootstrap-md/img/overlays/06.png
deleted file mode 100644
index 9c9006a2..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/06.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/07.png b/src/angular-bootstrap-md/img/overlays/07.png
deleted file mode 100644
index 218be609..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/07.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/08.png b/src/angular-bootstrap-md/img/overlays/08.png
deleted file mode 100644
index 1b9dffc9..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/08.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/overlays/09.png b/src/angular-bootstrap-md/img/overlays/09.png
deleted file mode 100644
index b9ed2ff1..00000000
Binary files a/src/angular-bootstrap-md/img/overlays/09.png and /dev/null differ
diff --git a/src/angular-bootstrap-md/img/svg/arrow_left.svg b/src/angular-bootstrap-md/img/svg/arrow_left.svg
deleted file mode 100644
index a88d2997..00000000
--- a/src/angular-bootstrap-md/img/svg/arrow_left.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/img/svg/arrow_right.svg b/src/angular-bootstrap-md/img/svg/arrow_right.svg
deleted file mode 100644
index f62d8852..00000000
--- a/src/angular-bootstrap-md/img/svg/arrow_right.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/index.ts b/src/angular-bootstrap-md/index.ts
deleted file mode 100755
index 9dc2e9f9..00000000
--- a/src/angular-bootstrap-md/index.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-// free
-import { ModuleWithProviders, NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
-import { DeepModule } from './inputs/deep.module';
-import { ButtonsModule } from './buttons/buttons.module';
-import { RippleModule } from './ripple/ripple.module';
-import { ActiveModule } from './inputs/active.module';
-import { InputsModule} from './inputs/inputs.module';
-import { NavbarModule } from './navbars/navbar.module';
-import { DropdownModule } from './dropdown/dropdown.module';
-import { CarouselModule } from './carousel/carousel.module';
-import { ChartsModule } from './charts/chart.module';
-import { CollapseModule } from './collapse/collapse.module';
-import { ModalModule } from './modals/modal.module';
-import { TooltipModule } from './tooltip/tooltip.module';
-import { PopoverModule } from './popover/popover.module';
-import { WavesModule } from './waves/waves.module';
-
-export {
- ButtonsModule, ButtonRadioDirective, ButtonCheckboxDirective
-} from './buttons/index';
-
-export {
- RippleModule, RippleDirective
-} from './ripple/index';
-
-export {
- WavesModule, WavesDirective
-} from './waves/index';
-
-export {
- DeepModule, DeepDirective
-} from './inputs/index';
-
-export {
- InputsModule, MdbInputDirective
-} from './inputs/index';
-
-export {
- ActiveModule, ActiveDirective, InputValidateDirective
-} from './inputs/index';
-
-export {
- NavbarModule
-} from './navbars/index';
-
-export {
- BsDropdownConfig, BsDropdownContainerComponent, BsDropdownDirective, BsDropdownMenuDirective,
- DropdownModule, BsDropdownState, BsDropdownToggleDirective
-} from './dropdown/index';
-
-export {
- CarouselComponent, CarouselConfig, CarouselModule
-} from './carousel/index';
-
-export {
- ChartsModule, BaseChartDirective
-} from './charts/index';
-
-export {
- CollapseDirective, CollapseModule
-} from './collapse/index';
-
-export {
- ModalBackdropComponent, ModalBackdropOptions, ModalDirective, ModalModule, ModalOptions, MDBModalService,
- ModalContainerComponent, MDBModalRef
-} from './modals/index';
-
-export {
- TooltipConfig, TooltipContainerComponent, TooltipDirective, TooltipModule
-} from './tooltip/index';
-
-export {
- PopoverConfig, PopoverContainerComponent, PopoverModule, PopoverDirective
-} from './popover/index';
-
-
-
-const MODULES = [
- DeepModule,
- ButtonsModule,
- RippleModule,
- WavesModule,
- InputsModule,
- ActiveModule,
- NavbarModule,
- DropdownModule,
- CarouselModule,
- ChartsModule,
- CollapseModule,
- ModalModule,
- TooltipModule,
- PopoverModule,
-];
-
-@NgModule({
- imports: [
- ButtonsModule,
- DeepModule,
- RippleModule.forRoot(),
- WavesModule.forRoot(),
- InputsModule.forRoot(),
- ActiveModule.forRoot(),
- NavbarModule,
- DropdownModule.forRoot(),
- CarouselModule.forRoot(),
- ChartsModule,
- CollapseModule.forRoot(),
- ModalModule.forRoot(),
- TooltipModule.forRoot(),
- PopoverModule.forRoot(),
- ],
- exports: MODULES,
- schemas: [NO_ERRORS_SCHEMA]
-})
-export class MDBRootModule {
-}
-
-@NgModule({ exports: MODULES })
-export class MDBBootstrapModule {
- public static forRoot(): ModuleWithProviders {
- return { ngModule: MDBRootModule };
- }
-}
diff --git a/src/angular-bootstrap-md/inputs/active.class.ts b/src/angular-bootstrap-md/inputs/active.class.ts
deleted file mode 100755
index 649bc79e..00000000
--- a/src/angular-bootstrap-md/inputs/active.class.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { Directive, ElementRef, Input, HostListener, Renderer2, AfterViewInit, AfterViewChecked } from '@angular/core';
-import { isPlatformBrowser } from '@angular/common';
-import { PLATFORM_ID, Inject } from '@angular/core';
-@Directive({
- selector: '[mdbActive]'
-})
-
-export class ActiveDirective implements AfterViewInit, AfterViewChecked {
- isBrowser: any = false;
-
- @Input() public mdbActive: ActiveDirective;
- isClicked: boolean = false;
- // public el: ElementRef = null;
- public el: ElementRef | any = null;
- // public elLabel: ElementRef = null;
- public elLabel: ElementRef | any = null;
- // public elIcon: Element = null;
- public elIcon: Element | any = null;
-
-
- constructor(el: ElementRef, public renderer: Renderer2, @Inject(PLATFORM_ID) platformId: string) {
- this.el = el;
- this.isBrowser = isPlatformBrowser(platformId);
- }
- @HostListener('focus', ['$event']) onClick() {
- this.initComponent();
- this.isClicked = true;
- }
-
- @HostListener('click', ['$event']) Click() {
- this.isClicked = true;
- }
-
- @HostListener('blur', ['$event']) onBlur() {
- this.checkValue();
- this.isClicked = false;
- }
-
-
- // ngAfterViewInit with checkValue after setTimeout is needed in situation when we have prefilled
- // forms, and label has to be lifted up.
- ngAfterViewInit() {
- this.initComponent();
- setTimeout(() => {
- this.checkValue();
- }, 0);
- }
- ngAfterViewChecked() {
- this.initComponent();
- this.checkValue();
- }
-
-
- private initComponent(): void {
- let inputId;
- let inputP;
- if (this.isBrowser) {
- try {
- inputId = this.el.nativeElement.id;
- } catch (err) { }
-
- try {
- inputP = this.el.nativeElement.parentNode;
- } catch (err) { }
-
- this.elLabel = inputP.querySelector('label[for="' + inputId + '"]') || inputP.querySelector('label');
- if (this.elLabel != null) {
- this.renderer.addClass(this.elLabel, 'active');
- }
-
- this.elIcon = inputP.querySelector('i') || false;
-
- if (this.elIcon) {
- this.renderer.addClass(this.elIcon, 'active');
- }
- }
- }
- private checkValue(): void {
- let value = '';
- if (this.elLabel != null) {
- value = this.el.nativeElement.value || '';
- if (value === '') {
- this.renderer.removeClass(this.elLabel, 'active');
- if (this.elIcon) {
- this.renderer.removeClass(this.elIcon, 'active');
- }
- // tslint:disable-next-line:max-line-length
- } if (value === '' && this.isClicked || value === '' && this.el.nativeElement.placeholder || value === '' && this.el.nativeElement.attributes.placeholder) {
- this.renderer.addClass(this.elLabel, 'active');
- }
- }
- }
-}
diff --git a/src/angular-bootstrap-md/inputs/active.module.ts b/src/angular-bootstrap-md/inputs/active.module.ts
deleted file mode 100755
index d857aa4a..00000000
--- a/src/angular-bootstrap-md/inputs/active.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { NgModule, ModuleWithProviders } from '@angular/core';
-import { ActiveDirective } from './active.class';
-import { InputValidateDirective } from './input-validate.directive';
-
-@NgModule({
- declarations: [ActiveDirective, InputValidateDirective],
- exports: [ActiveDirective, InputValidateDirective]
-})
-
-export class ActiveModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: ActiveModule, providers: []};
- }
-}
diff --git a/src/angular-bootstrap-md/inputs/deep.directive.ts b/src/angular-bootstrap-md/inputs/deep.directive.ts
deleted file mode 100755
index 05c452f4..00000000
--- a/src/angular-bootstrap-md/inputs/deep.directive.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Directive, ElementRef, AfterViewInit, Renderer2 } from '@angular/core';
-
-
-@Directive({
- selector: '[mdbDeepDirective]',
-})
-
-export class DeepDirective implements AfterViewInit {
- constructor(private el: ElementRef, private renderer: Renderer2) { }
-
- ngAfterViewInit() {
- this.renderer.addClass(this.el.nativeElement, 'onFocusSelect');
- }
-}
diff --git a/src/angular-bootstrap-md/inputs/deep.module.ts b/src/angular-bootstrap-md/inputs/deep.module.ts
deleted file mode 100755
index 22654848..00000000
--- a/src/angular-bootstrap-md/inputs/deep.module.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { DeepDirective } from './deep.directive';
-import { NgModule, ModuleWithProviders } from '@angular/core';
-
-@NgModule({
- declarations: [DeepDirective],
- exports: [DeepDirective]
-})
-
-export class DeepModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: DeepModule, providers: []};
- }
-}
diff --git a/src/angular-bootstrap-md/inputs/equal-validator.directive.ts b/src/angular-bootstrap-md/inputs/equal-validator.directive.ts
deleted file mode 100755
index ec5a2128..00000000
--- a/src/angular-bootstrap-md/inputs/equal-validator.directive.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Directive, forwardRef, Attribute } from '@angular/core';
-import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
-
-@Directive({
- selector: '[mdb-validateEqual][formControlName],[validateEqual][formControl],[validateEqual][ngModel]',
- providers: [
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidatorDirective), multi: true }
- ]
-})
-
-export class EqualValidatorDirective implements Validator {
- constructor( @Attribute('validateEqual') public validateEqual: string,
- @Attribute('reverse') public reverse: string) {
-
- }
-
- private get isReverse() {
- if (!this.reverse) {
- return false;
- }
- return this.reverse === 'true' ? true : false;
- }
-
- validate(c: AbstractControl): { [key: string]: any } {
- const setToNullValue: any = null;
- // self value (e.g. retype password)
- const v = c.value;
-
- // control value (e.g. password)
- // const e: any = c.root.get(this.validateEqual);
- const e: any = c.root.get(this.validateEqual);
-
- // value not equal
- if (e && v !== e.value) {
- return { validateEqual: false };
- }
-
- // value equal and reverse
- if (e && v === e.value && this.isReverse) {
- delete e.errors['validateEqual'];
- if (!Object.keys(e.errors).length) {
- e.setErrors(null);
- }
- }
-
- // value not equal and reverse
- if (e && v !== e.value && this.isReverse) {
- e.setErrors({
- validateEqual: false
- });
- }
-
- // return null;
- return setToNullValue;
- }
-}
-
diff --git a/src/angular-bootstrap-md/inputs/index.ts b/src/angular-bootstrap-md/inputs/index.ts
deleted file mode 100755
index 669210ca..00000000
--- a/src/angular-bootstrap-md/inputs/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-
-export { InputsModule } from './inputs.module';
-export { DeepModule } from './deep.module';
-export { DeepDirective } from './deep.directive';
-export { MdbInputDirective } from './mdb-input.directive';
-export { InputValidateDirective} from './input-validate.directive';
-export { EqualValidatorDirective} from './equal-validator.directive';
-export { ActiveDirective } from './active.class';
-export { ActiveModule } from './active.module';
diff --git a/src/angular-bootstrap-md/inputs/input-validate.directive.ts b/src/angular-bootstrap-md/inputs/input-validate.directive.ts
deleted file mode 100755
index 0d7bb6a5..00000000
--- a/src/angular-bootstrap-md/inputs/input-validate.directive.ts
+++ /dev/null
@@ -1,207 +0,0 @@
-
-import { Directive, Input, ElementRef, Renderer, HostListener, OnInit } from '@angular/core';
-
-
-
-@Directive({
- selector: '[mdbInputValidate]',
-})
-
-export class InputValidateDirective implements OnInit {
-
- @Input() public value = '';
- @Input('minLength') public minLength: string = '0';
- @Input('maxLength') public maxLength: string = '524288';
- @Input('customRegex') customRegex: any;
- public wrongTextContainer: any;
- public rightTextContainer: any;
-
-
- constructor(private _elRef: ElementRef, private _renderer: Renderer) {
-
- }
- ngOnInit() {
- // Inititalise a new wrong/right elements and render it below the host component.
- this.wrongTextContainer = this._renderer.createElement(this._elRef.nativeElement.parentElement, 'span');
- this._renderer.setElementClass(this.wrongTextContainer, 'inputVal', true);
- this._renderer.setElementClass(this.wrongTextContainer, 'text-danger', true);
- const textWrong = this._elRef.nativeElement.getAttribute('data-error');
- this.wrongTextContainer.innerHTML = (textWrong ? textWrong : 'wrong');
- this._renderer.setElementStyle(this.wrongTextContainer, 'visibility', 'hidden');
-
- this.rightTextContainer = this._renderer.createElement(this._elRef.nativeElement.parentElement, 'span');
- this._renderer.setElementClass(this.rightTextContainer, 'inputVal', true);
- this._renderer.setElementClass(this.rightTextContainer, 'text-success', true);
- const textSuccess = this._elRef.nativeElement.getAttribute('data-success');
- this.rightTextContainer.innerHTML = (textSuccess ? textSuccess : 'success');
- this._renderer.setElementStyle(this.rightTextContainer, 'visibility', 'hidden');
-
- }
-
-
- @HostListener('blur', ['$event']) onBlur() {
-
-
- const inputType = this._elRef.nativeElement.type;
-
- if (inputType === 'email') {
- if (this.customRegex) {
- const re = new RegExp(this._elRef.nativeElement.getAttribute('customRegex'));
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- /*tslint:disable:max-line-length*/
- } else if (re.test(this._elRef.nativeElement.value) && this._elRef.nativeElement.value.length >= this.minLength && this._elRef.nativeElement.value.length <= this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- } else if (!re.test(this._elRef.nativeElement.value) || this._elRef.nativeElement.value.length < this.minLength || this._elRef.nativeElement.value.length > this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- }
-
- } else if (!this.customRegex) {
- /*tslint:disable:max-line-length*/
- const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- } else if (re.test(this._elRef.nativeElement.value) && this._elRef.nativeElement.value.length >= this.minLength && this._elRef.nativeElement.value.length <= this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- } else if (!re.test(this._elRef.nativeElement.value) || this._elRef.nativeElement.value.length < this.minLength || this._elRef.nativeElement.value.length > this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- }
- }
-
-
- } else if (inputType === 'password') {
- if (this.customRegex) {
- const re = new RegExp(this._elRef.nativeElement.getAttribute('customRegex'));
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- // tslint:disable-next-line:max-line-length
- } else if (this._elRef.nativeElement.value.match(re) && this._elRef.nativeElement.value.length >= this.minLength && this._elRef.nativeElement.value.length <= this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- // tslint:disable-next-line:max-line-length
- } else if (!this._elRef.nativeElement.value.match(re) || this._elRef.nativeElement.value.length < this.minLength || this._elRef.nativeElement.value.length > this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- } else if (!this.customRegex) {
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- // tslint:disable-next-line:max-line-length
- } else if (this._elRef.nativeElement.value.match(/^(?=(.*\d){1})(.*\S)(?=.*[a-zA-Z\S])[0-9a-zA-Z\S]/g) && this._elRef.nativeElement.value.length >= this.minLength && this._elRef.nativeElement.value.length <= this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- // tslint:disable-next-line:max-line-length
- } else if (!this._elRef.nativeElement.value.match(/^(?=(.*\d){1})(.*\S)(?=.*[a-zA-Z\S])[0-9a-zA-Z\S]/g) || this._elRef.nativeElement.value.length < this.minLength || this._elRef.nativeElement.value.length > this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- }
-
- } else if (inputType === 'text') {
- if (this.customRegex) {
- const re = new RegExp(this._elRef.nativeElement.getAttribute('customRegex'));
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- // tslint:disable-next-line:max-line-length
- } else if (this._elRef.nativeElement.value.match(re) && this._elRef.nativeElement.value.length >= this.minLength && this._elRef.nativeElement.value.length <= this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- // tslint:disable-next-line:max-line-length
- } else if (!this._elRef.nativeElement.value.match(re) || this._elRef.nativeElement.value.length < this.minLength || this._elRef.nativeElement.value.length > this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- } else if (!this.customRegex) {
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- // tslint:disable-next-line:max-line-length
- } else if (this._elRef.nativeElement.value.match(/^[a-zA-Z0-9]+$/g) && this._elRef.nativeElement.value.length >= this.minLength && this._elRef.nativeElement.value.length <= this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- // tslint:disable-next-line:max-line-length
- } else if (!this._elRef.nativeElement.value.match(/^[a-zA-Z0-9]+$/g) || this._elRef.nativeElement.value.length < this.minLength || this._elRef.nativeElement.value.length > this.maxLength) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- }
-
- } else if (inputType === 'submit') {
- for (let i = 0; i < this._elRef.nativeElement.parentElement.length; i++) {
- if (this._elRef.nativeElement.parentElement[i].value == null || this._elRef.nativeElement.parentElement[i].value === '') {
- this._renderer.setElementClass(this._elRef.nativeElement.parentElement[i], 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement.parentElement[i], 'counter-success', false);
-
- } else if (!this._elRef.nativeElement.parentElement[i].value == null) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- }
-
- } else if (inputType === 'tel') {
- if (this.customRegex) {
- const re = new RegExp(this._elRef.nativeElement.getAttribute('customRegex'));
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- } else if (re.test(this._elRef.nativeElement.value) && this._elRef.nativeElement.value.length >= 8 && this._elRef.nativeElement.value.length <= 20) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- } else if (!re.test(this._elRef.nativeElement.value) || this._elRef.nativeElement.value.length > 20) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- } else if (!this.customRegex) {
- const re = /^(1[ \-\+]{0,3}|\+1[ -\+]{0,3}|\+1|\+)?((\(\+?1-[2-9][0-9]{1,2}\))|(\(\+?[2-8][0-9][0-9]\))|(\(\+?[1-9][0-9]\))|(\(\+?[17]\))|(\([2-9][2-9]\))|([ \-\.]{0,3}[0-9]{2,4}))?([ \-\.][0-9])?([ \-\.]{0,3}[0-9]{2,4}){2,3}$/;
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- } else if (re.test(this._elRef.nativeElement.value) && this._elRef.nativeElement.value.length >= 8 && this._elRef.nativeElement.value.length <= 20) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- } else if (!re.test(this._elRef.nativeElement.value) || this._elRef.nativeElement.value.length > 20) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- }
-
-
- } else if (inputType === 'number') {
- if (this.customRegex) {
- const re = new RegExp(this._elRef.nativeElement.getAttribute('customRegex'));
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- } else if (re.test(this._elRef.nativeElement.value) && this._elRef.nativeElement.value.length > 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- } else if (!re.test(this._elRef.nativeElement.value) || this._elRef.nativeElement.value.length < 1) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- } else if (!this.customRegex) {
- const re = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:(\.|,)\d+)?$/;
- if (this._elRef.nativeElement.length === 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- } else if (re.test(this._elRef.nativeElement.value) && this._elRef.nativeElement.value.length > 0) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', false);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', true);
- } else if (!re.test(this._elRef.nativeElement.value) || this._elRef.nativeElement.value.length < 1) {
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-danger', true);
- this._renderer.setElementClass(this._elRef.nativeElement, 'counter-success', false);
- }
- }
- }
- }
-}
-
diff --git a/src/angular-bootstrap-md/inputs/inputs.module.ts b/src/angular-bootstrap-md/inputs/inputs.module.ts
deleted file mode 100644
index f0bf9502..00000000
--- a/src/angular-bootstrap-md/inputs/inputs.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { MdbInputDirective } from './mdb-input.directive';
-import { NgModule, ModuleWithProviders } from '@angular/core';
-import { EqualValidatorDirective } from './equal-validator.directive';
-
-@NgModule({
- declarations: [MdbInputDirective, EqualValidatorDirective],
- exports: [MdbInputDirective, EqualValidatorDirective]
-})
-
-export class InputsModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: InputsModule, providers: []};
- }
-}
diff --git a/src/angular-bootstrap-md/inputs/licens.md b/src/angular-bootstrap-md/inputs/licens.md
deleted file mode 100755
index 8e1c25fb..00000000
--- a/src/angular-bootstrap-md/inputs/licens.md
+++ /dev/null
@@ -1,3 +0,0 @@
-https://github.com/SebastianM/angular-google-maps/blob/master/LICENSE
-https://www.npmjs.com/package/ng2-completer
-https://github.com/jkuri/ngx-uploader
diff --git a/src/angular-bootstrap-md/inputs/mdb-input.directive.ts b/src/angular-bootstrap-md/inputs/mdb-input.directive.ts
deleted file mode 100644
index 858401d9..00000000
--- a/src/angular-bootstrap-md/inputs/mdb-input.directive.ts
+++ /dev/null
@@ -1,340 +0,0 @@
-
-import { isPlatformBrowser } from '@angular/common';
-import {
- Directive,
- ElementRef,
- Renderer2,
- Input,
- AfterViewInit,
- HostListener,
- PLATFORM_ID,
- Inject,
- AfterViewChecked,
- OnInit
-} from '@angular/core';
-
-@Directive({
- selector: '[mdbInputDirective]'
-})
-export class MdbInputDirective implements AfterViewChecked, OnInit, AfterViewInit {
-
- @Input('mdbInputDirective') mdbInputDirective: MdbInputDirective;
- @Input('placeholder') public placeholder: string;
- @Input('minLength') public minLength: string = '0';
- @Input('maxLength') public maxLength: string = '524288';
- @Input('customRegex') customRegex: any;
- @Input('mdbValidate') mdbValidate: boolean = true;
- @Input('focusCheckbox') focusCheckbox: boolean = true;
- @Input('focusRadio') focusRadio: boolean = true;
-
- isBrowser: any = false;
- isClicked: boolean = false;
-
- public wrongTextContainer: any;
- public rightTextContainer: any;
- public el: ElementRef | any = null;
- public elLabel: ElementRef | any = null;
- public elIcon: Element | any = null;
-
-
- @HostListener('focus') onfocus() {
- try {
- this.renderer.addClass(this.elLabel, 'active');
- this.isClicked = true;
- } catch (error) {
-
- }
- }
-
- @HostListener('blur') onBlur() {
- try {
- if (this.el.nativeElement.value === '') {
- this.renderer.removeClass(this.elLabel, 'active');
- }
- this.isClicked = false;
- } catch (error) {
-
- }
-
-
- try {
- // Validation:
- if (this.mdbValidate) {
- const inputType = this.el.nativeElement.type;
-
- if (inputType === 'email') {
- if (this.customRegex) {
- const re = new RegExp(this.el.nativeElement.getAttribute('customRegex'));
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- /*tslint:disable:max-line-length*/
- } else if (re.test(this.el.nativeElement.value) && this.el.nativeElement.value.length >= this.minLength && this.el.nativeElement.value.length <= this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- } else if (!re.test(this.el.nativeElement.value) || this.el.nativeElement.value.length < this.minLength || this.el.nativeElement.value.length > this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- }
-
- } else if (!this.customRegex) {
- /*tslint:disable:max-line-length*/
- const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- } else if (re.test(this.el.nativeElement.value) && this.el.nativeElement.value.length >= this.minLength && this.el.nativeElement.value.length <= this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- } else if (!re.test(this.el.nativeElement.value) || this.el.nativeElement.value.length < this.minLength || this.el.nativeElement.value.length > this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- }
- }
-
-
- } else if (inputType === 'password') {
- if (this.customRegex) {
- const re = new RegExp(this.el.nativeElement.getAttribute('customRegex'));
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (this.el.nativeElement.value.match(re) && this.el.nativeElement.value.length >= this.minLength && this.el.nativeElement.value.length <= this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (!this.el.nativeElement.value.match(re) || this.el.nativeElement.value.length < this.minLength || this.el.nativeElement.value.length > this.maxLength) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- } else if (!this.customRegex) {
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (this.el.nativeElement.value.match(/^(?=(.*\d){1})(.*\S)(?=.*[a-zA-Z\S])[0-9a-zA-Z\S]/g) && this.el.nativeElement.value.length >= this.minLength && this.el.nativeElement.value.length <= this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (!this.el.nativeElement.value.match(/^(?=(.*\d){1})(.*\S)(?=.*[a-zA-Z\S])[0-9a-zA-Z\S]/g) || this.el.nativeElement.value.length < this.minLength || this.el.nativeElement.value.length > this.maxLength) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- }
-
- } else if (inputType === 'text') {
- if (this.customRegex) {
- const re = new RegExp(this.el.nativeElement.getAttribute('customRegex'));
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (this.el.nativeElement.value.match(re) && this.el.nativeElement.value.length >= this.minLength && this.el.nativeElement.value.length <= this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (!this.el.nativeElement.value.match(re) || this.el.nativeElement.value.length < this.minLength || this.el.nativeElement.value.length > this.maxLength) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- } else if (!this.customRegex) {
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (this.el.nativeElement.value.match(/^[a-zA-Z0-9]+$/g) && this.el.nativeElement.value.length >= this.minLength && this.el.nativeElement.value.length <= this.maxLength) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- // tslint:disable-next-line:max-line-length
- } else if (!this.el.nativeElement.value.match(/^[a-zA-Z0-9]+$/g) || this.el.nativeElement.value.length < this.minLength || this.el.nativeElement.value.length > this.maxLength) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- }
-
- } else if (inputType === 'submit') {
- for (let i = 0; i < this.el.nativeElement.parentElement.length; i++) {
- if (this.el.nativeElement.parentElement[i].value == null || this.el.nativeElement.parentElement[i].value === '') {
- this.renderer.addClass(this.el.nativeElement.parentElement[i], 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement.parentElement[i], 'counter-success');
-
- } else if (!this.el.nativeElement.parentElement[i].value == null) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- }
-
- } else if (inputType === 'tel') {
- if (this.customRegex) {
- const re = new RegExp(this.el.nativeElement.getAttribute('customRegex'));
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- } else if (re.test(this.el.nativeElement.value) && this.el.nativeElement.value.length >= 8 && this.el.nativeElement.value.length <= 20) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- } else if (!re.test(this.el.nativeElement.value) || this.el.nativeElement.value.length > 20) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- } else if (!this.customRegex) {
- const re = /^(1[ \-\+]{0,3}|\+1[ -\+]{0,3}|\+1|\+)?((\(\+?1-[2-9][0-9]{1,2}\))|(\(\+?[2-8][0-9][0-9]\))|(\(\+?[1-9][0-9]\))|(\(\+?[17]\))|(\([2-9][2-9]\))|([ \-\.]{0,3}[0-9]{2,4}))?([ \-\.][0-9])?([ \-\.]{0,3}[0-9]{2,4}){2,3}$/;
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- } else if (re.test(this.el.nativeElement.value) && this.el.nativeElement.value.length >= 8 && this.el.nativeElement.value.length <= 20) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- } else if (!re.test(this.el.nativeElement.value) || this.el.nativeElement.value.length > 20) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- }
-
-
- } else if (inputType === 'number') {
- if (this.customRegex) {
- const re = new RegExp(this.el.nativeElement.getAttribute('customRegex'));
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- } else if (re.test(this.el.nativeElement.value) && this.el.nativeElement.value.length > 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- } else if (!re.test(this.el.nativeElement.value) || this.el.nativeElement.value.length < 1) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- } else if (!this.customRegex) {
- const re = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:(\.|,)\d+)?$/;
- if (this.el.nativeElement.length === 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- } else if (re.test(this.el.nativeElement.value) && this.el.nativeElement.value.length > 0) {
- this.renderer.removeClass(this.el.nativeElement, 'counter-danger');
- this.renderer.addClass(this.el.nativeElement, 'counter-success');
- } else if (!re.test(this.el.nativeElement.value) || this.el.nativeElement.value.length < 1) {
- this.renderer.addClass(this.el.nativeElement, 'counter-danger');
- this.renderer.removeClass(this.el.nativeElement, 'counter-success');
- }
- }
- }
- }
- } catch (error) {
-
- }
-
-
- }
-
-
- @HostListener('change') onchange() {
- try {
- this.checkValue();
- } catch (error) {
-
- }
- }
-
-
- constructor(el: ElementRef, private renderer: Renderer2, @Inject(PLATFORM_ID) platformId: string) {
- this.el = el;
- this.isBrowser = isPlatformBrowser(platformId);
- }
-
- ngOnInit() {
- if (this.mdbValidate) {
- // Inititalise a new wrong/right elements and render it below the host component.
- // this.wrongTextContainer = this.renderer.createElement(this.el.nativeElement.parentElement, 'span');
- this.wrongTextContainer = this.renderer.createElement('span');
- this.renderer.addClass(this.wrongTextContainer, 'inputVal');
- this.renderer.addClass(this.wrongTextContainer, 'text-danger');
- this.renderer.appendChild(this.el.nativeElement.parentElement, this.wrongTextContainer);
- const textWrong = this.el.nativeElement.getAttribute('data-error');
- this.wrongTextContainer.innerHTML = (textWrong ? textWrong : 'wrong');
- this.renderer.setStyle(this.wrongTextContainer, 'visibility', 'hidden');
-
- // this.rightTextContainer = this.renderer.createElement(this.el.nativeElement.parentElement, 'span');
- this.rightTextContainer = this.renderer.createElement('span');
- this.renderer.addClass(this.rightTextContainer, 'inputVal');
- this.renderer.addClass(this.rightTextContainer, 'text-success');
- this.renderer.appendChild(this.el.nativeElement.parentElement, this.rightTextContainer);
- const textSuccess = this.el.nativeElement.getAttribute('data-success');
- this.rightTextContainer.innerHTML = (textSuccess ? textSuccess : 'success');
- this.renderer.setStyle(this.rightTextContainer, 'visibility', 'hidden');
- }
-
- }
-
- ngAfterViewInit() {
- const type = this.el.nativeElement.type;
- if (this.focusCheckbox && type === 'checkbox') {
- this.renderer.addClass(this.el.nativeElement, 'onFocusSelect');
- }
-
- if (this.focusRadio && type === 'radio') {
- this.renderer.addClass(this.el.nativeElement, 'onFocusSelect');
- }
- }
-
- ngAfterViewChecked() {
- this.initComponent();
- this.checkValue();
- // tslint:disable-next-line:max-line-length
- if (this.el.nativeElement.tagName === 'MDB-COMPLETER' && this.el.nativeElement.getAttribute('ng-reflect-model') == null && !this.isClicked) {
- this.renderer.removeClass(this.elLabel, 'active');
- }
- }
-
- public initComponent(): void {
- let inputId;
- let inputP;
- if (this.isBrowser) {
- try {
- inputId = this.el.nativeElement.id;
- } catch (err) { }
-
- try {
- inputP = this.el.nativeElement.parentNode;
- } catch (err) { }
-
- this.elLabel = inputP.querySelector('label[for="' + inputId + '"]') || inputP.querySelector('label');
- if (this.elLabel && this.el.nativeElement.value !== '') {
- this.renderer.addClass(this.elLabel, 'active');
- }
-
- this.elIcon = inputP.querySelector('i') || false;
-
- if (this.elIcon) {
- this.renderer.addClass(this.elIcon, 'active');
- }
- }
- }
-
-
-
- private checkValue(): void {
- let value = '';
- if (this.elLabel != null) {
- value = this.el.nativeElement.value || '';
- if (value === '') {
- this.renderer.removeClass(this.elLabel, 'active');
- if (this.elIcon) {
- this.renderer.removeClass(this.elIcon, 'active');
- }
- // tslint:disable-next-line:max-line-length
- } if (value === '' && this.isClicked ||
- value === '' && this.el.nativeElement.placeholder ||
- value === '' && this.el.nativeElement.attributes.placeholder
- ) {
- this.renderer.addClass(this.elLabel, 'active');
- }
- if (this.el.nativeElement.getAttribute('ng-reflect-model') != null) {
- if (this.el.nativeElement.tagName === 'MDB-COMPLETER' && this.el.nativeElement.getAttribute('ng-reflect-model').length !== 0) {
- this.renderer.addClass(this.elLabel, 'active');
- }
- }
- }
- }
-}
diff --git a/src/angular-bootstrap-md/mdb-free.module.ts b/src/angular-bootstrap-md/mdb-free.module.ts
deleted file mode 100755
index 815dd6d9..00000000
--- a/src/angular-bootstrap-md/mdb-free.module.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-// free
-import { ModuleWithProviders, NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
-import { DeepModule } from './inputs/deep.module';
-import { ButtonsModule } from './buttons/buttons.module';
-import { RippleModule } from './ripple/ripple.module';
-import { NavbarModule } from './navbars/navbar.module';
-import { DropdownModule } from './dropdown/dropdown.module';
-import { CarouselModule } from './carousel/carousel.module';
-import { ChartsModule } from './charts/chart.module';
-import { CollapseModule } from './collapse/collapse.module';
-import { ModalModule } from './modals/modal.module';
-import { TooltipModule } from './tooltip/tooltip.module';
-import { PopoverModule } from './popover/popover.module';
-import { InputsModule } from './inputs/inputs.module';
-import { WavesModule } from './waves/waves.module';
-import { ActiveModule } from './inputs/active.module';
-export {
- ButtonsModule, ButtonRadioDirective, ButtonCheckboxDirective
-} from './buttons/index';
-
-export {
- RippleModule, RippleDirective
-} from './ripple/index';
-
-export {
- WavesModule, WavesDirective
-} from './waves/index';
-
-export {
- DeepModule, DeepDirective
-} from './inputs/index';
-
-export {
- InputsModule, MdbInputDirective
-} from './inputs/index';
-
-export {
- ActiveModule, ActiveDirective, InputValidateDirective
-} from './inputs/index';
-
-export {
- NavbarModule
-} from './navbars/index';
-
-export {
- BsDropdownConfig, BsDropdownContainerComponent, BsDropdownDirective, BsDropdownMenuDirective,
- DropdownModule, BsDropdownState, BsDropdownToggleDirective
-} from './dropdown/index';
-
-export {
- CarouselComponent, CarouselConfig, CarouselModule
-} from './carousel/index';
-
-export {
- ChartsModule, BaseChartDirective
-} from './charts/index';
-
-export {
- CollapseDirective, CollapseModule
-} from './collapse/index';
-
-export {
- ModalBackdropComponent, ModalBackdropOptions, ModalDirective, ModalModule, ModalOptions, MDBModalService,
- ModalContainerComponent, MDBModalRef
-} from './modals/index';
-
-export {
- TooltipConfig, TooltipContainerComponent, TooltipDirective, TooltipModule
-} from './tooltip/index';
-
-export {
- PopoverConfig, PopoverContainerComponent, PopoverModule, PopoverDirective
-} from './popover/index';
-
-
-
-const MODULES = [
- DeepModule,
- ButtonsModule,
- RippleModule,
- WavesModule,
- InputsModule,
- ActiveModule,
- NavbarModule,
- DropdownModule,
- CarouselModule,
- ChartsModule,
- CollapseModule,
- ModalModule,
- TooltipModule,
- PopoverModule,
-];
-
-@NgModule({
- imports: [
- ButtonsModule,
- DeepModule,
- RippleModule.forRoot(),
- WavesModule.forRoot(),
- InputsModule.forRoot(),
- ActiveModule.forRoot(),
- NavbarModule,
- DropdownModule.forRoot(),
- CarouselModule.forRoot(),
- ChartsModule,
- CollapseModule.forRoot(),
- ModalModule.forRoot(),
- TooltipModule.forRoot(),
- PopoverModule.forRoot(),
- ],
- exports: MODULES,
- schemas: [NO_ERRORS_SCHEMA]
-})
-export class MDBRootModule {
-}
-
-@NgModule({ exports: MODULES })
-export class MDBBootstrapModule {
- public static forRoot(): ModuleWithProviders {
- return { ngModule: MDBRootModule };
- }
-}
diff --git a/src/angular-bootstrap-md/modals/index.ts b/src/angular-bootstrap-md/modals/index.ts
deleted file mode 100755
index d9407229..00000000
--- a/src/angular-bootstrap-md/modals/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export { ModalBackdropComponent, ModalBackdropOptions } from './modalBackdrop.component';
-export { ModalOptions, MDBModalRef } from './modal.options';
-export { ModalDirective } from './modal.directive';
-export { ModalModule } from './modal.module';
-export { MDBModalService } from './modal.service';
-export { ModalContainerComponent } from './modalContainer.component';
diff --git a/src/angular-bootstrap-md/modals/licens.md b/src/angular-bootstrap-md/modals/licens.md
deleted file mode 100755
index 68dc12fd..00000000
--- a/src/angular-bootstrap-md/modals/licens.md
+++ /dev/null
@@ -1 +0,0 @@
-https://github.com/valor-software/ngx-bootstrap/blob/development/LICENSE
\ No newline at end of file
diff --git a/src/angular-bootstrap-md/modals/modal.directive.ts b/src/angular-bootstrap-md/modals/modal.directive.ts
deleted file mode 100755
index 00e06ee4..00000000
--- a/src/angular-bootstrap-md/modals/modal.directive.ts
+++ /dev/null
@@ -1,350 +0,0 @@
-import {
- AfterViewInit,
- ComponentRef,
- Directive,
- ElementRef,
- EventEmitter,
- HostListener,
- Input,
- OnDestroy,
- Output,
- Renderer2, ViewContainerRef
-} from '@angular/core';
-
-import { document } from '../utils/facade/browser';
-
-import { isBs3 } from '../utils/ng2-bootstrap-config';
-import { Utils } from '../utils/utils.class';
-import { ModalBackdropComponent } from './modalBackdrop.component';
-import { ClassName, modalConfigDefaults, ModalOptions, DISMISS_REASONS } from './modal.options';
-
-import { window } from '../utils/facade/browser';
-import { ComponentLoader } from '../utils/component-loader/component-loader.class';
-import { ComponentLoaderFactory } from '../utils/component-loader/component-loader.factory';
-
-const TRANSITION_DURATION = 300;
-const BACKDROP_TRANSITION_DURATION = 150;
-
-/** Mark any code with directive to show it's content in modal */
-@Directive({
- selector: '[mdbModal]',
- exportAs: 'mdb-modal'
-})
-export class ModalDirective implements AfterViewInit, OnDestroy {
- /** allows to set modal configuration via element property */
- @Input()
- // public set config(conf: ModalOptions) {
- public set config(conf: ModalOptions | any) {
- this._config = this.getConfig(conf);
- }
-
- // public get config(): ModalOptions {
- public get config(): ModalOptions | any {
- return this._config;
- }
-
- /** This event fires immediately when the `show` instance method is called. */
- @Output() public onShow: EventEmitter = new EventEmitter();
- /** This event is fired when the modal has been made visible to the user (will wait for CSS transitions to complete) */
- @Output() public onShown: EventEmitter = new EventEmitter();
- /** This event is fired immediately when the hide instance method has been called. */
- @Output() public onHide: EventEmitter = new EventEmitter();
- /** This event is fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete). */
- @Output() public onHidden: EventEmitter = new EventEmitter();
-
- // seems like an Options
- public isAnimated = true;
- /** This field contains last dismiss reason.
- Possible values: `backdrop-click`, `esc` and `null` (if modal was closed by direct call of `.hide()`). */
- public dismissReason: string | any;
-
- public get isShown(): boolean {
- return this._isShown;
- }
-
- protected _config: ModalOptions | any;
- protected _isShown = false;
-
- protected isBodyOverflowing = false;
- protected originalBodyPadding = 0;
- protected scrollbarWidth = 0;
-
- protected timerHideModal: any = 0;
- protected timerRmBackDrop: any = 0;
-
- // constructor props
- protected _element: ElementRef;
- protected _renderer: Renderer2;
-
- // reference to backdrop component
- protected backdrop: ComponentRef;
- private _backdrop: ComponentLoader;
- // todo: implement _dialog
- _dialog: any;
-
- isNested = false;
-
- @HostListener('click', ['$event'])
- public onClick(event: any): void {
- if (this.config.ignoreBackdropClick || this.config.backdrop === 'static' || event.target !== this._element.nativeElement) {
- return;
- }
- this.dismissReason = DISMISS_REASONS.BACKRDOP;
- this.hide(event);
- }
-
- // todo: consider preventing default and stopping propagation
- @HostListener('keydown.esc')
- public onEsc(): void {
- if (this.config.keyboard) {
- this.dismissReason = DISMISS_REASONS.ESC;
- this.hide();
- }
- }
-
- public constructor(_element: ElementRef, _viewContainerRef: ViewContainerRef, _renderer: Renderer2, clf: ComponentLoaderFactory) {
- this._element = _element;
- this._renderer = _renderer;
- this._backdrop = clf.createLoader(_element, _viewContainerRef, _renderer);
- }
-
- public ngOnDestroy(): any {
- this.config = void 0;
- if (this._isShown) {
- this._isShown = false;
- this.hideModal();
- this._backdrop.dispose();
- }
- }
-
- public ngAfterViewInit(): any {
- this._config = this._config || this.getConfig();
- setTimeout(() => {
- if (this._config.show) {
- this.show();
- }
- }, 0);
- }
-
- /* Public methods */
-
- /** Allows to manually toggle modal visibility */
- public toggle(): void {
- return this._isShown ? this.hide() : this.show();
- }
-
- /** Allows to manually open modal */
- public show(): void {
- this.dismissReason = null;
- this.onShow.emit(this);
- if (this._isShown) {
- return;
- }
- clearTimeout(this.timerHideModal);
- clearTimeout(this.timerRmBackDrop);
-
- this._isShown = true;
-
- this.checkScrollbar();
- this.setScrollbar();
-
- if (document && document.body) {
- if (document.body.classList.contains(ClassName.OPEN)) {
- this.isNested = true;
- } else {
- this._renderer.addClass(document.body, ClassName.OPEN);
- }
- }
-
- this.showBackdrop(() => {
- this.showElement();
- });
- }
-
- /** Allows to manually close modal */
- public hide(event?: Event): void {
- if (event) {
- event.preventDefault();
- }
-
- this.onHide.emit(this);
-
- // todo: add an option to prevent hiding
- if (!this._isShown) {
- return;
- }
-
- clearTimeout(this.timerHideModal);
- clearTimeout(this.timerRmBackDrop);
-
- this._isShown = false;
- this._renderer.removeClass(this._element.nativeElement, ClassName.IN);
- if (!isBs3()) {
- this._renderer.removeClass(this._element.nativeElement, ClassName.SHOW);
- }
-
- if (this.isAnimated) {
- this.timerHideModal = setTimeout(() => this.hideModal(), TRANSITION_DURATION);
- } else {
- this.hideModal();
- }
- }
-
- /** Private methods @internal */
- protected getConfig(config?: ModalOptions): ModalOptions {
- return Object.assign({}, modalConfigDefaults, config);
- }
-
- /**
- * Show dialog
- * @internal
- */
- protected showElement(): void {
- // todo: replace this with component loader usage
- if (!this._element.nativeElement.parentNode ||
- (this._element.nativeElement.parentNode.nodeType !== Node.ELEMENT_NODE)) {
- // don't move modals dom position
- if (document && document.body) {
- document.body.appendChild(this._element.nativeElement);
- }
- }
-
- this._renderer.setAttribute(this._element.nativeElement, 'aria-hidden', 'false');
- this._renderer.setStyle(this._element.nativeElement, 'display', 'block');
- this._renderer.setProperty(this._element.nativeElement, 'scrollTop', 0);
-
- if (this.isAnimated) {
- Utils.reflow(this._element.nativeElement);
- }
-
- this._renderer.addClass(this._element.nativeElement, ClassName.IN);
- if (!isBs3()) {
- this._renderer.addClass(this._element.nativeElement, ClassName.SHOW);
- }
-
- const transitionComplete = () => {
- if (this._config.focus) {
- this._element.nativeElement.focus();
- }
- this.onShown.emit(this);
- };
-
- if (this.isAnimated) {
- setTimeout(transitionComplete, TRANSITION_DURATION);
- } else {
- transitionComplete();
- }
- }
-
- /** @internal */
- protected hideModal(): void {
- this._renderer.setAttribute(this._element.nativeElement, 'aria-hidden', 'true');
- this._renderer.setStyle(this._element.nativeElement, 'display', 'none');
- this.showBackdrop(() => {
- if (!this.isNested) {
- if (document && document.body) {
- this._renderer.removeClass(document.body, ClassName.OPEN);
- }
- this.resetScrollbar();
- }
- this.resetAdjustments();
- this.focusOtherModal();
- this.onHidden.emit(this);
- });
- }
-
- // todo: original show was calling a callback when done, but we can use promise
- /** @internal */
- protected showBackdrop(callback?: Function): void {
- if (this._isShown && this.config.backdrop && (!this.backdrop || !this.backdrop.instance.isShown)) {
- this.removeBackdrop();
- this._backdrop
- .attach(ModalBackdropComponent)
- .to('body')
- .show({isAnimated: this.isAnimated});
- this.backdrop = this._backdrop._componentRef;
-
- if (!callback) {
- return;
- }
-
- if (!this.isAnimated) {
- callback();
- return;
- }
-
- setTimeout(callback, BACKDROP_TRANSITION_DURATION);
- } else if (!this._isShown && this.backdrop) {
- this.backdrop.instance.isShown = false;
-
- const callbackRemove = () => {
- this.removeBackdrop();
- if (callback) {
- callback();
- }
- };
-
- if (this.backdrop.instance.isAnimated) {
- this.timerRmBackDrop = setTimeout(callbackRemove, BACKDROP_TRANSITION_DURATION);
- } else {
- callbackRemove();
- }
- } else if (callback) {
- callback();
- }
- }
-
- /** @internal */
- protected removeBackdrop(): void {
- this._backdrop.hide();
- }
-
-
- protected focusOtherModal() {
- const otherOpenedModals = this._element.nativeElement.parentElement.querySelectorAll('.in[mdbModal]');
- if (!otherOpenedModals.length) {
- return;
- }
- // this._renderer.invokeElementMethod(otherOpenedModals[otherOpenedModals.length - 1], 'focus');
- otherOpenedModals[otherOpenedModals.length - 1].nativeElement.focus();
- }
-
- /** @internal */
- protected resetAdjustments(): void {
- this._renderer.setStyle(this._element.nativeElement, 'paddingLeft', '');
- this._renderer.setStyle(this._element.nativeElement, 'paddingRight', '');
- }
-
- /** Scroll bar tricks */
- /** @internal */
- protected checkScrollbar(): void {
- this.isBodyOverflowing = document.body.clientWidth < window.innerWidth;
- this.scrollbarWidth = this.getScrollbarWidth();
- }
-
- protected setScrollbar(): void {
- if (!document) {
- return;
- }
-
- this.originalBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right') || 0, 10);
-
- if (this.isBodyOverflowing) {
- document.body.style.paddingRight = `${this.originalBodyPadding + this.scrollbarWidth}px`;
- }
- }
-
- protected resetScrollbar(): void {
- document.body.style.paddingRight = this.originalBodyPadding;
- }
-
- // thx d.walsh
- protected getScrollbarWidth(): number {
- const scrollDiv = this._renderer.createElement('div', void 0);
- this._renderer.appendChild(document.body, scrollDiv);
- scrollDiv.className = ClassName.SCROLLBAR_MEASURER;
- const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
- document.body.removeChild(scrollDiv);
- return scrollbarWidth;
- }
-}
diff --git a/src/angular-bootstrap-md/modals/modal.module.ts b/src/angular-bootstrap-md/modals/modal.module.ts
deleted file mode 100755
index 187c0cbf..00000000
--- a/src/angular-bootstrap-md/modals/modal.module.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { NgModule, ModuleWithProviders, NO_ERRORS_SCHEMA } from '@angular/core';
-
-import { ModalBackdropComponent } from './modalBackdrop.component';
-import { ModalDirective } from './modal.directive';
-import { PositioningService } from '../utils/positioning/positioning.service';
-import { ComponentLoaderFactory } from '../utils/component-loader/component-loader.factory';
-import { ModalContainerComponent } from './modalContainer.component';
-import { MDBModalService } from './modal.service';
-
-@NgModule({
- declarations: [ModalBackdropComponent, ModalDirective, ModalContainerComponent],
- exports: [ModalBackdropComponent, ModalDirective],
- entryComponents: [ModalBackdropComponent, ModalContainerComponent],
- schemas: [NO_ERRORS_SCHEMA]
-})
-export class ModalModule {
- public static forRoot(): ModuleWithProviders {
- return {ngModule: ModalModule, providers: [MDBModalService, ComponentLoaderFactory, PositioningService]};
- }
-}
diff --git a/src/angular-bootstrap-md/modals/modal.options.ts b/src/angular-bootstrap-md/modals/modal.options.ts
deleted file mode 100755
index a0f18b5e..00000000
--- a/src/angular-bootstrap-md/modals/modal.options.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable()
-export class ModalOptions {
- /**
- * Includes a modal-backdrop element. Alternatively, specify static for a backdrop which doesn't close the modal on click.
- */
- // backdrop?: boolean | 'static';
- backdrop?: boolean | 'static' | any;
- /**
- * Closes the modal when escape key is pressed.
- */
- keyboard?: boolean;
-
- focus?: boolean;
- /**
- * Shows the modal when initialized.
- */
- show?: boolean;
- /**
- * Ignore the backdrop click
- */
- ignoreBackdropClick?: boolean;
- /**
- * Css class for opened modal
- */
- class?: string;
- /**
- * Toggle animation
- */
- animated?: boolean;
- }
-
- @Injectable()
- export class MDBModalRef {
- /**
- * Reference to a component inside the modal. Null if modal's been created with TemplateRef
- */
- content?: any | null;
- /**
- * Hides the modal
- */
- hide(): void {}
- }
-
- export const modalConfigDefaults: ModalOptions = {
- backdrop: true,
- keyboard: true,
- focus: true,
- show: false,
- ignoreBackdropClick: false,
- class: '',
- animated: true
- };
-
- export const ClassName: any = {
- SCROLLBAR_MEASURER: 'modal-scrollbar-measure',
- BACKDROP: 'modal-backdrop',
- OPEN: 'modal-open',
- FADE: 'fade',
- IN: 'in', // bs3
- SHOW: 'show' // bs4
- };
-
- export const Selector: any = {
- DIALOG: '.modal-dialog',
- DATA_TOGGLE: '[data-toggle="modal"]',
- DATA_DISMISS: '[data-dismiss="modal"]',
- FIXED_CONTENT: '.navbar-fixed-top, .navbar-fixed-bottom, .is-fixed'
- };
-
- export const TransitionDurations: any = {
- MODAL: 300,
- BACKDROP: 150
- };
-
- export const DISMISS_REASONS = {
- BACKRDOP: 'backdrop-click',
- ESC: 'esc'
- };
diff --git a/src/angular-bootstrap-md/modals/modal.service.ts b/src/angular-bootstrap-md/modals/modal.service.ts
deleted file mode 100755
index a8054420..00000000
--- a/src/angular-bootstrap-md/modals/modal.service.ts
+++ /dev/null
@@ -1,183 +0,0 @@
-import { ComponentRef, Injectable, TemplateRef, EventEmitter, Renderer2, ViewContainerRef, ElementRef } from '@angular/core';
-
-import { ComponentLoader } from '../utils/component-loader/component-loader.class';
-import { ComponentLoaderFactory } from '../utils/component-loader/component-loader.factory';
-import { ModalBackdropComponent } from './modalBackdrop.component';
-import { ModalContainerComponent } from './modalContainer.component';
-import { MDBModalRef, ClassName, modalConfigDefaults, ModalOptions, TransitionDurations } from './modal.options';
-import { msConfig } from './modalService.config';
-
-
-@Injectable()
-export class MDBModalService {
- // constructor props
- public config: ModalOptions = modalConfigDefaults;
-
- public onShow: EventEmitter = new EventEmitter();
- public onShown: EventEmitter = new EventEmitter();
- public onHide: EventEmitter = new EventEmitter();
- public onHidden: EventEmitter = new EventEmitter();
-
- protected isBodyOverflowing = false;
- protected originalBodyPadding = 0;
-
- protected scrollbarWidth = 0;
-
- // protected backdropRef: ComponentRef;
- protected backdropRef: ComponentRef | any;
- private _backdropLoader: ComponentLoader;
- private modalsCount = 0;
- // private lastDismissReason = '';
- private lastDismissReason: any = '';
-
- private loaders: ComponentLoader[] = [];
- // public constructor(private clf: ComponentLoaderFactory) {
- public constructor(private clf: ComponentLoaderFactory, private el: ElementRef, private v: ViewContainerRef, private r: Renderer2) {
- // this._backdropLoader = this.clf.createLoader(null, null, null);
- this._backdropLoader = this.clf.createLoader(this.el, this.v, this.r);
- msConfig.serviceInstance = this;
- }
-
- /** Shows a modal */
- show(content: string | TemplateRef