diff --git a/core/api.txt b/core/api.txt index a8217994eef..6dabdc41401 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1934,13 +1934,23 @@ ion-progress-bar,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | ion-progress-bar,prop,mode,"ios" | "md",undefined,false,false ion-progress-bar,prop,reversed,boolean,false,false,false ion-progress-bar,prop,shape,"rectangular" | "round" | undefined,undefined,false,false -ion-progress-bar,prop,theme,"ios" | "md" | "ionic",undefined,false,false ion-progress-bar,prop,type,"determinate" | "indeterminate",'determinate',false,false ion-progress-bar,prop,value,number,0,false,false -ion-progress-bar,css-prop,--background,ios -ion-progress-bar,css-prop,--background,md -ion-progress-bar,css-prop,--progress-background,ios -ion-progress-bar,css-prop,--progress-background,md +ion-progress-bar,css-prop,--ion-progress-bar-determinate-buffer-bar-default-background +ion-progress-bar,css-prop,--ion-progress-bar-determinate-buffer-bar-semantic-default-background +ion-progress-bar,css-prop,--ion-progress-bar-determinate-buffer-bar-solid-default-background +ion-progress-bar,css-prop,--ion-progress-bar-determinate-buffer-circles-default-background +ion-progress-bar,css-prop,--ion-progress-bar-determinate-buffer-circles-semantic-default-background +ion-progress-bar,css-prop,--ion-progress-bar-determinate-progress-default-background +ion-progress-bar,css-prop,--ion-progress-bar-determinate-progress-semantic-default-background +ion-progress-bar,css-prop,--ion-progress-bar-height +ion-progress-bar,css-prop,--ion-progress-bar-indeterminate-buffer-bar-default-background +ion-progress-bar,css-prop,--ion-progress-bar-indeterminate-buffer-bar-semantic-default-background +ion-progress-bar,css-prop,--ion-progress-bar-indeterminate-buffer-bar-solid-default-background +ion-progress-bar,css-prop,--ion-progress-bar-indeterminate-progress-default-background +ion-progress-bar,css-prop,--ion-progress-bar-indeterminate-progress-semantic-default-background +ion-progress-bar,css-prop,--ion-progress-bar-shape-rectangular-border-radius +ion-progress-bar,css-prop,--ion-progress-bar-shape-round-border-radius ion-progress-bar,part,progress ion-progress-bar,part,stream ion-progress-bar,part,track diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 5d1eff6638c..6a6131c1692 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -28,6 +28,7 @@ import { PickerChangeEventDetail } from "./components/picker/picker-interfaces"; import { PickerColumnChangeEventDetail, PickerColumnValue } from "./components/picker-column/picker-column-interfaces"; import { PickerButton, PickerColumn } from "./components/picker-legacy/picker-interface"; import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface"; +import { IonProgressBarShape } from "./components/progress-bar/progress-bar.interfaces"; import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; import { RefresherEventDetail } from "./components/refresher/refresher-interface"; @@ -68,6 +69,7 @@ export { PickerChangeEventDetail } from "./components/picker/picker-interfaces"; export { PickerColumnChangeEventDetail, PickerColumnValue } from "./components/picker-column/picker-column-interfaces"; export { PickerButton, PickerColumn } from "./components/picker-legacy/picker-interface"; export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface"; +export { IonProgressBarShape } from "./components/progress-bar/progress-bar.interfaces"; export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; export { RefresherEventDetail } from "./components/refresher/refresher-interface"; @@ -2977,6 +2979,9 @@ export namespace Components { */ "triggerAction": TriggerAction; } + /** + * * + */ interface IonProgressBar { /** * If the buffer and value are smaller than 1, the buffer circles will show. The buffer should be between [0, 1]. @@ -2997,13 +3002,9 @@ export namespace Components { */ "reversed": boolean; /** - * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` if both the shape property and theme config are unset. */ - "shape"?: 'round' | 'rectangular'; - /** - * The theme determines the visual appearance of the component. - */ - "theme"?: "ios" | "md" | "ionic"; + "shape"?: IonProgressBarShape; /** * The state of the progress bar, based on if the time the process takes is known or not. Default options are: `"determinate"` (no animation), `"indeterminate"` (animate from left to right). * @default 'determinate' @@ -5372,6 +5373,9 @@ declare global { prototype: HTMLIonPopoverElement; new (): HTMLIonPopoverElement; }; + /** + * * + */ interface HTMLIonProgressBarElement extends Components.IonProgressBar, HTMLStencilElement { } var HTMLIonProgressBarElement: { @@ -8901,6 +8905,9 @@ declare namespace LocalJSX { */ "triggerAction"?: TriggerAction; } + /** + * * + */ interface IonProgressBar { /** * If the buffer and value are smaller than 1, the buffer circles will show. The buffer should be between [0, 1]. @@ -8921,13 +8928,9 @@ declare namespace LocalJSX { */ "reversed"?: boolean; /** - * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` for a progress bar without rounded corners. Defaults to `"round"` if both the shape property and theme config are unset. */ - "shape"?: 'round' | 'rectangular'; - /** - * The theme determines the visual appearance of the component. - */ - "theme"?: "ios" | "md" | "ionic"; + "shape"?: IonProgressBarShape; /** * The state of the progress bar, based on if the time the process takes is known or not. Default options are: `"determinate"` (no animation), `"indeterminate"` (animate from left to right). * @default 'determinate' @@ -10667,6 +10670,9 @@ declare module "@stencil/core" { "ion-picker-legacy": LocalJSX.IonPickerLegacy & JSXBase.HTMLAttributes; "ion-picker-legacy-column": LocalJSX.IonPickerLegacyColumn & JSXBase.HTMLAttributes; "ion-popover": LocalJSX.IonPopover & JSXBase.HTMLAttributes; + /** + * * + */ "ion-progress-bar": LocalJSX.IonProgressBar & JSXBase.HTMLAttributes; "ion-radio": LocalJSX.IonRadio & JSXBase.HTMLAttributes; "ion-radio-group": LocalJSX.IonRadioGroup & JSXBase.HTMLAttributes; diff --git a/core/src/components/chip/chip.tsx b/core/src/components/chip/chip.tsx index 6d55dc8f439..d1b9776c47e 100644 --- a/core/src/components/chip/chip.tsx +++ b/core/src/components/chip/chip.tsx @@ -84,7 +84,7 @@ export class Chip implements ComponentInterface { /** * Gets the chip fill. Uses the `fill` property if set, otherwise - * checks the theme config and falls back to 'solid' if neither is provided. + * checks the theme config. Defaults to `solid` if neither is set. */ get fillValue(): IonChipFill { const fillConfig = config.getObjectValue('IonChip.fill', 'solid') as IonChipFill; @@ -95,7 +95,7 @@ export class Chip implements ComponentInterface { /** * Gets the chip hue. Uses the `hue` property if set, otherwise - * checks the theme config and falls back to 'subtle' if neither is provided. + * checks the theme config. Defaults to `subtle` if neither is set. */ get hueValue(): IonChipHue { const hueConfig = config.getObjectValue('IonChip.hue', 'subtle') as IonChipHue; @@ -106,7 +106,7 @@ export class Chip implements ComponentInterface { /** * Gets the chip shape. Uses the `shape` property if set, otherwise - * checks the theme config and falls back to 'round' if neither is provided. + * checks the theme config. Defaults to `round` if neither is set. */ get shapeValue(): IonChipShape { const shapeConfig = config.getObjectValue('IonChip.shape', 'round') as IonChipShape; @@ -117,7 +117,7 @@ export class Chip implements ComponentInterface { /** * Gets the chip size. Uses the `size` property if set, otherwise - * checks the theme config and falls back to 'large' if neither is provided. + * checks the theme config. Defaults to `large` if neither is set. */ get sizeValue(): IonChipSize { const sizeConfig = config.getObjectValue('IonChip.size', 'large') as IonChipSize; diff --git a/core/src/components/progress-bar/progress-bar.common.scss b/core/src/components/progress-bar/progress-bar.common.scss deleted file mode 100644 index 95541ae1449..00000000000 --- a/core/src/components/progress-bar/progress-bar.common.scss +++ /dev/null @@ -1,249 +0,0 @@ -@import "../../themes/mixins"; - -// Progress bar -// ------------------------------------------------------------------------ - -// Host has no background by default - this will be added to the progress-buffer-bar -:host { - display: block; - - position: relative; - - width: 100%; - - contain: strict; - - direction: ltr; - overflow: hidden; -} - -.progress, -.progress-indeterminate, -.indeterminate-bar-primary, -.indeterminate-bar-secondary, -.progress-buffer-bar { - @include position(0, 0, 0, 0); - position: absolute; - - width: 100%; - height: 100%; -} - -.buffer-circles-container, -.buffer-circles { - @include position(0, 0, 0, 0); - position: absolute; -} - -// Extend a bit to overflow. The size of animated distance. -.buffer-circles { - /* stylelint-disable property-disallowed-list */ - right: -10px; - left: -10px; - /* stylelint-enable property-disallowed-list */ -} - -// Determinate progress bar -// -------------------------------------------------- - -.progress, -.progress-buffer-bar, -.buffer-circles-container { - /* stylelint-disable-next-line property-disallowed-list */ - transform-origin: left top; - - transition: transform 150ms linear; -} - -// Progress and background bar -// -------------------------------------------------- - -.progress, -.progress-indeterminate { - background: var(--progress-background); - - z-index: 2; -} - -.progress-buffer-bar { - background: var(--background); - - z-index: 1; -} - -.buffer-circles-container { - overflow: hidden; -} - -// MD based animation on indeterminate type -// -------------------------------------------------- - -.indeterminate-bar-primary { - /* stylelint-disable property-disallowed-list */ - top: 0; - right: 0; - bottom: 0; - left: -145.166611%; - /* stylelint-enable property-disallowed-list */ - - animation: primary-indeterminate-translate 2s infinite linear; - - .progress-indeterminate { - animation: primary-indeterminate-scale 2s infinite linear; - animation-play-state: inherit; - } -} - -.indeterminate-bar-secondary { - /* stylelint-disable property-disallowed-list */ - top: 0; - right: 0; - bottom: 0; - left: -54.888891%; - /* stylelint-enable property-disallowed-list */ - - animation: secondary-indeterminate-translate 2s infinite linear; - - .progress-indeterminate { - animation: secondary-indeterminate-scale 2s infinite linear; - animation-play-state: inherit; - } -} - -// Progress Bar: Buffer Circles -// ------------------------------------------------------------------------ - -.buffer-circles { - background-image: radial-gradient(ellipse at center, var(--background) 0%, var(--background) 30%, transparent 30%); - - /* stylelint-disable property-disallowed-list */ - background-repeat: repeat-x; - background-position: 5px center; - background-size: 10px 10px; - /* stylelint-enable property-disallowed-list */ - - z-index: 0; - animation: buffering 450ms infinite linear; -} - -// Progress Bar: Reversed -// ------------------------------------------------------------------------ -// If reversed is set to true, the animation will be reversed -// and the bar will start at the top right - -:host(.progress-bar-reversed) { - transform: scaleX(-1); -} - -// Progress Bar: Paused -// ------------------------------------------------------------------------ - -:host(.progress-paused) { - .indeterminate-bar-secondary, - .indeterminate-bar-primary, - .buffer-circles { - animation-play-state: paused; - } -} - -// Progress Bar: Animation Keyframes -// ------------------------------------------------------------------------ -// Source: https://github.com/material-components/material-components-web/blob/master/packages/mdc-linear-progress/_keyframes.scss - -@keyframes primary-indeterminate-translate { - 0% { - transform: translateX(0); - } - - 20% { - animation-timing-function: cubic-bezier(0.5, 0, 0.701732, 0.495819); - - transform: translateX(0); - } - - 59.15% { - animation-timing-function: cubic-bezier(0.302435, 0.381352, 0.55, 0.956352); - - transform: translateX(83.67142%); - } - - 100% { - transform: translateX(200.611057%); - } -} - -@keyframes primary-indeterminate-scale { - 0% { - transform: scaleX(0.08); - } - - 36.65% { - animation-timing-function: cubic-bezier(0.334731, 0.12482, 0.785844, 1); - - transform: scaleX(0.08); - } - - 69.15% { - animation-timing-function: cubic-bezier(0.06, 0.11, 0.6, 1); - - transform: scaleX(0.661479); - } - - 100% { - transform: scaleX(0.08); - } -} - -@keyframes secondary-indeterminate-translate { - 0% { - animation-timing-function: cubic-bezier(0.15, 0, 0.515058, 0.409685); - - transform: translateX(0); - } - - 25% { - animation-timing-function: cubic-bezier(0.31033, 0.284058, 0.8, 0.733712); - - transform: translateX(37.651913%); - } - - 48.35% { - animation-timing-function: cubic-bezier(0.4, 0.627035, 0.6, 0.902026); - - transform: translateX(84.386165%); - } - - 100% { - transform: translateX(160.277782%); - } -} - -@keyframes secondary-indeterminate-scale { - 0% { - animation-timing-function: cubic-bezier(0.205028, 0.057051, 0.57661, 0.453971); - - transform: scaleX(0.08); - } - - 19.15% { - animation-timing-function: cubic-bezier(0.152313, 0.196432, 0.648374, 1.004315); - - transform: scaleX(0.457104); - } - - 44.15% { - animation-timing-function: cubic-bezier(0.257759, -0.003163, 0.211762, 1.38179); - - transform: scaleX(0.72796); - } - - 100% { - transform: scaleX(0.08); - } -} - -@keyframes buffering { - to { - transform: translateX(-10px); - } -} diff --git a/core/src/components/progress-bar/progress-bar.interfaces.ts b/core/src/components/progress-bar/progress-bar.interfaces.ts new file mode 100644 index 00000000000..04ba8826f18 --- /dev/null +++ b/core/src/components/progress-bar/progress-bar.interfaces.ts @@ -0,0 +1,107 @@ +export type IonProgressBarRecipe = { + height?: string | number; + + indeterminate?: { + progress?: { + default?: { + background?: string; + }; + + /** Any of the semantic colors like primary, secondary, etc. */ + semantic?: { + default?: { + background?: string; + }; + }; + }; + + buffer?: { + bar?: { + default?: { + background?: string; + }; + + /** Any of the semantic colors like primary, secondary, etc. */ + semantic?: { + default?: { + background?: string; + }; + }; + + solid?: { + default?: { + background?: string; + }; + }; + }; + }; + }; + + determinate?: { + progress?: { + default?: { + background?: string; + }; + + /** Any of the semantic colors like primary, secondary, etc. */ + semantic?: { + default?: { + background?: string; + }; + }; + }; + + buffer?: { + bar?: { + default?: { + background?: string; + }; + + /** Any of the semantic colors like primary, secondary, etc. */ + semantic?: { + default?: { + background?: string; + }; + }; + + /* When progress bar is solid (buffer = 1) */ + solid?: { + default?: { + background?: string; + }; + }; + }; + + circles?: { + default?: { + background?: string; + }; + + /** Any of the semantic colors like primary, secondary, etc. */ + semantic?: { + default?: { + background?: string; + }; + }; + }; + }; + }; + + // Shapes + shape?: { + [K in IonProgressBarShape]?: IonProgressBarShapeDefinition; + }; +}; + +type IonProgressBarShapeDefinition = { + border?: { + radius?: string | number; + }; +}; + +export type IonProgressBarConfig = { + shape?: IonProgressBarShape; +}; + +export const ION_PROGRESS_BAR_SHAPES = ['round', 'rectangular'] as const; +export type IonProgressBarShape = (typeof ION_PROGRESS_BAR_SHAPES)[number]; diff --git a/core/src/components/progress-bar/progress-bar.ionic.scss b/core/src/components/progress-bar/progress-bar.ionic.scss deleted file mode 100644 index 3fad235b07f..00000000000 --- a/core/src/components/progress-bar/progress-bar.ionic.scss +++ /dev/null @@ -1,42 +0,0 @@ -@use "../../themes/ionic/ionic.globals.scss" as globals; -@use "./progress-bar.common.scss"; - -// Ionic Progress bar -// -------------------------------------------------- - -:host { - --background: #{globals.$ion-bg-neutral-subtle-default}; - --progress-background: #{globals.ion-color(primary, base)}; - - height: globals.$ion-scale-100; -} - -// Progress Bar Shapes -// ------------------------------------------------------------------------------- - -:host(.progress-bar-shape-round) { - @include globals.border-radius(globals.$ion-round-xs); -} - -:host(.progress-bar-shape-rectangular) { - @include globals.border-radius(globals.$ion-rectangular-xs); -} - -// Progress Bar: Color -// ------------------------------------------------------------------------ - -:host(.ion-color) { - .progress, - .progress-indeterminate { - background: #{globals.current-color(base)}; - } -} - -:host(.ion-color) .buffer-circles { - background-image: radial-gradient( - ellipse at center, - #{globals.current-color(base, 0.3)} 0%, - #{globals.current-color(base, 0.3)} 30%, - transparent 30% - ); -} diff --git a/core/src/components/progress-bar/progress-bar.ios.scss b/core/src/components/progress-bar/progress-bar.ios.scss deleted file mode 100644 index 2c8c60fbcdb..00000000000 --- a/core/src/components/progress-bar/progress-bar.ios.scss +++ /dev/null @@ -1,22 +0,0 @@ -@import "./progress-bar.native"; -@import "./progress-bar.ios.vars"; - -// iOS Progress bar -// -------------------------------------------------- - -:host { - @include border-radius($progress-bar-ios-border-radius); - - height: $progress-bar-ios-height; -} - -:host(.progress-bar-solid) { - /** - * On iOS the unfilled portion of the progress bar - * is always a consistent background color. We - * only apply this style when the progress bar is - * solid (with a buffer value of 1). This maintains - * the custom Ionic appearance for a buffered progress bar. - */ - --background: #{$background-color-step-100}; -} diff --git a/core/src/components/progress-bar/progress-bar.ios.vars.scss b/core/src/components/progress-bar/progress-bar.ios.vars.scss deleted file mode 100644 index ff83c57d2e7..00000000000 --- a/core/src/components/progress-bar/progress-bar.ios.vars.scss +++ /dev/null @@ -1,8 +0,0 @@ -// iOS Progress bar -// -------------------------------------------------- - -/// @prop - Height of the progress bar -$progress-bar-ios-height: 4px; - -/// @prop - Border radius of the progress bar container -$progress-bar-ios-border-radius: 9999px; diff --git a/core/src/components/progress-bar/progress-bar.md.scss b/core/src/components/progress-bar/progress-bar.md.scss deleted file mode 100644 index d19e4bcf30e..00000000000 --- a/core/src/components/progress-bar/progress-bar.md.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import "./progress-bar.native"; -@import "./progress-bar.md.vars"; - -// Material Design Progress bar -// -------------------------------------------------- - -:host { - height: $progress-bar-md-height; -} - -:host(.ion-color) .progress-buffer-bar { - background: #{current-color(base, 0.3)}; -} diff --git a/core/src/components/progress-bar/progress-bar.md.vars.scss b/core/src/components/progress-bar/progress-bar.md.vars.scss deleted file mode 100644 index 0ff0af1f4a4..00000000000 --- a/core/src/components/progress-bar/progress-bar.md.vars.scss +++ /dev/null @@ -1,5 +0,0 @@ -// MD Progress bar -// -------------------------------------------------- - -/// @prop - Height of the progress bar -$progress-bar-md-height: 4px; diff --git a/core/src/components/progress-bar/progress-bar.native.scss b/core/src/components/progress-bar/progress-bar.native.scss deleted file mode 100644 index 5bc8e728b77..00000000000 --- a/core/src/components/progress-bar/progress-bar.native.scss +++ /dev/null @@ -1,31 +0,0 @@ -@import "../../themes/native/native.globals"; -@import "./progress-bar.common.scss"; - -// Host has no background by default - this will be added to the progress-buffer-bar -:host { - /** - * @prop --background: Background of the progress track, or the buffer bar if `buffer` is set - * @prop --progress-background: Background of the progress bar representing the current value - */ - --background: #{ion-color(primary, base, 0.3)}; - --progress-background: #{ion-color(primary, base)}; -} - -// Progress Bar: Color -// ------------------------------------------------------------------------ - -:host(.ion-color) { - .progress, - .progress-indeterminate { - background: #{current-color(base)}; - } -} - -:host(.ion-color) .buffer-circles { - background-image: radial-gradient( - ellipse at center, - #{current-color(base, 0.3)} 0%, - #{current-color(base, 0.3)} 30%, - transparent 30% - ); -} diff --git a/core/src/components/progress-bar/progress-bar.scss b/core/src/components/progress-bar/progress-bar.scss new file mode 100644 index 00000000000..48fb1d0b312 --- /dev/null +++ b/core/src/components/progress-bar/progress-bar.scss @@ -0,0 +1,358 @@ +@use "../../themes/mixins" as mixins; + +// Progress Bar: Common Styles +// -------------------------------------------------- + +:host { + /** + * @prop --ion-progress-bar-height: Height of the progress bar + * + * @prop --ion-progress-bar-determinate-progress-default-background: Background of the filled progress indicator in a determinate progress bar + * @prop --ion-progress-bar-determinate-progress-semantic-default-background: Background of the filled progress indicator in a determinate progress bar when a semantic color is applied + * + * @prop --ion-progress-bar-determinate-buffer-bar-default-background: Background of the track representing the buffered amount in a determinate progress bar + * @prop --ion-progress-bar-determinate-buffer-bar-semantic-default-background: Background of the track representing the buffered amount in a determinate progress bar when a semantic color is applied + * @prop --ion-progress-bar-determinate-buffer-bar-solid-default-background: Background of the track representing the buffered amount in a determinate progress bar when it's fully buffered + * + * @prop --ion-progress-bar-determinate-buffer-circles-default-background: Background of the buffer circles in a determinate progress bar + * @prop --ion-progress-bar-determinate-buffer-circles-semantic-default-background: Background of the buffer circles in a determinate progress bar when a semantic color is applied + * @prop --ion-progress-bar-indeterminate-progress-default-background: Background of the filled progress indicator in an indeterminate progress bar + * @prop --ion-progress-bar-indeterminate-progress-semantic-default-background: Background of the filled progress indicator in an indeterminate progress bar when a semantic color is applied + * + * @prop --ion-progress-bar-indeterminate-buffer-bar-default-background: Background of the track representing the buffered amount in an indeterminate progress bar + * @prop --ion-progress-bar-indeterminate-buffer-bar-semantic-default-background: Background of the track representing the buffered amount in an indeterminate progress bar when a semantic color is applied + * @prop --ion-progress-bar-indeterminate-buffer-bar-solid-default-background: Background of the track representing the buffered amount in an indeterminate progress bar when it's fully buffered + * + * @prop --ion-progress-bar-shape-round-border-radius: Border radius of the `round` progress bar shape + * @prop --ion-progress-bar-shape-rectangular-border-radius: Border radius of the `rectangular` progress bar shape + */ + + display: block; + + position: relative; + + width: 100%; + height: var(--ion-progress-bar-height); + + contain: strict; + + direction: ltr; + overflow: hidden; +} + +.progress, +.progress-indeterminate, +.indeterminate-bar-primary, +.indeterminate-bar-secondary, +.progress-buffer-bar { + @include mixins.position(0, 0, 0, 0); + position: absolute; + + width: 100%; + height: 100%; +} + +.progress, +.progress-indeterminate { + z-index: 2; +} + +.progress-buffer-bar { + z-index: 1; +} + +.buffer-circles-container, +.buffer-circles { + @include mixins.position(0, 0, 0, 0); + position: absolute; +} + +.buffer-circles-container { + overflow: hidden; +} + +.buffer-circles-container-hidden { + display: none; +} + +// Extend a bit to overflow. The size of animated distance. +.buffer-circles { + /* stylelint-disable property-disallowed-list */ + right: -10px; + left: -10px; + /* stylelint-enable property-disallowed-list */ +} + +// Progress Bar Types +// -------------------------------------------------- + +// Determinate +.progress, +.progress-buffer-bar, +.buffer-circles-container { + /* stylelint-disable-next-line property-disallowed-list */ + transform-origin: left top; + + transition: transform 150ms linear; +} + +.progress { + background: var(--ion-progress-bar-determinate-progress-default-background); +} + +:host(.progress-bar-type-determinate) .progress-buffer-bar { + background: var(--ion-progress-bar-determinate-buffer-bar-default-background); +} + +.buffer-circles { + background-image: radial-gradient( + ellipse at center, + var(--ion-progress-bar-determinate-buffer-circles-default-background) 0%, + var(--ion-progress-bar-determinate-buffer-circles-default-background) 30%, + transparent 30% + ); + + /* stylelint-disable property-disallowed-list */ + background-repeat: repeat-x; + background-position: 5px center; + background-size: 10px 10px; + /* stylelint-enable property-disallowed-list */ + + z-index: 0; + animation: buffering 450ms infinite linear; +} + +// Indeterminate +.progress-indeterminate { + background: var(--ion-progress-bar-indeterminate-progress-default-background); +} + +:host(.progress-bar-type-indeterminate) .progress-buffer-bar { + background: var(--ion-progress-bar-indeterminate-buffer-bar-default-background); +} + +// Progress Bar Shapes +// -------------------------------------------------- + +:host(.progress-bar-shape-round) { + @include mixins.border-radius(var(--ion-progress-bar-shape-round-border-radius)); +} + +:host(.progress-bar-shape-rectangular) { + @include mixins.border-radius(var(--ion-progress-bar-shape-rectangular-border-radius)); +} + +// Progress Bar Semantic Colors +// -------------------------------------------------- + +// Determinate +:host(.ion-color) .progress { + background: var(--ion-progress-bar-determinate-progress-semantic-default-background); +} + +:host(.ion-color.progress-bar-type-determinate:not(.progress-bar-solid)) .progress-buffer-bar { + background: var( + --ion-progress-bar-determinate-buffer-bar-semantic-default-background, + var(--ion-progress-bar-determinate-buffer-bar-default-background) + ); +} + +:host(.ion-color) .buffer-circles { + background-image: radial-gradient( + ellipse at center, + var(--ion-progress-bar-determinate-buffer-circles-semantic-default-background) 0%, + var(--ion-progress-bar-determinate-buffer-circles-semantic-default-background) 30%, + transparent 30% + ); +} + +// Indeterminate +:host(.ion-color) .progress-indeterminate { + background: var(--ion-progress-bar-indeterminate-progress-semantic-default-background); +} + +:host(.ion-color.progress-bar-type-indeterminate:not(.progress-bar-solid)) .progress-buffer-bar { + background: var( + --ion-progress-bar-indeterminate-buffer-bar-semantic-default-background, + var(--ion-progress-bar-indeterminate-buffer-bar-default-background) + ); +} + +// Progress Bar States +// -------------------------------------------------- + +// Reversed +// If set to true, the animation will be reversed +// and the bar will start at the top right +:host(.progress-bar-reversed) { + transform: scaleX(-1); +} + +// Paused +:host(.progress-paused) { + .indeterminate-bar-secondary, + .indeterminate-bar-primary, + .buffer-circles { + animation-play-state: paused; + } +} + +// Progress Bar Solid State (buffer = 1) +// -------------------------------------------------- + +// Determinate +:host(.progress-bar-solid.progress-bar-type-determinate) .progress-buffer-bar { + background: var( + --ion-progress-bar-determinate-buffer-bar-solid-default-background, + var(--ion-progress-bar-determinate-buffer-bar-default-background) + ); +} + +// Indeterminate +:host(.progress-bar-solid.progress-bar-type-indeterminate) .progress-buffer-bar { + background: var( + --ion-progress-bar-indeterminate-buffer-bar-solid-default-background, + var(--ion-progress-bar-indeterminate-buffer-bar-default-background) + ); +} + +// Progress Bar Animation +// -------------------------------------------------- + +// Indeterminate +.indeterminate-bar-primary { + /* stylelint-disable property-disallowed-list */ + top: 0; + right: 0; + bottom: 0; + left: -145.166611%; + /* stylelint-enable property-disallowed-list */ + + animation: primary-indeterminate-translate 2s infinite linear; + + .progress-indeterminate { + animation: primary-indeterminate-scale 2s infinite linear; + animation-play-state: inherit; + } +} + +.indeterminate-bar-secondary { + /* stylelint-disable property-disallowed-list */ + top: 0; + right: 0; + bottom: 0; + left: -54.888891%; + /* stylelint-enable property-disallowed-list */ + + animation: secondary-indeterminate-translate 2s infinite linear; + + .progress-indeterminate { + animation: secondary-indeterminate-scale 2s infinite linear; + animation-play-state: inherit; + } +} + +// Progress Bar Animation Keyframes +// -------------------------------------------------- + +// Source: https://github.com/material-components/material-components-web/blob/6ceadb81df508ac08ab007c52c4d3dc1d811cc15/packages/mdc-linear-progress/_linear-progress.scss#L323-L357 +@keyframes primary-indeterminate-translate { + 0% { + transform: translateX(0); + } + + 20% { + animation-timing-function: cubic-bezier(0.5, 0, 0.701732, 0.495819); + + transform: translateX(0); + } + + 59.15% { + animation-timing-function: cubic-bezier(0.302435, 0.381352, 0.55, 0.956352); + + transform: translateX(83.67142%); + } + + 100% { + transform: translateX(200.611057%); + } +} + +// Source: https://github.com/material-components/material-components-web/blob/6ceadb81df508ac08ab007c52c4d3dc1d811cc15/packages/mdc-linear-progress/_linear-progress.scss#L359-L379 +@keyframes primary-indeterminate-scale { + 0% { + transform: scaleX(0.08); + } + + 36.65% { + animation-timing-function: cubic-bezier(0.334731, 0.12482, 0.785844, 1); + + transform: scaleX(0.08); + } + + 69.15% { + animation-timing-function: cubic-bezier(0.06, 0.11, 0.6, 1); + + transform: scaleX(0.661479); + } + + 100% { + transform: scaleX(0.08); + } +} + +// Source: https://github.com/material-components/material-components-web/blob/6ceadb81df508ac08ab007c52c4d3dc1d811cc15/packages/mdc-linear-progress/_linear-progress.scss#L381-L414 +@keyframes secondary-indeterminate-translate { + 0% { + animation-timing-function: cubic-bezier(0.15, 0, 0.515058, 0.409685); + + transform: translateX(0); + } + + 25% { + animation-timing-function: cubic-bezier(0.31033, 0.284058, 0.8, 0.733712); + + transform: translateX(37.651913%); + } + + 48.35% { + animation-timing-function: cubic-bezier(0.4, 0.627035, 0.6, 0.902026); + + transform: translateX(84.386165%); + } + + 100% { + transform: translateX(160.277782%); + } +} + +// Source: https://github.com/material-components/material-components-web/blob/6ceadb81df508ac08ab007c52c4d3dc1d811cc15/packages/mdc-linear-progress/_linear-progress.scss#L416-L452 +@keyframes secondary-indeterminate-scale { + 0% { + animation-timing-function: cubic-bezier(0.205028, 0.057051, 0.57661, 0.453971); + + transform: scaleX(0.08); + } + + 19.15% { + animation-timing-function: cubic-bezier(0.152313, 0.196432, 0.648374, 1.004315); + + transform: scaleX(0.457104); + } + + 44.15% { + animation-timing-function: cubic-bezier(0.257759, -0.003163, 0.211762, 1.38179); + + transform: scaleX(0.72796); + } + + 100% { + transform: scaleX(0.08); + } +} + +@keyframes buffering { + to { + transform: translateX(-10px); + } +} diff --git a/core/src/components/progress-bar/progress-bar.tsx b/core/src/components/progress-bar/progress-bar.tsx index 729448aff1e..6cb471e5c9d 100644 --- a/core/src/components/progress-bar/progress-bar.tsx +++ b/core/src/components/progress-bar/progress-bar.tsx @@ -4,12 +4,12 @@ import { clamp } from '@utils/helpers'; import { createColorClasses } from '@utils/theme'; import { config } from '../../global/config'; -import { getIonTheme } from '../../global/ionic-global'; import type { Color } from '../../interface'; +import type { IonProgressBarShape } from './progress-bar.interfaces'; + /** - * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. - * @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component. + * * @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component. * * @part progress - The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`. * @part stream - The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`. @@ -18,11 +18,7 @@ import type { Color } from '../../interface'; */ @Component({ tag: 'ion-progress-bar', - styleUrls: { - ios: 'progress-bar.ios.scss', - md: 'progress-bar.md.scss', - ionic: 'progress-bar.ionic.scss', - }, + styleUrl: 'progress-bar.scss', shadow: true, }) export class ProgressBar implements ComponentInterface { @@ -61,31 +57,23 @@ export class ProgressBar implements ComponentInterface { * Set to `"round"` for a progress bar with rounded corners, or `"rectangular"` * for a progress bar without rounded corners. * - * Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Defaults to `"round"` if both the shape property and theme config are unset. */ - @Prop() shape?: 'round' | 'rectangular'; - - private getShape(): string | undefined { - const theme = getIonTheme(this); - const { shape } = this; - - // TODO(ROU-11638): Remove theme check when shapes are defined for all themes. - if (theme !== 'ionic') { - return undefined; - } + @Prop() shape?: IonProgressBarShape; - if (shape === undefined) { - return 'round'; - } - - return shape; + /** + * Gets the progress bar shape. Uses the `shape` property if set, + * otherwise checks the theme config. Defaults to `round` if + * neither is set. + */ + get shapeValue(): IonProgressBarShape { + return this.shape ?? (config.getObjectValue('IonProgressBar.shape', 'round') as IonProgressBarShape); } render() { const { color, type, reversed, value, buffer } = this; const paused = config.getBoolean('_testing'); - const theme = getIonTheme(this); - const shape = this.getShape(); + const shape = this.shapeValue; // If the progress is displayed as a solid bar. const progressSolid = buffer === 1; return ( @@ -95,12 +83,11 @@ export class ProgressBar implements ComponentInterface { aria-valuemin="0" aria-valuemax="1" class={createColorClasses(color, { - [theme]: true, - [`progress-bar-${type}`]: true, + [`progress-bar-type-${type}`]: true, + [`progress-bar-shape-${shape}`]: true, 'progress-paused': paused, 'progress-bar-reversed': document.dir === 'rtl' ? !reversed : reversed, 'progress-bar-solid': progressSolid, - [`progress-bar-shape-${shape}`]: shape !== undefined, })} > {type === 'indeterminate' ? renderIndeterminate() : renderProgress(value, buffer)} @@ -135,9 +122,13 @@ const renderProgress = (value: number, buffer: number) => { * When finalBuffer === 1, we use display: none * instead of removing the element to avoid flickering. */ - // TODO(FW-6697): change `ion-hide` class to `ion-display-none` or another class + // TODO(FW-6697): remove `ion-hide` class in favor of `buffer-circles-container-hidden`
diff --git a/core/src/components/progress-bar/test/basic/index.html b/core/src/components/progress-bar/test/basic/index.html index 31ab53d9137..da38c6a7faa 100644 --- a/core/src/components/progress-bar/test/basic/index.html +++ b/core/src/components/progress-bar/test/basic/index.html @@ -15,11 +15,11 @@ - -
- -
- `, - config - ); +import { ION_PROGRESS_BAR_SHAPES } from '../../progress-bar.interfaces'; - const container = page.locator('.container'); - - await expect(container).toHaveScreenshot(screenshot(`progress-bar-shape-round`)); - }); - - test('rectangular - should not have visual regressions', async ({ page }) => { - await page.setContent( - ` +/** + * This behavior does not vary across directions + */ +configs({ directions: ['ltr'], modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('progress-bar: shape'), () => { + ION_PROGRESS_BAR_SHAPES.forEach((shape) => { + test(`${shape} - should not have visual regressions`, async ({ page }) => { + await page.setContent( + `
- +
`, - config - ); + config + ); - const container = page.locator('.container'); + const container = page.locator('.container'); - await expect(container).toHaveScreenshot(screenshot(`progress-bar-shape-rectangular`)); + await expect(container).toHaveScreenshot(screenshot(`progress-bar-shape-${shape}`)); + }); }); }); }); diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..2faf00329de Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..176da87c2ea Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Safari-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..dff26d19747 Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Chrome-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f9d0a41fbda Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Firefox-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..80ee161f871 Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Safari-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..3f38e2da23b Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-rectangular-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..5444b75e201 Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..f012572f353 Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Safari-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..41e8ffb4f02 Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Chrome-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..90fa8463f5b Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Firefox-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..61320b8e3d5 Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Safari-linux.png b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..2fc8d2e2c8b Binary files /dev/null and b/core/src/components/progress-bar/test/shape/progress-bar.e2e.ts-snapshots/progress-bar-shape-round-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/spinner/spinner.tsx b/core/src/components/spinner/spinner.tsx index 9ba5e8f3a38..25232666b3f 100644 --- a/core/src/components/spinner/spinner.tsx +++ b/core/src/components/spinner/spinner.tsx @@ -64,7 +64,7 @@ export class Spinner implements ComponentInterface { /** * Gets the spinner size. Uses the `size` property if set, otherwise - * checks the theme config and falls back to 'medium' if neither is provided. + * checks the theme config. Defaults to `medium` if neither is set. */ get sizeValue(): SpinnerSize { const sizeConfig = config.getObjectValue('IonSpinner.size', 'medium') as SpinnerSize; diff --git a/core/src/themes/ionic/default.tokens.ts b/core/src/themes/ionic/default.tokens.ts index e4d4407e2b6..32690bbba12 100644 --- a/core/src/themes/ionic/default.tokens.ts +++ b/core/src/themes/ionic/default.tokens.ts @@ -1,4 +1,4 @@ -import { currentColor, mix, dynamicFont } from '../../utils/theme'; +import { currentColor, ionColor, mix, dynamicFont } from '../../utils/theme'; import { defaultTheme as baseDefaultTheme } from '../base/default.tokens'; import { colors as baseColors } from '../base/shared.tokens'; import type { DefaultTheme } from '../themes.interfaces'; @@ -28,6 +28,10 @@ export const defaultTheme: DefaultTheme = { size: 'large', }, + IonProgressBar: { + shape: 'round', + }, + IonSpinner: { size: 'xsmall', }, @@ -463,6 +467,80 @@ export const defaultTheme: DefaultTheme = { }, }, + IonProgressBar: { + height: 'var(--token-scale-100, var(--ion-scaling-100))', + + indeterminate: { + progress: { + default: { + background: ionColor('primary', 'base'), + }, + + semantic: { + default: { + background: currentColor('base'), + }, + }, + }, + + buffer: { + bar: { + default: { + background: 'var(--token-bg-neutral-subtle-default, #eae9e9)', + }, + }, + }, + }, + + determinate: { + progress: { + default: { + background: ionColor('primary', 'base'), + }, + + semantic: { + default: { + background: currentColor('base'), + }, + }, + }, + + buffer: { + bar: { + default: { + background: 'var(--token-bg-neutral-subtle-default, #eae9e9)', + }, + }, + + circles: { + default: { + background: 'var(--token-bg-neutral-subtle-default, #eae9e9)', + }, + + semantic: { + default: { + background: currentColor('base', 0.3), + }, + }, + }, + }, + }, + + shape: { + round: { + border: { + radius: 'var(--token-round-xs, var(--ion-radii-xxxxl))', + }, + }, + + rectangular: { + border: { + radius: 'var(--token-rectangular-xs, var(--ion-radii-xxxxs))', + }, + }, + }, + }, + IonSpinner: { color: '#626262', diff --git a/core/src/themes/ios/default.tokens.ts b/core/src/themes/ios/default.tokens.ts index f79dd62558b..3c91bef9871 100644 --- a/core/src/themes/ios/default.tokens.ts +++ b/core/src/themes/ios/default.tokens.ts @@ -1,4 +1,4 @@ -import { rgba, currentColor, clamp, mix, dynamicFont } from '../../utils/theme'; +import { rgba, currentColor, ionColor, clamp, mix, dynamicFont } from '../../utils/theme'; import { defaultTheme as baseDefaultTheme } from '../base/default.tokens'; import { colors as baseColors } from '../base/shared.tokens'; import type { DefaultTheme } from '../themes.interfaces'; @@ -30,6 +30,10 @@ export const defaultTheme: DefaultTheme = { size: 'large', }, + IonProgressBar: { + shape: 'round', + }, + IonSpinner: { size: 'medium', }, @@ -475,6 +479,92 @@ export const defaultTheme: DefaultTheme = { }, }, + IonProgressBar: { + height: 'var(--ion-scaling-100)', + + indeterminate: { + progress: { + default: { + background: ionColor('primary', 'base'), + }, + + semantic: { + default: { + background: currentColor('base'), + }, + }, + }, + + buffer: { + bar: { + default: { + background: ionColor('primary', 'base', { alpha: 0.3 }), + }, + + solid: { + default: { + background: `var(--ion-background-color-step-100, ${mix(baseColors.black, baseColors.white, '90%')})`, + }, + }, + }, + }, + }, + + determinate: { + progress: { + default: { + background: ionColor('primary', 'base'), + }, + + semantic: { + default: { + background: currentColor('base'), + }, + }, + }, + + buffer: { + bar: { + default: { + background: ionColor('primary', 'base', { alpha: 0.3 }), + }, + + solid: { + default: { + background: `var(--ion-background-color-step-100, ${mix(baseColors.black, baseColors.white, '90%')})`, + }, + }, + }, + + circles: { + default: { + background: ionColor('primary', 'base', { alpha: 0.3 }), + }, + + semantic: { + default: { + background: currentColor('base', 0.3), + }, + }, + }, + }, + }, + + shape: { + round: { + border: { + radius: 'var(--ion-radii-xxxxl)', + }, + }, + + rectangular: { + border: { + radius: 'var(--ion-radii-xxxxs)', + }, + }, + }, + }, + IonSpinner: { color: 'var(--ion-text-color, #000)', diff --git a/core/src/themes/md/default.tokens.ts b/core/src/themes/md/default.tokens.ts index b978c59ef9b..0f4f2331be7 100644 --- a/core/src/themes/md/default.tokens.ts +++ b/core/src/themes/md/default.tokens.ts @@ -1,4 +1,4 @@ -import { rgba, currentColor, mix, dynamicFont } from '../../utils/theme'; +import { rgba, currentColor, ionColor, mix, dynamicFont } from '../../utils/theme'; import { defaultTheme as baseDefaultTheme } from '../base/default.tokens'; import { colors as baseColors } from '../base/shared.tokens'; import type { DefaultTheme } from '../themes.interfaces'; @@ -33,6 +33,10 @@ export const defaultTheme: DefaultTheme = { size: 'large', }, + IonProgressBar: { + shape: 'rectangular', + }, + IonSpinner: { size: 'medium', }, @@ -594,6 +598,92 @@ export const defaultTheme: DefaultTheme = { }, }, + IonProgressBar: { + height: 'var(--ion-scaling-100)', + + indeterminate: { + progress: { + default: { + background: ionColor('primary', 'base'), + }, + + semantic: { + default: { + background: currentColor('base'), + }, + }, + }, + + buffer: { + bar: { + default: { + background: ionColor('primary', 'base', { alpha: 0.3 }), + }, + + semantic: { + default: { + background: currentColor('base', 0.3), + }, + }, + }, + }, + }, + + determinate: { + progress: { + default: { + background: ionColor('primary', 'base'), + }, + + semantic: { + default: { + background: currentColor('base'), + }, + }, + }, + + buffer: { + bar: { + default: { + background: ionColor('primary', 'base', { alpha: 0.3 }), + }, + + semantic: { + default: { + background: currentColor('base', 0.3), + }, + }, + }, + + circles: { + default: { + background: ionColor('primary', 'base', { alpha: 0.3 }), + }, + + semantic: { + default: { + background: currentColor('base', 0.3), + }, + }, + }, + }, + }, + + shape: { + round: { + border: { + radius: 'var(--ion-radii-xxxxl)', + }, + }, + + rectangular: { + border: { + radius: 'var(--ion-radii-xxxxs)', + }, + }, + }, + }, + IonSpinner: { color: 'var(--ion-text-color, #000)', diff --git a/core/src/themes/themes.interfaces.ts b/core/src/themes/themes.interfaces.ts index e7e8685cff9..9a2476680f7 100644 --- a/core/src/themes/themes.interfaces.ts +++ b/core/src/themes/themes.interfaces.ts @@ -1,5 +1,6 @@ import type { IonChipConfig, IonChipRecipe } from '../components/chip/chip.interfaces'; import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interfaces'; +import type { IonProgressBarConfig, IonProgressBarRecipe } from '../components/progress-bar/progress-bar.interfaces'; import type { IonSpinnerConfig, IonSpinnerRecipe } from '../components/spinner/spinner.interfaces'; import type { IonicConfig as IonicGlobalConfig } from '../utils/config'; @@ -243,6 +244,7 @@ export type BaseTheme = { export type IonicConfig = IonicGlobalConfig & { components?: { IonChip?: IonChipConfig; + IonProgressBar?: IonProgressBarConfig; IonSpinner?: IonSpinnerConfig; }; }; @@ -282,6 +284,7 @@ export type DefaultTheme = BaseTheme & { type Components = { IonChip?: IonChipRecipe; IonItemDivider?: IonItemDividerRecipe; + IonProgressBar?: IonProgressBarRecipe; IonSpinner?: IonSpinnerRecipe; IonCard?: any; diff --git a/core/src/utils/theme.ts b/core/src/utils/theme.ts index d0c098807cc..eb1eb1e6283 100644 --- a/core/src/utils/theme.ts +++ b/core/src/utils/theme.ts @@ -622,6 +622,45 @@ export function currentColor(variation: string, alpha: number | string | null = } } +interface IonColorOptions { + alpha?: number | string | null; + rgb?: boolean; + subtle?: boolean; +} + +export function ionColor(name: string, variation: string, options: IonColorOptions = {}): string { + const { alpha = null, rgb = false, subtle = false } = options; + + // Build base variable name + const base = subtle ? `--ion-color-${name}-subtle` : `--ion-color-${name}`; + const variationSuffix = variation === 'base' ? '' : `-${variation}`; + let variable = `${base}${variationSuffix}`; + + // Build the fallback variable name (only for bold colors) + let fallbackVariable: string | null = null; + + if (!subtle) { + const fallbackBase = `--ion-color-${name}-bold`; + fallbackVariable = `${fallbackBase}${variationSuffix}`; + } + + // Handle alpha transparency + if (alpha !== null) { + const rgbVar = `${variable}-rgb`; + const fallbackRgb = fallbackVariable ? `${fallbackVariable}-rgb` : null; + + return fallbackRgb ? `rgba(var(${rgbVar}, var(${fallbackRgb})), ${alpha})` : `rgba(var(${rgbVar}), ${alpha})`; + } + + // Handle RGB variables + if (rgb) { + variable = `${variable}-rgb`; + fallbackVariable = fallbackVariable ? `${fallbackVariable}-rgb` : null; + } + + return fallbackVariable ? `var(${variable}, var(${fallbackVariable}))` : `var(${variable})`; +} + /** * Mimics the CSS `clamp` function logic. * diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index b1073a2cfbf..973b9120d9c 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1674,14 +1674,14 @@ Shorthand for ionPickerDidDismiss. @ProxyCmp({ - inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'theme', 'type', 'value'] + inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'type', 'value'] }) @Component({ selector: 'ion-progress-bar', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'theme', 'type', 'value'], + inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'type', 'value'], }) export class IonProgressBar { protected el: HTMLIonProgressBarElement; diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 62d708102b3..4873db65a1a 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1618,14 +1618,14 @@ Shorthand for ionPickerDidDismiss. @ProxyCmp({ defineCustomElementFn: defineIonProgressBar, - inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'theme', 'type', 'value'] + inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'type', 'value'] }) @Component({ selector: 'ion-progress-bar', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'theme', 'type', 'value'], + inputs: ['buffer', 'color', 'mode', 'reversed', 'shape', 'type', 'value'], standalone: true }) export class IonProgressBar {