diff --git a/projects/igniteui-angular/src/lib/core/styles/components/_index.scss b/projects/igniteui-angular/src/lib/core/styles/components/_index.scss index a91b2f4a1af..d5bbef81f3f 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/_index.scss @@ -35,7 +35,8 @@ @forward 'navdrawer/navdrawer-theme'; @forward 'overlay/overlay-theme'; @forward 'paginator/paginator-theme'; -@forward 'progress/progress-theme'; +@forward 'progress/linear/linear-theme'; +@forward 'progress/circular/circular-theme'; @forward 'radio/radio-theme'; @forward 'ripple/ripple-theme'; @forward 'query-builder/query-builder-theme'; diff --git a/projects/igniteui-angular/src/lib/core/styles/components/progress/_progress-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/progress/_progress-component.scss deleted file mode 100644 index d40047073bd..00000000000 --- a/projects/igniteui-angular/src/lib/core/styles/components/progress/_progress-component.scss +++ /dev/null @@ -1,144 +0,0 @@ -@use '../../base' as *; -@use 'sass:string'; - -/// @access private -/// @author Simeon Simeonoff -@mixin component { - /// Linear Progress - @include b(igx-linear-bar) { - $this: bem--selector-to-string(&); - @include register-component( - $name: string.slice($this, 2, -1), - $deps: () - ); - - @extend %linear-display !optional; - - @include e(base) { - @extend %linear-bar !optional; - } - - @include e(indicator) { - @extend %linear-indicator !optional; - @extend %linear-indicator--default !optional; - } - - @include e(value) { - @extend %linear-value !optional; - @extend %linear-value--start !optional; - } - - @include e(value, $m: start) { - @extend %linear-value !optional; - @extend %linear-value--start !optional; - } - - @include e(value, $m: center) { - @extend %linear-value !optional; - @extend %linear-value--center !optional; - } - - @include e(value, $m: end) { - @extend %linear-value !optional; - @extend %linear-value--end !optional; - } - - @include e(value, $m: top) { - @extend %linear-value !optional; - @extend %linear-value--top !optional; - } - - @include e(value, $m: hidden) { - @extend %linear-value !optional; - @extend %linear-value--hidden !optional; - } - - @include m(danger) { - @include e(indicator) { - @extend %linear-indicator--danger !optional; - } - } - - @include m(warning) { - @include e(indicator) { - @extend %linear-indicator--warning !optional; - } - } - - @include m(info) { - @include e(indicator) { - @extend %linear-indicator--info !optional; - } - } - - @include m(success) { - @include e(indicator) { - @extend %linear-indicator--success !optional; - } - } - - @include m(striped) { - @include e(indicator) { - @extend %linear-indicator--striped !optional; - } - } - - @include m(indeterminate) { - @include e(indicator) { - @extend %linear-indicator--indeterminate-primary !optional; - } - - @include e(indicator-secondary) { - @extend %linear-indicator--indeterminate-secondary !optional; - } - - @include e(value) { - @extend %linear-value !optional; - @extend %linear-value--hidden !optional; - } - } - } - - /// Circular Progress - @include b(igx-circular-bar) { - $this: bem--selector-to-string(&); - @include register-component( - $name: string.slice($this, 2, -1), - $deps: () - ); - - @extend %circular-display !optional; - - @include e(inner) { - @extend %circular-inner !optional; - } - - @include e(outer) { - @extend %circular-outer !optional; - } - - @include e(text) { - @extend %circular-text !optional; - } - - @include e(gradient-start) { - @extend %circular-gradient-start !optional; - } - - @include e(gradient-end) { - @extend %circular-gradient-end !optional; - } - - @include m(indeterminate) { - @extend %circular-display--indeterminate !optional; - - @include e(outer) { - @extend %circular-outer--indeterminate !optional; - } - - @include e(text) { - @extend %circular-text--hidden !optional; - } - } - } -} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/progress/_progress-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/progress/_progress-theme.scss deleted file mode 100644 index 0a1c494b942..00000000000 --- a/projects/igniteui-angular/src/lib/core/styles/components/progress/_progress-theme.scss +++ /dev/null @@ -1,627 +0,0 @@ -@use 'sass:map'; -@use 'sass:math'; -@use 'sass:meta'; -@use 'sass:list'; -@use '../../base' as *; -@use 'igniteui-theming/sass/animations' as *; -@use '../../themes/schemas' as *; - -//// -/// @group themes -/// @access public -/// @author Simeon Simeonoff -/// @author Marin Popov -//// - -/// @param {Map} $schema [$light-material-schema] - The schema used as basis for styling the component. -/// @param {Color} $track-color [null] - The main track color. -/// @param {Color} $fill-color-default [null] - The track default fill color. -/// @param {Color} $fill-color-danger [null] - The track danger fill color. -/// @param {Color} $fill-color-warning [null] - The track warning fill color. -/// @param {Color} $fill-color-info [null] - The track info fill color. -/// @param {Color} $fill-color-success [null] - The track success fill color. -/// @param {Color} $stripes-color [null] - The track stripes color. -/// @param {Color} $text-color [null] - The track value text color. -/// @param {List} $track-border-radius [null] - The border radius fraction, between 0 - 8 to be used fot the progress bar track -/// @requires $light-material-schema -/// @example scss Change the stripes color -/// $my-progress-linear-theme: progress-linear-theme( -/// $stripes-color: orange -/// ); -/// // Pass the theme to the css-vars() mixin -/// @include css-vars($my-progress-linear-theme); -@function progress-linear-theme( - $schema: $light-material-schema, - - $track-color: null, - $fill-color-default: null, - $fill-color-danger: null, - $fill-color-warning: null, - $fill-color-info: null, - $fill-color-success: null, - $stripes-color: null, - $text-color: null, - $track-border-radius: null, -) { - $name: 'igx-linear-bar'; - $linear-bar-schema: (); - - @if map.has-key($schema, 'linear-bar') { - $linear-bar-schema: map.get($schema, 'linear-bar'); - } @else { - $linear-bar-schema: $schema; - } - - $theme: digest-schema($linear-bar-schema); - $meta: map.get($theme, '_meta'); - - @if not($track-border-radius) { - $track-border-radius: map.get($theme, 'track-border-radius'); - } - - @return extend($theme, ( - name: $name, - track-color: $track-color, - fill-color-default: $fill-color-default, - fill-color-danger: $fill-color-danger, - fill-color-warning: $fill-color-warning, - fill-color-info: $fill-color-info, - fill-color-success: $fill-color-success, - stripes-color: $stripes-color, - text-color: $text-color, - track-border-radius: $track-border-radius, - theme: map.get($schema, '_meta', 'theme'), - _meta: map.merge(if($meta, $meta, ()), ( - variant: map.get($schema, '_meta', 'theme') - )), - )); -} - -@mixin striped-gradient($gradient-orientation, $stripe-color) { - background-image: linear-gradient( - $gradient-orientation, - $stripe-color 25%, - transparent 25%, - transparent 50%, - $stripe-color 50%, - $stripe-color 75%, - transparent 75%, - transparent - ); -} - -@mixin striped-gradient--indigo($gradient-orientation, $stripe-color) { - background-image: linear-gradient( - $gradient-orientation, - transparent 25%, - $stripe-color 25%, - $stripe-color 75%, - transparent 75%, - ); -} - -/// @deprecated Use the `css-vars` mixin instead. -/// @see {mixin} css-vars -/// @param {Map} $theme - The theme used to style the component. -@mixin progress-linear($theme) { - @include css-vars($theme); - - $variant: map.get($theme, '_meta', 'variant'); - - $bar-height: map.get(( - 'material': rem(4px), - 'fluent': rem(2px), - 'bootstrap': rem(16px), - 'indigo': rem(4px), - ), $variant); - - $gradient-orientation: map.get(( - 'material': -45deg, - 'fluent': -45deg, - 'bootstrap': 45deg, - 'indigo': 45deg, - ), $variant); - - $gradient-orientation-rtl: map.get(( - 'material': 45deg, - 'fluent': 45deg, - 'bootstrap': -45deg, - 'indigo': -45deg, - ), $variant); - - $background-size: map.get(( - 'material': rem(40px) rem(40px), - 'fluent': rem(40px) rem(40px), - 'bootstrap': rem(16px) rem(16px), - 'indigo': rem(20px), - ), $variant); - - $stripe-color: var-get($theme, 'stripes-color'); - $value-fs: rem(14px, 16px); - $value-fw: 600; - $value-margin: 0; - - %linear-display { - position: relative; - display: flex; - width: 100%; - flex: 1 1 100%; - flex-direction: column; - - @if $variant == 'indigo' { - gap: rem(4px); - } @else if $variant == 'fluent' { - gap: rem(2px); - } - } - - %linear-bar { - position: relative; - width: inherit; - height: $bar-height; - background: var-get($theme, 'track-color'); - overflow: hidden; - border-radius: var-get($theme, 'track-border-radius'); - z-index: 0; - } - - %linear-indicator { - width: 100%; - position: absolute; - height: 100%; - } - - %linear-indicator--striped { - @if $variant != 'indigo' { - @include striped-gradient($gradient-orientation, $stripe-color); - } @else { - @include striped-gradient--indigo($gradient-orientation, $stripe-color); - } - - background-size: $background-size; - - [dir='rtl'] & { - @if $variant != 'indigo' { - @include striped-gradient($gradient-orientation-rtl, $stripe-color); - } @else { - @include striped-gradient--indigo($gradient-orientation-rtl, $stripe-color); - } - } - } - - %linear-indicator--indeterminate-secondary, - %linear-indicator--indeterminate-primary - { - transform-origin: top left; - width: 100% !important; - height: inherit; - position: absolute; - background: transparent; - - &::after { - content: ''; - position: absolute; - top: 0; - inset-inline-start: 0; - width: inherit; - height: inherit; - background: var-get($theme, 'fill-color-default'); - backface-visibility: hidden; - } - } - - %linear-indicator--indeterminate-primary { - transform: scale3d(0, 1, 1); - animation: indeterminate-primary 2000ms infinite linear; - left: -145.166611%; - - &::after { - animation: indeterminate-primary-scale 2000ms infinite linear; - } - - [dir="rtl"] & { - animation: indeterminate-primary-rtl 2000ms infinite linear; - left: auto; - right: -145.166611%; - transform-origin: top right; - } - } - - %linear-indicator--indeterminate-secondary { - animation: indeterminate-secondary 2000ms infinite linear; - left: -54.888891%; - - &::after { - animation: indeterminate-secondary-scale 2000ms infinite linear; - width: 100%; - height: inherit; - } - - [dir="rtl"] & { - animation: indeterminate-secondary-rtl 2000ms infinite linear; - left: auto; - right: -54.888891%; - transform-origin: top right; - } - } - - %linear-indicator--default { - background: var-get($theme, 'fill-color-default') - } - - %linear-indicator--danger { - background-color: var-get($theme, 'fill-color-danger'); - } - - %linear-indicator--warning { - background-color: var-get($theme, 'fill-color-warning'); - } - - %linear-indicator--info { - background-color: var-get($theme, 'fill-color-info'); - } - - %linear-indicator--success { - background-color: var-get($theme, 'fill-color-success'); - } - - %linear-value { - margin: $value-margin; - color: var-get($theme, 'text-color'); - } - - %linear-value--start { - align-self: flex-start; - } - - %linear-value--center { - align-self: center; - } - - %linear-value--end { - align-self: flex-end; - } - - %linear-value--top { - order: -1; - } - - %linear-value--hidden { - display: none; - } - - $indeterminate-primary-translate-step-2: cubic-bezier(.5, 0, .701732, .495819); - $indeterminate-primary-translate-step-3: cubic-bezier(.302435, .381352, .55, .956352); - - $indeterminate-primary-scale-step-2: cubic-bezier(.334731, .12482, .785844, 1); - $indeterminate-primary-scale-step-3: cubic-bezier(.06, .11, .6, 1); - - $indeterminate-secondary-translate-step-1: cubic-bezier(.15, 0, .515058, .409685); - $indeterminate-secondary-translate-step-2: cubic-bezier(.31033, .284058, .8, .733712); - $indeterminate-secondary-translate-step-3: cubic-bezier(.4, .627035, .6, .902026); - - $indeterminate-secondary-scale-step-1: cubic-bezier(.15, 0, .515058, .409685); - $indeterminate-secondary-scale-step-2: cubic-bezier(.31033, .284058, .8, .733712); - $indeterminate-secondary-scale-step-3: cubic-bezier(.4, .627035, .6, .902026); - - @keyframes indeterminate-primary { - 0% { - transform: translateX(0); - } - - 20% { - animation-timing-function: $indeterminate-primary-translate-step-2; - transform: translateX(0); - } - - 59.15% { - animation-timing-function: $indeterminate-primary-translate-step-3; - transform: translateX(83.67142%); - } - - 100% { - transform: translateX(200.611057%); - } - } - - @keyframes indeterminate-primary-rtl { - 0% { - transform: translateX(0); - } - - 20% { - animation-timing-function: $indeterminate-primary-translate-step-2; - transform: translateX(0); - } - - 59.15% { - animation-timing-function: $indeterminate-primary-translate-step-3; - transform: translateX(-83.67142%); - } - - 100% { - transform: translateX(-200.611057%); - } - } - - @keyframes indeterminate-primary-scale { - 0% { - transform: scaleX(.08); - } - - 36.65% { - animation-timing-function: $indeterminate-primary-scale-step-2; - transform: scaleX(.08); - } - - 69.15% { - animation-timing-function: $indeterminate-primary-scale-step-2; - transform: scaleX(.661479); - } - - 100% { - transform: scaleX(.08); - } - } - - @keyframes indeterminate-secondary { - 0% { - animation-timing-function: $indeterminate-secondary-translate-step-1; - transform: translateX(0); - } - - 25% { - animation-timing-function: $indeterminate-secondary-translate-step-2; - - transform: translateX(37.651913%); - } - - 48.35% { - animation-timing-function: $indeterminate-secondary-translate-step-3; - transform: translateX(84.386165%); - } - - 100% { - transform: translateX(160.277782%); - } - } - - @keyframes indeterminate-secondary-rtl { - 0% { - animation-timing-function: $indeterminate-secondary-translate-step-1; - transform: translateX(0); - } - - 25% { - animation-timing-function: $indeterminate-secondary-translate-step-2; - transform: translateX(-37.651913%); - } - - 48.35% { - animation-timing-function: $indeterminate-secondary-translate-step-3; - transform: translateX(-84.386165%); - } - - 100% { - transform: translateX(-160.277782%); - } - } - - @keyframes indeterminate-secondary-scale { - 0% { - animation-timing-function: $indeterminate-secondary-scale-step-1; - transform: scaleX(.08); - } - - 19.15% { - animation-timing-function: $indeterminate-secondary-scale-step-2; - transform: scaleX(.457104); - } - - 44.15% { - animation-timing-function: $indeterminate-secondary-scale-step-3; - transform: scaleX(.72796); - } - - 100% { - transform: scaleX(.08); - } - } -} - -/// Adds typography styles for the igx-linear-bar component. -/// Uses the 'subtitle-2' category from the typographic scale. -/// @group typography -/// @param {Map} $categories [(value: 'subtitle-2')] - The categories from the typographic scale used for type styles. -@mixin linear-bar-typography($categories: (value: 'subtitle-2')) { - $value: map.get($categories, 'value'); - - %linear-value { - @include type-style($value) { - margin: 0; - } - } -} - -/// @param {Map} $schema [$light-material-schema] - The schema used as basis for styling the component. -/// -/// @param {Color} $base-circle-color [null] - The base circle fill color. -/// @param {Color | List} $progress-circle-color [null] - The progress circle fill color. -/// @param {Color} $text-color [null] - The value text color. -/// @param {Number} $diameter [null] - The progress circle diameter. -/// -/// @requires $light-material-schema -/// -/// @example scss Change the circle progress color -/// $my-progress-circular-theme: igx-progress-circular-theme( -/// $progress-circle-color: purple -/// ); -/// // Pass the theme to the igx-progress-circular component mixin -/// @include igx-progress-circular($my-progress-circle-theme); -@function progress-circular-theme( - $schema: $light-material-schema, - - $base-circle-color: null, - $progress-circle-color: null, - $text-color: null, - $diameter: null -) { - $name: 'igx-circular-bar'; - $circular-bar-schema: (); - - @if map.has-key($schema, 'circular-bar') { - $circular-bar-schema: map.get($schema, 'circular-bar'); - } @else { - $circular-bar-schema: $schema; - } - - $theme: digest-schema($circular-bar-schema); - $meta: map.get($theme, '_meta'); - - $progress-circle-color-start: map.get($theme, 'progress-circle-color'); - $progress-circle-color-end: map.get($theme, 'progress-circle-color'); - - @if meta.type-of($progress-circle-color) == 'color' { - $progress-circle-color-start: $progress-circle-color; - $progress-circle-color-end: $progress-circle-color; - } - - @if list.length($progress-circle-color) == 2 { - $progress-circle-color-start: list.nth($progress-circle-color, 1); - $progress-circle-color-end: list.nth($progress-circle-color, 2); - } - - @return extend($theme, ( - name: $name, - base-circle-color: $base-circle-color, - progress-circle-color-start: $progress-circle-color-start, - progress-circle-color-end: $progress-circle-color-end, - text-color: $text-color, - diameter: $diameter, - theme: map.get($schema, '_meta', 'theme'), - _meta: map.merge(if($meta, $meta, ()), ( - variant: map.get($schema, '_meta', 'theme') - )), - )); -} - -/// @deprecated Use the `css-vars` mixin instead. -/// @see {mixin} css-vars -/// @param {Map} $theme - The theme used to style the component. -@mixin progress-circular($theme) { - // Include rotate animation - @include rotate-center(); - - @include css-vars($theme); - - $variant: map.get($theme, '_meta', 'variant'); - $stroke-width: rem(4px); - $diameter: var-get($theme, 'diameter'); - - %circular-display { - display: inline-flex; - flex: 1 1 auto; - width: $diameter; - height: $diameter; - position: relative; - - svg { - width: 100%; - height: 100%; - min-width: inherit; - min-height: inherit; - //transform-origin: 50% 50%; - transform: rotate(-90deg); - } - } - - %circular-display--indeterminate { - @include animation(rotate-center 3s linear normal infinite); - - [dir='rtl'] & { - animation-direction: reverse; - } - } - - %circular-inner { - stroke-width: $stroke-width; - fill: transparent; - stroke: var-get($theme, 'base-circle-color'); - } - - %circular-outer { - fill: transparent; - stroke-width: $stroke-width; - stroke-dashoffset: 289; - stroke-dasharray: 289; - - @if $variant == 'indigo' { - stroke-width: calc($stroke-width + rem(1px)); - } - - @if $variant == 'material' { - stroke-width: calc($stroke-width + rem(2px)); - } - } - - %circular-outer--indeterminate { - stroke-dasharray: 289; - @include animation(indeterminate-accordion 1.5s cubic-bezier(0, .085, .68, .53) normal infinite); - - [dir='rtl'] & { - animation-direction: reverse; - } - } - - %circular-text { - $scale-factor: 2.28571; - - @if $variant == 'indigo' { - $scale-factor: 3; - } - - @if $variant == 'bootstrap' { - $scale-factor: 2.7; - } - - --stroke-scale: calc(#{$diameter} / #{unitless(map.get($theme, 'diameter'))}); - $font-size: round(calc($diameter / $scale-factor - #{unitless($stroke-width)} * var(--stroke-scale)), 1px); - - position: absolute; - inset-block-start: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - color: var-get($theme, 'text-color'); - font-size: #{$font-size}; - line-height: normal; - text-align: center; - font-weight: 600; - - @if $variant == 'bootstrap' or $variant == 'fluent' { - font-weight: 700; - } - } - - %circular-text--hidden { - visibility: hidden; - } - - %circular-gradient-start { - stop-color: var-get($theme, 'progress-circle-color-start'); - } - - %circular-gradient-end { - stop-color: var-get($theme, 'progress-circle-color-end'); - } - - @include keyframes('indeterminate-accordion') { - from { - stroke-dashoffset: 578; - stroke-dasharray: 259; - } - - to { - stroke-dashoffset: 120; - } - } -} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/progress/circular/_circular-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/progress/circular/_circular-component.scss new file mode 100644 index 00000000000..c0b09fb0338 --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/components/progress/circular/_circular-component.scss @@ -0,0 +1,63 @@ +@use '../../../base' as *; +@use 'sass:string'; + + +/// @access private +/// @author Simeon Simeonoff +@mixin component { + @include b(igx-circular-bar) { + $this: bem--selector-to-string(&); + @include register-component( + $name: string.slice($this, 2, -1), + $deps: () + ); + + @extend %circular-display !optional; + + @include e(inner) { + @extend %circular-inner !optional; + } + + @include e(outer) { + @extend %circular-outer !optional; + } + + @include e(text) { + @extend %circular-text !optional; + } + + @include e(value, $m: fraction) { + @extend %circular-text--fraction !optional; + } + + @include e(gradient-start) { + @extend %circular-gradient-start !optional; + } + + @include e(gradient-end) { + @extend %circular-gradient-end !optional; + } + + @include m(indeterminate) { + @extend %circular-display--indeterminate !optional; + + @include e(outer) { + @extend %circular-outer--indeterminate !optional; + } + + @include e(text) { + @extend %circular-text--hidden !optional; + } + } + + @include m(animation-none) { + @extend %animation-none !optional; + } + + @include m(hide-counter) { + @include e(text) { + @extend %hide-counter !optional; + } + } + } +} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/progress/circular/_circular-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/progress/circular/_circular-theme.scss new file mode 100644 index 00000000000..e44b9e5116b --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/components/progress/circular/_circular-theme.scss @@ -0,0 +1,213 @@ +@use 'sass:map'; +@use 'sass:math'; +@use 'sass:meta'; +@use 'sass:list'; +@use '../../../base' as *; +@use 'igniteui-theming/sass/animations' as *; +@use '../../../themes/schemas' as *; + +/// @param {Map} $schema [$light-material-schema] - The schema used as basis for styling the component. +/// +/// @param {Color} $base-circle-color [null] - The base circle fill color. +/// @param {Color | List} $progress-circle-color [null] - The progress circle fill color. +/// @param {Color} $text-color [null] - The value text color. +/// @param {Number} $diameter [null] - The progress circle diameter. +/// +/// @requires $light-material-schema +/// +/// @example scss Change the circle progress color +/// $my-progress-circular-theme: igx-progress-circular-theme( +/// $progress-circle-color: purple +/// ); +/// // Pass the theme to the igx-progress-circular component mixin +/// @include igx-progress-circular($my-progress-circle-theme); +@function progress-circular-theme( + $schema: $light-material-schema, + $base-circle-color: null, + $progress-circle-color: null, + $text-color: null, + $diameter: null +) { + $name: 'igx-circular-bar'; + $circular-bar-schema: (); + + @if map.has-key($schema, 'circular-bar') { + $circular-bar-schema: map.get($schema, 'circular-bar'); + } @else { + $circular-bar-schema: $schema; + } + + $theme: digest-schema($circular-bar-schema); + $meta: map.get($theme, '_meta'); + + $progress-circle-color-start: map.get($theme, 'progress-circle-color'); + $progress-circle-color-end: map.get($theme, 'progress-circle-color'); + + @if meta.type-of($progress-circle-color) == 'color' { + $progress-circle-color-start: $progress-circle-color; + $progress-circle-color-end: $progress-circle-color; + } + + @if list.length($progress-circle-color) == 2 { + $progress-circle-color-start: list.nth($progress-circle-color, 1); + $progress-circle-color-end: list.nth($progress-circle-color, 2); + } + + @return extend($theme, ( + name: $name, + base-circle-color: $base-circle-color, + progress-circle-color-start: $progress-circle-color-start, + progress-circle-color-end: $progress-circle-color-end, + text-color: $text-color, + diameter: $diameter, + theme: map.get($schema, '_meta', 'theme'), + _meta: map.merge(if($meta, $meta, ()), ( + variant: map.get($schema, '_meta', 'theme') + )), + )); +} + +/// @deprecated Use the `css-vars` mixin instead. +/// @see {mixin} css-vars +/// @param {Map} $theme - The theme used to style the component. +@mixin progress-circular($theme) { + // Include rotate animation + @include rotate-center(); + + @include css-vars($theme); + + $variant: map.get($theme, '_meta', 'variant'); + $stroke-width: rem(4px); + $diameter: var-get($theme, 'diameter'); + + %circular-display { + display: inline-flex; + flex: 1 1 auto; + width: $diameter; + height: $diameter; + position: relative; + + svg { + width: 100%; + height: 100%; + min-width: inherit; + min-height: inherit; + //transform-origin: 50% 50%; + transform: rotate(-90deg); + } + } + + %circular-display--indeterminate { + @include animation(rotate-center 3s linear normal infinite); + + [dir='rtl'] & { + animation-direction: reverse; + } + } + + %circular-inner { + stroke-width: $stroke-width; + fill: transparent; + stroke: var-get($theme, 'base-circle-color'); + } + + %circular-outer { + fill: transparent; + stroke-width: $stroke-width; + stroke-dashoffset: 289; + stroke-dasharray: 289; + + @if $variant == 'indigo' { + stroke-width: calc($stroke-width + rem(1px)); + } + + @if $variant == 'material' { + stroke-width: calc($stroke-width + rem(2px)); + } + } + + %circular-outer--indeterminate { + stroke-dasharray: 289; + @include animation(indeterminate-accordion 1.5s cubic-bezier(0, .085, .68, .53) normal infinite); + + [dir='rtl'] & { + animation-direction: reverse; + } + } + + %circular-text { + $scale-factor: 2.28571; + + @if $variant == 'indigo' { + $scale-factor: 3; + } + + @if $variant == 'bootstrap' { + $scale-factor: 2.7; + } + + --stroke-scale: calc(#{$diameter} / #{unitless(map.get($theme, 'diameter'))}); + $font-size: round(calc($diameter / $scale-factor - #{unitless($stroke-width)} * var(--stroke-scale)), 1px); + + position: absolute; + inset-block-start: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + color: var-get($theme, 'text-color'); + font-size: #{$font-size}; + line-height: normal; + text-align: center; + font-weight: 600; + + @if $variant == 'bootstrap' or $variant == 'fluent' { + font-weight: 700; + } + + counter-reset: + progress-integer var(--_progress-integer, 0) + progress-fraction var(--_progress-fraction, 0); + transition: + --_progress-integer var(--_transition-duration) ease-in-out, + --_progress-fraction var(--_transition-duration) ease-in-out; + } + + %circular-text:not(%circular-text--fraction) { + &::before { + content: counter(progress-integer) '%'; + } + } + + %circular-text--fraction { + &::before { + content: counter(progress-integer) '.' counter(progress-fraction, decimal-leading-zero) '%'; + } + } + + %circular-text--hidden { + visibility: hidden; + } + + %circular-gradient-start { + stop-color: var-get($theme, 'progress-circle-color-start'); + } + + %circular-gradient-end { + stop-color: var-get($theme, 'progress-circle-color-end'); + } + + @include keyframes('indeterminate-accordion') { + from { + stroke-dashoffset: 578; + stroke-dasharray: 259; + } + + to { + stroke-dashoffset: 120; + } + } + + %animation-none { + animation-duration: 0s; + animation: none; + } +} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/progress/linear/_linear-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/progress/linear/_linear-component.scss new file mode 100644 index 00000000000..4bd9e027abd --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/components/progress/linear/_linear-component.scss @@ -0,0 +1,83 @@ +@use '../../../base' as *; +@use 'sass:string'; +@use 'sass:list'; + +/// @access private +/// @author Simeon Simeonoff +@mixin component { + @include b(igx-linear-bar) { + $this: bem--selector-to-string(&); + @include register-component( + $name: string.slice($this, 2, -1), + $deps: () + ); + + @extend %display !optional; + + @include e(base) { + @extend %base !optional; + } + + @include e(indicator) { + @extend %indicator !optional; + } + + @include e(indicator-secondary) { + @extend %indicator__secondary !optional; + } + + @include e(value) { + @extend %value !optional; + } + + @each $modifier in ('start', 'center', 'end', 'top', 'hidden') { + @include e(value, $m: $modifier) { + @extend %value--#{$modifier} !optional; + } + } + + @include e(value, $m: hidden) { + @extend %value--hidden !optional; + } + + @include e(value, $m: fraction) { + @extend %value--fraction !optional; + } + + @each $modifier in ('danger', 'warning', 'info', 'success') { + @include m($modifier) { + @extend %display--#{$modifier} !optional; + } + } + + @include m(striped) { + @include e(indicator) { + @extend %indicator--striped !optional; + } + } + + @include m(indeterminate) { + @extend %display--indeterminate !optional; + + @include e(indicator) { + @extend %indicator__indeterminate !optional; + } + + @include e(indicator-secondary) { + @extend %indicator__indeterminate-secondary !optional; + } + } + + @include m(animation-none) { + @extend %animation-none !optional; + } + + @include m(hide-counter) { + @include e(value) { + &::before { + @extend %value--hidden !optional; + } + } + } + } +} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/progress/linear/_linear-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/progress/linear/_linear-theme.scss new file mode 100644 index 00000000000..9773a9e893b --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/components/progress/linear/_linear-theme.scss @@ -0,0 +1,506 @@ +@use 'sass:map'; +@use 'sass:math'; +@use 'sass:meta'; +@use 'sass:list'; +@use '../../../base/index' as *; +@use 'igniteui-theming/sass/animations' as *; +@use '../../../themes/schemas' as *; + +//// +/// @group themes +/// @access public +/// @author Simeon Simeonoff +/// @author Marin Popov +//// + +/// @param {Map} $schema [$light-material-schema] - The schema used as basis for styling the component. +/// @param {Color} $track-color [null] - The main track color. +/// @param {Color} $fill-color-default [null] - The track default fill color. +/// @param {Color} $fill-color-danger [null] - The track danger fill color. +/// @param {Color} $fill-color-warning [null] - The track warning fill color. +/// @param {Color} $fill-color-info [null] - The track info fill color. +/// @param {Color} $fill-color-success [null] - The track success fill color. +/// @param {Color} $stripes-color [null] - The track stripes color. +/// @param {Color} $text-color [null] - The track value text color. +/// @param {List} $track-border-radius [null] - The border radius fraction, between 0 - 8 to be used fot the progress bar track +/// @requires $light-material-schema +/// @example scss Change the stripes color +/// $my-progress-linear-theme: progress-linear-theme( +/// $stripes-color: orange +/// ); +/// // Pass the theme to the css-vars() mixin +/// @include css-vars($my-progress-linear-theme); +@function progress-linear-theme( + $schema: $light-material-schema, + $track-color: null, + $fill-color-default: null, + $fill-color-danger: null, + $fill-color-warning: null, + $fill-color-info: null, + $fill-color-success: null, + $stripes-color: null, + $text-color: null, + $track-border-radius: null, +) { + $name: 'igx-linear-bar'; + $linear-bar-schema: (); + + $linear-bar-schema: if(map.has-key($schema, 'linear-bar'), map.get($schema, 'linear-bar'), $schema); + + $theme: digest-schema($linear-bar-schema); + $meta: map.get($theme, '_meta'); + + @if not($track-border-radius) { + $track-border-radius: map.get($theme, 'track-border-radius'); + } + + @return extend($theme, ( + name: $name, + track-color: $track-color, + fill-color-default: $fill-color-default, + fill-color-danger: $fill-color-danger, + fill-color-warning: $fill-color-warning, + fill-color-info: $fill-color-info, + fill-color-success: $fill-color-success, + stripes-color: $stripes-color, + text-color: $text-color, + track-border-radius: $track-border-radius, + theme: map.get($schema, '_meta', 'theme'), + _meta: map.merge(if($meta, $meta, ()), ( + variant: map.get($schema, '_meta', 'theme') + )), + )); +} + +@mixin striped-gradient($variant: default, $gradient-orientation, $stripe-color) { + $fill-color-default: if($variant == 'indigo', transparent, $stripe-color); + $stripes-color: if($variant == 'indigo', $stripe-color, transparent); + + & { + background-image: repeating-linear-gradient( + $gradient-orientation, + $fill-color-default 0, + $fill-color-default var(--stripe-size), + $stripes-color var(--stripe-size), + $stripes-color calc(var(--stripe-size) * 2) + ); + } +} + +// Animation easing curves +$easing-curves: ( + // Primary translate easing curves + primary-translate-start: cubic-bezier(0.5, 0, 0.7017, 0.4958), + primary-translate-mid: cubic-bezier(0.3024, 0.3813, 0.55, 0.9563), + + // Primary scale easing curves + primary-scale-slow-start: cubic-bezier(0.3347, 0.124, 0.7858, 1), + primary-scale-quick-end: cubic-bezier(0.06, 0.11, 0.6, 1), + + // Secondary translate easing curves + secondary-translate-start: cubic-bezier(0.15, 0, 0.515, 0.4096), + secondary-translate-mid: cubic-bezier(0.31, 0.284, 0.8, 0.7337), + secondary-translate-end: cubic-bezier(0.4, 0.627, 0.6, 0.902), + + // Secondary scale easing curves + secondary-scale-slow-start: cubic-bezier(0.15, 0, 0.515, 0.4096), + secondary-scale-mid: cubic-bezier(0.31, 0.284, 0.8, 0.7337), + secondary-scale-smooth-end: cubic-bezier(0.4, 0.627, 0.6, 0.902) +); + +// Helper function to retrieve easing curves +@function get-easing($curve) { + @if not map.has-key($easing-curves, $curve) { + @warn 'Easing curve #{$curve} does not exist.'; + } + @return map.get($easing-curves, $curve); +} + + +/// @deprecated Use the `css-vars` mixin instead. +/// @see {mixin} css-vars +/// @param {Map} $theme - The theme used to style the component. +@mixin progress-linear($theme) { + @include css-vars($theme); + + $variant: map.get($theme, '_meta', 'variant'); + + $bar-height: map.get(( + 'material': rem(4px), + 'fluent': rem(2px), + 'bootstrap': rem(16px), + 'indigo': rem(4px), + ), $variant); + + $bar-height: map.get(( + 'material': rem(4px), + 'fluent': rem(2px), + 'bootstrap': rem(16px), + 'indigo': rem(4px), + ), $variant); + + $strip-size: map.get(( + 'material': rem(16px), + 'fluent': rem(16px), + 'bootstrap': rem(5px), + 'indigo': rem(16px), + ), $variant); + + %display { + --stripe-size: #{$strip-size}; + --linear-animation-duration: var(--_transition-duration); + position: relative; + display: flex; + width: 100%; + flex: 1 1 100%; + flex-direction: column; + + @if $variant == 'indigo' { + gap: rem(4px); + } @else if $variant == 'fluent' { + gap: rem(2px); + } + } + + %base { + position: relative; + width: inherit; + height: $bar-height; + background: var-get($theme, 'track-color'); + overflow: hidden; + border-radius: var-get($theme, 'track-border-radius'); + z-index: 0; + } + + %indicator { + width: calc(var(--_progress-whole) * 1%); + position: absolute; + height: 100%; + transition: width var(--_transition-duration) linear; + } + + %indicator--striped { + $gradient-orientation: map.get(( + 'material': -45deg, + 'fluent': -45deg, + 'bootstrap': 45deg, + 'indigo': 45deg, + ), $variant); + + &:dir(rtl) { + $gradient-orientation: map.get(( + 'material': 45deg, + 'fluent': 45deg, + 'bootstrap': -45deg, + 'indigo': -45deg, + ), $variant) ; + } + + @include striped-gradient(map.get($theme, 'variant'), $gradient-orientation, var-get($theme, 'stripes-color')); + } + + %indicator--indeterminate:dir(rtl) { + @if $variant != 'fluent' { + %base { + transform: rotateY(180deg); + } + } @else { + %indicator { + animation-name: indeterminate-bar-fluent-rtl; + } + } + } + + %display:not(%display--indeterminate) { + %indicator { + animation: initial-width var(--_transition-duration) linear; + } + } + + %indicator__indeterminate, + %indicator__indeterminate-secondary { + @if $variant != 'fluent' { + transform-origin: top left; + width: 100% !important; + height: inherit; + position: absolute; + + &::after { + content: ''; + position: absolute; + top: 0; + inset-inline-start: 0; + width: inherit; + height: inherit; + backface-visibility: hidden; + background-color: var-get($theme, 'fill-color-default'); + } + } + } + + %indicator__indeterminate { + @if $variant != 'fluent' { + transform: scale3d(0, 1, 1); + animation: indeterminate-primary var(--linear-animation-duration) infinite linear; + left: -145.1666%; + + &::after { + animation: indeterminate-primary-scale var(--linear-animation-duration) infinite linear; + } + } @else { + width: 33% !important; + min-width: 33%; + animation-name: indeterminate-bar-fluent; + animation-duration: var(--linear-animation-duration); + animation-timing-function: ease; + animation-iteration-count: infinite; + left: auto; + } + } + + %indicator__indeterminate-secondary { + animation: indeterminate-secondary var(--linear-animation-duration) infinite linear; + left: -54.8888%; + + &::after { + animation: indeterminate-secondary-scale var(--linear-animation-duration) infinite linear; + width: 100%; + height: inherit; + } + + @if $variant == 'fluent' { + display: none; + } + } + + %display:not(%display--indeterminate) { + %indicator { + background-color: var-get($theme, 'fill-color-default') + } + } + + @if $variant == 'fluent' { + %indicator__indeterminate { + background: linear-gradient(90deg, transparent 0%, var-get($theme, 'fill-color-default') 50%, transparent 100%) + } + } + + @each $mod in ('success','info','warning','danger') { + %display--#{$mod}:not(%display--indeterminate) { + %indicator, + %indicator__secondary { + background-color: var-get($theme, 'fill-color-#{$mod}') + } + } + + %display--#{$mod} { + @if $variant != 'fluent' { + %indicator, + %indicator__secondary { + &::after { + background-color: var-get($theme, 'fill-color-#{$mod}'); + } + } + } @else { + %indicator { + background: linear-gradient(90deg, transparent 0%, var-get($theme, 'fill-color-#{$mod}') 50%, transparent 100%) + } + } + } + } + + %value { + color: var-get($theme, 'text-color'); + + animation: initial-counter var(--_transition-duration) ease-in-out; + + counter-reset: + progress-integer var(--_progress-integer, 0) + progress-fraction var(--_progress-fraction, 0); + transition: + --_progress-integer var(--_transition-duration) ease-in-out, + --_progress-fraction var(--_transition-duration) ease-in-out; + } + + %value--fraction { + &::before { + content: counter(progress-integer) '.' counter(progress-fraction, decimal-leading-zero) '%'; + } + } + + %value:not(%value--fraction) { + &::before { + content: counter(progress-integer) '%'; + } + } + + %value--start { + align-self: flex-start; + } + + %value--center { + align-self: center; + } + + %value--end { + align-self: flex-end; + } + + %value--top { + order: -1; + } + + %value--hidden { + display: none; + } + + %animation-none { + animation-duration: 0s !important; + animation: none !important; + } + + // Primary animation + @keyframes indeterminate-primary { + 0% { + transform: translateX(0); + } + + 20% { + animation-timing-function: get-easing('primary-translate-start'); + transform: translateX(0); + } + + 59.15% { + animation-timing-function: get-easing('primary-translate-mid'); + transform: translateX(83.671%); + } + + 100% { + transform: translateX(200.611%); + } + } + + @keyframes indeterminate-primary-scale { + 0% { + transform: scaleX(0.08); + } + + 36.65% { + animation-timing-function: get-easing('primary-scale-slow-start'); + transform: scaleX(0.08); + } + + 69.15% { + animation-timing-function: get-easing('primary-scale-quick-end'); + transform: scaleX(0.6614); + } + + 100% { + transform: scaleX(0.08); + } + } + + // Secondary animation + @keyframes indeterminate-secondary { + 0% { + animation-timing-function: get-easing('secondary-translate-start'); + transform: translateX(0); + } + + 25% { + animation-timing-function: get-easing('secondary-translate-mid'); + transform: translateX(37.6519%); + } + + 48.35% { + animation-timing-function: get-easing('secondary-translate-end'); + transform: translateX(84.3861%); + } + + 100% { + transform: translateX(160.2777%); + } + } + + @keyframes indeterminate-secondary-scale { + 0% { + animation-timing-function: get-easing('secondary-scale-slow-start'); + transform: scaleX(0.08); + } + + 19.15% { + animation-timing-function: get-easing('secondary-scale-mid'); + transform: scaleX(0.4571); + } + + 44.15% { + animation-timing-function: get-easing('secondary-scale-smooth-end'); + transform: scaleX(0.727); + } + + 100% { + transform: scaleX(0.08); + } + } + + // Fluent linear animations + @keyframes indeterminate-bar-fluent { + 0% { + transform: translateX(-100%); + transform-origin: left; + } + + 100% { + transform: translateX(310%); + transform-origin: right; + } + } + + @keyframes indeterminate-bar-fluent-rtl { + 0% { + transform: translateX(100%); + transform-origin: right; + } + + 100% { + transform: translateX(-310%); + transform-origin: left; + } + } + + // Initial animations + @keyframes initial-width { + from { + width: 0; + } + + to { + width: calc(var(--_progress-whole, 0) * 1%); + } + } + + @keyframes initial-width { + from { + width: 0; + } + + to { + width: calc(var(--_progress-whole, 0) * 1%); + } + } +} + +/// Adds typography styles for the igx-linear-bar component. +/// Uses the 'subtitle-2' category from the typographic scale. +/// @group typography +/// @param {Map} $categories [(value: 'subtitle-2')] - The categories from the typographic scale used for type styles. +@mixin linear-bar-typography($categories: (value: 'subtitle-2')) { + $value: map.get($categories, 'value'); + + %value { + @include type-style($value) { + margin: 0; + } + } +} + diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss b/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss index fd4a7cf86f1..5a8d5a8d420 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss @@ -64,7 +64,8 @@ @use '../components/navdrawer/navdrawer-component' as navdrawer; @use '../components/overlay/overlay-component' as overlay; @use '../components/paginator/paginator-component' as paginator; -@use '../components/progress/progress-component' as progress; +@use '../components/progress/linear/linear-component' as linear-progress; +@use '../components/progress/circular/circular-component' as circular-progress; @use '../components/radio/radio-component' as radio; @use '../components/query-builder/query-builder-component' as query-builder; @use '../components/scrollbar/scrollbar-component' as scrollbar; @@ -111,6 +112,18 @@ } } + @property --_progress-integer { + syntax: ''; + initial-value: 0; + inherits: true; + } + + @property --_progress-fraction { + syntax: ''; + initial-value: 0; + inherits: true; + } + // Component styles @include ripple.component(); @include action-strip.component(); @@ -161,7 +174,8 @@ @include navdrawer.component(); @include overlay.component(); @include paginator.component(); - @include progress.component(); + @include linear-progress.component(); + @include circular-progress.component(); @include radio.component(); @include query-builder.component(); @include scrollbar.component(); @@ -177,7 +191,7 @@ @include tree.component(); @include watermark.component(); - // Build the component dependecy-tree + // Build the component dependency-tree @include base.dependecy-tree(base.$components); @if $print-layout == true { diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss index c7c87882b42..3358a8d1d64 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss @@ -20,7 +20,7 @@ @use '../components/list/list-theme' as *; @use '../components/navbar/navbar-theme' as *; @use '../components/navdrawer/navdrawer-theme' as *; -@use '../components/progress/progress-theme' as *; +@use '../components/progress/linear/linear-theme' as *; @use '../components/radio/radio-theme' as *; @use '../components/slider/slider-theme' as *; @use '../components/snackbar/snackbar-theme' as *; diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss index 627ea6eb0f4..d203af9fbf1 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss @@ -19,7 +19,7 @@ @use '../components/list/list-theme' as *; @use '../components/navbar/navbar-theme' as *; @use '../components/navdrawer/navdrawer-theme' as *; -@use '../components/progress/progress-theme' as *; +@use '../components/progress/linear/linear-theme' as *; @use '../components/radio/radio-theme' as *; @use '../components/slider/slider-theme' as *; @use '../components/snackbar/snackbar-theme' as *; diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss index f8abb934685..d9a0e32013e 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss @@ -19,7 +19,7 @@ @use '../components/list/list-theme' as *; @use '../components/navbar/navbar-theme' as *; @use '../components/navdrawer/navdrawer-theme' as *; -@use '../components/progress/progress-theme' as *; +@use '../components/progress/linear/linear-theme' as *; @use '../components/radio/radio-theme' as *; @use '../components/slider/slider-theme' as *; @use '../components/snackbar/snackbar-theme' as *; diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss index 5dac7cac02e..d48041d30ed 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss @@ -19,7 +19,7 @@ @use '../components/list/list-theme' as *; @use '../components/navbar/navbar-theme' as *; @use '../components/navdrawer/navdrawer-theme' as *; -@use '../components/progress/progress-theme' as *; +@use '../components/progress/linear/linear-theme' as *; @use '../components/radio/radio-theme' as *; @use '../components/slider/slider-theme' as *; @use '../components/snackbar/snackbar-theme' as *; diff --git a/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts b/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts index 4c821b777a5..9e5610e75b5 100644 --- a/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts +++ b/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts @@ -42,6 +42,7 @@ export interface IChangeProgressEventArgs extends IBaseEventArgs { previousValue: number; currentValue: number; } +export const valueInRange = (value: number, max: number, min = 0): number => Math.max(Math.min(value, max), min); /** * @hidden @@ -82,21 +83,14 @@ export abstract class BaseProgressDirective { */ @Input() public animationDuration = 2000; - public _interval; - protected _initValue = 0; protected _contentInit = false; protected _max = 100; protected _value = MIN_VALUE; - protected _newVal = MIN_VALUE; protected _animate = true; - protected _step; - protected _animation; - protected _valueInPercent; - protected _internalState = { - oldVal: 0, - newVal: 0 - }; + protected _step: number; + private _fraction = 0; + private _integer = 0; constructor() { } @@ -145,11 +139,6 @@ export abstract class BaseProgressDirective { @Input({ transform: booleanAttribute }) public set animate(animate: boolean) { this._animate = animate; - if (animate) { - this.animationDuration = 2000; - } else { - this.animationDuration = 0; - } } /** @@ -177,19 +166,17 @@ export abstract class BaseProgressDirective { @HostBinding('attr.aria-valuemax') @Input() public set max(maxNum: number) { - if (maxNum < MIN_VALUE || this._max === maxNum || - (this._animation && this._animation.playState !== 'finished')) { - return; + if (maxNum < MIN_VALUE || this._max === maxNum) { + return; // Ignore invalid or unchanged max } - this._internalState.newVal = Math.round(toValue(toPercent(this.value, maxNum), maxNum)); - this._value = this._internalState.oldVal = Math.round(toValue(this.valueInPercent, maxNum)); - this._max = maxNum; - this.triggerProgressTransition(this._internalState.oldVal, this._internalState.newVal, true); + this._max = maxNum; // Update max value + this._value = valueInRange(this._value, this._max); // Revalidate current value + this._updateProgressValues(); // Refresh CSS variables } /** - * Returns the the maximum progress value of the `progress bar`. + * Returns the maximum progress value of the `progress bar`. * ```typescript * @ViewChild("MyProgressBar") * public progressBar: IgxLinearProgressBarComponent | IgxCircularBarComponent; @@ -203,6 +190,24 @@ export abstract class BaseProgressDirective { return this._max; } + @HostBinding('style') + public get hostStyles(): { [key: string]: string } { + return { + '--_progress-integer': this._integer.toString(), + '--_progress-fraction': this._fraction.toString(), + '--_progress-whole': this.valueInPercent.toFixed(2), + '--_transition-duration': `${this.animationDuration}ms`, + }; + } + + protected get hasFraction(): boolean { + const percentage = this.valueInPercent; + const integerPart = Math.floor(percentage); + const fractionalPart = percentage - integerPart; + + return fractionalPart > 0; + } + /** * Returns the `IgxLinearProgressBarComponent`/`IgxCircularProgressBarComponent` value in percentage. * ```typescript @@ -215,8 +220,8 @@ export abstract class BaseProgressDirective { * ``` */ public get valueInPercent(): number { - const val = toPercent(this._value, this._max); - return val; + const result = this.max > 0 ? (this._value / this.max) * 100 : 0; + return Math.round(result * 100) / 100; // Round to two decimal places } /** @@ -236,6 +241,18 @@ export abstract class BaseProgressDirective { return this._value; } + protected _updateProgressValues(): void { + const percentage = this.valueInPercent; + const integerPart = Math.floor(percentage); + const fractionalPart = Math.round((percentage % 1) * 100); + + // Set CSS variables for animation + setTimeout(() => { + this._integer = integerPart; + this._fraction = fractionalPart; + }, 0); + } + /** * Set value that indicates the current `IgxLinearProgressBarComponent` position. * ```html @@ -243,81 +260,14 @@ export abstract class BaseProgressDirective { * ``` */ public set value(val) { - if (this._animation && this._animation.playState !== 'finished' || val < 0) { - return; - } - - const valInRange = valueInRange(val, this.max); - - if (isNaN(valInRange) || this._value === val || this.indeterminate) { - return; - } - - if (this._contentInit) { - this.triggerProgressTransition(this._value, valInRange); - } else { - this._initValue = valInRange; - } - } + const valInRange = valueInRange(val, this.max); // Ensure value is in range - protected triggerProgressTransition(oldVal, newVal, maxUpdate = false) { - if (oldVal === newVal) { - return; - } - - const changedValues = { - currentValue: newVal, - previousValue: oldVal - }; - - const stepDirection = this.directionFlow(oldVal, newVal); - if (this._animate) { - const newToPercent = toPercent(newVal, this.max); - const oldToPercent = toPercent(oldVal, this.max); - const duration = this.animationDuration / Math.abs(newToPercent - oldToPercent) / (this._step ? this._step : 1); - this.runAnimation(newVal); - this._interval = setInterval(() => this.increase(newVal, stepDirection), duration); - } else { - this.updateProgress(newVal); - } - - if (maxUpdate) { - return; - } - this.progressChanged.emit(changedValues); - } - - /** - * @hidden - */ - protected increase(newValue: number, step: number) { - const targetValue = toPercent(newValue, this._max); - this._value = valueInRange(this._value, this._max) + step; - if ((step > 0 && this.valueInPercent >= targetValue) || (step < 0 && this.valueInPercent <= targetValue)) { - if (this._value !== newValue) { - this._value = newValue; - } - return clearInterval(this._interval); + if (isNaN(valInRange) || this._value === valInRange || this.indeterminate) { + return; // Avoid redundant updates } - } - /** - * @hidden - */ - protected directionFlow(currentValue: number, prevValue: number): number { - return currentValue < prevValue ? this.step : -this.step; - } - - protected abstract runAnimation(value: number); - - /** - * @hidden - * @param step - */ - private updateProgress(val: number) { - this._value = valueInRange(val, this._max); - // this.valueInPercent = toPercent(val, this._max); - this.runAnimation(val); + this._value = valInRange; // Update internal value + this._updateProgressValues(); // Refresh CSS variables } } let NEXT_LINEAR_ID = 0; @@ -374,6 +324,16 @@ export class IgxLinearProgressBarComponent extends BaseProgressDirective impleme @Input() public id = `igx-linear-bar-${NEXT_LINEAR_ID++}`; + @HostBinding('class.igx-linear-bar--animation-none') + public get disableAnimationClass(): boolean { + return !this._animate; + } + + @HostBinding('class.igx-linear-bar--hide-counter') + public get hasText(): boolean { + return !!this.text; + } + /** * Set the position that defines where the text is aligned. * Possible options - `IgxTextAlign.START` (default), `IgxTextAlign.CENTER`, `IgxTextAlign.END`. @@ -427,13 +387,6 @@ export class IgxLinearProgressBarComponent extends BaseProgressDirective impleme @Input() public type = 'default'; - @ViewChild('indicator', { static: true }) - private _progressIndicator: ElementRef; - - private animationState = { - width: '0%' - }; - /** * @hidden */ @@ -467,33 +420,9 @@ export class IgxLinearProgressBarComponent extends BaseProgressDirective impleme } public ngAfterContentInit() { - this.triggerProgressTransition(MIN_VALUE, this._initValue); + this._updateProgressValues(); this._contentInit = true; } - - public runAnimation(value: number) { - if (this._animation && this._animation.playState !== 'finished') { - return; - } - - const valueInPercent = this.max <= 0 ? 0 : toPercent(value, this.max); - - const FRAMES = []; - FRAMES[0] = { - ...this.animationState - }; - - this.animationState.width = valueInPercent + '%'; - FRAMES[1] = { - ...this.animationState - }; - - this._animation = this._progressIndicator.nativeElement.animate(FRAMES, { - easing: 'ease-out', - fill: 'forwards', - duration: this.animationDuration - }); - } } @Component({ @@ -502,7 +431,6 @@ export class IgxLinearProgressBarComponent extends BaseProgressDirective impleme imports: [NgTemplateOutlet, NgIf] }) export class IgxCircularProgressBarComponent extends BaseProgressDirective implements AfterViewInit, AfterContentInit { - /** @hidden */ @HostBinding('class.igx-circular-bar') public cssClass = 'igx-circular-bar'; @@ -520,12 +448,22 @@ export class IgxCircularProgressBarComponent extends BaseProgressDirective imple /** * @hidden */ - @HostBinding('class.igx-circular-bar--indeterminate') + @HostBinding('class.igx-circular-bar--animation-none') @Input() public get isIndeterminate() { return this.indeterminate; } + @HostBinding('class.igx-circular-bar--animation-none') + public get disableAnimationClass(): boolean { + return !this._animate; + } + + @HostBinding('class.igx-circular-bar--hide-counter') + public get hasText(): boolean { + return !!this.text; + } + /** * Sets the text visibility. By default it is set to true. * ```html @@ -570,23 +508,12 @@ export class IgxCircularProgressBarComponent extends BaseProgressDirective imple }; } - private _circleRadius = 46; - private _circumference = 2 * Math.PI * this._circleRadius; - - private readonly STROKE_OPACITY_DVIDER = 100; - private readonly STROKE_OPACITY_ADDITION = .2; - - private animationState = { - strokeDashoffset: 289, - strokeOpacity: 1 - }; - - constructor(private renderer: Renderer2, private _directionality: IgxDirectionality) { + constructor(private renderer: Renderer2) { super(); } public ngAfterContentInit() { - this.triggerProgressTransition(MIN_VALUE, this._initValue); + this._updateProgressValues(); this._contentInit = true; } @@ -604,42 +531,4 @@ export class IgxCircularProgressBarComponent extends BaseProgressDirective imple public get textContent(): string { return this.text; } - - public runAnimation(value: number) { - if (this._animation && this._animation.playState !== 'finished') { - return; - } - - const valueInPercent = this.max <= 0 ? 0 : toPercent(value, this.max); - - const FRAMES = []; - FRAMES[0] = { ...this.animationState }; - - this.animationState.strokeDashoffset = this.getProgress(valueInPercent); - this.animationState.strokeOpacity = toPercent(value, this.max) / this.STROKE_OPACITY_DVIDER + this.STROKE_OPACITY_ADDITION; - - FRAMES[1] = { - ...this.animationState - }; - - this._animation = this._svgCircle.nativeElement.animate(FRAMES, { - easing: 'ease-out', - fill: 'forwards', - duration: this.animationDuration - }); - } - - private getProgress(percentage: number) { - return this._directionality.rtl ? - this._circumference + (percentage * this._circumference / 100) : - this._circumference - (percentage * this._circumference / 100); - } } - -export const valueInRange = (value: number, max: number, min = 0): number => Math.max(Math.min(value, max), min); - -export const toPercent = (value: number, max: number) => !max ? 0 : Math.floor(100 * value / max); - -export const toValue = (value: number, max: number) => max * value / 100; - - diff --git a/projects/igniteui-angular/src/lib/progressbar/templates/circular-bar.component.html b/projects/igniteui-angular/src/lib/progressbar/templates/circular-bar.component.html index b4380489339..29280d373a1 100644 --- a/projects/igniteui-angular/src/lib/progressbar/templates/circular-bar.component.html +++ b/projects/igniteui-angular/src/lib/progressbar/templates/circular-bar.component.html @@ -30,6 +30,6 @@ - {{textContent ? textContent: valueInPercent + '%'}} + {{textContent}} diff --git a/projects/igniteui-angular/src/lib/progressbar/templates/linear-bar.component.html b/projects/igniteui-angular/src/lib/progressbar/templates/linear-bar.component.html index ad8db918822..72c11f2a3b6 100644 --- a/projects/igniteui-angular/src/lib/progressbar/templates/linear-bar.component.html +++ b/projects/igniteui-angular/src/lib/progressbar/templates/linear-bar.component.html @@ -1,5 +1,5 @@
-
+
@@ -10,7 +10,8 @@ 'igx-linear-bar__value--center': textAlign === 'center', 'igx-linear-bar__value--end': textAlign === 'end', 'igx-linear-bar__value--top': textTop, - 'igx-linear-bar__value--hidden': !textVisibility + 'igx-linear-bar__value--hidden': !textVisibility, + 'igx-linear-bar__value--fraction': hasFraction }"> - {{text ? text : valueInPercent + '%'}} + {{text}} diff --git a/src/app/progressbar/progressbar.sample.html b/src/app/progressbar/progressbar.sample.html index ceb82259298..6652ca3fbf4 100644 --- a/src/app/progressbar/progressbar.sample.html +++ b/src/app/progressbar/progressbar.sample.html @@ -1,6 +1,17 @@

