From 18c96acebe1e948a02eab347ff08238d891ca53a Mon Sep 17 00:00:00 2001 From: "Nichols, Kieran" Date: Wed, 8 Nov 2023 10:51:42 -0500 Subject: [PATCH] chore: general cleanup and consistency sweep --- .stylelintrc | 5 +++- .../circular-progress/circular-progress.html | 8 +++-- .../circular-progress/circular-progress.ts | 12 ++------ .../linear-progress/linear-progress.html | 15 ++++++++++ .../pages/linear-progress/linear-progress.ts | 7 ++++- src/lib/button/button-constants.ts | 4 +-- src/lib/checkbox/checkbox.scss | 4 +-- src/lib/circular-progress/_core.scss | 11 ++++--- .../circular-progress-constants.ts | 6 +++- .../circular-progress-foundation.ts | 27 ++++++++++++++++- .../circular-progress/circular-progress.scss | 28 +++++++++++++++++ .../circular-progress.test.ts | 30 +++++++++++++++++++ .../circular-progress/circular-progress.ts | 30 +++++++++++++++++-- src/lib/constants.ts | 2 ++ src/lib/core/base/base-adapter.ts | 2 +- src/lib/core/styles/_utils.scss | 17 +++++++++-- .../core/styles/tokens/button/_tokens.scss | 4 +-- .../tokens/circular-progress/_tokens.scss | 16 ++++++++-- .../tokens/focus-indicator/_tokens.scss | 3 +- .../tokens/linear-progress/_tokens.scss | 17 +++++++++-- src/lib/linear-progress/_core.scss | 11 ++++--- .../linear-progress-constants.ts | 5 +++- .../linear-progress-foundation.ts | 20 ++++++++++--- src/lib/linear-progress/linear-progress.scss | 23 +++++++++++++- .../linear-progress/linear-progress.test.ts | 16 ++++++++++ src/lib/linear-progress/linear-progress.ts | 16 ++++++++-- src/lib/slider/slider.scss | 3 +- src/lib/switch/switch.scss | 4 +-- 28 files changed, 286 insertions(+), 60 deletions(-) diff --git a/.stylelintrc b/.stylelintrc index 8891bd634..f44786866 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,3 +1,6 @@ { - "extends": "@tylertech/stylelint-rules" + "extends": "@tylertech/stylelint-rules", + "rules": { + "length-zero-no-unit": null + } } diff --git a/src/dev/pages/circular-progress/circular-progress.html b/src/dev/pages/circular-progress/circular-progress.html index 9f8e0d7bc..0f3cc551e 100644 --- a/src/dev/pages/circular-progress/circular-progress.html +++ b/src/dev/pages/circular-progress/circular-progress.html @@ -4,7 +4,7 @@ title: 'Circular progress', includePath: './pages/circular-progress/circular-progress.ejs', options: [ - { + { type: 'select', label: 'Theme', id: 'opt-theme', @@ -12,7 +12,11 @@ options: [ { label: 'Primary', value: 'primary' }, { label: 'Secondary', value: 'secondary' }, - { label: 'Tertiary', value: 'tertiary' } + { label: 'Tertiary', value: 'tertiary' }, + { label: 'Success', value: 'success' }, + { label: 'Error', value: 'error' }, + { label: 'Warning', value: 'warning' }, + { label: 'Info', value: 'info' } ] }, { type: 'text-field', label: 'Size', id: 'opt-size', defaultValue: '48px' }, diff --git a/src/dev/pages/circular-progress/circular-progress.ts b/src/dev/pages/circular-progress/circular-progress.ts index 546af10c6..9274e9c71 100644 --- a/src/dev/pages/circular-progress/circular-progress.ts +++ b/src/dev/pages/circular-progress/circular-progress.ts @@ -8,11 +8,7 @@ let determinateIntervalTimer: number | undefined; const showTrackToggle = document.getElementById('opt-show-track') as ISwitchComponent; showTrackToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { - if (selected) { - circularProgress.style.removeProperty('--forge-circular-progress-track-background'); - } else { - circularProgress.style.setProperty('--forge-circular-progress-track-background', 'transparent'); - } + circularProgress.track = selected; }); const showPercentToggle = document.getElementById('opt-show-percent') as ISwitchComponent; @@ -22,11 +18,7 @@ showPercentToggle.addEventListener('forge-switch-change', ({ detail: selected }) const themeSelect = document.getElementById('opt-theme') as ISelectComponent; themeSelect.addEventListener('change', ({ detail }) => { - if (detail === 'tertiary') { - circularProgress.style.removeProperty('--forge-theme-tertiary'); - } else { - circularProgress.style.setProperty('--forge-theme-tertiary', `var(--forge-theme-${detail})`); - } + circularProgress.theme = detail; }); const determinateToggle = document.getElementById('opt-determinate') as ISwitchComponent; diff --git a/src/dev/pages/linear-progress/linear-progress.html b/src/dev/pages/linear-progress/linear-progress.html index 4c7bf1122..d3dc70026 100644 --- a/src/dev/pages/linear-progress/linear-progress.html +++ b/src/dev/pages/linear-progress/linear-progress.html @@ -13,6 +13,21 @@ { label: 'Determinate', value: 'determinate' }, { label: 'Buffer', value: 'buffer' } ] + }, + { + type: 'select', + label: 'Theme', + id: 'opt-theme', + defaultValue: 'tertiary', + options: [ + { label: 'Primary', value: 'primary' }, + { label: 'Secondary', value: 'secondary' }, + { label: 'Tertiary', value: 'tertiary' }, + { label: 'Success', value: 'success' }, + { label: 'Error', value: 'error' }, + { label: 'Warning', value: 'warning' }, + { label: 'Info', value: 'info' } + ] } ] } diff --git a/src/dev/pages/linear-progress/linear-progress.ts b/src/dev/pages/linear-progress/linear-progress.ts index a1699097d..3a58baf6a 100644 --- a/src/dev/pages/linear-progress/linear-progress.ts +++ b/src/dev/pages/linear-progress/linear-progress.ts @@ -3,8 +3,8 @@ import type { ILinearProgressComponent, ISelectComponent } from '@tylertech/forg import '@tylertech/forge/linear-progress'; const linearProgress = document.getElementById('linear-progress') as ILinearProgressComponent; -const modeSelect = document.getElementById('opt-mode') as ISelectComponent; +const modeSelect = document.getElementById('opt-mode') as ISelectComponent; modeSelect.addEventListener('change', ({ detail: value }) => { if (value === 'determinate' || value === 'indeterminate') { linearProgress.determinate = value === 'determinate'; @@ -16,3 +16,8 @@ modeSelect.addEventListener('change', ({ detail: value }) => { linearProgress.buffer = 0.75; } }); + +const themeSelect = document.getElementById('opt-theme') as ISelectComponent; +themeSelect.addEventListener('change', ({ detail }) => { + linearProgress.theme = detail; +}); diff --git a/src/lib/button/button-constants.ts b/src/lib/button/button-constants.ts index 36bf2631d..a146ba2b0 100644 --- a/src/lib/button/button-constants.ts +++ b/src/lib/button/button-constants.ts @@ -1,4 +1,4 @@ -import { COMPONENT_NAME_PREFIX } from '../constants'; +import { COMPONENT_NAME_PREFIX, Theme } from '../constants'; const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}button`; @@ -19,4 +19,4 @@ export const BUTTON_CONSTANTS = { }; export type ButtonVariant = 'text' | 'raised' | 'outlined' | 'flat' | 'link'; -export type ButtonTheme = 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'danger' | 'info'; +export type ButtonTheme = Theme; diff --git a/src/lib/checkbox/checkbox.scss b/src/lib/checkbox/checkbox.scss index 9ec0a2954..2ecb65650 100644 --- a/src/lib/checkbox/checkbox.scss +++ b/src/lib/checkbox/checkbox.scss @@ -142,11 +142,9 @@ forge-state-layer { )); } -/* stylelint-disable length-zero-no-unit */ forge-focus-indicator { @include focus-indicator.provide-theme(( - shape: 0px, + shape: 0px, // Requires unit outward-offset: 8px )); } -/* stylelint-enable length-zero-no-unit */ diff --git a/src/lib/circular-progress/_core.scss b/src/lib/circular-progress/_core.scss index 8c4f8f913..470179461 100644 --- a/src/lib/circular-progress/_core.scss +++ b/src/lib/circular-progress/_core.scss @@ -50,7 +50,8 @@ $_cycle-duration: calc(4 * #{token(arc-duration)}); box-sizing: border-box; border-radius: 50%; border: solid calc(#{token(size)} * #{token(track-width)} / 100); - border-color: #{token(track-color)} #{token(track-color)} transparent transparent; + border-color: #{token(indicator-color)} #{token(indicator-color)} transparent transparent; + transition: border-color #{token(theme-transition-duration)} #{token(theme-transition-timing)}; will-change: transform; animation: #{animation.$indeterminate-circle-animation}; animation-iteration-count: infinite; @@ -81,8 +82,9 @@ $_cycle-duration: calc(4 * #{token(arc-duration)}); } @mixin determinate-progress { - transition: stroke-dashoffset 500ms #{animation.$determinate-easing}; - stroke: #{token(track-color)}; + transition: stroke-dashoffset 500ms #{animation.$determinate-easing}, + stroke #{token(theme-transition-duration)} #{token(theme-transition-timing)}; + stroke: #{token(indicator-color)}; } @mixin determinate-track { @@ -90,7 +92,8 @@ $_cycle-duration: calc(4 * #{token(arc-duration)}); } @mixin determinate-track-background { - stroke: #{token(track-background)}; + transition: stroke #{token(theme-transition-duration)} #{token(theme-transition-timing)}; + stroke: #{token(track-color)}; } @mixin determinate-spinner { diff --git a/src/lib/circular-progress/circular-progress-constants.ts b/src/lib/circular-progress/circular-progress-constants.ts index e894d9d31..b7e58198f 100644 --- a/src/lib/circular-progress/circular-progress-constants.ts +++ b/src/lib/circular-progress/circular-progress-constants.ts @@ -1,10 +1,12 @@ -import { COMPONENT_NAME_PREFIX } from '../constants'; +import { COMPONENT_NAME_PREFIX, Theme } from '../constants'; const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}circular-progress`; const attributes = { DETERMINATE: 'determinate', PROGRESS: 'progress', + THEME: 'theme', + NO_TRACK: 'no-track', ARIA_LABEL: 'data-aria-label' }; @@ -24,3 +26,5 @@ export const CIRCULAR_PROGRESS_CONSTANTS = { classes, selectors }; + +export type CircularProgressTheme = Theme; diff --git a/src/lib/circular-progress/circular-progress-foundation.ts b/src/lib/circular-progress/circular-progress-foundation.ts index bd4c97679..1dbf209fb 100644 --- a/src/lib/circular-progress/circular-progress-foundation.ts +++ b/src/lib/circular-progress/circular-progress-foundation.ts @@ -1,16 +1,20 @@ import { ICustomElementFoundation } from '@tylertech/forge-core'; import { ICircularProgressAdapter } from './circular-progress-adapter'; -import { CIRCULAR_PROGRESS_CONSTANTS } from './circular-progress-constants'; +import { CircularProgressTheme, CIRCULAR_PROGRESS_CONSTANTS } from './circular-progress-constants'; export interface ICircularProgressFoundation extends ICustomElementFoundation { determinate: boolean; progress: number; + theme: CircularProgressTheme; + track: boolean; } export class CircularProgressFoundation implements ICircularProgressFoundation { private _determinate = false; private _progress = 0; + private _theme: CircularProgressTheme = 'primary'; + private _track = true; constructor(private _adapter: ICircularProgressAdapter) {} @@ -51,6 +55,27 @@ export class CircularProgressFoundation implements ICircularProgressFoundation { } } + public get theme(): CircularProgressTheme { + return this._theme; + } + public set theme(value: CircularProgressTheme) { + if (this._theme !== value) { + this._theme = value; + this._adapter.toggleHostAttribute(CIRCULAR_PROGRESS_CONSTANTS.attributes.THEME, !!this._theme, this._theme); + } + } + + public get track(): boolean { + return this._track; + } + public set track(value: boolean) { + value = Boolean(value); + if (this._track !== value) { + this._track = value; + this._adapter.toggleHostAttribute(CIRCULAR_PROGRESS_CONSTANTS.attributes.NO_TRACK, !this._track); + } + } + public set ariaLabel(value: string) { this._adapter.setAriaLabel(value); } diff --git a/src/lib/circular-progress/circular-progress.scss b/src/lib/circular-progress/circular-progress.scss index f932de424..4a5337dd4 100644 --- a/src/lib/circular-progress/circular-progress.scss +++ b/src/lib/circular-progress/circular-progress.scss @@ -1,6 +1,8 @@ @use './core'; @use './animation'; @use './configuration'; +@use '../core/styles/theme'; +@use './token-utils' as *; // // Host styles @@ -99,6 +101,32 @@ svg { @include core.determinate-spinner; } +// +// Theme +// + +@mixin theme($theme) { + :host([theme=#{$theme}]) { + .forge-circular-progress { + @include override(indicator-color, theme.variable($theme)); + @include override(track-color, theme.variable(#{$theme}-container)); + } + } +} + +@include theme(primary); +@include theme(secondary); +@include theme(success); +@include theme(error); +@include theme(warning); +@include theme(info); + +:host([no-track]) { + .forge-circular-progress { + @include override(track-color, transparent); + } +} + @media screen and (forced-colors: active) { .progress { stroke: CanvasText; diff --git a/src/lib/circular-progress/circular-progress.test.ts b/src/lib/circular-progress/circular-progress.test.ts index 64474847c..5495da0c4 100644 --- a/src/lib/circular-progress/circular-progress.test.ts +++ b/src/lib/circular-progress/circular-progress.test.ts @@ -86,4 +86,34 @@ describe('Circular Progress', () => { expect(rootElement.getAttribute('aria-label')).to.equal(expectedLabel); }); + + it('should set theme', async () => { + const el = await fixture(html``); + el.theme = 'secondary'; + + expect(el.theme).to.equal('secondary'); + expect(el.getAttribute(CIRCULAR_PROGRESS_CONSTANTS.attributes.THEME)).to.equal('secondary'); + }); + + it('should set theme via attribute', async () => { + const el = await fixture(html``); + + expect(el.theme).to.equal('secondary'); + expect(el.getAttribute(CIRCULAR_PROGRESS_CONSTANTS.attributes.THEME)).to.equal('secondary'); + }); + + it('should set track', async () => { + const el = await fixture(html``); + el.track = false; + + expect(el.track).to.equal(false); + expect(el.hasAttribute(CIRCULAR_PROGRESS_CONSTANTS.attributes.NO_TRACK)).to.equal(true); + }); + + it('should set track via attribute', async () => { + const el = await fixture(html``); + + expect(el.track).to.equal(false); + expect(el.hasAttribute(CIRCULAR_PROGRESS_CONSTANTS.attributes.NO_TRACK)).to.equal(true); + }); }); diff --git a/src/lib/circular-progress/circular-progress.ts b/src/lib/circular-progress/circular-progress.ts index c48e9fe0c..d3eba205a 100644 --- a/src/lib/circular-progress/circular-progress.ts +++ b/src/lib/circular-progress/circular-progress.ts @@ -2,7 +2,7 @@ import { CustomElement, attachShadowTemplate, FoundationProperty, coerceBoolean, import { CircularProgressAdapter } from './circular-progress-adapter'; import { CircularProgressFoundation } from './circular-progress-foundation'; -import { CIRCULAR_PROGRESS_CONSTANTS } from './circular-progress-constants'; +import { CircularProgressTheme, CIRCULAR_PROGRESS_CONSTANTS } from './circular-progress-constants'; import { BaseComponent, IBaseComponent } from '../core/base/base-component'; import template from './circular-progress.html'; @@ -11,6 +11,8 @@ import styles from './circular-progress.scss'; export interface ICircularProgressComponent extends IBaseComponent { determinate: boolean; progress: number; + theme: CircularProgressTheme; + track: boolean; } declare global { @@ -32,8 +34,13 @@ declare global { * * @property {boolean} determinate - Controls the determinate state. * @property {boolean} progress - Controls the progress while in a determinate state. Accepts values from `0` to `1`. + * @property {CircularProgressTheme} theme - Controls the theme of the progress indicator. + * @property {boolean} track - Controls the visibility of the track background. * * @attribute {boolean} determinate - Controls the determinate state. + * @attribute {boolean} progress - Controls the progress while in a determinate state. Accepts values from `0` to `1`. + * @attribute {CircularProgressTheme} theme - Controls the theme of the progress indicator. + * @attribute {boolean} no-track - Controls the visibility of the track background. * @attribute {string} data-aria-label - Propagates an `aria-label` to the underlying progressbar element. * * @slot - The is the default/unnamed slot. Renders content at the center of the progress indicator. @@ -41,8 +48,11 @@ declare global { * @cssproperty --forge-circular-progress-size - The height and width of the indicator container. * @cssproperty --forge-circular-progress-padding - The padding inside the bounding box of the container. * @cssproperty --forge-circular-progress-track-width - The track indicator width. - * @cssproperty --forge-circular-progress-track-color - The track indicator color. - * @cssproperty --forge-circular-progress-track-background - The track background color. + * @cssproperty --forge-circular-progress-track-color - The track background color. + * @cssproperty --forge-circular-progress-indicator-color - The track indicator color. + * @cssproperty --forge-circular-progress-arc-duration - The duration of the arc animation. + * @cssproperty --forge-circular-progress-theme-transition-duration - The duration of the theme transition. + * @cssproperty --forge-circular-progress-theme-transition-timing - The easing function to use for the theme transition. * * @csspart progressbar - Styles the progress bar container element */ @@ -54,6 +64,8 @@ export class CircularProgressComponent extends BaseComponent implements ICircula return [ CIRCULAR_PROGRESS_CONSTANTS.attributes.DETERMINATE, CIRCULAR_PROGRESS_CONSTANTS.attributes.PROGRESS, + CIRCULAR_PROGRESS_CONSTANTS.attributes.THEME, + CIRCULAR_PROGRESS_CONSTANTS.attributes.NO_TRACK, CIRCULAR_PROGRESS_CONSTANTS.attributes.ARIA_LABEL ]; } @@ -78,6 +90,12 @@ export class CircularProgressComponent extends BaseComponent implements ICircula case CIRCULAR_PROGRESS_CONSTANTS.attributes.PROGRESS: this.progress = coerceNumber(newValue); break; + case CIRCULAR_PROGRESS_CONSTANTS.attributes.THEME: + this.theme = newValue as CircularProgressTheme; + break; + case CIRCULAR_PROGRESS_CONSTANTS.attributes.NO_TRACK: + this.track = !coerceBoolean(newValue); + break; case CIRCULAR_PROGRESS_CONSTANTS.attributes.ARIA_LABEL: this._foundation.ariaLabel = newValue; break; @@ -89,4 +107,10 @@ export class CircularProgressComponent extends BaseComponent implements ICircula @FoundationProperty() public declare progress: number; + + @FoundationProperty() + public declare theme: CircularProgressTheme; + + @FoundationProperty() + public declare track: boolean; } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index ee1768a47..9af928471 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -3,3 +3,5 @@ export const KEYSTROKE_DEBOUNCE_THRESHOLD = 500; export const ICON_CLASS_NAME = 'tyler-icons'; export const CDN_BASE_URL = 'https://cdn.forge.tylertech.com/'; export const internals = Symbol('ElementInternals'); + +export type Theme = 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'info'; diff --git a/src/lib/core/base/base-adapter.ts b/src/lib/core/base/base-adapter.ts index 2278d36b0..f6685c366 100644 --- a/src/lib/core/base/base-adapter.ts +++ b/src/lib/core/base/base-adapter.ts @@ -11,7 +11,7 @@ export interface IBaseAdapter { redispatchEvent(event: Event, options?: { bubbles?: boolean; cancelable?: boolean; composed?: boolean }): boolean; emitHostEvent(type: string, data?: any, bubble?: boolean, cancelable?: boolean): boolean; addHostListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void; - removeHostListener(event: string, callback: (event: Event) => void): void; + removeHostListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void; addWindowListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void; removeWindowListener(event: string, callback: (event: Event) => void, options?: boolean | EventListenerOptions): void; addDocumentListener(event: string, callback: (event: Event) => void, options?: boolean | AddEventListenerOptions): void; diff --git a/src/lib/core/styles/_utils.scss b/src/lib/core/styles/_utils.scss index 815bf383a..59f33da5a 100644 --- a/src/lib/core/styles/_utils.scss +++ b/src/lib/core/styles/_utils.scss @@ -103,17 +103,28 @@ /// /// Example: /// ```sass -/// #{override(tokens, color, new-color)}; // => --_component-name-color: var(--_component-name-new-color); -/// #{override(tokens, color, red)}; // => --_component-name-color: red; +/// #{override(component-name, tokens, color, new-color)}; // => --_component-name-color: var(--_component-name-new-color); +/// #{override(component-name, tokens, color, red)}; // => --_component-name-color: red; /// ``` /// +/// @param {String} $module - The module name. /// @param {String} $module-tokens - The available tokens. /// @param {String} $token - The token name to override. /// @param {String} $token-or-value - The token name (if exists) or value to point to. /// @param {Boolean} $type - The type of token this is. Valid values are `token` and `value`. /// @mixin override($module, $module-tokens, $token, $token-or-value, $type: token) { - $value: if($type == 'token' and map.get($module-tokens, $token-or-value), module-var($module, $token-or-value), $token-or-value); + // When the "token" type is specified, we output a module-level CSS custom property override + // that points to the provided token. This ensures that the module is taking ownership of the + // token value (which breaks the global cascading of internal tokens) and the module can choose + // to provide its own token specific to that override. + // + // When the "value" type is specified, we output a global-level CSS custom property override + // that points to the provided value. This keeps the global cascading of internal tokens intact + // instead of just overriding the token to a specific unchangeable value. + $value: if($type == 'token' and map.has-key($module-tokens, $token-or-value), + module-var($module, $token-or-value), + create-var($module, $token, $token-or-value)); #{create-module-var($module, $token)}: #{$value}; } diff --git a/src/lib/core/styles/tokens/button/_tokens.scss b/src/lib/core/styles/tokens/button/_tokens.scss index f96bc78d7..6f3851e64 100644 --- a/src/lib/core/styles/tokens/button/_tokens.scss +++ b/src/lib/core/styles/tokens/button/_tokens.scss @@ -9,10 +9,9 @@ @use '../../typography'; @use '../../utils'; -/* stylelint-disable length-zero-no-unit */ $tokens: ( // Shared - primary-color: utils.module-val(button, primary, theme.variable(primary)), + primary-color: utils.module-val(button, primary-color, theme.variable(primary)), text-color: utils.module-ref(button, text-color, primary-color), disabled-color: utils.module-val(button, disabled, theme.variable(surface-container)), padding: utils.module-val(button, padding, 8px), @@ -104,7 +103,6 @@ $tokens: ( popover-icon-transition-timing: utils.module-val(button, popover-icon-transition-timing, animation.variable(easing-standard)), popover-icon-open-rotation: utils.module-val(button, popover-icon-open-rotation, 180deg) ) !default; -/* stylelint-enable length-zero-no-unit */ @function get($key) { @return map.get($tokens, $key); diff --git a/src/lib/core/styles/tokens/circular-progress/_tokens.scss b/src/lib/core/styles/tokens/circular-progress/_tokens.scss index 7c24e713d..86b715ae0 100644 --- a/src/lib/core/styles/tokens/circular-progress/_tokens.scss +++ b/src/lib/core/styles/tokens/circular-progress/_tokens.scss @@ -1,14 +1,24 @@ @use 'sass:map'; @use '../../theme'; +@use '../../animation'; @use '../../utils'; $tokens: ( + // Dimensions size: utils.module-val(circular-progress, size, 48px), padding: utils.module-val(circular-progress, padding, 0), + + // Track track-width: utils.module-val(circular-progress, track-width, 12), - track-color: utils.module-val(circular-progress, track-color, theme.variable(tertiary)), - track-background: utils.module-val(circular-progress, track-background, theme.variable(surface-container)), - arc-duration: utils.module-val(circular-progress, arc-duration, 1333ms) + track-color: utils.module-val(circular-progress, track-color, theme.variable(tertiary-container)), + + // Indicator + indicator-color: utils.module-val(circular-progress, indicator-color, theme.variable(tertiary)), + + // Animation + arc-duration: utils.module-val(circular-progress, arc-duration, 1333ms), + theme-transition-duration: utils.module-val(circular-progress, theme-transition-duration, animation.variable(duration-short3)), + theme-transition-timing: utils.module-val(circular-progress, theme-transition-timing, animation.variable(easing-standard)), ) !default; @function get($key) { diff --git a/src/lib/core/styles/tokens/focus-indicator/_tokens.scss b/src/lib/core/styles/tokens/focus-indicator/_tokens.scss index 5fb42062c..5fbf7a43f 100644 --- a/src/lib/core/styles/tokens/focus-indicator/_tokens.scss +++ b/src/lib/core/styles/tokens/focus-indicator/_tokens.scss @@ -6,14 +6,13 @@ @use '../../spacing'; @use '../../utils'; -/* stylelint-disable length-zero-no-unit */ $tokens: ( active-width: utils.module-val(focus-indicator, active-width, 6px), color: utils.module-val(focus-indicator, color, theme.variable(primary)), duration: utils.module-val(focus-indicator, duration, animation.variable(duration-long4)), outward-offset: utils.module-val(focus-indicator, outward-offset, spacing.variable(xxsmall)), inward-offset: utils.module-val(focus-indicator, inward-offset, 0px), // Requires unit - shape: utils.module-val(focus-indicator, shape, shape.variable(extra-small)), + shape: utils.module-val(focus-indicator, shape, shape.variable(extra-small)), // Requires unit width: utils.module-val(focus-indicator, width, border.variable(medium)), easing: utils.module-val(focus-indicator, easing, animation.variable(easing-emphasized)), shape-start-start: utils.module-ref(focus-indicator, shape-start-start, shape), diff --git a/src/lib/core/styles/tokens/linear-progress/_tokens.scss b/src/lib/core/styles/tokens/linear-progress/_tokens.scss index b69ffb265..119b33586 100644 --- a/src/lib/core/styles/tokens/linear-progress/_tokens.scss +++ b/src/lib/core/styles/tokens/linear-progress/_tokens.scss @@ -5,14 +5,25 @@ @use '../../utils'; $tokens: ( + // Track track-height: utils.module-val(linear-progress, track-height, 4px), track-color: utils.module-val(linear-progress, track-color, theme.variable(tertiary-container)), - active-indicator-color: utils.module-val(linear-progress, indicator-color, theme.variable(tertiary)), track-shape: utils.module-val(linear-progress, track-shape, shape.variable(full)), - active-indicator-height: utils.module-ref(linear-progress, indicator-height, track-height), + + // Indicator + indicator-color: utils.module-val(linear-progress, indicator-color, theme.variable(tertiary)), + indicator-height: utils.module-ref(linear-progress, indicator-height, track-height), + + // Determinate determinate-duration: utils.module-val(linear-progress, determinate-duration, animation.variable(duration-medium1)), - indeterminate-duration: utils.module-val(linear-progress, indeterminate-duration, 2s), determinate-easing: utils.module-val(linear-progress, determinate-easing, cubic-bezier(0.4, 0, 0.6, 1)), + + // Indeterminate + indeterminate-duration: utils.module-val(linear-progress, indeterminate-duration, 2s), + + // Theme + theme-transition-duration: utils.module-val(linear-progress, theme-transition-duration, animation.variable(duration-short3)), + theme-transition-timing: utils.module-val(linear-progress, theme-transition-timing, animation.variable(easing-standard)), ) !default; @function get($key) { diff --git a/src/lib/linear-progress/_core.scss b/src/lib/linear-progress/_core.scss index 4eda912ba..38a008ddf 100644 --- a/src/lib/linear-progress/_core.scss +++ b/src/lib/linear-progress/_core.scss @@ -23,7 +23,7 @@ @mixin bar { animation: none; inline-size: 100%; - block-size: #{token(active-indicator-height)}; + block-size: #{token(indicator-height)}; transform-origin: left center; will-change: transform; transition: transform #{token(determinate-duration)} #{token(determinate-easing)}; @@ -36,14 +36,16 @@ @mixin bar-inner { inset: 0; animation: none; - background: #{token(active-indicator-color)}; + transition: background-color #{token(theme-transition-duration)} #{token(theme-transition-timing)}; + background: #{token(indicator-color)}; } @mixin buffer-bar { background: #{token(track-color)}; inset: 0; will-change: transform; - transition: transform #{token(determinate-duration)} #{token(determinate-easing)}; + transition: transform #{token(determinate-duration)} #{token(determinate-easing)}, + background-color #{token(theme-transition-duration)} #{token(theme-transition-timing)}; transform-origin: left center; } @@ -52,7 +54,8 @@ will-change: transform; animation: linear infinite #{token(determinate-duration)}; animation-name: buffering; - background: #{token(track-background, custom)}; + background: #{token(track-color, custom)}; + transition: background-color #{token(theme-transition-duration)} #{token(theme-transition-timing)}; } @mixin indeterminate-bar { diff --git a/src/lib/linear-progress/linear-progress-constants.ts b/src/lib/linear-progress/linear-progress-constants.ts index 70a60b484..326954180 100644 --- a/src/lib/linear-progress/linear-progress-constants.ts +++ b/src/lib/linear-progress/linear-progress-constants.ts @@ -1,4 +1,4 @@ -import { COMPONENT_NAME_PREFIX } from '../constants'; +import { COMPONENT_NAME_PREFIX, Theme } from '../constants'; const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}linear-progress`; @@ -16,6 +16,7 @@ const attributes = { DETERMINATE: 'determinate', PROGRESS: 'progress', BUFFER: 'buffer', + THEME: 'theme', ARIA_LABEL: 'data-aria-label' }; @@ -25,3 +26,5 @@ export const LINEAR_PROGRESS_CONSTANTS = { selectors, attributes }; + +export type LinearProgressTheme = Theme; diff --git a/src/lib/linear-progress/linear-progress-foundation.ts b/src/lib/linear-progress/linear-progress-foundation.ts index c2c172f85..e7a998f80 100644 --- a/src/lib/linear-progress/linear-progress-foundation.ts +++ b/src/lib/linear-progress/linear-progress-foundation.ts @@ -1,17 +1,19 @@ import { ICustomElementFoundation } from '@tylertech/forge-core'; import { ILinearProgressAdapter } from './linear-progress-adapter'; -import { LINEAR_PROGRESS_CONSTANTS } from './linear-progress-constants'; +import { LinearProgressTheme, LINEAR_PROGRESS_CONSTANTS } from './linear-progress-constants'; export interface ICircularProgressFoundation extends ICustomElementFoundation { determinate: boolean; progress: number; buffer: number; + theme: LinearProgressTheme; } export class LinearProgressFoundation { private _determinate = false; private _progress = 0; private _buffer = 1; + private _theme: LinearProgressTheme = 'primary'; constructor(private _adapter: ILinearProgressAdapter) {} @@ -28,7 +30,7 @@ export class LinearProgressFoundation { this._adapter.setBuffer(this._determinate ? this._buffer : 1); this._adapter.setProgress(this._determinate ? this._progress : 0); this._adapter.setDeterminate(this._determinate); - this._adapter.toggleHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.DETERMINATE, value); + this._adapter.toggleHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.DETERMINATE, this._determinate); } } @@ -41,7 +43,7 @@ export class LinearProgressFoundation { if (this._determinate) { this._adapter.setProgress(this._progress); } - this._adapter.setHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS, value.toString()); + this._adapter.setHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS, this._progress.toString()); } } @@ -54,7 +56,17 @@ export class LinearProgressFoundation { if (this._determinate) { this._adapter.setBuffer(this._buffer); } - this._adapter.setHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.BUFFER, value.toString()); + this._adapter.setHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.BUFFER, this._buffer.toString()); + } + } + + public get theme(): LinearProgressTheme { + return this._theme; + } + public set theme(value: LinearProgressTheme) { + if (this._theme !== value) { + this._theme = value; + this._adapter.toggleHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.THEME, !!this._theme, this._theme); } } } diff --git a/src/lib/linear-progress/linear-progress.scss b/src/lib/linear-progress/linear-progress.scss index b19279690..8d7225326 100644 --- a/src/lib/linear-progress/linear-progress.scss +++ b/src/lib/linear-progress/linear-progress.scss @@ -1,3 +1,4 @@ +@use '../core/styles/theme'; @use './animations'; @use './configuration'; @use './core'; @@ -117,9 +118,29 @@ $rtl-selectors: ( } } +// +// Theme +// + +@mixin theme($theme) { + :host([theme=#{$theme}]) { + .forge-linear-progress { + @include override(indicator-color, theme.variable($theme)); + @include override(track-color, theme.variable(#{$theme}-container)); + } + } +} + +@include theme(primary); +@include theme(secondary); +@include theme(success); +@include theme(error); +@include theme(warning); +@include theme(info); + @media screen and (forced-colors: active) { .forge-linear-progress { - @include override(active-indicator-color, CanvasText, value); + @include override(indicator-color, CanvasText, value); @include override(track-color, GrayText, value); border: 1px solid CanvasText; diff --git a/src/lib/linear-progress/linear-progress.test.ts b/src/lib/linear-progress/linear-progress.test.ts index cd7c40946..ea7f4281a 100644 --- a/src/lib/linear-progress/linear-progress.test.ts +++ b/src/lib/linear-progress/linear-progress.test.ts @@ -123,4 +123,20 @@ describe('Linear Progress', () => { expect(rootElement.getAttribute('aria-label')).to.equal(expectedLabel); }); + + it('should set theme', async () => { + const el = await fixture(html``); + + el.theme = 'secondary'; + + expect(el.theme).to.equal('secondary'); + expect(el.getAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.THEME)).to.equal('secondary'); + }); + + it('should set theme via attribute', async () => { + const el = await fixture(html``); + + expect(el.theme).to.equal('secondary'); + expect(el.getAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.THEME)).to.equal('secondary'); + }); }); diff --git a/src/lib/linear-progress/linear-progress.ts b/src/lib/linear-progress/linear-progress.ts index ce47def65..a00fb0ba4 100644 --- a/src/lib/linear-progress/linear-progress.ts +++ b/src/lib/linear-progress/linear-progress.ts @@ -1,7 +1,7 @@ import { attachShadowTemplate, coerceBoolean, coerceNumber, CustomElement, FoundationProperty } from '@tylertech/forge-core'; import { BaseComponent, IBaseComponent } from '../core/base/base-component'; import { LinearProgressAdapter } from './linear-progress-adapter'; -import { LINEAR_PROGRESS_CONSTANTS } from './linear-progress-constants'; +import { LinearProgressTheme, LINEAR_PROGRESS_CONSTANTS } from './linear-progress-constants'; import { LinearProgressFoundation } from './linear-progress-foundation'; import template from './linear-progress.html'; @@ -11,6 +11,7 @@ export interface ILinearProgressComponent extends IBaseComponent { determinate: boolean; progress: number; buffer: number; + theme: LinearProgressTheme; } declare global { @@ -33,20 +34,24 @@ declare global { * @property {boolean} determinate - Controls the determinate state. * @property {boolean} progress - Controls the progress while in a determinate state. Accepts values from `0` to `1`. * @property {boolean} buffer - Controls the buffer progress while in a determinate state. Accepts values from `0` to `1`. + * @property {string} theme - Sets the theme. * * @attribute {boolean} determinate - Controls the determinate state. * @attribute {number} progress - Controls the progress while in a determinate state. Accepts values from `0` to `1`. * @attribute {number} buffer - Controls the buffer progress while in a determinate state. Accepts values from `0` to `1`. + * @attribute {string} theme - Sets the theme. * @attribute {string} data-aria-label - Propagates an `aria-label` to the underlying progress bar element. * * @cssproperty --forge-linear-progress-height - The height of the element. * @cssproperty --forge-linear-progress-track-color - The background color of the indicator. - * @cssproperty --forge-linear-progress-indicator-color - The color of the indicator. * @cssproperty --forge-linear-progress-track-shape - The shape of the indicator. + * @cssproperty --forge-linear-progress-indicator-color - The color of the indicator. * @cssproperty --forge-linear-progress-indicator-height - The height of the indicator only. * @cssproperty --forge-linear-progress-determinate-duration - The duration of the determinate animation. * @cssproperty --forge-linear-progress-indeterminate-duration - The duration of the indeterminate animation. * @cssproperty --forge-linear-progress-determinate-easing - The easing function to use for the determinate animation. + * @cssproperty --forge-linear-progress-theme-transition-duration - The duration of the theme transition. + * @cssproperty --forge-linear-progress-theme-transition-timing - The easing function to use for the theme transition. * * @csspart progressbar - Styles the progress bar container element */ @@ -59,6 +64,7 @@ export class LinearProgressComponent extends BaseComponent implements ILinearPro LINEAR_PROGRESS_CONSTANTS.attributes.DETERMINATE, LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS, LINEAR_PROGRESS_CONSTANTS.attributes.BUFFER, + LINEAR_PROGRESS_CONSTANTS.attributes.THEME, LINEAR_PROGRESS_CONSTANTS.attributes.ARIA_LABEL ]; } @@ -82,6 +88,9 @@ export class LinearProgressComponent extends BaseComponent implements ILinearPro case LINEAR_PROGRESS_CONSTANTS.attributes.BUFFER: this.buffer = coerceNumber(newValue); break; + case LINEAR_PROGRESS_CONSTANTS.attributes.THEME: + this.theme = newValue as LinearProgressTheme; + break; case LINEAR_PROGRESS_CONSTANTS.attributes.ARIA_LABEL: this._foundation.ariaLabel = newValue; break; @@ -96,4 +105,7 @@ export class LinearProgressComponent extends BaseComponent implements ILinearPro @FoundationProperty() public declare buffer: number; + + @FoundationProperty() + public declare theme: LinearProgressTheme; } diff --git a/src/lib/slider/slider.scss b/src/lib/slider/slider.scss index 9638bda49..d28bb95a7 100644 --- a/src/lib/slider/slider.scss +++ b/src/lib/slider/slider.scss @@ -210,10 +210,9 @@ input[type=range] { } forge-focus-indicator { - /* stylelint-disable length-zero-no-unit */ @include focus-indicator.provide-theme(( shape: 50%, - outward-offset: 0px + outward-offset: 0px // Requires unit )); } diff --git a/src/lib/switch/switch.scss b/src/lib/switch/switch.scss index 0f34d750d..3c78f5d32 100644 --- a/src/lib/switch/switch.scss +++ b/src/lib/switch/switch.scss @@ -187,11 +187,9 @@ forge-state-layer { )); } -/* stylelint-disable length-zero-no-unit */ forge-focus-indicator { @include focus-indicator.provide-theme(( shape: #{token(track-shape)}, - outward-offset: 0px + outward-offset: 0px // Requires unit )); } -/* stylelint-enable length-zero-no-unit */