Linear progress bar

+ +
+ + +
+ +
+ + +
+
@@ -12,12 +23,12 @@

Linear progress bar

- +
- +
@@ -27,7 +38,7 @@

Linear progress bar

- +
@@ -45,7 +56,7 @@

Striped linear progress bar

- +
@@ -66,6 +77,9 @@

Circular progress indicator

+
+ +
@@ -88,7 +102,6 @@

Circular progress indicator

-
{{process.value}}
TOTAL%
diff --git a/src/app/progressbar/progressbar.sample.ts b/src/app/progressbar/progressbar.sample.ts index 4faf21bfeb6..f60196348eb 100644 --- a/src/app/progressbar/progressbar.sample.ts +++ b/src/app/progressbar/progressbar.sample.ts @@ -1,12 +1,21 @@ -import { Component, ViewEncapsulation, OnInit } from '@angular/core'; -import { IgxButtonDirective, IgxCircularProgressBarComponent, IgxIconComponent, IgxLinearProgressBarComponent, IgxProgressBarGradientDirective, IgxProgressBarTextTemplateDirective, IgxRippleDirective, IgxTextAlign } from 'igniteui-angular'; +import {Component, OnInit, ViewEncapsulation} from '@angular/core'; +import { + IgxButtonDirective, + IgxCircularProgressBarComponent, + IgxIconComponent, + IgxLinearProgressBarComponent, + IgxProgressBarGradientDirective, + IgxProgressBarTextTemplateDirective, + IgxRippleDirective, + IgxTextAlign +} from 'igniteui-angular'; @Component({ encapsulation: ViewEncapsulation.None, selector: 'app-progressbar-sample', styleUrls: ['progressbar.sample.scss'], templateUrl: 'progressbar.sample.html', - imports: [IgxLinearProgressBarComponent, IgxCircularProgressBarComponent, IgxProgressBarTextTemplateDirective, IgxProgressBarGradientDirective, IgxButtonDirective, IgxRippleDirective, IgxIconComponent] + imports: [IgxLinearProgressBarComponent, IgxCircularProgressBarComponent, IgxProgressBarTextTemplateDirective, IgxProgressBarGradientDirective, IgxButtonDirective, IgxRippleDirective, IgxIconComponent, IgxLinearProgressBarComponent, IgxLinearProgressBarComponent] }) export class ProgressbarSampleComponent implements OnInit { @@ -21,9 +30,7 @@ export class ProgressbarSampleComponent implements OnInit { } public generateNewProgressValues() { - const value = this.randomIntFromInterval(this.currentValue, 100); - - this.currentValue = value; + this.currentValue = this.randomIntFromInterval(0, 100); } public randomIntFromInterval(min: number, max: number) { diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index fa429165538..21d2f079812 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -1,10 +1,10 @@ @use 'sass:map'; @use '../../projects/igniteui-angular/src/lib/core/styles/themes' as *; -$palette: $light-material-palette; -$schema: $light-material-schema; -$typeface: $material-typeface; -$type-scale: $material-type-scale; +$palette: $light-bootstrap-palette; +$schema: $light-bootstrap-schema; +$typeface: $bootstrap-typeface; +$type-scale: $bootstrap-type-scale; $variant: map.get($schema, '_meta', 'variant'); $background-color: var(--ig-gray-900-contrast);