diff --git a/.github/workflows/pr-e2e.yml b/.github/workflows/pr-e2e.yml index 4f32058093b..12ff13bcaaa 100644 --- a/.github/workflows/pr-e2e.yml +++ b/.github/workflows/pr-e2e.yml @@ -1,5 +1,6 @@ name: E2E on: + workflow_dispatch: pull_request: branches: [main, rc] jobs: diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 78fd33f5403..77294252364 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,7 +1,7 @@ { - "packages/calcite-components": "2.5.1", - "packages/calcite-components-react": "2.5.1", + "packages/calcite-components": "2.6.0", + "packages/calcite-components-react": "2.6.0", "packages/calcite-design-tokens": "2.1.1", "packages/eslint-plugin-calcite-components": "1.1.0", - "packages/calcite-components-angular/projects/component-library": "2.5.1" + "packages/calcite-components-angular/projects/component-library": "2.6.0" } diff --git a/package-lock.json b/package-lock.json index 8379a9d451f..1f1d7ebe10d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47160,7 +47160,7 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "2.5.2-next.2", + "version": "2.6.0", "license": "SEE LICENSE.md", "dependencies": { "@floating-ui/dom": "1.6.3", @@ -47210,10 +47210,10 @@ }, "packages/calcite-components-angular/projects/component-library": { "name": "@esri/calcite-components-angular", - "version": "2.5.2-next.2", + "version": "2.6.0", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^2.5.2-next.2", + "@esri/calcite-components": "^2.6.0", "tslib": "2.6.2" }, "peerDependencies": { @@ -47223,10 +47223,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "2.5.2-next.2", + "version": "2.6.0", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "^2.5.2-next.2" + "@esri/calcite-components": "^2.6.0" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components-angular/projects/component-library/CHANGELOG.md b/packages/calcite-components-angular/projects/component-library/CHANGELOG.md index 310e4df142b..04af696bd59 100644 --- a/packages/calcite-components-angular/projects/component-library/CHANGELOG.md +++ b/packages/calcite-components-angular/projects/component-library/CHANGELOG.md @@ -3,17 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [2.5.2-next.2](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-angular@2.5.2-next.1...@esri/calcite-components-angular@2.5.2-next.2) (2024-02-21) +## [2.6.0](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-angular@2.5.1...@esri/calcite-components-angular@2.6.0) (2024-02-27) -**Note:** Version bump only for package @esri/calcite-components-angular - -## [2.5.2-next.1](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-angular@2.5.2-next.0...@esri/calcite-components-angular@2.5.2-next.1) (2024-02-21) +### Miscellaneous Chores -**Note:** Version bump only for package @esri/calcite-components-angular +- **@esri/calcite-components-angular:** Synchronize components versions -## [2.5.2-next.0](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-angular@2.5.1...@esri/calcite-components-angular@2.5.2-next.0) (2024-02-20) +### Dependencies -**Note:** Version bump only for package @esri/calcite-components-angular +- The following workspace dependencies were updated + - dependencies + - @esri/calcite-components bumped from ^2.6.0-next.3 to ^2.6.0 ## [2.5.1](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-angular@2.5.0...@esri/calcite-components-angular@2.5.1) (2024-02-16) diff --git a/packages/calcite-components-angular/projects/component-library/package.json b/packages/calcite-components-angular/projects/component-library/package.json index b8ef4f6fdb9..343e31142ac 100644 --- a/packages/calcite-components-angular/projects/component-library/package.json +++ b/packages/calcite-components-angular/projects/component-library/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components-angular", - "version": "2.5.2-next.2", + "version": "2.6.0", "sideEffects": false, "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of Angular components that wrap Esri's Calcite Components.", @@ -20,7 +20,7 @@ "@angular/core": ">=16.0.0" }, "dependencies": { - "@esri/calcite-components": "^2.5.2-next.2", + "@esri/calcite-components": "^2.6.0", "tslib": "2.6.2" }, "lerna": { diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 3e78961a9ae..d0d21ceea3d 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,17 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [2.5.2-next.2](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@2.5.2-next.1...@esri/calcite-components-react@2.5.2-next.2) (2024-02-21) +## [2.6.0](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@2.5.1...@esri/calcite-components-react@2.6.0) (2024-02-27) -**Note:** Version bump only for package @esri/calcite-components-react - -## [2.5.2-next.1](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@2.5.2-next.0...@esri/calcite-components-react@2.5.2-next.1) (2024-02-21) +### Miscellaneous Chores -**Note:** Version bump only for package @esri/calcite-components-react +- **@esri/calcite-components-react:** Synchronize components versions -## [2.5.2-next.0](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@2.5.1...@esri/calcite-components-react@2.5.2-next.0) (2024-02-20) +### Dependencies -**Note:** Version bump only for package @esri/calcite-components-react +- The following workspace dependencies were updated + - dependencies + - @esri/calcite-components bumped from ^2.6.0-next.3 to ^2.6.0 ## [2.5.1](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@2.5.0...@esri/calcite-components-react@2.5.1) (2024-02-16) diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index 735c32443b8..80e8542c928 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,7 +1,7 @@ { "name": "@esri/calcite-components-react", "sideEffects": false, - "version": "2.5.2-next.2", + "version": "2.6.0", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "A set of React components that wrap calcite components", "license": "SEE LICENSE.md", @@ -23,7 +23,7 @@ "dist/" ], "dependencies": { - "@esri/calcite-components": "^2.5.2-next.2" + "@esri/calcite-components": "^2.6.0" }, "peerDependencies": { "react": ">=16.7", diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 9917466b3a6..dee0d288e53 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,23 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [2.5.2-next.2](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@2.5.2-next.1...@esri/calcite-components@2.5.2-next.2) (2024-02-21) +## [2.6.0](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@2.5.1...@esri/calcite-components@2.6.0) (2024-02-27) -### Bug Fixes - -- only show validation message when status='invalid' ([#8649](https://github.com/Esri/calcite-design-system/issues/8649)) ([7eac8d7](https://github.com/Esri/calcite-design-system/commit/7eac8d7c1e24ab89a3bff67ce9b4f38555023a47)) - -## [2.5.2-next.1](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@2.5.2-next.0...@esri/calcite-components@2.5.2-next.1) (2024-02-21) - -### Bug Fixes - -- **combobox:** long text truncates on single and single-persist modes ([#8731](https://github.com/Esri/calcite-design-system/issues/8731)) ([8fc42b1](https://github.com/Esri/calcite-design-system/commit/8fc42b164ceacad97ac9153673280ae63638b03c)), closes [#7020](https://github.com/Esri/calcite-design-system/issues/7020) +### Features -## [2.5.2-next.0](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@2.5.1...@esri/calcite-components@2.5.2-next.0) (2024-02-20) +- **card-group:** Add Card Group component ([#8749](https://github.com/Esri/calcite-design-system/issues/8749)) ([b012324](https://github.com/Esri/calcite-design-system/commit/b012324fdbf125662f1fb0ca1defc348b08a340d)) +- **tile-group:** Add Tile Group component ([#8806](https://github.com/Esri/calcite-design-system/issues/8806)) ([4f65bdd](https://github.com/Esri/calcite-design-system/commit/4f65bddb521af32047f9f5e6659c1b644291a83d)) ### Bug Fixes -- **table:** Ensure border are correctly applied with complex rowSpan ([#8779](https://github.com/Esri/calcite-design-system/issues/8779)) ([69f05d4](https://github.com/Esri/calcite-design-system/commit/69f05d4f29938b0a3ce2ab3bb806d3d831ebf8d0)), closes [#8778](https://github.com/Esri/calcite-design-system/issues/8778) +- **combobox:** Long text truncates on single and single-persist modes ([#8731](https://github.com/Esri/calcite-design-system/issues/8731)) ([8fc42b1](https://github.com/Esri/calcite-design-system/commit/8fc42b164ceacad97ac9153673280ae63638b03c)) +- **navigation-logo:** No longer changes icon color when `href` is parsed ([#8830](https://github.com/Esri/calcite-design-system/issues/8830)) ([16d456f](https://github.com/Esri/calcite-design-system/commit/16d456fa113da09daf16d73e819ddf2910cb73d5)) +- Only show validation message when status='invalid' ([#8649](https://github.com/Esri/calcite-design-system/issues/8649)) ([7eac8d7](https://github.com/Esri/calcite-design-system/commit/7eac8d7c1e24ab89a3bff67ce9b4f38555023a47)) +- **preset:** Calcite—color-brand to calcite-color-brand ([#8809](https://github.com/Esri/calcite-design-system/issues/8809)) ([ee2cf4e](https://github.com/Esri/calcite-design-system/commit/ee2cf4e62229753224fd3e54ed7caebe9b097040)) +- **table:** Ensure border are correctly applied with complex rowSpan ([#8779](https://github.com/Esri/calcite-design-system/issues/8779)) ([69f05d4](https://github.com/Esri/calcite-design-system/commit/69f05d4f29938b0a3ce2ab3bb806d3d831ebf8d0)) ## [2.5.1](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@2.5.0...@esri/calcite-components@2.5.1) (2024-02-16) diff --git a/packages/calcite-components/calcite-preset.ts b/packages/calcite-components/calcite-preset.ts index e89a48531ff..d72c1eabe57 100644 --- a/packages/calcite-components/calcite-preset.ts +++ b/packages/calcite-components/calcite-preset.ts @@ -259,16 +259,16 @@ export default { }, ".focus-normal": { outline: - "2px solid var(--calcite-ui-focus-color, var(--calcite-color-brand-hover, var(--calcite--color-brand)))", + "2px solid var(--calcite-ui-focus-color, var(--calcite-color-brand-hover, var(--calcite-color-brand)))", }, ".focus-outset": { outline: - "2px solid var(--calcite-ui-focus-color, var(--calcite-color-brand-hover, var(--calcite--color-brand)))", + "2px solid var(--calcite-ui-focus-color, var(--calcite-color-brand-hover, var(--calcite-color-brand)))", "outline-offset": invert("2px", "--calcite-offset-invert-focus"), }, ".focus-inset": { outline: - "2px solid var(--calcite-ui-focus-color, var(--calcite-color-brand-hover, var(--calcite--color-brand)))", + "2px solid var(--calcite-ui-focus-color, var(--calcite-color-brand-hover, var(--calcite-color-brand)))", "outline-offset": invert("-2px", "--calcite-offset-invert-focus"), }, ".focus-outset-danger": { diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 816243ef2ad..55a243bfb0d 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "2.5.2-next.2", + "version": "2.6.0", "homepage": "https://developers.arcgis.com/calcite-design-system/", "description": "Web Components for Esri's Calcite Design System.", "main": "dist/index.cjs.js", diff --git a/packages/calcite-components/readme.md b/packages/calcite-components/readme.md index 7a0342aaeb9..5ae5b8295f2 100644 --- a/packages/calcite-components/readme.md +++ b/packages/calcite-components/readme.md @@ -17,12 +17,12 @@ The most common approach for loading Calcite Components is to use the version ho ```html ``` diff --git a/packages/calcite-components/src/assets/styles/_floating-ui.scss b/packages/calcite-components/src/assets/styles/_floating-ui.scss index ac9397f3699..31f728a4758 100644 --- a/packages/calcite-components/src/assets/styles/_floating-ui.scss +++ b/packages/calcite-components/src/assets/styles/_floating-ui.scss @@ -10,12 +10,12 @@ $floating-ui-transform-right: translateX(-5px); transition-property: transform, visibility, opacity; opacity: 0; border-style: solid; - border-width: var(#{$borderWidth}); - box-shadow: var(#{$boxShadow}); - border-radius: var(#{$borderRadius}); + border-width: #{$borderWidth}; + box-shadow: #{$boxShadow}; + border-radius: #{$borderRadius}; z-index: var(--calcite-z-index); - background-color: var(#{$backgroundColor}); - border-color: var(#{$borderColor}); + background-color: #{$backgroundColor}; + border-color: #{$borderColor}; } } @@ -26,11 +26,11 @@ $floating-ui-transform-right: translateX(-5px); @mixin floatingUIElemAnim( $selector, - $boxShadow: "--calcite-shadow-md", - $borderRadius: "--calcite-corner-radius-0", - $borderWidth: "--calcite-border-width-none", - $borderColor: "--calcite-color-border-3", - $backgroundColor: "--calcite-color-foreground-1" + $boxShadow: "var(--calcite-shadow-md)", + $borderRadius: "var(--calcite-corner-radius-0)", + $borderWidth: "var(--calcite-border-width-none)", + $borderColor: "var(--calcite-color-border-3)", + $backgroundColor: "var(--calcite-color-foreground-1)" ) { #{$selector} { @include floatingUIAnim($boxShadow, $borderRadius, $borderWidth, $borderColor, $backgroundColor); @@ -81,10 +81,10 @@ $floating-ui-transform-right: translateX(-5px); } } -@mixin floatingUIContainer($zIndex: "--calcite-z-index-dropdown") { +@mixin floatingUIContainer($zIndex) { display: block; position: absolute; - z-index: var(#{$zIndex}); + z-index: #{$zIndex}; } @mixin floatingUIWrapper { @@ -96,12 +96,12 @@ $floating-ui-transform-right: translateX(-5px); } @mixin floatingUIHost( - $zIndex: "--calcite-z-index-dropdown", - $boxShadow: "--calcite-shadow-md", - $borderRadius: "--calcite-corner-radius-0", - $borderWidth: "--calcite-border-width-none", - $borderColor: "--calcite-color-border-3", - $backgroundColor: "--calcite-color-foreground-1" + $zIndex: "var(--calcite-z-index-dropdown)", + $boxShadow: "var(--calcite-shadow-md)", + $borderRadius: "var(--calcite-corner-radius-0)", + $borderWidth: "var(--calcite-border-width-none)", + $borderColor: "var(--calcite-color-border-3)", + $backgroundColor: "var(--calcite-color-foreground-1)" ) { :host { @include floatingUIContainer($zIndex); @@ -114,15 +114,15 @@ $floating-ui-transform-right: translateX(-5px); @include floatingUIArrow($backgroundColor, $borderColor); } -@mixin floatingUIArrow($backgroundColor: "--calcite-color-foreground-1", $borderColor: "--calcite-color-border-3") { +@mixin floatingUIArrow($backgroundColor, $borderColor) { .calcite-floating-ui-arrow { z-index: var(--calcite-z-index-default); position: absolute; pointer-events: none; - fill: var(#{$backgroundColor}); + fill: #{$backgroundColor}; } .calcite-floating-ui-arrow__stroke { - stroke: var(#{$borderColor}); + stroke: #{$borderColor}; } } diff --git a/packages/calcite-components/src/assets/styles/_header.scss b/packages/calcite-components/src/assets/styles/_header.scss index 6b1befbdcc1..6639e20f323 100644 --- a/packages/calcite-components/src/assets/styles/_header.scss +++ b/packages/calcite-components/src/assets/styles/_header.scss @@ -1,17 +1,26 @@ -/* Shared styles for header elements */ -.header { - @apply text-color-2 - fill-color-2 - m-0 - flex - content-between - items-center; -} +@mixin header( + $textColor: "--calcite-color-text-2", + $fontWeight: "--calcite-font-weight-medium", + $spacing: "--calcite-spacing-xxs" +) { + /* Shared styles for header elements */ + .header { + color: var(#{$textColor}, var(--calcite-color-text-2)); + fill: var(#{$textColor}, var(--calcite-color-text-2)); + margin: 0px; + display: flex; + align-content: space-between; + align-items: center; + } -.heading { - @apply m-0 p-0 font-medium; -} + .heading { + margin: 0px; + padding: 0px; + font-weight: var(#{$fontWeight}, var(--calcite-font-weight-medium)); + } -.header .heading { - @apply flex-auto p-2; + .header .heading { + flex: 1 1 auto; + padding: var(#{$spacing}, var(--calcite-spacing-xxs)); + } } diff --git a/packages/calcite-components/src/assets/styles/_shadow.scss b/packages/calcite-components/src/assets/styles/_shadow.scss deleted file mode 100644 index 1945754888b..00000000000 --- a/packages/calcite-components/src/assets/styles/_shadow.scss +++ /dev/null @@ -1,14 +0,0 @@ -%shadow-vars { - /* - * Component shadow variables - */ - --calcite-shadow-0: 0 4px 8px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.04); - --calcite-shadow-1: 0 4px 8px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.04); - --calcite-shadow-1-hover: 0 4px 16px 0 rgba(0, 0, 0, 0.08), 0 2px 8px 0 rgba(0, 0, 0, 0.04); - --calcite-shadow-1-press: 0 1px 6px -1px rgba(0, 0, 0, 0.16), 0 1px 2px -1px rgba(0, 0, 0, 0.08); - --calcite-shadow-2: 0 6px 20px -4px rgba(0, 0, 0, 0.1), 0 4px 12px -2px rgba(0, 0, 0, 0.08); - --calcite-shadow-2-hover: 0 12px 32px -2px rgba(0, 0, 0, 0.1), 0 4px 20px 0 rgba(0, 0, 0, 0.08); - --calcite-shadow-2-press: 0 2px 12px -4px rgba(0, 0, 0, 0.2), 0 2px 4px -2px rgba(0, 0, 0, 0.16); - --calcite-shadow-border-bottom: 0 1px 0 var(--calcite-color-border-3); - --calcite-shadow-border-active: 0 0 0 1px var(--calcite-color-brand-press); -} diff --git a/packages/calcite-components/src/assets/styles/_sortable.scss b/packages/calcite-components/src/assets/styles/_sortable.scss index ae119bc7ea5..43a497bf17d 100644 --- a/packages/calcite-components/src/assets/styles/_sortable.scss +++ b/packages/calcite-components/src/assets/styles/_sortable.scss @@ -9,20 +9,17 @@ .calcite-sortable--ghost::before { content: ""; - @apply box-border - border - border-dashed - border-color-brand - bg-foreground-2 - absolute - top-0 - left-0 - bottom-0 - right-0 - z-default; + box-sizing: border-box; + border-width: var(--calcite-border-width-sm); + border-style: dashed; + border-color: var(--calcite-color-brand); + background-color: var(--calcite-color-foreground-2); + position: absolute; + inset: 0px; + z-index: var(--calcite-z-index); } .calcite-sortable--drag { - @apply shadow-2; + box-shadow: var(--calcite-shadow-2); } } diff --git a/packages/calcite-components/src/assets/styles/_spacing.scss b/packages/calcite-components/src/assets/styles/_spacing.scss deleted file mode 100644 index 2a8682e1ffe..00000000000 --- a/packages/calcite-components/src/assets/styles/_spacing.scss +++ /dev/null @@ -1,26 +0,0 @@ -%component-spacing { - /* Component spacing variables */ - - --calcite-icon-size: 1rem; - - --calcite-spacing-eighth: theme("spacing[0.5]"); - - --calcite-spacing-quarter: theme("spacing.1"); - - --calcite-spacing-half: theme("spacing.2"); - - --calcite-spacing-three-quarters: theme("spacing.3"); - - --calcite-spacing: theme("spacing.4"); - - --calcite-spacing-plus-quarter: theme("spacing.5"); - - --calcite-spacing-plus-half: theme("spacing.6"); - - --calcite-spacing-double: theme("spacing.8"); - - --calcite-menu-min-width: theme("spacing.40"); - - --calcite-header-min-height: theme("spacing.12"); - --calcite-footer-min-height: theme("spacing.12"); -} diff --git a/packages/calcite-components/src/assets/styles/_x-button.scss b/packages/calcite-components/src/assets/styles/_x-button.scss new file mode 100644 index 00000000000..d17ab1bf819 --- /dev/null +++ b/packages/calcite-components/src/assets/styles/_x-button.scss @@ -0,0 +1,61 @@ +@mixin xButton( + $textColor: "--calcite-color-text-3", + $backgroundColor: "--calcite-color-foreground-2", + $hoverTextColor: "--calcite-color-text-3", + $hoverBackgroundColor: "--calcite-color-foreground-3", + $activeBorderColor: "--calcite-color-brand", + $borderWidth: "--calcite-border-width-md" +) { + .x-button { + appearance: none; + align-content: center; + cursor: pointer; + display: flex; + outline-color: transparent; + align-items: center; + justify-content: center; + margin: 0px; + align-self: center; + color: var(#{$textColor}, var(--calcite-color-text-3)); + transition: + color, + background-color, + border-color var(--calcite-animation-timing) ease-in-out 0s, + outline 0s, + outline-offset 0s; + border-width: var(#{$borderWidth}, var(--calcite-border-width-md)); + border-radius: 50%; + border-color: transparent; + background-color: var(#{$backgroundColor}, var(--calcite-color-foreground-2)); + + &:active, + &:hover { + color: var(#{$hoverTextColor}); + background-color: var(#{$hoverBackgroundColor}, var(--calcite-color-foreground-3)); + } + + &:active { + border-style: solid; + border-color: var(#{$activeBorderColor}, var(--calcite-color-brand)); + } + + & calcite-icon { + color: inherit; + } + } + + :host([scale="s"]) .x-button { + inline-size: var(--calcite-spacing-lg); + block-size: var(--calcite-spacing-lg); + } + + :host([scale="m"]) .x-button { + inline-size: var(--calcite-size-xxl); + block-size: var(--calcite-size-xxl); + } + + :host([scale="l"]) .x-button { + inline-size: var(--calcite-size-xxxl); + block-size: var(--calcite-size-xxxl); + } +} diff --git a/packages/calcite-components/src/assets/styles/global.scss b/packages/calcite-components/src/assets/styles/global.scss index c893bc34168..c81d719461c 100644 --- a/packages/calcite-components/src/assets/styles/global.scss +++ b/packages/calcite-components/src/assets/styles/global.scss @@ -1,4 +1,4 @@ -@import "~@esri/calcite-base/dist/_index"; +@import "~@esri/calcite-base/dist/index"; @import "~@esri/calcite-design-tokens/dist/scss/index"; @import "~@esri/calcite-design-tokens/dist/scss/core"; @@ -19,12 +19,10 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; --calcite-border-radius: var(--calcite-border-radius-round, 4px); - --calcite-border-radius-base: 0; --calcite-offset-invert-focus: 0; // should be 0 or 1 - --calcite-panel-width-multiplier: 1; --calcite-ui-icon-color: currentColor; - @apply font-sans; + font-family: var(--calcite-sans-family); } .calcite-mode-auto { diff --git a/packages/calcite-components/src/assets/styles/includes.scss b/packages/calcite-components/src/assets/styles/includes.scss index 222c67ae839..1a80329ef94 100644 --- a/packages/calcite-components/src/assets/styles/includes.scss +++ b/packages/calcite-components/src/assets/styles/includes.scss @@ -1,15 +1,16 @@ @import "~@esri/calcite-colors/dist/colors"; -@import "~@esri/calcite-base/dist/_index"; +@import "~@esri/calcite-base/dist/index"; @import "~@esri/calcite-design-tokens/dist/scss/index"; @import "~@esri/calcite-design-tokens/dist/scss/core"; /* mixins & extensions */ @import "animation"; @import "focus"; +@import "header"; @import "host"; -@import "spacing"; @import "floating-ui"; @import "sortable"; +@import "x-button"; @mixin slotted($selector, $tag, $scope: "") { #{$scope} slot[name="#{$selector}"]::slotted(#{$tag}), @@ -67,7 +68,7 @@ ::slotted([calcite-hydrated][disabled]), [calcite-hydrated][disabled] { /* prevent opacity stacking */ - @apply opacity-100; + opacity: var(--calcite-opacity-full); } } @@ -78,14 +79,16 @@ // used for host-specific styling when the `disabled` mixin cannot be applied on the host (e.g., `display: contents`) %disabled-host-only { - @apply opacity-disabled cursor-default select-none; + opacity: var(--calcite-opacity-disabled); + cursor: default; + user-select: none; @extend %non-interactive-host-contents; } %non-interactive-host-contents { *, ::slotted(*) { - @apply pointer-events-none; + pointer-events: none; } } @@ -99,56 +102,10 @@ @mixin base-component() { :host([hidden]) { - @apply hidden; + display: none; } [hidden] { - @apply hidden; - } -} - -@mixin xButton() { - :host([scale="s"]) { - .x-button { - inline-size: theme("spacing.4"); - block-size: theme("spacing.4"); - } - } - - :host([scale="m"]) { - .x-button { - inline-size: theme("spacing.6"); - block-size: theme("spacing.6"); - } - } - - :host([scale="l"]) { - .x-button { - inline-size: theme("spacing.8"); - block-size: theme("spacing.8"); - } - } - - .x-button { - @apply appearance-none bg-transparent border-2 content-center cursor-pointer flex focus-base items-center justify-center m-0 self-center text-color-3 transition-default; - - border-radius: 50%; - border-color: transparent; - background-color: var(--calcite-color-foreground-2); - - &:active, - &:hover { - @apply text-color-1; - background-color: var(--calcite-color-foreground-3); - } - - &:active { - @apply border-solid; - border-color: theme("borderColor.color-brand"); - } - - & calcite-icon { - color: inherit; - } + display: none; } } diff --git a/packages/calcite-components/src/components.d.ts b/packages/calcite-components/src/components.d.ts index dbdc6085aa2..dd09af52c61 100644 --- a/packages/calcite-components/src/components.d.ts +++ b/packages/calcite-components/src/components.d.ts @@ -77,6 +77,7 @@ import { StepperItemChangeEventDetail, StepperItemEventDetail, StepperItemKeyEve import { StepperMessages } from "./components/stepper/assets/stepper/t9n"; import { StepperItemMessages } from "./components/stepper-item/assets/stepper-item/t9n"; import { TabID, TabLayout, TabPosition } from "./components/tabs/interfaces"; +import { TabNavMessages } from "./components/tab-nav/assets/tab-nav/t9n"; import { TabChangeEventDetail, TabCloseEventDetail } from "./components/tab/interfaces"; import { TabTitleMessages } from "./components/tab-title/assets/tab-title/t9n"; import { RowType, TableInteractionMode, TableLayout, TableRowFocusEvent } from "./components/table/interfaces"; @@ -84,7 +85,6 @@ import { TableMessages } from "./components/table/assets/table/t9n"; import { TableCellMessages } from "./components/table-cell/assets/table-cell/t9n"; import { TableHeaderMessages } from "./components/table-header/assets/table-header/t9n"; import { TextAreaMessages } from "./components/text-area/assets/text-area/t9n"; -import { TileGroupLayout } from "./components/tile-group/interfaces"; import { TileSelectType } from "./components/tile-select/interfaces"; import { TileSelectGroupLayout } from "./components/tile-select-group/interfaces"; import { TipMessages } from "./components/tip/assets/tip/t9n"; @@ -164,6 +164,7 @@ export { StepperItemChangeEventDetail, StepperItemEventDetail, StepperItemKeyEve export { StepperMessages } from "./components/stepper/assets/stepper/t9n"; export { StepperItemMessages } from "./components/stepper-item/assets/stepper-item/t9n"; export { TabID, TabLayout, TabPosition } from "./components/tabs/interfaces"; +export { TabNavMessages } from "./components/tab-nav/assets/tab-nav/t9n"; export { TabChangeEventDetail, TabCloseEventDetail } from "./components/tab/interfaces"; export { TabTitleMessages } from "./components/tab-title/assets/tab-title/t9n"; export { RowType, TableInteractionMode, TableLayout, TableRowFocusEvent } from "./components/table/interfaces"; @@ -171,7 +172,6 @@ export { TableMessages } from "./components/table/assets/table/t9n"; export { TableCellMessages } from "./components/table-cell/assets/table-cell/t9n"; export { TableHeaderMessages } from "./components/table-header/assets/table-header/t9n"; export { TextAreaMessages } from "./components/text-area/assets/text-area/t9n"; -export { TileGroupLayout } from "./components/tile-group/interfaces"; export { TileSelectType } from "./components/tile-select/interfaces"; export { TileSelectGroupLayout } from "./components/tile-select-group/interfaces"; export { TipMessages } from "./components/tip/assets/tip/t9n"; @@ -754,6 +754,14 @@ export namespace Components { "width": Width; } interface CalciteCard { + /** + * When `true`, interaction is prevented and the component is displayed with lower opacity. + */ + "disabled": boolean; + /** + * Accessible name for the component. + */ + "label": string; /** * When `true`, a busy indicator is displayed. */ @@ -768,17 +776,56 @@ export namespace Components { "messages": CardMessages; /** * When `true`, the component is selectable. + * @deprecated use `selectionMode` property on a parent `calcite-card-group` instead. */ "selectable": boolean; /** * When `true`, the component is selected. */ "selected": boolean; + /** + * This internal property, managed by a containing `calcite-card-group`, is conditionally set based on the `selectionMode` of the parent + */ + "selectionMode": Extract<"multiple" | "single" | "single-persist" | "none", SelectionMode>; + /** + * Sets focus on the component. + */ + "setFocus": () => Promise; /** * Sets the placement of the thumbnail defined in the `thumbnail` slot. */ "thumbnailPosition": LogicalFlowPosition; } + interface CalciteCardGroup { + /** + * When `true`, interaction is prevented and the component is displayed with lower opacity. + */ + "disabled": boolean; + /** + * Accessible name for the component. + */ + "label": string; + /** + * Specifies the size of the component. Child `calcite-card`s inherit the component's value. + */ + "scale": Scale; + /** + * Specifies the component's selected items. + * @readonly + */ + "selectedItems": HTMLCalciteCardElement[]; + /** + * Specifies the selection mode of the component. + */ + "selectionMode": Extract< + "multiple" | "single" | "single-persist" | "none", + SelectionMode + >; + /** + * Sets focus on the component's first focusable element. + */ + "setFocus": () => Promise; + } interface CalciteCheckbox { /** * When `true`, the component is checked. @@ -4546,6 +4593,14 @@ export namespace Components { "indicatorOffset": number; "indicatorWidth": number; "layout": TabLayout; + /** + * Use this property to override individual strings used by the component. + */ + "messageOverrides": Partial; + /** + * Made into a prop for testing purposes only. + */ + "messages": TabNavMessages; /** * Specifies the position of `calcite-tab-nav` and `calcite-tab-title` components in relation to, and is inherited from the parent `calcite-tabs`, defaults to `top`. */ @@ -4960,6 +5015,7 @@ export namespace Components { "disabled": boolean; /** * The component's embed mode. When `true`, renders without a border and padding for use by other components. + * @deprecated No longer necessary. */ "embed": boolean; /** @@ -4995,7 +5051,7 @@ export namespace Components { /** * Defines the layout of the component. Use `"horizontal"` for rows, and `"vertical"` for a single column. */ - "layout": TileGroupLayout; + "layout": Exclude; /** * Specifies the size of the component. */ @@ -5421,6 +5477,10 @@ export interface CalciteCardCustomEvent extends CustomEvent { detail: T; target: HTMLCalciteCardElement; } +export interface CalciteCardGroupCustomEvent extends CustomEvent { + detail: T; + target: HTMLCalciteCardGroupElement; +} export interface CalciteCheckboxCustomEvent extends CustomEvent { detail: T; target: HTMLCalciteCheckboxElement; @@ -5866,6 +5926,7 @@ declare global { }; interface HTMLCalciteCardElementEventMap { "calciteCardSelect": void; + "calciteInternalCardKeyEvent": KeyboardEvent; } interface HTMLCalciteCardElement extends Components.CalciteCard, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLCalciteCardElement, ev: CalciteCardCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -5881,6 +5942,23 @@ declare global { prototype: HTMLCalciteCardElement; new (): HTMLCalciteCardElement; }; + interface HTMLCalciteCardGroupElementEventMap { + "calciteCardGroupSelect": void; + } + interface HTMLCalciteCardGroupElement extends Components.CalciteCardGroup, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLCalciteCardGroupElement, ev: CalciteCardGroupCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLCalciteCardGroupElement, ev: CalciteCardGroupCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLCalciteCardGroupElement: { + prototype: HTMLCalciteCardGroupElement; + new (): HTMLCalciteCardGroupElement; + }; interface HTMLCalciteCheckboxElementEventMap { "calciteInternalCheckboxBlur": boolean; "calciteCheckboxChange": void; @@ -7361,6 +7439,7 @@ declare global { "calcite-block-section": HTMLCalciteBlockSectionElement; "calcite-button": HTMLCalciteButtonElement; "calcite-card": HTMLCalciteCardElement; + "calcite-card-group": HTMLCalciteCardGroupElement; "calcite-checkbox": HTMLCalciteCheckboxElement; "calcite-chip": HTMLCalciteChipElement; "calcite-chip-group": HTMLCalciteChipGroupElement; @@ -8056,6 +8135,14 @@ declare namespace LocalJSX { "width"?: Width; } interface CalciteCard { + /** + * When `true`, interaction is prevented and the component is displayed with lower opacity. + */ + "disabled"?: boolean; + /** + * Accessible name for the component. + */ + "label"?: string; /** * When `true`, a busy indicator is displayed. */ @@ -8069,22 +8156,58 @@ declare namespace LocalJSX { */ "messages"?: CardMessages; /** - * Fires when `selectable` is `true` and the component is selected. + * Fires when the deprecated `selectable` is true, or `selectionMode` set on parent `calcite-card-group` is not `none` and the component is selected. */ "onCalciteCardSelect"?: (event: CalciteCardCustomEvent) => void; + "onCalciteInternalCardKeyEvent"?: (event: CalciteCardCustomEvent) => void; /** * When `true`, the component is selectable. + * @deprecated use `selectionMode` property on a parent `calcite-card-group` instead. */ "selectable"?: boolean; /** * When `true`, the component is selected. */ "selected"?: boolean; + /** + * This internal property, managed by a containing `calcite-card-group`, is conditionally set based on the `selectionMode` of the parent + */ + "selectionMode"?: Extract<"multiple" | "single" | "single-persist" | "none", SelectionMode>; /** * Sets the placement of the thumbnail defined in the `thumbnail` slot. */ "thumbnailPosition"?: LogicalFlowPosition; } + interface CalciteCardGroup { + /** + * When `true`, interaction is prevented and the component is displayed with lower opacity. + */ + "disabled"?: boolean; + /** + * Accessible name for the component. + */ + "label": string; + /** + * Emits when the component's selection changes and the `selectionMode` is not `none`. + */ + "onCalciteCardGroupSelect"?: (event: CalciteCardGroupCustomEvent) => void; + /** + * Specifies the size of the component. Child `calcite-card`s inherit the component's value. + */ + "scale"?: Scale; + /** + * Specifies the component's selected items. + * @readonly + */ + "selectedItems"?: HTMLCalciteCardElement[]; + /** + * Specifies the selection mode of the component. + */ + "selectionMode"?: Extract< + "multiple" | "single" | "single-persist" | "none", + SelectionMode + >; + } interface CalciteCheckbox { /** * When `true`, the component is checked. @@ -12052,6 +12175,14 @@ declare namespace LocalJSX { "indicatorOffset"?: number; "indicatorWidth"?: number; "layout"?: TabLayout; + /** + * Use this property to override individual strings used by the component. + */ + "messageOverrides"?: Partial; + /** + * Made into a prop for testing purposes only. + */ + "messages"?: TabNavMessages; "onCalciteInternalTabChange"?: (event: CalciteTabNavCustomEvent) => void; /** * Emits when the selected `calcite-tab` changes. @@ -12486,6 +12617,7 @@ declare namespace LocalJSX { "disabled"?: boolean; /** * The component's embed mode. When `true`, renders without a border and padding for use by other components. + * @deprecated No longer necessary. */ "embed"?: boolean; /** @@ -12521,7 +12653,7 @@ declare namespace LocalJSX { /** * Defines the layout of the component. Use `"horizontal"` for rows, and `"vertical"` for a single column. */ - "layout"?: TileGroupLayout; + "layout"?: Exclude; /** * Specifies the size of the component. */ @@ -12947,6 +13079,7 @@ declare namespace LocalJSX { "calcite-block-section": CalciteBlockSection; "calcite-button": CalciteButton; "calcite-card": CalciteCard; + "calcite-card-group": CalciteCardGroup; "calcite-checkbox": CalciteCheckbox; "calcite-chip": CalciteChip; "calcite-chip-group": CalciteChipGroup; @@ -13060,6 +13193,7 @@ declare module "@stencil/core" { "calcite-block-section": LocalJSX.CalciteBlockSection & JSXBase.HTMLAttributes; "calcite-button": LocalJSX.CalciteButton & JSXBase.HTMLAttributes; "calcite-card": LocalJSX.CalciteCard & JSXBase.HTMLAttributes; + "calcite-card-group": LocalJSX.CalciteCardGroup & JSXBase.HTMLAttributes; "calcite-checkbox": LocalJSX.CalciteCheckbox & JSXBase.HTMLAttributes; "calcite-chip": LocalJSX.CalciteChip & JSXBase.HTMLAttributes; "calcite-chip-group": LocalJSX.CalciteChipGroup & JSXBase.HTMLAttributes; diff --git a/packages/calcite-components/src/components/action-bar/action-bar.scss b/packages/calcite-components/src/components/action-bar/action-bar.scss index 9e8cec562c4..e47a8cabb3f 100755 --- a/packages/calcite-components/src/components/action-bar/action-bar.scss +++ b/packages/calcite-components/src/components/action-bar/action-bar.scss @@ -3,7 +3,17 @@ * * These properties can be overridden using the component's tag as selector. * + * @prop --calcite-action-bar-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-action-bar-action-indicator-color: defines the indicator color of an action sub-component inside the component. + * @prop --calcite-action-bar-action-text-color: defines the text color of an action sub-component inside the component. + * @prop --calcite-action-bar-action-background-color-hover: defines the background color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-bar-action-indicator-color-hover: defines the indicator color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-bar-action-text-color-hover: defines the text color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-bar-action-background-color-active: defines the background color of an action sub-component when active inside the component. + * @prop --calcite-action-bar-action-indicator-color-active: defines the indicator color of an action sub-component when active inside the component. + * @prop --calcite-action-bar-action-text-color-active: defines the text color of an action sub-component when active inside the component. * @prop --calcite-action-bar-expanded-max-width: When `layout` is `"vertical"`, specifies the expanded max width of the component. + * */ :host { @@ -62,4 +72,23 @@ @apply justify-end; } +calcite-action { + --calcite-action-indicator-color: var(--calcite-action-bar-action-indicator-color); + --calcite-action-background-color: var(--calcite-action-bar-action-background-color); + --calcite-action-text-color: var(--calcite-action-bar-action-text-color); + + &:hover, + &:focus { + --calcite-action-indicator-color: var(--calcite-action-bar-action-indicator-color-hover); + --calcite-action-background-color: var(--calcite-action-bar-action-background-color-hover); + --calcite-action-text-color: var(--calcite-action-bar-action-text-color-hover); + } + + &:active { + --calcite-action-indicator-color: var(--calcite-action-bar-action-indicator-color-active); + --calcite-action-background-color: var(--calcite-action-bar-action-background-color-active); + --calcite-action-text-color: var(--calcite-action-bar-action-text-color-active); + } +} + @include base-component(); diff --git a/packages/calcite-components/src/components/action-group/action-group.scss b/packages/calcite-components/src/components/action-group/action-group.scss index 84457f2f916..3b39776de97 100755 --- a/packages/calcite-components/src/components/action-group/action-group.scss +++ b/packages/calcite-components/src/components/action-group/action-group.scss @@ -3,9 +3,24 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-action-group-columns: Sets number of grid-template-columns when the `layout` property is `"grid"`. - * @prop --calcite-action-group-gap: Sets the gap (gutters) between rows and columns when the `layout` property is `"grid"`. - * @prop --calcite-action-group-padding: Sets the padding when the `layout` property is `"grid"`. + * @prop --calcite-action-group-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-action-group-action-indicator-color: defines the indicator color of an action sub-component inside the component. + * @prop --calcite-action-group-action-background-color-hover: defines the background color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-group-action-indicator-color-hover: defines the indicator color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-group-action-text-color-hover: defines the text color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-group-action-background-color-active: defines the background color of an action sub-component when active inside the component. + * @prop --calcite-action-group-action-indicator-color-active: defines the indicator color of an action sub-component when active inside the component. + * @prop --calcite-action-group-action-text-color-active: defines the text color of an action sub-component when active inside the component. + * @prop --calcite-action-group-action-menu-border-color: The border color of the sub-component. + * @prop --calcite-action-group-action-menu-text-color: The text color of the sub-component. + * @prop --calcite-action-group-action-text-color: defines the text color of an action sub-component inside the component. + * @prop --calcite-action-group-background-color: defines the background color of the component. + * @prop --calcite-action-group-columns: *deprecated* Sets number of grid-template-columns when the `layout` property is `"grid"`. + * @prop --calcite-action-group-gap: *deprecated* Sets the gap (gutters) between rows and columns when the `layout` property is `"grid"`. + * @prop --calcite-action-group-padding: *deprecated* Sets the padding when the `layout` property is `"grid"`. + * @prop --calcite-internal-action-group-columns: Sets number of grid-template-columns when the `layout` property is `"grid"`. + * @prop --calcite-internal-action-group-gap, var(te-internal-action-group-gap: Sets the gap (gutters) between rows and columns when the `layout` property is `"grid"`). + * */ :host { @@ -15,9 +30,8 @@ flex-col p-0; - --calcite-action-group-columns: 3; - --calcite-action-group-gap: theme("gap.px"); - --calcite-action-group-padding: theme("padding.px"); + --calcite-internal-action-group-columns: 3; + --calcite-internal-action-group-gap: theme("gap.px"); } .container { @@ -25,22 +39,22 @@ } :host([columns="1"]) { - --calcite-action-group-columns: 1; + --calcite-internal-action-group-columns: 1; } :host([columns="2"]) { - --calcite-action-group-columns: 2; + --calcite-internal-action-group-columns: 2; } :host([columns="3"]) { - --calcite-action-group-columns: 3; + --calcite-internal-action-group-columns: 3; } :host([columns="4"]) { - --calcite-action-group-columns: 4; + --calcite-internal-action-group-columns: 4; } :host([columns="5"]) { - --calcite-action-group-columns: 5; + --calcite-internal-action-group-columns: 5; } :host([columns="6"]) { - --calcite-action-group-columns: 6; + --calcite-internal-action-group-columns: 6; } :host(:first-child) { @@ -57,12 +71,39 @@ } :host([layout="grid"]) .container { - @apply bg-background - grid + @apply grid place-content-stretch; - gap: var(--calcite-action-group-gap); - padding: var(--calcite-action-group-gap); - grid-template-columns: repeat(var(--calcite-action-group-columns), auto); + gap: var(--calcite-action-group-gap, var(--calcite-internal-action-group-gap)); + padding: var(--calcite-action-group-gap, var(--calcite-internal-action-group-gap)); + grid-template-columns: repeat( + var(--calcite-action-group-columns, var(--calcite-internal-action-group-columns)), + auto + ); + background-color: var(--calcite-action-group-background-color, var(--calcite-color-background)); +} + +calcite-action-menu { + --calcite-action-menu-border-color: var(--calcite-action-group-action-menu-border-color); + --calcite-action-menu-text-color: var(--calcite-action-group-action-menu-text-color); +} + +calcite-action { + --calcite-action-indicator-color: var(--calcite-action-group-action-indicator-color); + --calcite-action-background-color: var(--calcite-action-group-action-background-color); + --calcite-action-text-color: var(--calcite-action-group-action-text-color); + + &:hover, + &:focus { + --calcite-action-indicator-color: var(--calcite-action-group-action-indicator-color-hover); + --calcite-action-background-color: var(--calcite-action-group-action-background-color-hover); + --calcite-action-text-color: var(--calcite-action-group-action-text-color-hover); + } + + &:active { + --calcite-action-indicator-color: var(--calcite-action-group-action-indicator-color-active); + --calcite-action-background-color: var(--calcite-action-group-action-background-color-active); + --calcite-action-text-color: var(--calcite-action-group-action-text-color-active); + } } @include base-component(); diff --git a/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts b/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts index e31eb2dd2e6..aa89b9d4338 100755 --- a/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts +++ b/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts @@ -1,4 +1,4 @@ -import { newE2EPage } from "@stencil/core/testing"; +import { E2EPage, newE2EPage } from "@stencil/core/testing"; import { html } from "../../../support/formatting"; import { accessible, @@ -526,4 +526,50 @@ describe("calcite-action-menu", () => { expect(clickSpy).toHaveReceivedEventTimes(1); }); }); + + describe("Theme-ing", () => { + let page: E2EPage; + const customTheme = { + "--calcite-action-menu-group-separator-border-color": "rgb(192, 255, 238)", + }; + + beforeEach(async () => { + page = await newE2EPage({ + html: ` + + + + + + + + + + + + `, + }); + await page.waitForChanges(); + }); + + it("should allow theme-ing of the border-color", async () => { + const actionMenu = await page.find("calcite-action-menu"); + const slottedActionGroup = await page.find("calcite-action-group"); + const defaultStyle = await slottedActionGroup.getComputedStyle(); + + await actionMenu.setAttribute( + "style", + `--calcite-action-menu-group-separator-border-color: ${customTheme["--calcite-action-menu-group-separator-border-color"]}`, + ); + await page.waitForChanges(); + + const styles = await slottedActionGroup.getComputedStyle(); + expect(defaultStyle.borderBlockEndColor).not.toBe( + customTheme["--calcite-action-menu-group-separator-border-color"], + ); + expect(styles.borderBlockEndColor).toBe(customTheme["--calcite-action-menu-group-separator-border-color"]); + }); + }); }); diff --git a/packages/calcite-components/src/components/action-menu/action-menu.scss b/packages/calcite-components/src/components/action-menu/action-menu.scss index b04468fa512..e2019b14de5 100755 --- a/packages/calcite-components/src/components/action-menu/action-menu.scss +++ b/packages/calcite-components/src/components/action-menu/action-menu.scss @@ -1,31 +1,50 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-action-menu-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-action-menu-action-indicator-color: defines the indicator color of an action sub-component inside the component. + * @prop --calcite-action-menu-action-text-color: defines the text color of an action sub-component inside the component. + * @prop --calcite-action-menu-action-background-color-hover: defines the background color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-menu-action-indicator-color-hover: defines the indicator color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-menu-action-text-color-hover: defines the text color of an action sub-component when hovered or focused inside the component. + * @prop --calcite-action-menu-action-background-color-active: defines the background color of an action sub-component when active inside the component. + * @prop --calcite-action-menu-action-indicator-color-active: defines the indicator color of an action sub-component when active inside the component. + * @prop --calcite-action-menu-action-text-color-active: defines the text color of an action sub-component when active inside the component. + * @prop --calcite-action-menu-group-separator-border-color: The border color of the component. + * + */ + :host { - @apply text-color-2 - text-1 + @apply text-1 box-border flex flex-col; } ::slotted(calcite-action-group) { - border-block-end: 1px solid var(--calcite-color-border-3); + border-block-end-style: solid; + border-block-end-width: var(--calcite-border-width-sm); + border-block-end-color: var(--calcite-action-menu-group-separator-border-color, var(--calcite-color-border-3)); } ::slotted(calcite-action-group:last-child) { - border-block-end: 0; + border-block-end: var(--calcite-border-width-none); } .default-trigger { @apply relative - h-full flex-initial self-stretch; + block-size: var(--calcite-container-size-content-fluid); } @include slotted("trigger", "calcite-action") { @apply relative - h-full flex-initial self-stretch; + block-size: var(--calcite-container-size-content-fluid); } .menu { @@ -37,4 +56,27 @@ max-h-menu; } +calcite-action { + --calcite-action-indicator-color: var(--calcite-action-menu-action-indicator-color); + --calcite-action-background-color: var(--calcite-action-menu-action-background-color); + --calcite-action-text-color: var(--calcite-action-menu-action-text-color); + + &:hover, + &:focus { + --calcite-action-indicator-color: var(--calcite-action-menu-action-indicator-color-hover); + --calcite-action-background-color: var(--calcite-action-menu-action-background-color-hover); + --calcite-action-text-color: var(--calcite-action-menu-action-text-color-hover); + } + + &:active { + --calcite-action-indicator-color: var(--calcite-action-menu-action-indicator-color-active); + --calcite-action-background-color: var(--calcite-action-menu-action-background-color-active); + --calcite-action-text-color: var(--calcite-action-menu-action-text-color-active); + } +} + +calcite-popover { + // TODO: add sub-component tokens +} + @include base-component(); diff --git a/packages/calcite-components/src/components/action/action.e2e.ts b/packages/calcite-components/src/components/action/action.e2e.ts index 8c1bec762a4..7fb4f2f7e29 100755 --- a/packages/calcite-components/src/components/action/action.e2e.ts +++ b/packages/calcite-components/src/components/action/action.e2e.ts @@ -205,4 +205,33 @@ describe("calcite-action", () => { expect(liveRegion.getAttribute("role")).toBe("region"); expect(liveRegion.textContent).toBe("Indicator present"); }); + + describe("theme-ing", () => { + it("should theme the background color", async () => { + const page = await newE2EPage(); + const color = ["--calcite-action-background-color", "rgb(186, 218, 85)"]; + await page.setContent(``); + await page.waitForChanges(); + + const action = await page.find("calcite-action"); + const defaultStyle = await action.getComputedStyle(); + + await action.setAttribute("style", `${color[0]}: ${color[1]}`); + await page.waitForChanges(); + + // action = await page.find(`calcite-action >>> .${CSS.action}`); + const styles = await action.getComputedStyle(); + + expect(defaultStyle.backgroundColor).not.toBe(color[1]); + expect(styles.backgroundColor).toBe(color[1]); + }); + }); }); diff --git a/packages/calcite-components/src/components/action/action.scss b/packages/calcite-components/src/components/action/action.scss index 54f7d2e3c9d..13a20cb2d88 100755 --- a/packages/calcite-components/src/components/action/action.scss +++ b/packages/calcite-components/src/components/action/action.scss @@ -3,15 +3,18 @@ * * These properties can be overridden using the component's tag as selector. * + * @prop --calcite-action-background-color: Specifies the background color of the component. * @prop --calcite-action-indicator-color: Specifies the color of the component's indicator. + * @prop --calcite-action-text-color: Specifies the text color of the component. + * */ +@include base-component(); +@include disabled(); :host { @extend %component-host; - @apply flex bg-transparent; - --calcite-action-indicator-color: theme("colors.brand"); - --calcite-action-color-transparent-hover: var(--calcite-color-transparent-hover); - --calcite-action-color-transparent-press: var(--calcite-color-transparent-press); + @apply flex; + background-color: var(--calcite-action-background-color, transparent); } @mixin action-indicator() { @@ -28,14 +31,9 @@ } } -@include disabled(); - -.button { - @apply bg-foreground-1 - fill-color-3 - focus-base +button { + @apply focus-base text-n2h - text-color-3 relative m-0 flex @@ -46,19 +44,24 @@ border-none font-sans font-medium; - + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-1)); + color: var(--calcite-action-text-color, var(--calcite-color-text-3)); text-align: unset; flex: 1 0 auto; - &:hover { - @apply bg-foreground-2 text-color-1 fill-color-1; - } + &:hover, &:focus { - @apply bg-foreground-2 text-color-1 fill-color-1 focus-inset; + @apply focus-inset; + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-2)); + color: var(--calcite-action-text-color, var(--calcite-color-text-1)); } &:active { - @apply bg-foreground-3; + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-3)); + } + + &.button--text-visible { + inline-size: 100%; } .icon-container { @@ -89,144 +92,155 @@ } } -:host([data-active]) .button { +.indicator-with-icon { + @include action-indicator(); +} + +.indicator-without-icon { + @apply w-4 mx-1; + @include action-indicator(); +} + +:host([data-active]) button { @apply focus-inset; } +// Scale :host([scale="s"]) { - .button { + button { @apply text-n2h px-2 py-1 font-normal; - } - .button--text-visible .icon-container { - margin-inline-end: theme("spacing.2"); + + &.button--text-visible .icon-container { + margin-inline-end: theme("spacing.2"); + } } } - :host([scale="m"]) { - .button { + button { @apply text-n1h px-4 py-3 font-normal; - } - .button--text-visible .icon-container { - margin-inline-end: theme("spacing.3"); + + &.button--text-visible .icon-container { + margin-inline-end: theme("spacing.3"); + } } } - :host([scale="l"]) { - .button { + button { @apply text-0h p-5 font-normal; - } - .button--text-visible .icon-container { - margin-inline-end: theme("spacing.4"); - } -} -:host([alignment="center"]) .button { - @apply justify-center; + &.button--text-visible .icon-container { + margin-inline-end: theme("spacing.4"); + } + } } - -:host([alignment="end"]) .button { - @apply justify-end; +// Compact +:host([scale="s"][compact]), +:host([scale="m"][compact]), +:host([scale="l"][compact]) { + button { + @apply px-0; + } } +// Alignment :host([alignment="center"]), :host([alignment="end"]) { - .button .text-container--visible { + button .text-container--visible { @apply flex-initial; } } - -// Compact -:host([scale="s"][compact]) .button, -:host([scale="m"][compact]) .button, -:host([scale="l"][compact]) .button { - @apply px-0; -} - -.slot-container { - @apply flex; -} - -.slot-container--hidden { - @apply hidden; +:host([alignment="center"]) button { + @apply justify-center; } - -.button--text-visible { - @apply w-full; +:host([alignment="end"]) button { + @apply justify-end; } -:host([active]) .button, -:host([active]) .button:hover, -:host([active]) .button:focus, -:host([active][loading]) .button { - @apply text-color-1 fill-color-1 bg-foreground-3; -} +:host(:is([active], [active][loading])) { + button { + &, + &:hover, + &:focus { + color: var(--calcite-action-text-color, var(--calcite-color-text-1)); -:host([active]) .button:active { - @apply bg-foreground-1; + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-3)); + } + } } -:host([appearance="transparent"]) .button { - @apply bg-transparent; +:host([active]) button:active { + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-1)); } -:host([appearance="transparent"][active]) .button, -:host([appearance="transparent"]) .button:hover, -:host([appearance="transparent"]) .button:focus { - background-color: var(--calcite-action-color-transparent-hover); +:host([appearance="transparent"]) button, +:host([appearance="transparent"][disabled]) button { + background-color: transparent; } -:host([appearance="transparent"]) .button:active { - background-color: var(--calcite-action-color-transparent-press); +:host([appearance="transparent"][active]) button, +:host([appearance="transparent"]) button:hover, +:host([appearance="transparent"]) button:focus { + background-color: var(--calcite-action-background-color, var(--calcite-color-transparent-hover)); } -:host([appearance="transparent"][disabled]) .button { - @apply bg-transparent; +:host([appearance="transparent"]) button:active { + background-color: var(--calcite-action-background-color, var(--calcite-color-transparent-press)); } -:host([loading][appearance="solid"]) .button, -:host([loading][appearance="solid"]) .button:hover, -:host([loading][appearance="solid"]) .button:focus { - @apply bg-foreground-1; +:host([loading][appearance="solid"]) button, +:host([loading][appearance="solid"]) button:hover, +:host([loading][appearance="solid"]) button:focus { + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-1)); .text-container { @apply opacity-disabled; } } :host([loading]) calcite-loader[inline] { - @apply text-color-3; + color: var(--calcite-color-text-3); margin-inline-end: theme("spacing.0"); } -:host([disabled]) .button, -:host([disabled]) .button:hover, -:host([disabled]) .button:focus { - @apply opacity-disabled bg-foreground-1 cursor-default; +:host([disabled]), +:host([disabled]), +:host([disabled]) { + button { + &, + &:hover, + &:focus { + @apply opacity-disabled cursor-default; + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-1)); + } + } } -:host([disabled][active]) .button, -:host([disabled][active]) .button:hover, -:host([disabled][active]) .button:focus { - @apply opacity-disabled bg-foreground-3; +:host([disabled][active]) button, +:host([disabled][active]) button:hover, +:host([disabled][active]) button:focus { + button { + &, + &:hover, + &:focus { + @apply opacity-disabled; + background-color: var(--calcite-action-background-color, var(--calcite-color-foreground-3)); + } + } } -:host([appearance="transparent"]) .button { - @apply bg-transparent - transition-shadow +:host([appearance="transparent"]) button { + @apply transition-shadow duration-150 ease-in-out; } -.indicator-with-icon { - @include action-indicator(); +.indicator-text { + @apply sr-only; } -.indicator-without-icon { - @apply w-4 mx-1; - @include action-indicator(); +calcite-loader { + //TODO: sub-component tokens } -.indicator-text { - @apply sr-only; +calcite-icon { + //TODO: sub-component tokens } - -@include base-component(); diff --git a/packages/calcite-components/src/components/action/resources.ts b/packages/calcite-components/src/components/action/resources.ts index 464cf6a0891..6e8763b201f 100644 --- a/packages/calcite-components/src/components/action/resources.ts +++ b/packages/calcite-components/src/components/action/resources.ts @@ -4,12 +4,9 @@ export const CSS = { buttonCompact: "button--compact", indicatorText: "indicator-text", iconContainer: "icon-container", - slotContainer: "slot-container", - slotContainerHidden: "slot-container--hidden", textContainer: "text-container", textContainerVisible: "text-container--visible", - indicatorWithIcon: "indicator-with-icon", - indicatorWithoutIcon: "indicator-without-icon", + actionIndicator: "action-indicator", }; export const SLOTS = { diff --git a/packages/calcite-components/src/components/avatar/avatar.scss b/packages/calcite-components/src/components/avatar/avatar.scss index df0f91758f7..896d87f8581 100644 --- a/packages/calcite-components/src/components/avatar/avatar.scss +++ b/packages/calcite-components/src/components/avatar/avatar.scss @@ -1,33 +1,57 @@ -:host { - @apply rounded-half inline-block overflow-hidden; -} +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-avatar-corner-radius: defines the corner radius of the component. + * @prop --calcite-avatar-text-color: defines the text color of the component. + * + */ -:host([scale="s"]) { - @apply text-n3 h-6 w-6; +:host { + @apply inline-block overflow-hidden; } -:host([scale="m"]) { - @apply text-n2 h-8 w-8; +:host, +.background, +.thumbnail { + border-radius: var(--calcite-avatar-corner-radius, 50%); } -:host([scale="l"]) { - @apply text-0 h-11 w-11; +.background, +.thumbnail { + block-size: var(--calcite-container-size-content-fluid); + inline-size: var(--calcite-container-size-content-fluid); } -.icon { +.icon, +.background { @apply flex; } .background { - @apply rounded-half flex h-full w-full items-center justify-center; + @apply items-center justify-center; } .initials { - @apply text-color-2 font-bold uppercase; + @apply font-bold uppercase; + color: var(--calcite-avatar-text-color, var(--calcite-color-text-2)); } -.thumbnail { - @apply rounded-half h-full w-full; +:host([scale="s"]) { + @apply text-n3 h-6 w-6; +} + +:host([scale="m"]) { + @apply text-n2 h-8 w-8; +} + +:host([scale="l"]) { + @apply text-0 h-11 w-11; +} + +calcite-icon { + // TODO: component tokens } @include base-component(); diff --git a/packages/calcite-components/src/components/block/block.scss b/packages/calcite-components/src/components/block/block.scss index 4f7e9e890f8..62cdfcd3d44 100644 --- a/packages/calcite-components/src/components/block/block.scss +++ b/packages/calcite-components/src/components/block/block.scss @@ -1,23 +1,32 @@ /** - * CSS Custom Properties - * - * These properties can be overridden using the component's tag as selector. - * - * @prop --calcite-block-padding: Specifies the padding of the block `default` slot. - */ +* CSS Custom Properties +* +* These properties can be overridden using the component's tag as selector. +* +* @prop --calcite-block-action-menu-border-color: The border color of the sub-component. +* @prop --calcite-block-action-menu-text-color: The text color of the sub-component. +* @prop --calcite-block-padding: Specifies the padding of the block `default` slot. +* +* @prop --calcite-block-handle-icon-color: Specifies the icon color of the sub-component. +* @prop --calcite-block-handle-icon-color-hover: Specifies the icon color of the sub-component when in hover state. +* @prop --calcite-block-handle-icon-color-focus: Specifies the icon color of the sub-component when in focus state. +* @prop --calcite-block-handle-icon-color-selected: Specifies the icon color of the sub-component when selected. + +* @prop --calcite-block-handle-background-color: Specifies the background color of the sub-component. +* @prop --calcite-block-handle-background-color-hover: Specifies the background color of the sub-component when in hover state. +* @prop --calcite-block-handle-background-color-focus: Specifies the background color of the sub-component when in focus state. +* @prop --calcite-block-handle-background-color-selected: Specifies the background color of the sub-component when selected. +*/ :host { @extend %component-host; - @extend %component-spacing; @apply transition-margin ease-cubic border-color-3 flex flex-shrink-0 flex-grow-0 flex-col border-0 border-b border-solid p-0 duration-150; flex-basis: auto; } @include disabled(); - -@import "../../assets/styles/animation"; -@import "../../assets/styles/header"; +@include header(); .header { @apply justify-start p-0; @@ -161,4 +170,23 @@ calcite-action-menu { } } +calcite-action-menu { + --calcite-action-menu-border-color: var(--calcite-block-action-menu-border-color); + --calcite-action-menu-text-color: var(--calcite-block-action-menu-text-color); +} + +calcite-handle { + --calcite-handle-icon-color: var(--calcite-block-handle-icon-color); + --calcite-handle-background-color: var(--calcite-block-handle-background-color); + + --calcite-handle-icon-color-hover: var(--calcite-block-handle-icon-color-hover); + --calcite-handle-background-color-hover: var(--calcite-block-handle-background-color-hover); + + --calcite-handle-icon-color-focus: var(--calcite-block-handle-icon-color-focus); + --calcite-handle-background-color-focus: var(--calcite-block-handle-background-color-focus); + + --calcite-handle-icon-color-selected: var(--calcite-block-handle-icon-color-selected); + --calcite-handle-background-color-selected: var(--calcite-block-handle-background-color-selected); +} + @include base-component(); diff --git a/packages/calcite-components/src/components/button/button.e2e.ts b/packages/calcite-components/src/components/button/button.e2e.ts index 60507aeea43..bdad412f737 100644 --- a/packages/calcite-components/src/components/button/button.e2e.ts +++ b/packages/calcite-components/src/components/button/button.e2e.ts @@ -648,8 +648,7 @@ describe("calcite-button", () => { t9n("calcite-button"); }); - describe('automatic tooltip', ()=>{ - + describe("automatic tooltip", () => { it("shows tooltip for buttons with truncated long text", async () => { const shortText = "Hi!"; const longText = @@ -685,7 +684,6 @@ describe("calcite-button", () => { expect(button).not.toHaveAttribute("title"); }); - }); it("should set aria-expanded attribute on shadowDOM element when used as trigger", async () => { diff --git a/packages/calcite-components/src/components/card-group/card-group.e2e.ts b/packages/calcite-components/src/components/card-group/card-group.e2e.ts new file mode 100644 index 00000000000..7629b82eeb9 --- /dev/null +++ b/packages/calcite-components/src/components/card-group/card-group.e2e.ts @@ -0,0 +1,462 @@ +import { E2EPage, newE2EPage } from "@stencil/core/testing"; +import { html } from "../../../support/formatting"; +import { accessible, renders, hidden, disabled } from "../../tests/commonTests"; +import { GlobalTestProps } from "../../tests/utils"; +import { CSS } from "../card/resources"; + +describe("calcite-card-group", () => { + describe("renders", () => { + renders("", { + display: "block", + }); + }); + + describe("honors hidden attribute", () => { + hidden("calcite-card-group"); + }); + + describe("disabled", () => { + disabled("", { focusTarget: "none" }); + }); + + describe("is accessible in selection mode none (default)", () => { + accessible( + html` + Heading + Heading + `, + ); + }); + + describe("is accessible in selection mode single", () => { + accessible( + html` + Heading + Heading + `, + ); + }); + + describe("is accessible in selection mode single-persist", () => { + accessible( + html` + Heading + Heading + `, + ); + }); + + describe("is accessible in selection mode multiple", () => { + accessible( + html` + Heading + Heading + `, + ); + }); + + describe("selection modes function as intended", () => { + it("selection mode single allows one or no cards to be selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + Heading + Heading + Heading + `, + ); + await page.waitForChanges(); + + const element = await page.find("calcite-card-group"); + const card1 = await page.find("#card-1"); + const card2 = await page.find("#card-2"); + const card1CheckAction = await page.find(`#card-1 >>> .${CSS.checkboxWrapper}`); + const card2CheckAction = await page.find(`#card-2 >>> .${CSS.checkboxWrapper}`); + + const cardGroupSelectSpy = await element.spyOnEvent("calciteCardGroupSelect"); + await assertSelectedItems.setUpEvents(page); + + const cardSelectSpy1 = await card1.spyOnEvent("calciteCardSelect"); + const cardSelectSpy2 = await card2.spyOnEvent("calciteCardSelect"); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(0); + expect(cardSelectSpy1).toHaveReceivedEventTimes(0); + expect(cardSelectSpy2).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card2.id] }); + + card1CheckAction.click(); + await page.waitForChanges(); + expect(await cardGroupSelectSpy).toHaveReceivedEventTimes(1); + expect(await cardSelectSpy1).toHaveReceivedEventTimes(1); + expect(await cardSelectSpy2).toHaveReceivedEventTimes(0); + expect(await card1.getProperty("selected")).toBe(true); + expect(await card2.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card1.id] }); + + card2CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(2); + expect(cardSelectSpy1).toHaveReceivedEventTimes(1); + expect(cardSelectSpy2).toHaveReceivedEventTimes(1); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card2.id] }); + + card2CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(3); + expect(cardSelectSpy1).toHaveReceivedEventTimes(1); + expect(cardSelectSpy2).toHaveReceivedEventTimes(2); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + + it("selection mode single-persist allows one card to be selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + Heading + Heading + `, + ); + await page.waitForChanges(); + + const element = await page.find("calcite-card-group"); + const card1 = await page.find("#card-1"); + const card2 = await page.find("#card-2"); + const card1CheckAction = await page.find(`#card-1 >>> .${CSS.checkboxWrapper}`); + const card2CheckAction = await page.find(`#card-2 >>> .${CSS.checkboxWrapper}`); + + const cardGroupSelectSpy = await element.spyOnEvent("calciteCardGroupSelect"); + await assertSelectedItems.setUpEvents(page); + + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card1.id] }); + + card1CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(1); + expect(await card1.getProperty("selected")).toBe(true); + expect(await card2.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card1.id] }); + + card2CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(2); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card2.id] }); + + card2CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(3); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card2.id] }); + }); + + it("selection mode multiple allows none, one, or multiple to be selected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + Heading + Heading + Heading + `, + ); + await page.waitForChanges(); + + const element = await page.find("calcite-card-group"); + const card1 = await page.find("#card-1"); + const card2 = await page.find("#card-2"); + const card3 = await page.find("#card-3"); + const card1CheckAction = await page.find(`#card-1 >>> .${CSS.checkboxWrapper}`); + const card2CheckAction = await page.find(`#card-2 >>> .${CSS.checkboxWrapper}`); + const card3CheckAction = await page.find(`#card-3 >>> .${CSS.checkboxWrapper}`); + + const cardGroupSelectSpy = await element.spyOnEvent("calciteCardGroupSelect"); + await assertSelectedItems.setUpEvents(page); + + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + + card1CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(1); + expect(await card1.getProperty("selected")).toBe(true); + expect(await card2.getProperty("selected")).toBe(false); + expect(await card3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card1.id] }); + + card2CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(2); + expect(await card1.getProperty("selected")).toBe(true); + expect(await card2.getProperty("selected")).toBe(true); + expect(await card3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [card1.id, card2.id] }); + + card3CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(3); + expect(await card1.getProperty("selected")).toBe(true); + expect(await card2.getProperty("selected")).toBe(true); + expect(await card3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(3); + await assertSelectedItems(page, { expectedItemIds: [card1.id, card2.id, card3.id] }); + + card1CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(4); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(true); + expect(await card3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [card2.id, card3.id] }); + + card2CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(5); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(false); + expect(await card3.getProperty("selected")).toBe(true); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card3.id] }); + + card3CheckAction.click(); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(6); + expect(await card1.getProperty("selected")).toBe(false); + expect(await card2.getProperty("selected")).toBe(false); + expect(await card3.getProperty("selected")).toBe(false); + expect(await element.getProperty("selectedItems")).toEqual([]); + await assertSelectedItems(page, { expectedItemIds: [] }); + }); + }); + + describe("focus and interaction function as intended", () => { + it("navigation and selection with keyboard works as expected", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + Heading + Heading + Heading + Heading + Heading + `, + ); + + const element = await page.find("calcite-card-group"); + const cardGroupSelectSpy = await element.spyOnEvent("calciteCardGroupSelect"); + await assertSelectedItems.setUpEvents(page); + + const card1 = await page.find("#card-1"); + const card2 = await page.find("#card-2"); + const card3 = await page.find("#card-3"); + const card4 = await page.find("#card-4"); + const card5 = await page.find("#card-5"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card1.id); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card2.id); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card3.id); + + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card5.id); + + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(1); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card5.id] }); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card4.id); + + await page.keyboard.press("Enter"); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(2); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [card4.id, card5.id] }); + + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(3); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await assertSelectedItems(page, { expectedItemIds: [card5.id] }); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card1.id); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card5.id); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card1.id); + }); + it("navigation with keyboard works as expected when selection mode none (default)", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + Heading + Heading + Heading + Heading + Heading + `, + ); + + const element = await page.find("calcite-card-group"); + const cardGroupSelectSpy = await element.spyOnEvent("calciteCardGroupSelect"); + await assertSelectedItems.setUpEvents(page); + + const card1 = await page.find("#card-1"); + const card2 = await page.find("#card-2"); + const card3 = await page.find("#card-3"); + const card4 = await page.find("#card-4"); + const card5 = await page.find("#card-5"); + + await page.keyboard.press("Tab"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card1.id); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card2.id); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card3.id); + + await page.keyboard.press("End"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card5.id); + + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card4.id); + + await page.keyboard.press("Enter"); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.keyboard.press("Space"); + await page.waitForChanges(); + expect(cardGroupSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await assertSelectedItems(page, { expectedItemIds: [] }); + + await page.keyboard.press("Home"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card1.id); + + await page.keyboard.press("ArrowLeft"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card5.id); + + await page.keyboard.press("ArrowRight"); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toEqual(card1.id); + }); + }); + + it("selectedItems property is correctly populated at load when property is set on cards in DOM", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + Heading + Heading + Heading + Heading + Heading + `, + ); + const element = await page.find("calcite-card-group"); + const card4 = await page.find("#card-4"); + const card5 = await page.find("#card-5"); + await page.waitForChanges(); + + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await assertSelectedItems(page, { expectedItemIds: [card4.id, card5.id] }); + }); +}); + +// Borrowed from Dropdown until a generic utility is set up. +interface SelectedItemsAssertionOptions { + /** + * IDs from items to assert selection + */ + expectedItemIds: string[]; +} + +/** + * Test helper for selected calcite-card-group items. Expects items to have IDs to test against. + * + * Note: assertSelectedItems.setUpEvents must be called before using this method + * + * @param page + * @param root0 + * @param root0.expectedItemIds + */ +async function assertSelectedItems(page: E2EPage, { expectedItemIds }: SelectedItemsAssertionOptions): Promise { + await page.waitForTimeout(100); + const selectedItemIds = await page.evaluate(() => { + const cardGroup = document.querySelector("calcite-card-group"); + return cardGroup.selectedItems.map((item) => item.id); + }); + + expect(selectedItemIds).toHaveLength(expectedItemIds.length); + + expectedItemIds.forEach((itemId, index) => expect(selectedItemIds[index]).toEqual(itemId)); +} + +type SelectionEventTestWindow = GlobalTestProps<{ eventDetail: Selection }>; + +/** + * Helper to wire up the page to assert on the event detail + * + * @param page + */ +assertSelectedItems.setUpEvents = async (page: E2EPage) => { + await page.evaluate(() => { + document.addEventListener("calciteCardGroupSelect", ({ detail }: CustomEvent) => { + (window as SelectionEventTestWindow).eventDetail = detail; + }); + }); +}; diff --git a/packages/calcite-components/src/components/card-group/card-group.scss b/packages/calcite-components/src/components/card-group/card-group.scss new file mode 100644 index 00000000000..1c1bdfc2c58 --- /dev/null +++ b/packages/calcite-components/src/components/card-group/card-group.scss @@ -0,0 +1,20 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-card-group-gap: Specifies the gap between slotted components. + */ + +:host { + display: block; +} + +.container { + display: flex; + flex-wrap: wrap; + gap: var(--calcite-card-group-gap, var(--calcite-size-md)); +} + +@include disabled(); +@include base-component(); diff --git a/packages/calcite-components/src/components/card-group/card-group.stories.ts b/packages/calcite-components/src/components/card-group/card-group.stories.ts new file mode 100644 index 00000000000..eec36f640b7 --- /dev/null +++ b/packages/calcite-components/src/components/card-group/card-group.stories.ts @@ -0,0 +1,362 @@ +import { select } from "@storybook/addon-knobs"; +import { storyFilters } from "../../../.storybook/helpers"; +import { modesDarkDefault } from "../../../.storybook/utils"; +import readme from "./readme.md"; +import { html } from "../../../support/formatting"; +import { placeholderImage } from "../../../.storybook/placeholderImage"; + +export default { + title: "Components/Card Group", + parameters: { + notes: readme, + }, + ...storyFilters(), +}; + +export const simple = (): string => html` + + + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt + by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+
+`; + +export const single_TestOnly = (): string => html` + + + Heading + Description + + + Heading + Description + + + Heading + Description + + + Heading + Description + + +`; + +export const singlePersistWithPreSelected_TestOnly = (): string => html` + + + Heading + Description + + + Heading + Description + + + Heading + Description + + + Heading + Description + + +`; + +export const multiple_TestOnly = (): string => html` + + + Heading + Description + + + Heading + Description + + + Heading + Description + + + Heading + Description + + +`; + +export const multipleCardHeightsMatchPerRow_TestOnly = (): string => html` +
+ + + + Sample image alt +

Portland Businesses

+
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + + + + View details + Duplicate + Delete + + +
+
+ +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+
+
+`; + +export const darkThemeRTL_TestOnly = (): string => html` +
+ + + Heading + Description + + + Heading + Description + + + Heading + Description + + + Heading + Description + + +
+`; + +darkThemeRTL_TestOnly.parameters = { themes: modesDarkDefault }; diff --git a/packages/calcite-components/src/components/card-group/card-group.tsx b/packages/calcite-components/src/components/card-group/card-group.tsx new file mode 100644 index 00000000000..0f25a836eeb --- /dev/null +++ b/packages/calcite-components/src/components/card-group/card-group.tsx @@ -0,0 +1,266 @@ +import { + Component, + h, + VNode, + Prop, + Element, + Listen, + EventEmitter, + Event, + Method, + Watch, + Host, +} from "@stencil/core"; +import { focusElement, focusElementInGroup, toAriaBoolean } from "../../utils/dom"; +import { + connectInteractive, + disconnectInteractive, + InteractiveComponent, + InteractiveContainer, + updateHostInteraction, +} from "../../utils/interactive"; +import { Scale, SelectionMode } from "../interfaces"; +import { + LoadableComponent, + componentLoaded, + setComponentLoaded, + setUpLoadableComponent, +} from "../../utils/loadable"; + +/** + * @slot - A slot for adding one or more `calcite-card`s. + */ + +@Component({ + tag: "calcite-card-group", + styleUrl: "card-group.scss", + shadow: true, +}) +export class CardGroup implements InteractiveComponent, LoadableComponent { + //-------------------------------------------------------------------------- + // + // Element + // + //-------------------------------------------------------------------------- + + @Element() el: HTMLCalciteCardGroupElement; + + //-------------------------------------------------------------------------- + // + // Public Properties + // + //-------------------------------------------------------------------------- + + /** When `true`, interaction is prevented and the component is displayed with lower opacity. */ + @Prop({ reflect: true }) disabled = false; + + /** Accessible name for the component. */ + @Prop() label!: string; + + /** Specifies the size of the component. Child `calcite-card`s inherit the component's value. */ + @Prop({ reflect: true }) scale: Scale = "m"; + + /** Specifies the selection mode of the component. */ + @Prop({ reflect: true }) selectionMode: Extract< + "multiple" | "single" | "single-persist" | "none", + SelectionMode + > = "none"; + + @Watch("selectionMode") + onSelectionModeChange(): void { + this.udpateItemsOnSelectionModeChange(); + } + + /** + * Specifies the component's selected items. + * + * @readonly + */ + @Prop({ mutable: true }) selectedItems: HTMLCalciteCardElement[] = []; + + //-------------------------------------------------------------------------- + // + // Private Properties + // + //-------------------------------------------------------------------------- + + private items: HTMLCalciteCardElement[] = []; + + private slotRefEl: HTMLSlotElement; + + //-------------------------------------------------------------------------- + // + // Events + // + //-------------------------------------------------------------------------- + + /** Emits when the component's selection changes and the `selectionMode` is not `none`. */ + @Event({ cancelable: false }) calciteCardGroupSelect: EventEmitter; + + //-------------------------------------------------------------------------- + // + // Lifecycle + // + //-------------------------------------------------------------------------- + + connectedCallback(): void { + connectInteractive(this); + } + + componentDidRender(): void { + updateHostInteraction(this); + } + + componentDidLoad(): void { + setComponentLoaded(this); + } + + disconnectedCallback(): void { + disconnectInteractive(this); + } + + async componentWillLoad(): Promise { + setUpLoadableComponent(this); + } + //-------------------------------------------------------------------------- + // + // Event Listeners + // + //-------------------------------------------------------------------------- + + @Listen("calciteInternalCardKeyEvent") + calciteInternalCardKeyEventListener(event: KeyboardEvent): void { + if (event.composedPath().includes(this.el)) { + const interactiveItems = this.items.filter((el) => !el.disabled); + switch (event.detail["key"]) { + case "ArrowRight": + focusElementInGroup(interactiveItems, event.target as HTMLCalciteCardElement, "next"); + break; + case "ArrowLeft": + focusElementInGroup(interactiveItems, event.target as HTMLCalciteCardElement, "previous"); + break; + case "Home": + focusElementInGroup(interactiveItems, event.target as HTMLCalciteCardElement, "first"); + break; + case "End": + focusElementInGroup(interactiveItems, event.target as HTMLCalciteCardElement, "last"); + break; + } + } + } + + @Listen("calciteCardSelect") + calciteCardSelectListener(event: CustomEvent): void { + if ( + event.composedPath().includes(this.el) && + !(event.target as HTMLCalciteCardElement).selectable + ) { + this.setSelectedItems(true, event.target as HTMLCalciteCardElement); + } + } + + // -------------------------------------------------------------------------- + // + // Public Methods + // + // -------------------------------------------------------------------------- + + /** + * Sets focus on the component's first focusable element. + */ + @Method() + async setFocus(): Promise { + await componentLoaded(this); + if (!this.disabled) { + focusElement(this.items[0]); + } + } + + //-------------------------------------------------------------------------- + // + // Private Methods + // + //-------------------------------------------------------------------------- + + private udpateItemsOnSelectionModeChange = (): void => { + this.updateSlottedItems(this.slotRefEl); + this.updateSelectedItems(); + }; + + private updateItemsOnSlotChange = (event: Event): void => { + this.updateSlottedItems(event.target as HTMLSlotElement); + this.updateSelectedItems(); + }; + + private updateSlottedItems = (target: HTMLSlotElement): void => { + this.items = target + .assignedElements({ flatten: true }) + .filter((el) => el?.matches("calcite-card")) as HTMLCalciteCardElement[]; + }; + + private updateSelectedItems = (): void => { + this.items.forEach((el) => { + el.selectionMode = this.selectionMode; + }); + + this.setSelectedItems(false); + }; + + private setSelectedItems = (emit: boolean, elToMatch?: HTMLCalciteCardElement): void => { + if (elToMatch) { + this.items.forEach((el) => { + const matchingEl = elToMatch === el; + switch (this.selectionMode) { + case "multiple": + if (matchingEl) { + el.selected = !el.selected; + } + break; + + case "single": + el.selected = matchingEl ? !el.selected : false; + break; + + case "single-persist": + el.selected = !!matchingEl; + break; + } + }); + } + + this.selectedItems = this.items.filter((el) => el.selected); + + if (emit && this.selectionMode !== "none" && !this.disabled) { + this.calciteCardGroupSelect.emit(); + } + }; + + //-------------------------------------------------------------------------- + // + // Render Methods + // + //-------------------------------------------------------------------------- + + render(): VNode { + const role = + this.selectionMode === "none" || this.selectionMode === "multiple" ? "group" : "radiogroup"; + + return ( + + +
+ (this.slotRefEl = el as HTMLSlotElement)} + /> +
+
+
+ ); + } +} diff --git a/packages/calcite-components/src/components/card-group/readme.md b/packages/calcite-components/src/components/card-group/readme.md new file mode 100644 index 00000000000..a807e4fa2dd --- /dev/null +++ b/packages/calcite-components/src/components/card-group/readme.md @@ -0,0 +1,5 @@ +# calcite-card-group + + + +## Properties diff --git a/packages/calcite-components/src/components/card-group/usage/Basic.md b/packages/calcite-components/src/components/card-group/usage/Basic.md new file mode 100644 index 00000000000..10343bd2e4c --- /dev/null +++ b/packages/calcite-components/src/components/card-group/usage/Basic.md @@ -0,0 +1,20 @@ +```html + + + Baseball stadium layer + This layer contains baseball stadiums + + + Hockey stadium layer + This layer contains hockey stadiums + + + Football stadium layer + This layer contains football stadiums + + + Soccer stadium layer + This layer contains soccer stadiums + + +``` diff --git a/packages/calcite-components/src/components/card-group/usage/SelectionMode.md b/packages/calcite-components/src/components/card-group/usage/SelectionMode.md new file mode 100644 index 00000000000..cee3c2d5532 --- /dev/null +++ b/packages/calcite-components/src/components/card-group/usage/SelectionMode.md @@ -0,0 +1,20 @@ +```html + + + Baseball stadium layer + This layer contains baseball stadiums + + + Hockey stadium layer + This layer contains hockey stadiums + + + Football stadium layer + This layer contains football stadiums + + + Soccer stadium layer + This layer contains soccer stadiums + + +``` diff --git a/packages/calcite-components/src/components/card/assets/card/t9n/messages.json b/packages/calcite-components/src/components/card/assets/card/t9n/messages.json index eac24c88066..634d7a6d0f8 100644 --- a/packages/calcite-components/src/components/card/assets/card/t9n/messages.json +++ b/packages/calcite-components/src/components/card/assets/card/t9n/messages.json @@ -1,4 +1,4 @@ { - "select": "Selectable card", + "select": "Select", "loading": "Loading" } diff --git a/packages/calcite-components/src/components/card/assets/card/t9n/messages_en.json b/packages/calcite-components/src/components/card/assets/card/t9n/messages_en.json index eac24c88066..634d7a6d0f8 100644 --- a/packages/calcite-components/src/components/card/assets/card/t9n/messages_en.json +++ b/packages/calcite-components/src/components/card/assets/card/t9n/messages_en.json @@ -1,4 +1,4 @@ { - "select": "Selectable card", + "select": "Select", "loading": "Loading" } diff --git a/packages/calcite-components/src/components/card/card.e2e.ts b/packages/calcite-components/src/components/card/card.e2e.ts index 1905a7e50f5..622760f8121 100644 --- a/packages/calcite-components/src/components/card/card.e2e.ts +++ b/packages/calcite-components/src/components/card/card.e2e.ts @@ -22,50 +22,48 @@ describe("calcite-card", () => { accessible("calcite-card"); }); - describe("accessible when selectable", () => { + describe("accessible when selectable (deprecated)", () => { accessible( - html` + html` Test image `, ); }); describe("slots", () => { - slots("calcite-card", SLOTS); + slots("calcite-card", SLOTS, true); }); it("renders with default props if none are provided", async () => { const page = await newE2EPage(); await page.setContent(` - + Test image `); const element = await page.find("calcite-card"); expect(element).not.toHaveAttribute("disabled"); expect(element).not.toHaveAttribute("loading"); - expect(element).not.toHaveAttribute("selectable"); expect(element).not.toHaveAttribute("selected"); }); it("renders with requested props", async () => { const page = await newE2EPage(); await page.setContent(` - + Test image `); const element = await page.find("calcite-card"); expect(element).toHaveAttribute("disabled"); expect(element).toHaveAttribute("loading"); - expect(element).toHaveAttribute("selectable"); expect(element).toHaveAttribute("selected"); }); it("should have a thumbnail container", async () => { const page = await newE2EPage(); await page.setContent(` - + Test image `); @@ -75,25 +73,12 @@ describe("calcite-card", () => { expect(thumbContainer).not.toBeNull(); }); - it("should render a checkbox if selectable", async () => { - const page = await newE2EPage(); - await page.setContent(` - - Test image - - `); - - const thumbContainer = await page.find(`calcite-card >>> .${CSS.checkboxWrapper}`); - - expect(thumbContainer).not.toBeNull(); - }); - - describe("when a card is selectable", () => { + describe("when a card is selectable (deprecated)", () => { it("should update the card's selected state when its checkbox is clicked", async () => { const page = await newE2EPage(); await page.setContent(`
- +

ArcGIS Online: Gallery and Organization pages

A great example of a study description that might wrap to a line or two, but isn't overly verbose. @@ -102,9 +87,10 @@ describe("calcite-card", () => {
`); const card = await page.find("calcite-card"); - const checkbox = await page.find(`calcite-card >>> .${CSS.checkboxWrapper} calcite-checkbox`); + const checkbox = await page.find(`calcite-card >>> .${CSS.checkboxWrapperDeprecated} calcite-checkbox`); const cardSelectSpy = await card.spyOnEvent("calciteCardSelect"); const clickSpy = await card.spyOnEvent("calciteCardSelect"); + await checkbox.click(); await page.waitForChanges(); @@ -122,7 +108,7 @@ describe("calcite-card", () => { it("should have aria-live attribute set to polite on loader container when loading", async () => { const page = await newE2EPage(); await page.setContent(` - + Test image `); diff --git a/packages/calcite-components/src/components/card/card.scss b/packages/calcite-components/src/components/card/card.scss index 40ae1dd81ae..1af4e777291 100644 --- a/packages/calcite-components/src/components/card/card.scss +++ b/packages/calcite-components/src/components/card/card.scss @@ -5,8 +5,16 @@ * * @prop --calcite-card-background-color: Specifies the background color of the component. * @prop --calcite-card-border-color: Specifies the border color of the component. - * @prop --calcite-card-box-shadow: Specifies the box shadow of the component. + * @prop --calcite-card-shadow: Specifies the shadow of the component. * @prop --calcite-card-corner-radius: Specifies the corner radius of the component. + * @prop --calcite-card-accent-color-selected: Specifies the accent color of the component when `selected`. + * @prop --calcite-card-selection-background-color: Specifies the background color of the component's selection element. + * @prop --calcite-card-selection-background-color-hover: Specifies the background color of the component's selection element when hovered. + * @prop --calcite-card-selection-background-color-active: Specifies the background color of the component's selection element when active. + * @prop --calcite-card-selection-background-color-selected: Specifies the icon color of the component's selection element when `selected`. + * @prop --calcite-card-selection-icon-color: Specifies the icon color of the component's selection element. + * @prop --calcite-card-selection-icon-color-hover: Specifies the icon color of the component's selection element when hovered. + * @prop --calcite-card-selection-icon-color-selected: Specifies the icon color of the component's selection element when `selected`. * */ @@ -14,26 +22,40 @@ @apply block max-w-full; } -.calcite-card-container { +.content-wrapper { @apply relative flex h-full flex-col justify-between - duration-150 - ease-in-out overflow-hidden; border: var(--calcite-border-width-sm) solid var(--calcite-card-border-color, var(--calcite-color-border-3)); border-radius: var(--calcite-card-corner-radius, var(--calcite-corner-radius-sharp)); background-color: var(--calcite-card-background-color, var(--calcite-color-foreground-1)); - box-shadow: var(--calcite-card-box-shadow, var(--calcite-shadow-none)); + box-shadow: var(--calcite-card-shadow, var(--calcite-shadow-none)); + pointer-events: none; +} + +::slotted(*) { + @apply pointer-events-auto; +} + +:host(:not([selectable])) { + .content-wrapper { + &:not(.non-interactive) { + @apply focus-base; + } + &:not(.non-interactive):focus { + @apply focus-outset; + } + } } .container { @apply relative flex flex-auto flex-col; } -:host([loading]) .calcite-card-container *:not(calcite-loader):not(.calcite-card-loader-container) { +:host([loading]) .content-wrapper *:not(calcite-loader):not(.calcite-card-loader-container) { @apply pointer-events-none; opacity: $calcite-opacity-0; } @@ -43,10 +65,21 @@ } .header { - @apply flex flex-col; + @apply flex flex-row items-start; +} + +.footer { + @apply flex mt-auto flex-row content-between justify-between; padding-inline: var(--calcite-spacing-md); - padding-block-start: var(--calcite-spacing-md); - padding-block-end: var(--calcite-spacing-xxs); + padding-block-start: var(--calcite-spacing-xxs); + padding-block-end: var(--calcite-spacing-md); +} + +.header-text-container { + @apply flex flex-col px-3 py-2 w-full justify-center; + &:not(:only-child) { + @apply pr-0.5; + } } .footer { @@ -57,16 +90,21 @@ } .card-content { - @apply text-n2-wrap; - color: var(--calcite-color-text-3); - padding: var(--calcite-spacing-md); + @apply h-auto text-n2-wrap; } -:host([selected]) .calcite-card-container { - --calcite-card-border-color: var(--calcite-color-brand); +.has-slotted-content { + @apply p-3; +} + +:host([selected]) .content-wrapper { + box-shadow: inset 0 -4px 0 0 var(--calcite-card-accent-color-selected, var(--calcite-color-brand)); +} + +:host([selectable]) .header { + padding-inline-end: var(--calcite-spacing-xxxl); } -// slotted content @include slotted("title", "*") { @apply text-n1-wrap; margin: 0; @@ -74,18 +112,25 @@ color: var(--calcite-color-text-1); } -:host([selectable]) { - .header { - // prevents overlap with checkbox (default header padding + no-overlap spacing) - padding-inline-end: var(--calcite-spacing-xxxl); - } +@include slotted("subtitle", "*") { + @apply text-color-2 + text-n2-wrap + m-0 mt-0.5 + font-normal; } -@include slotted("subtitle", "*") { - @apply text-n2-wrap font-normal; - margin: 0; - margin-block-start: var(--calcite-spacing-sm); - color: var(--calcite-color-text-2); +@include slotted("heading", "*") { + @apply text-color-1 + text-n1-wrap + m-0 + font-medium; +} + +@include slotted("description", "*") { + @apply text-color-2 + text-n2-wrap + m-0 mt-0.5 + font-normal; } @include slotted("thumbnail", "img") { @@ -103,19 +148,59 @@ color: var(--calcite-color-text-3); } -.checkbox-wrapper { - @apply absolute; +.checkbox-wrapper-deprecated { + @apply absolute pointer-events-auto; inset-block-start: var(--calcite-spacing-sm); inset-inline-end: var(--calcite-spacing-sm); margin: 0; padding: 0; } +.checkbox-wrapper { + @apply m-2 p-2 focus-base cursor-pointer pointer-events-auto; + background-color: var(--calcite-card-selection-background-color, transparent); + display: flex; + align-items: center; + justify-items: center; + --calcite-ui-icon-color: var(--calcite-card-selection-icon-color, var(--calcite-color-text-3)); + &:hover { + background-color: var(--calcite-card-selection-background-color-hover, var(--calcite-color-transparent-hover)); + --calcite-ui-icon-color: var(--calcite-card-selection-icon-color-hover, var(--calcite-color-text-2)); + } + &:active { + background-color: var(--calcite-card-selection-background-color-active, var(--calcite-color-transparent-press)); + } + & calcite-icon { + pointer-events: none; + } +} + +:host([selected]) .checkbox-wrapper { + --calcite-ui-icon-color: var(--calcite-card-selection-icon-color-selected, var(--calcite-color-brand)); + background-color: var(--calcite-card-selection-background-color-selected, transparent); + &:hover { + background-color: var(--calcite-card-selection-background-color-hover, var(--calcite-color-transparent-hover)); + } + &:active { + background-color: var(--calcite-card-selection-background-color-active, var(--calcite-color-transparent-press)); + } +} + +:host(:not([selectable])) .content-wrapper:not(.non-interactive):focus .checkbox-wrapper { + background-color: var(--calcite-card-selection-background-color-hover, var(--calcite-color-transparent-hover)); + --calcite-ui-icon-color: var(--calcite-card-selection-icon-color-hover, var(--calcite-color-text-2)); +} + +:host([selected]:not([selectable])) .content-wrapper:not(.non-interactive):focus .checkbox-wrapper { + background-color: var(--calcite-card-selection-background-color-active, var(--calcite-color-transparent-press)); + --calcite-ui-icon-color: var(--calcite-card-selection-icon-color-selected, var(--calcite-color-brand)); +} + .thumbnail-wrapper { @apply flex; } -.calcite-card-container.inline { +.content-wrapper.inline { @apply flex-row; & > .container { @@ -136,3 +221,4 @@ slot[name="footer-end"]::slotted(*) { } @include base-component(); +@include disabled(); diff --git a/packages/calcite-components/src/components/card/card.stories.ts b/packages/calcite-components/src/components/card/card.stories.ts index aa01196d2bc..47e44241474 100644 --- a/packages/calcite-components/src/components/card/card.stories.ts +++ b/packages/calcite-components/src/components/card/card.stories.ts @@ -40,14 +40,6 @@ const createAttributes: (options?: { exceptions: string[] }) => Attributes = ({ return this; }, }, - { - name: "selectable", - commit(): Attribute { - this.value = boolean("selectable", false); - delete this.build; - return this; - }, - }, { name: "thumbnail-position", commit(): Attribute { @@ -62,8 +54,8 @@ const createAttributes: (options?: { exceptions: string[] }) => Attributes = ({ }; const titleHtml = html` - ArcGIS Online: Gallery and Organization pages - +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly verbose. `; @@ -129,8 +121,8 @@ export const thumbnail = (): string => html` createAttributes(), html` ${thumbnailHtml} - Portland Businesses - Portland Businesses + by example_user @@ -186,8 +178,8 @@ export const thumbnailRounded = (): string => html` ${thumbnailHtml} - Portland Businesses - Portland Businesses + by example_user @@ -209,7 +201,22 @@ export const thumbnailRounded = (): string => html` `; -export const headerDoesNotOverlapWithCheckbox_TestOnly = (): string => html` +export const headerDoesNotOverlapWithCheckboxDeprecated_TestOnly = (): string => html` + +

Pokem ipsum dolor sit amet Skitty Hoothoot

+ Pika-pi Soul Badge Zoroark Starly Spoink Diglett Rotom. Water Kyogre Hitmontop Rampardos +

+ Team Rocket Whimsicott Snover Duskull Servine Kakuna Bellsprout. Scratch Shelgon Oddish Hitmonchan Quagsire Earth + Badge Leaf Green. Pika-pi Bonsly Rare Candy Seadra blast off at the speed of light Shellos Kirlia. Celadon City + Seviper Omanyte Espeon Body Slam Victini Darumaka. Normal Krookodile Junichi Masuda Machoke Body Slam Zigzagoon to + protect the world from devastation. +

+
+`; + +export const deprecatedSlotsSelectable_TestOnly = (): string => html` Pokem ipsum dolor sit amet Skitty Hoothoot = + "none"; + /** * Use this property to override individual strings used by the component. */ @@ -91,9 +122,29 @@ export class Card implements ConditionalSlotComponent, LocalizedComponent, T9nCo // //-------------------------------------------------------------------------- - /** Fires when `selectable` is `true` and the component is selected. */ + /** Fires when the deprecated `selectable` is true, or `selectionMode` set on parent `calcite-card-group` is not `none` and the component is selected. */ @Event({ cancelable: false }) calciteCardSelect: EventEmitter; + /** + * @internal + */ + @Event({ cancelable: false }) calciteInternalCardKeyEvent: EventEmitter; + + //-------------------------------------------------------------------------- + // + // Public Methods + // + //-------------------------------------------------------------------------- + + /** Sets focus on the component. */ + @Method() + async setFocus(): Promise { + await componentFocusable(this); + if (!this.disabled) { + this.containerEl?.focus(); + } + } + // -------------------------------------------------------------------------- // // Lifecycle @@ -101,44 +152,30 @@ export class Card implements ConditionalSlotComponent, LocalizedComponent, T9nCo // -------------------------------------------------------------------------- connectedCallback(): void { - connectConditionalSlotComponent(this); + connectInteractive(this); connectLocalized(this); connectMessages(this); } + componentDidLoad(): void { + setComponentLoaded(this); + } + + componentDidRender(): void { + updateHostInteraction(this); + } + disconnectedCallback(): void { - disconnectConditionalSlotComponent(this); + disconnectInteractive(this); disconnectLocalized(this); disconnectMessages(this); } async componentWillLoad(): Promise { + setUpLoadableComponent(this); await setUpMessages(this); } - render(): VNode { - const thumbnailInline = this.thumbnailPosition.startsWith("inline"); - const thumbnailStart = this.thumbnailPosition.endsWith("start"); - return ( -
- {this.loading ? ( -
- -
- ) : null} - {thumbnailStart && this.renderThumbnail()} -
- {this.selectable ? this.renderCheckbox() : null} - {this.renderHeader()} -
- -
- {this.renderFooter()} -
- {!thumbnailStart && this.renderThumbnail()} -
- ); - } //-------------------------------------------------------------------------- // // Private State/Props @@ -156,30 +193,81 @@ export class Card implements ConditionalSlotComponent, LocalizedComponent, T9nCo @State() defaultMessages: CardMessages; + @State() private hasContent = false; + + private containerEl: HTMLDivElement; + //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- - private cardSelectClick = (): void => { - this.selectCard(); + private handleDefaultSlotChange = (event: Event): void => { + this.hasContent = slotChangeHasAssignedElement(event); + }; + + private keyDownHandler = (event: KeyboardEvent): void => { + if (event.target === this.containerEl && !this.selectable && !this.disabled) { + if (isActivationKey(event.key) && this.selectionMode !== "none") { + this.calciteCardSelect.emit(); + event.preventDefault(); + } else { + switch (event.key) { + case "ArrowRight": + case "ArrowLeft": + case "Home": + case "End": + this.calciteInternalCardKeyEvent.emit(event); + event.preventDefault(); + break; + } + } + } + }; + + private cardBodyClickHandler = (event: MouseEvent): void => { + const isFromScreenReader = event.target === this.containerEl; + if (isFromScreenReader && !this.selectable && !this.disabled && this.selectionMode !== "none") { + this.calciteCardSelect.emit(); + } }; - private cardSelectKeyDown = (event: KeyboardEvent): void => { + private renderCheckboxDeprecated(): VNode { + return ( + + + + ); + } + + private cardSelectKeyDownDeprecated = (event: KeyboardEvent): void => { switch (event.key) { case " ": case "Enter": - this.selectCard(); + this.selectCardDeprecated(); event.preventDefault(); break; } }; - private selectCard() { + private selectCardDeprecated = (): void => { this.selected = !this.selected; this.calciteCardSelect.emit(); - } + }; + + private cardSelectClick = (event): void => { + if (!this.disabled) { + event.preventDefault(); + this.calciteCardSelect.emit(); + this.setFocus(); + } + }; private renderThumbnail(): VNode { return getSlotted(this.el, SLOTS.thumbnail) ? ( @@ -189,28 +277,41 @@ export class Card implements ConditionalSlotComponent, LocalizedComponent, T9nCo ) : null; } - private renderCheckbox(): VNode { + private renderSelectionIcon(): VNode { + const icon = + this.selectionMode === "multiple" && this.selected + ? ICONS.selected + : this.selectionMode === "multiple" + ? ICONS.unselected + : this.selected + ? ICONS.selectedSingle + : ICONS.unselectedSingle; + return ( - - - +
+ +
); } private renderHeader(): VNode { const { el } = this; - const title = getSlotted(el, SLOTS.title); + const heading = getSlotted(el, SLOTS.heading); + const description = getSlotted(el, SLOTS.description); + const hasHeader = heading || description; const subtitle = getSlotted(el, SLOTS.subtitle); - const hasHeader = title || subtitle; - - return hasHeader ? ( + const title = getSlotted(el, SLOTS.title); + const hasDeprecatedHeader = subtitle || title; + return hasHeader || hasDeprecatedHeader ? (
- - + {this.selectable ? this.renderCheckboxDeprecated() : null} +
+ + + + +
+ {this.selectionMode !== "none" && this.renderSelectionIcon()}
) : null; } @@ -228,4 +329,53 @@ export class Card implements ConditionalSlotComponent, LocalizedComponent, T9nCo ) : null; } + + render(): VNode { + const thumbnailInline = this.thumbnailPosition.startsWith("inline"); + const thumbnailStart = this.thumbnailPosition.endsWith("start"); + const role = + this.selectionMode === "multiple" + ? "checkbox" + : this.selectionMode !== "none" + ? "radio" + : undefined; + return ( + + +
(this.containerEl = el)} + > + {this.loading ? ( +
+ +
+ ) : null} + {thumbnailStart && this.renderThumbnail()} +
+ {this.renderHeader()} +
+ +
+ {this.renderFooter()} +
+ {!thumbnailStart && this.renderThumbnail()} +
+
+
+ ); + } } diff --git a/packages/calcite-components/src/components/card/resources.ts b/packages/calcite-components/src/components/card/resources.ts index 70ee6825481..9f36ac2a531 100644 --- a/packages/calcite-components/src/components/card/resources.ts +++ b/packages/calcite-components/src/components/card/resources.ts @@ -1,17 +1,31 @@ export const CSS = { container: "container", + contentWrapper: "content-wrapper", header: "header", footer: "footer", - title: "title", - subtitle: "subtitle", + heading: "heading", + description: "description", checkboxWrapper: "checkbox-wrapper", + checkboxWrapperDeprecated: "checkbox-wrapper-deprecated", thumbnailWrapper: "thumbnail-wrapper", + headerTextContainer: "header-text-container", + cardContent: "card-content", + hasSlottedContent: "has-slotted-content", }; export const SLOTS = { thumbnail: "thumbnail", - title: "title", - subtitle: "subtitle", + heading: "heading", + description: "description", footerStart: "footer-start", footerEnd: "footer-end", + title: "title", + subtitle: "subtitle", +}; + +export const ICONS = { + selected: "check-square-f", + unselected: "square", + selectedSingle: "circle-f", + unselectedSingle: "circle", }; diff --git a/packages/calcite-components/src/components/chip-group/readme.md b/packages/calcite-components/src/components/chip-group/readme.md index e1c0bbe5cac..b788ac5c2e4 100644 --- a/packages/calcite-components/src/components/chip-group/readme.md +++ b/packages/calcite-components/src/components/chip-group/readme.md @@ -1,4 +1,4 @@ -# calcite-tile-select-group +# calcite-chip-group diff --git a/packages/calcite-components/src/components/color-picker-hex-input/color-picker-hex-input.scss b/packages/calcite-components/src/components/color-picker-hex-input/color-picker-hex-input.scss index 26d02a4ad1b..28dee7a5d32 100644 --- a/packages/calcite-components/src/components/color-picker-hex-input/color-picker-hex-input.scss +++ b/packages/calcite-components/src/components/color-picker-hex-input/color-picker-hex-input.scss @@ -1,3 +1,26 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-filter-input-prefix-background-color: defines the background color of the prefix in the input sub-component. + * @prop --calcite-filter-input-prefix-text-color: defines the text color of the prefix in the input sub-component. + * @prop --calcite-filter-input-suffix-background-color: defines the background color of the suffix in the input sub-component. + * @prop --calcite-filter-input-suffix-text-color: defines the text color of the suffix in the input sub-component. + * @prop --calcite-hex-input-background-color: defines the background color of the input sub-component. + * @prop --calcite-hex-input-border-color: defines the border color of the input sub-component. + * @prop --calcite-hex-input-button-background-color: defines the background color of the button in the input sub-component. + * @prop --calcite-hex-input-button-background-color-hover: defines the background color of the button when hovered in the input sub-component. + * @prop --calcite-hex-input-button-border-color: defines the border color of the button in the input sub-component. + * @prop --calcite-hex-input-button-icon-color: defines the icon color of the button in the input sub-component. + * @prop --calcite-hex-input-button-icon-color-active: defines the icon color of the button when active in the input sub-component. + * @prop --calcite-hex-input-button-icon-color-hover: defines the icon color of the button when hovered in the input sub-component. + * @prop --calcite-hex-input-corner-radius: defines the corner radius of the input sub-component. + * @prop --calcite-hex-input-icon-color: defines the icon color of the input sub-component. + * @prop --calcite-hex-input-text-color: defines the text color of the input sub-component. + * +*/ + :host { @apply block; } @@ -32,4 +55,21 @@ } } +calcite-input { + --calcite-input-suffix-background-color: var(--calcite-hex-input-suffix-background-color); + --calcite-input-suffix-text-color: var(--calcite-hex-input-suffix-text-color); + --calcite-input-prefix-background-color: var(--calcite-hex-input-prefix-background-color); + --calcite-input-prefix-text-color: var(--calcite-hex-input-prefix-text-color); + --calcite-input-text-color: var(--calcite-hex-input-text-color); + --calcite-input-background-color: var(--calcite-hex-input-background-color); + --calcite-input-border-color: var(--calcite-hex-input-border-color); + --calcite-input-corner-radius: var(--calcite-hex-input-corner-radius); + --calcite-input-button-border-color: var(--calcite-hex-input-button-border-color); + --calcite-input-button-background-color: var(--calcite-hex-input-button-background-color); + --calcite-input-button-background-color-hover: var(--calcite-hex-input-button-background-color-hover); + --calcite-input-button-icon-color: var(--calcite-hex-input-button-icon-color); + --calcite-input-button-icon-color-hover: var(--calcite-hex-input-button-icon-color-hover); + --calcite-input-button-icon-color-active: var(--calcite-hex-input-button-icon-color-active); +} + @include base-component(); diff --git a/packages/calcite-components/src/components/combobox/combobox.scss b/packages/calcite-components/src/components/combobox/combobox.scss index 36bf18294a8..12d8acd18ea 100644 --- a/packages/calcite-components/src/components/combobox/combobox.scss +++ b/packages/calcite-components/src/components/combobox/combobox.scss @@ -181,7 +181,7 @@ } .floating-ui-container { - @include floatingUIContainer("--calcite-z-index-dropdown"); + @include floatingUIContainer("var(--calcite-z-index-dropdown)"); @include floatingUIWrapper(); } diff --git a/packages/calcite-components/src/components/dropdown/dropdown.scss b/packages/calcite-components/src/components/dropdown/dropdown.scss index 83d43417560..44d91b0e40c 100644 --- a/packages/calcite-components/src/components/dropdown/dropdown.scss +++ b/packages/calcite-components/src/components/dropdown/dropdown.scss @@ -13,7 +13,7 @@ @include disabled(); :host .calcite-dropdown-wrapper { - @include floatingUIContainer("--calcite-z-index-dropdown"); + @include floatingUIContainer("var(--calcite-z-index-dropdown)"); @include floatingUIWrapper(); } diff --git a/packages/calcite-components/src/components/filter/filter.scss b/packages/calcite-components/src/components/filter/filter.scss index bad4129e18a..01064b9c003 100644 --- a/packages/calcite-components/src/components/filter/filter.scss +++ b/packages/calcite-components/src/components/filter/filter.scss @@ -1,3 +1,22 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-filter-input-background-color: defines the background color of the input sub-component. + * @prop --calcite-filter-input-border-color: defines the border color of the input sub-component. + * @prop --calcite-filter-input-button-background-color: defines the background color of the button in the input sub-component. + * @prop --calcite-filter-input-button-background-color-hover: defines the background color of the button when hovered in the input sub-component. + * @prop --calcite-filter-input-button-border-color: defines the border color of the button in the input sub-component. + * @prop --calcite-filter-input-button-icon-color: defines the icon color of the button in the input sub-component. + * @prop --calcite-filter-input-button-icon-color-active: defines the icon color of the button when active in the input sub-component. + * @prop --calcite-filter-input-button-icon-color-hover: defines the icon color of the button when hovered in the input sub-component. + * @prop --calcite-filter-input-corner-radius: defines the corner radius of the input sub-component. + * @prop --calcite-filter-input-icon-color: defines the icon color of the input sub-component. + * @prop --calcite-filter-input-placeholder-text-color: defines the placeholder text color of the input sub-component. + * @prop --calcite-filter-input-text-color: defines the text color of the input sub-component. + * +*/ :host { @extend %component-host; @apply flex w-full; @@ -38,7 +57,19 @@ input[type="text"] { } calcite-input { - @apply w-full; + --calcite-input-text-color: var(--calcite-filter-input-text-color); + --calcite-input-background-color: var(--calcite-filter-input-background-color); + --calcite-input-border-color: var(--calcite-filter-input-border-color); + --calcite-input-corner-radius: var(--calcite-filter-input-corner-radius); + --calcite-input-placeholder-text-color: var(--calcite-filter-input-placeholder-text-color); + --calcite-input-icon-color: var(--calcite-filter-input-icon-color); + --calcite-input-button-border-color: var(--calcite-filter-input-button-border-color); + --calcite-input-button-background-color: var(--calcite-filter-input-button-background-color); + --calcite-input-button-background-color-hover: var(--calcite-filter-input-button-background-color-hover); + --calcite-input-button-icon-color: var(--calcite-filter-input-button-icon-color); + --calcite-input-button-icon-color-hover: var(--calcite-filter-input-button-icon-color-hover); + --calcite-input-button-icon-color-active: var(--calcite-filter-input-button-icon-color-active); + inline-size: var(--calcite-container-size-content-fluid); } .search-icon { diff --git a/packages/calcite-components/src/components/flow-item/flow-item.scss b/packages/calcite-components/src/components/flow-item/flow-item.scss index 14045b2ec1c..2b9415d1659 100644 --- a/packages/calcite-components/src/components/flow-item/flow-item.scss +++ b/packages/calcite-components/src/components/flow-item/flow-item.scss @@ -3,6 +3,9 @@ * * These properties can be overridden using the component's tag as selector. * + * @prop --calcite-flow-item-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-flow-item-action-background-color-hover: defines the background color of an action sub-component is hovered or focused inside the component. + * @prop --calcite-flow-item-action-background-color-active: defines the background color of an action sub-component is active inside the component. * @prop --calcite-flow-item-background-color: Specifies the background color of the component. * @prop --calcite-flow-item-border-color: Specifies the border color of the component. * @prop --calcite-flow-item-description-text-color: Specifies the text color of the component's description. @@ -12,32 +15,18 @@ * @prop --calcite-flow-item-footer-space: Specifies the spacing of the component's footer. * @prop --calcite-flow-item-header-background-color: Specifies the component header's background color. * @prop --calcite-flow-item-header-border-block-end: [Deprecated] No longer necessary. Specifies the component header's block end border. - * @prop --calcite-flow-item-heading-text-color: Specifies the component's heading text color. * @prop --calcite-flow-item-header-z-index: Specifies the component header's z-index. + * @prop --calcite-flow-item-heading-text-color: Specifies the component's heading text color. + * */ :host { @include base-host(); - --calcite-flow-item-border-color: var(--calcite-color-border-3); - --calcite-flow-item-description-text-color: var(--calcite-color-text-2); - --calcite-flow-item-fab-z-index: var(--calcite-z-index-sticky); - --calcite-flow-item-footer-background-color: var(--calcite-color-foreground-1); - --calcite-flow-item-footer-space: var(--calcite-flow-item-footer-padding, var(--calcite-spacing-xxs)); - --calcite-flow-item-heading-text-color: var(--calcite-color-text-2); - --calcite-flow-item-header-background-color: var(--calcite-color-foreground-1); - --calcite-flow-item-header-z-index: var(--calcite-z-index-header); - --calcite-flow-item-header-border-width: var(--calcite-border-width-sm); - --calcite-flow-item-header-border-block-end: var(--calcite-border-width-sm) solid - var(--calcite-flow-item-border-color); - - --calcite-internal-flow-item-back-border-inline-end-width: var(--calcite-border-width-sm); - --calcite-internal-flow-item-back-border-color: var(--calcite-color-border-3); - - background-color: var(--calcite-flow-item-background-color); + background-color: var(--calcite-flow-item-background-color, var(--calcite-color-background)); font-size: var(--calcite-font-size--1); position: relative; display: flex; - inline-size: 100%; + inline-size: var(--calcite-container-size-content-fluid); flex: 1 1 auto; overflow: hidden; } @@ -45,10 +34,10 @@ @include disabled(); .back-button { - border-color: var(--calcite-internal-flow-item-back-border-color); + border-color: var(--calcite-flow-item-border-color, var(--calcite-color-border-3)); border-width: 0px; border-style: solid; - border-inline-end-width: var(--calcite-internal-flow-item-back-border-inline-end-width); + border-inline-end-width: var(--calcite-border-width-sm); } calcite-panel { @@ -57,7 +46,7 @@ calcite-panel { --calcite-panel-description-text-color: var(--calcite-flow-item-description-text-color); --calcite-panel-fab-z-index: var(--calcite-flow-item-fab-z-index); --calcite-panel-footer-background-color: var(--calcite-flow-item-footer-background-color); - --calcite-panel-footer-space: var(--calcite-flow-item-footer-space); + --calcite-panel-footer-space: var(--calcite-flow-item-footer-space, var(--calcite-flow-item-footer-padding)); --calcite-panel-heading-text-color: var(--calcite-flow-item-heading-text-color); --calcite-panel-header-background-color: var(--calcite-flow-item-header-background-color); --calcite-panel-header-z-index: var(--calcite-flow-item-header-z-index); @@ -65,4 +54,17 @@ calcite-panel { --calcite-panel-header-border-block-end: var(--calcite-flow-item-header-border-block-end); } +calcite-action { + --calcite-action-background-color: var(--calcite-flow-item-action-background-color); + + &:hover, + &:focus { + --calcite-action-background-color: var(--calcite-flow-item-action-background-color-hover); + } + + &:active { + --calcite-action-background-color: var(--calcite-flow-item-action-background-color-active); + } +} + @include base-component(); diff --git a/packages/calcite-components/src/components/flow/flow.scss b/packages/calcite-components/src/components/flow/flow.scss index 6ec7479511a..c46858d1a58 100755 --- a/packages/calcite-components/src/components/flow/flow.scss +++ b/packages/calcite-components/src/components/flow/flow.scss @@ -2,22 +2,16 @@ @include base-host(); position: relative; display: flex; - inline-size: 100%; + inline-size: var(--calcite-container-size-content-fluid); flex: 1 1 auto; align-items: stretch; overflow: hidden; - --calcite-internal-flow-animation-opacity-start: var(--calcite-opacity-50); - --calcite-internal-flow-animation-opacity-end: var(--calcite-opacity-100); - --calcite-internal-flow-animation-timing: var(--calcite-animation-timing); - --calcite-internal-flow-animation-advance: calcite-frame-advance var(--calcite-internal-flow-animation-timing); - --calcite-internal-flow-animation-retreat: calcite-frame-retreat var(--calcite-internal-flow-animation-timing); - .frame { position: relative; display: flex; margin: 0; - inline-size: 100%; + inline-size: var(--calcite-container-size-content-fluid); flex: 1 1 auto; flex-direction: column; align-items: stretch; @@ -26,7 +20,7 @@ ::slotted(calcite-flow-item), ::slotted(calcite-panel) { - block-size: 100%; + block-size: var(--calcite-container-size-content-fluid); } ::slotted(.calcite-match-height:last-child) { @@ -36,31 +30,31 @@ } .frame--advancing { - animation: var(--calcite-internal-flow-animation-advance); + animation: calcite-frame-advance var(--calcite-animation-timing); } .frame--retreating { - animation: var(--calcite-internal-flow-animation-retreat); + animation: calcite-frame-retreat var(--calcite-animation-timing); } @keyframes calcite-frame-advance { 0% { - opacity: var(--calcite-internal-flow-animation-opacity-start); + opacity: var(--calcite-opacity-50); transform: translate3d(50px, 0, 0); } 100% { - opacity: var(--calcite-internal-flow-animation-opacity-end); + opacity: var(--calcite-opacity-100); transform: translate3d(0, 0, 0); } } @keyframes calcite-frame-retreat { 0% { - opacity: var(--calcite-internal-flow-animation-opacity-start); + opacity: var(--calcite-opacity-50); transform: translate3d(-50px, 0, 0); } 100% { - opacity: var(--calcite-internal-flow-animation-opacity-end); + opacity: var(--calcite-opacity-100); transform: translate3d(0, 0, 0); } } diff --git a/packages/calcite-components/src/components/handle/handle.scss b/packages/calcite-components/src/components/handle/handle.scss index 2ca02c8f619..0fc27d71c5c 100644 --- a/packages/calcite-components/src/components/handle/handle.scss +++ b/packages/calcite-components/src/components/handle/handle.scss @@ -1,3 +1,19 @@ +/** +* CSS Custom Properties +* +* These properties can be overridden using the component's tag as selector. +* +* @prop --calcite-handle-icon-color: Specifies the component's icon color. +* @prop --calcite-handle-icon-color-hover: Specifies the component's icon color on hover. +* @prop --calcite-handle-icon-color-focus: Specifies the component's icon color on focus. +* @prop --calcite-handle-icon-color-selected: Specifies the component's icon color when selected. + +* @prop --calcite-handle-background-color: Specifies the component's background color. +* @prop --calcite-handle-background-color-hover: Specifies the component's background color on hover. +* @prop --calcite-handle-background-color-focus: Specifies the component's background color on focus. +* @prop --calcite-handle-background-color-selected: Specifies the component's background color when selected. +*/ + :host { @apply flex; } @@ -8,12 +24,15 @@ items-center justify-center self-stretch - border-none - bg-transparent; - color: theme("borderColor.color.input"); + border-none; + padding-block: theme("spacing.3"); padding-inline: theme("spacing.1"); line-height: 0; + cursor: move; + + background-color: var(--calcite-handle-background-color, transparent); + color: var(--calcite-handle-icon-color, var(--calcite-color-border-input)); calcite-icon { color: inherit; @@ -23,16 +42,23 @@ :host(:not([disabled])) .handle { @apply cursor-move; &:hover { - @apply bg-foreground-2 text-color-1; + color: var(--calcite-handle-icon-color-hover, var(--calcite-color-text-1)); + background-color: var(--calcite-handle-background-color-hover, var(--calcite-color-foreground-2)); } &:focus { - @apply text-color-1 focus-inset; + @apply focus-inset; + color: var(--calcite-handle-icon-color-focus, var(--calcite-color-text-1)); } &--selected { - @apply bg-foreground-3 text-color-1; + color: var(--calcite-handle-icon-color-selected, var(--calcite-color-text-1)); + background-color: var(--calcite-handle-background-color-selected, var(--calcite-color-foreground-3)); } } +:host([disabled]) { + cursor: pointer; +} + @include disabled(); @include base-component(); diff --git a/packages/calcite-components/src/components/handle/handle.stories.ts b/packages/calcite-components/src/components/handle/handle.stories.ts index 99174cdbb9e..fc1a32ebe0c 100644 --- a/packages/calcite-components/src/components/handle/handle.stories.ts +++ b/packages/calcite-components/src/components/handle/handle.stories.ts @@ -11,6 +11,17 @@ export default { export const simple = (): string => html` `; -export const activated_TestOnly = (): string => html` `; +export const themedStates_TestOnly = (): string => html` + +`; diff --git a/packages/calcite-components/src/components/inline-editable/inline-editable.scss b/packages/calcite-components/src/components/inline-editable/inline-editable.scss index 86e059cd8a8..ee8e82de397 100755 --- a/packages/calcite-components/src/components/inline-editable/inline-editable.scss +++ b/packages/calcite-components/src/components/inline-editable/inline-editable.scss @@ -1,5 +1,3 @@ -@import "~@esri/calcite-design-tokens/dist/scss/core"; - /** * CSS Custom Properties * diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss b/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss index ac808f57d25..b95d1886cfe 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss @@ -1,12 +1,31 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-input-background-color: defines the background color of the component. + * @prop --calcite-input-border-color: defines the border color of the component. + * @prop --calcite-input-button-background-color: defines the background color of a button in the component. + * @prop --calcite-input-button-background-color-hover: defines the background color of a button in the component when it's hovered. + * @prop --calcite-input-button-border-color: defines the border color of a button in the component. + * @prop --calcite-input-button-icon-color: defines the icon color of a button in the component. + * @prop --calcite-input-button-icon-color-active: defines the icon color of a button in the component when it's active. + * @prop --calcite-input-button-icon-color-hover: defines the icon color of a button in the component when it's hovered. + * @prop --calcite-input-corner-radius: defines the corner radius of the component. + * @prop --calcite-input-icon-color: defines the icon color of the component. + * @prop --calcite-input-placeholder-text-color: defines the color of placeholder text in the component. + * @prop --calcite-input-text-color: defines the text color of the component. + * + */ + :host { - @extend %component-spacing; @include floatingUIElemAnim(".menu-container"); @apply relative inline-block - w-full overflow-visible align-top shadow-none; + inline-size: var(--calcite-container-size-content-fluid); } @function get-trailing-text-input-padding($chevron-spacing) { @@ -56,9 +75,7 @@ } .horizontal-arrow-container { - @apply bg-background - border-color-input - flex + @apply flex items-center border border-l-0 @@ -66,12 +83,15 @@ border-solid py-0 px-1; + + background-color: var(--calcite-input-button-background-color, var(--calcite-color-background)); + border-color: var(--calcite-input-border-color, var(--calcite-color-border-input)); } } :host([range][layout="vertical"]) { .input-wrapper { - @apply w-full; + inline-size: var(--calcite-container-size-content-fluid); } .input-container { @@ -85,17 +105,17 @@ .vertical-arrow-container { inset-block-start: theme("spacing.6"); - @apply bg-foreground-1 - absolute + @apply absolute z-default mx-px px-2.5; + background-color: var(--calcite-input-button-background-color, var(--calcite-color-foreground-1)); inset-inline-start: 0; } } .menu-container { - @include floatingUIContainer("--calcite-z-index-dropdown"); + @include floatingUIContainer("var(--calcite-z-index-dropdown)"); @include floatingUIWrapper(); @apply invisible; } diff --git a/packages/calcite-components/src/components/input-message/input-message.scss b/packages/calcite-components/src/components/input-message/input-message.scss index 140d88f86ed..2267a37dc51 100644 --- a/packages/calcite-components/src/components/input-message/input-message.scss +++ b/packages/calcite-components/src/components/input-message/input-message.scss @@ -3,34 +3,42 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-input-message-spacing-value: The top margin spacing above the component. + * @prop --calcite-input-message-icon-color: defines the icon color in the component. + * @prop --calcite-input-message-spacing-value: *deprecated* The top margin spacing above the component. + * @prop --calcite-input-message-text-color: defines the text color of the component. + * */ :host { - @apply text-color-1 transition-default box-border flex h-auto w-full items-center font-medium opacity-100; - --calcite-input-message-spacing-value: theme("spacing.1"); - margin-block-start: var(--calcite-input-message-spacing-value); + @apply transition-default box-border flex h-auto items-center font-medium; + margin-block-start: var( + --calcite-input-message-spacing-value, + var(--calcite-internal-input-message-space, theme("spacing.1")) + ); + color: var(--calcite-input-message-text-color, var(--calcite-color-text-1)); + inline-size: var(--calcite-container-size-content-fluid); + opacity: var(--calcite-opacity-full); } -.calcite-input-message-icon { +calcite-icon { @apply transition-default pointer-events-none inline-flex flex-shrink-0; margin-inline-end: theme("margin.2"); } -:host([status="invalid"]) .calcite-input-message-icon { - color: var(--calcite-color-status-danger); +:host([status="invalid"]) calcite-icon { + color: var(--calcite-input-message-icon-color, var(--calcite-color-status-danger)); } -:host([status="warning"]) .calcite-input-message-icon { - color: var(--calcite-color-status-warning); +:host([status="warning"]) calcite-icon { + color: var(--calcite-input-message-icon-color, var(--calcite-color-status-warning)); } -:host([status="valid"]) .calcite-input-message-icon { - color: var(--calcite-color-status-success); +:host([status="valid"]) calcite-icon { + color: var(--calcite-input-message-icon-color, var(--calcite-color-status-success)); } -:host([status="idle"]) .calcite-input-message-icon { - color: var(--calcite-color-brand); +:host([status="idle"]) calcite-icon { + color: var(--calcite-input-message-icon-color, var(--calcite-color-brand)); } :host([scale="s"]) { diff --git a/packages/calcite-components/src/components/input-number/input-number.scss b/packages/calcite-components/src/components/input-number/input-number.scss index d349bfeaec0..b15481f6149 100755 --- a/packages/calcite-components/src/components/input-number/input-number.scss +++ b/packages/calcite-components/src/components/input-number/input-number.scss @@ -1,424 +1,27 @@ -:host { - @apply block; -} - -// scales -:host([scale="s"]) { - & input, - & .prefix, - & .suffix { - @apply text-n2h h-6 px-2; - } - & .number-button-wrapper, - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-6; - } - & .clear-button { - min-block-size: theme("spacing.6"); - min-inline-size: theme("spacing.6"); - } -} - -:host([scale="m"]) { - & input, - & .prefix, - & .suffix { - @apply text-n1h h-8 px-3; - } - & .number-button-wrapper, - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-8; - } - & .clear-button { - min-block-size: theme("spacing.8"); - min-inline-size: theme("spacing.8"); - } -} - -:host([scale="l"]) { - & input, - & .prefix, - & .suffix { - @apply text-0h h-11 px-4; - } - & .number-button-wrapper, - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-11; - } - & .clear-button { - min-block-size: theme("spacing.11"); - min-inline-size: theme("spacing.11"); - } -} - -@include disabled(); - -input { - transition: - var(--calcite-animation-timing), - block-size 0, - outline-offset 0s; - -webkit-appearance: none; - @apply bg-foreground-1 - box-border - flex - flex-1 - font-inherit - font-normal - m-0 - max-h-full - max-w-full - relative - rounded-none - text-color-1 - text-ellipsis - w-full; - - &:placeholder-shown { - @apply text-ellipsis; - } -} - -// states -input { - @apply text-color-1 - border-color-input - border - border-solid; - &::placeholder, - &:-ms-input-placeholder, - &::-ms-input-placeholder { - @apply text-color-3 font-normal; - } -} -input:focus { - @apply border-color-brand text-color-1; -} -input[readonly] { - @apply bg-background font-medium; -} -input[readonly]:focus { - @apply text-color-1; -} -calcite-icon { - @apply text-color-3; -} - -//focus -input { - @apply focus-base; -} - -input:focus { - @apply focus-inset; -} - -:host([status="invalid"]) { - & input { - @apply border-color-danger; - } - & input:focus { - @apply focus-inset-danger; - } -} - -// ICONS - -// position icons - -:host([scale="s"]) .icon { - inset-inline-start: theme("spacing.2"); -} - -:host([scale="m"]) .icon { - inset-inline-start: theme("spacing.3"); -} - -:host([scale="l"]) .icon { - inset-inline-start: theme("spacing.4"); -} - -// position placeholder/value text in relation to icons - -:host([icon][scale="s"]) input { - padding-inline-start: theme("padding.8"); -} - -:host([icon][scale="m"]) input { - padding-inline-start: theme("padding.10"); -} - -:host([icon][scale="l"]) input { - padding-inline-start: theme("padding.14"); -} - -// positioning wrapper for icon and loader - -.element-wrapper { - @apply relative - order-3 - inline-flex - flex-1 - items-center; -} - -.icon { - @apply transition-default - pointer-events-none - absolute - block - - z-default; // needed for firefox to display the icon properly -} - -.clear-button { - pointer-events: initial; - @apply focus-base - border-color-input - bg-foreground-1 - order-4 - m-0 - box-border - flex - min-h-full - cursor-pointer - items-center - justify-center - self-stretch - border - border-solid; - - border-inline-start-width: theme("borderWidth.0"); - - &:hover { - @apply bg-foreground-2 transition-default; - calcite-icon { - @apply text-color-1 transition-default; - } - } - &:active { - @apply bg-foreground-3; - calcite-icon { - @apply text-color-1; - } - } - &:focus { - @apply focus-inset; - } - &:disabled { - @apply opacity-disabled; - } -} - -// loading -.loader { - inset-block-start: 1px; - inset-inline: 1px; - @apply pointer-events-none - absolute - block; -} - -// slotted action -.action-wrapper { - @apply order-7 flex; -} - -// prefix and suffix -.prefix, -.suffix { - @apply border-color-input - bg-background - text-color-2 - box-border - flex - h-auto - min-h-full - select-none - content-center - items-center - break-words - border - border-solid - font-medium - leading-none; -} - -.prefix { - @apply order-2; - border-inline-end-width: theme("borderWidth.0"); -} -.suffix { - @apply order-5; - border-inline-start-width: theme("borderWidth.0"); -} - -// alignment type -:host([alignment="start"]) { - & input { - text-align: start; - } -} - -:host([alignment="end"]) { - & input { - text-align: end; - } -} - -.number-button-wrapper { - @apply transition-default - pointer-events-none - order-6 - box-border - flex - flex-col; -} - -:host([number-button-type="vertical"]) .wrapper { - flex-direction: row; - display: flex; -} - -:host([number-button-type="vertical"]) { - & input { - @apply order-2; - } -} - -:host([number-button-type="horizontal"]) .calcite--rtl { - & .number-button-item[data-adjustment="down"] calcite-icon { - transform: rotate(-90deg); - } - & .number-button-item[data-adjustment="up"] calcite-icon { - transform: rotate(-90deg); - } -} - -.number-button-item.number-button-item--horizontal { - &[data-adjustment="down"], - &[data-adjustment="up"] { - @apply order-1 - max-h-full - min-h-full - self-stretch; - & calcite-icon { - transform: rotate(90deg); - } - } -} - -.number-button-item.number-button-item--horizontal[data-adjustment="down"] { - @apply border-color-input - border - border-solid; - border-inline-end-width: theme("borderWidth.0"); - &:hover { - @apply bg-foreground-2; - calcite-icon { - @apply text-color-1; - } - } -} - -.number-button-item.number-button-item--horizontal[data-adjustment="up"] { - @apply order-5; - &:hover { - @apply bg-foreground-2; - calcite-icon { - @apply text-color-1; - } - } -} - -:host([number-button-type="vertical"]) .number-button-item[data-adjustment="down"]:hover { - @apply bg-foreground-2; - calcite-icon { - @apply text-color-1; - } -} - -:host([number-button-type="vertical"]) .number-button-item[data-adjustment="up"]:hover { - @apply bg-foreground-2; - calcite-icon { - @apply text-color-1; - } -} - -:host([number-button-type="vertical"]) .number-button-item[data-adjustment="down"] { - @apply border-t-0; -} - -.number-button-item { - max-block-size: 50%; - min-block-size: 50%; - pointer-events: initial; - @apply border-color-input - bg-foreground-1 - transition-default - m-0 - box-border - flex - cursor-pointer - items-center - self-center - border - border-solid - py-0 - px-2; - border-inline-start-width: theme("borderWidth.0"); - & calcite-icon { - @apply transition-default pointer-events-none; - } - &:focus { - @apply bg-foreground-2; - calcite-icon { - @apply text-color-1; - } - } - &:active { - @apply bg-foreground-3; - } - &:disabled { - @apply pointer-events-none; - } -} - -.wrapper { - @apply relative - flex - flex-row - items-center; -} - -:host(.no-bottom-border) input { - @apply border-b-0; -} - -:host(.border-top-color-one) input { - @apply border-t-color-1; -} - -// input needed for higher specificity of these overrides -input { - &.inline-child { - @apply transition-default bg-transparent; - .editing-enabled { - background-color: inherit; - } - } - - &.inline-child:not(.editing-enabled) { - @apply border-color-transparent - flex - cursor-pointer text-ellipsis; - padding-inline-start: 0; - } -} - -@include form-validation-message(); -@include hidden-form-input(); -@include base-component(); +@import "../input/common/mixins"; + +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-input-background-color: defines the background color of the component. + * @prop --calcite-input-border-color: defines the border color of the component. + * @prop --calcite-input-button-background-color: defines the background color of a button in the component. + * @prop --calcite-input-button-background-color-hover: defines the background color of a button in the component when it's hovered. + * @prop --calcite-input-button-border-color: defines the border color of a button in the component. + * @prop --calcite-input-button-icon-color: defines the icon color of a button in the component. + * @prop --calcite-input-button-icon-color-active: defines the icon color of a button in the component when it's active. + * @prop --calcite-input-button-icon-color-hover: defines the icon color of a button in the component when it's hovered. + * @prop --calcite-input-corner-radius: defines the corner radius of the component. + * @prop --calcite-input-icon-color: defines the icon color of the component. + * @prop --calcite-input-placeholder-text-color: defines the color of placeholder text in the component. + * @prop --calcite-input-prefix-background-color: defines the background color of the prefix sub-component. + * @prop --calcite-input-prefix-text-color: defines the text color of the prefix sub-component. + * @prop --calcite-input-suffix-background-color: defines the background color of the suffix sub-component. + * @prop --calcite-input-suffix-text-color: defines the text color of the suffix sub-component. + * @prop --calcite-input-text-color: defines the text color of the component. + * + */ + +@include addInputStyles("number"); diff --git a/packages/calcite-components/src/components/input-text/input-text.scss b/packages/calcite-components/src/components/input-text/input-text.scss index d46e7ec4315..83f27bd524f 100755 --- a/packages/calcite-components/src/components/input-text/input-text.scss +++ b/packages/calcite-components/src/components/input-text/input-text.scss @@ -1,348 +1,23 @@ -:host { - @apply block; -} - -// scales -:host([scale="s"]) { - & input { - padding-inline-start: theme("spacing.2"); - padding-inline-end: var(--calcite-internal-input-text-input-padding-inline-end, theme("spacing.2")); - } - - & input, - & .prefix, - & .suffix { - @apply text-n2h h-6; - } - - & .prefix, - & .suffix { - @apply px-2; - } - - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-6; - } - - & .clear-button { - min-block-size: theme("spacing.6"); - min-inline-size: theme("spacing.6"); - } -} - -:host([scale="m"]) { - & input { - padding-inline-start: theme("spacing.3"); - padding-inline-end: var(--calcite-internal-input-text-input-padding-inline-end, theme("spacing.3")); - } - - & input, - & .prefix, - & .suffix { - @apply text-n1h h-8; - } - - & .prefix, - & .suffix { - @apply px-3; - } - - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-8; - } - - & .clear-button { - min-block-size: theme("spacing.8"); - min-inline-size: theme("spacing.8"); - } -} - -:host([scale="l"]) { - & input { - padding-inline-start: theme("spacing.4"); - padding-inline-end: var(--calcite-internal-input-text-input-padding-inline-end, theme("spacing.4")); - } - - & input, - & .prefix, - & .suffix { - @apply text-0h h-11; - } - - & .prefix, - & .suffix { - @apply px-4; - } - - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-11; - } - - & .clear-button { - min-block-size: theme("spacing.11"); - min-inline-size: theme("spacing.11"); - } -} - -@include disabled(); - -input { - transition: - var(--calcite-animation-timing), - block-size 0, - outline-offset 0s; - -webkit-appearance: none; - @apply bg-foreground-1 - box-border - flex - flex-1 - font-inherit - font-normal - m-0 - max-h-full - max-w-full - relative - rounded-none - text-color-1 - text-ellipsis - w-full; - - &:placeholder-shown { - @apply text-ellipsis; - } -} - -// states -input { - @apply text-color-1 - border-color-input - border - border-solid; - &::placeholder, - &:-ms-input-placeholder, - &::-ms-input-placeholder { - @apply text-color-3 font-normal; - } -} -input:focus { - @apply border-color-brand text-color-1; -} -input[readonly] { - @apply bg-background font-medium; -} -input[readonly]:focus { - @apply text-color-1; -} -calcite-icon { - @apply text-color-3; -} - -//focus - -input { - @apply focus-base; -} -input:focus { - @apply focus-inset; -} - -:host([status="invalid"]) { - & input { - @apply border-color-danger; - } - & input:focus { - @apply focus-inset-danger; - } -} - -// ICONS - -// position icons - -:host([scale="s"]) .icon { - inset-inline-start: theme("spacing.2"); -} - -:host([scale="m"]) .icon { - inset-inline-start: theme("spacing.3"); -} - -:host([scale="l"]) .icon { - inset-inline-start: theme("spacing.4"); -} - -// position placeholder/value text in relation to icons - -:host([icon][scale="s"]) input { - padding-inline-start: theme("padding.8"); -} - -:host([icon][scale="m"]) input { - padding-inline-start: theme("padding.10"); -} - -:host([icon][scale="l"]) input { - padding-inline-start: theme("padding.14"); -} - -// positioning wrapper for icon and loader - -.element-wrapper { - @apply relative - order-3 - inline-flex - flex-1 - items-center; -} - -.icon { - @apply transition-default - pointer-events-none - absolute - block - - z-default; // needed for firefox to display the icon properly -} - -// hide browser default clear - -input[type="text"]::-ms-clear, -input[type="text"]::-ms-reveal { - @apply hidden - h-0 - w-0; -} - -.clear-button { - pointer-events: initial; - @apply focus-base - border-color-input - bg-foreground-1 - order-4 - m-0 - box-border - flex - min-h-full - cursor-pointer - items-center - justify-center - self-stretch - border - border-solid; - - border-inline-start-width: theme("borderWidth.0"); - - &:hover { - @apply bg-foreground-2 transition-default; - calcite-icon { - @apply text-color-1 transition-default; - } - } - &:active { - @apply bg-foreground-3; - calcite-icon { - @apply text-color-1; - } - } - &:focus { - @apply focus-inset; - } - &:disabled { - @apply opacity-disabled; - } -} - -// loading -.loader { - inset-block-start: 1px; - inset-inline: 1px; - @apply pointer-events-none - absolute - block; -} - -// slotted action -.action-wrapper { - @apply order-7 flex; -} - -// prefix and suffix -.prefix, -.suffix { - @apply border-color-input - bg-background - text-color-2 - box-border - flex - h-auto - min-h-full - select-none - content-center - items-center - break-words - border - border-solid - font-medium - leading-none; -} - -.prefix { - @apply order-2; - border-inline-end-width: theme("borderWidth.0"); -} -.suffix { - @apply order-5; - border-inline-start-width: theme("borderWidth.0"); -} - -// alignment type -:host([alignment="start"]) { - & input { - text-align: start; - } -} - -:host([alignment="end"]) { - & input { - text-align: end; - } -} - -.wrapper { - @apply relative - flex - flex-row - items-center; -} - -:host(.no-bottom-border) input { - @apply border-b-0; -} - -:host(.border-top-color-one) input { - @apply border-t-color-1; -} - -// input needed for higher specificity of these overrides -input { - &.inline-child { - @apply transition-default bg-transparent; - .editing-enabled { - background-color: inherit; - } - } - - &.inline-child:not(.editing-enabled) { - @apply border-color-transparent - flex - cursor-pointer text-ellipsis; - padding-inline-start: 0; - } -} - -@include form-validation-message(); -@include hidden-form-input(); -@include base-component(); +@import "../input/common/mixins"; + +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-input-background-color: defines the background color of the component. + * @prop --calcite-input-border-color: defines the border color of the component. + * @prop --calcite-input-button-background-color: defines the background color of a button in the component. + * @prop --calcite-input-button-background-color-hover: defines the background color of a button in the component when it's hovered. + * @prop --calcite-input-button-border-color: defines the border color of a button in the component. + * @prop --calcite-input-button-icon-color: defines the icon color of a button in the component. + * @prop --calcite-input-button-icon-color-active: defines the icon color of a button in the component when it's active. + * @prop --calcite-input-button-icon-color-hover: defines the icon color of a button in the component when it's hovered. + * @prop --calcite-input-corner-radius: defines the corner radius of the component. + * @prop --calcite-input-icon-color: defines the icon color of the component. + * @prop --calcite-input-placeholder-text-color: defines the color of placeholder text in the component. + * @prop --calcite-input-text-color: defines the text color of the component. + * + */ + +@include addInputStyles("text"); diff --git a/packages/calcite-components/src/components/input-time-picker/input-time-picker.scss b/packages/calcite-components/src/components/input-time-picker/input-time-picker.scss index 3ad1035368b..04d759ae691 100644 --- a/packages/calcite-components/src/components/input-time-picker/input-time-picker.scss +++ b/packages/calcite-components/src/components/input-time-picker/input-time-picker.scss @@ -1,3 +1,23 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-input-background-color: defines the background color of the component. + * @prop --calcite-input-border-color: defines the border color of the component. + * @prop --calcite-input-button-background-color: defines the background color of a button in the component. + * @prop --calcite-input-button-background-color-hover: defines the background color of a button in the component when it's hovered. + * @prop --calcite-input-button-border-color: defines the border color of a button in the component. + * @prop --calcite-input-button-icon-color: defines the icon color of a button in the component. + * @prop --calcite-input-button-icon-color-active: defines the icon color of a button in the component when it's active. + * @prop --calcite-input-button-icon-color-hover: defines the icon color of a button in the component when it's hovered. + * @prop --calcite-input-corner-radius: defines the corner radius of the component. + * @prop --calcite-input-icon-color: defines the icon color of the component. + * @prop --calcite-input-placeholder-text-color: defines the color of placeholder text in the component. + * @prop --calcite-input-text-color: defines the text color of the component. + * + */ + :host { @apply inline-block select-none; @@ -36,5 +56,17 @@ padding-inline: var(--calcite-toggle-spacing); } +calcite-popover { + // TODO: sub-component tokens go here +} + +calcite-time-picker { + // TODO: sub-component tokens go here +} + +calcite-icon { + // TODO: sub-component tokens go here +} + @include form-validation-message(); @include base-component(); diff --git a/packages/calcite-components/src/components/input-time-zone/input-time-zone.scss b/packages/calcite-components/src/components/input-time-zone/input-time-zone.scss index b3ac7411e63..31f1dc1e2b0 100644 --- a/packages/calcite-components/src/components/input-time-zone/input-time-zone.scss +++ b/packages/calcite-components/src/components/input-time-zone/input-time-zone.scss @@ -2,6 +2,14 @@ display: block; } +calcite-combobox { + // TODO: sub-component tokens goes here +} + +calcite-combobox-item { + // TODO: sub-component tokens goes here +} + @include base-component(); @include disabled(); @include hidden-form-input(); diff --git a/packages/calcite-components/src/components/input/common/_mixins.scss b/packages/calcite-components/src/components/input/common/_mixins.scss new file mode 100644 index 00000000000..7d4cb9a054c --- /dev/null +++ b/packages/calcite-components/src/components/input/common/_mixins.scss @@ -0,0 +1,532 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-input-text-color: defines the text color of the component. + * @prop --calcite-input-background-color: defines the background color of the component. + * @prop --calcite-input-border-color: defines the border color of the component. + * @prop --calcite-input-corner-radius: defines the corner radius of the component. + * @prop --calcite-input-shadow: defines the shadow of the component. + * + * @prop --calcite-input-placeholder-text-color: defines the color of placeholder text in the component. + * + * @prop --calcite-input-prefix-background-color: defines the background color of the prefix sub-component. + * @prop --calcite-input-prefix-text-color: defines the text color of the prefix sub-component. + * + * @prop --calcite-input-suffix-background-color: defines the background color of the suffix sub-component. + * @prop --calcite-input-suffix-text-color: defines the text color of the suffix sub-component. + * + * @prop --calcite-input-icon-color: defines the icon color of the component. + * + * @prop --calcite-input-button-border-color: defines the border color of a button in the component. + * @prop --calcite-input-button-background-color: defines the background color of a button in the component. + * @prop --calcite-input-button-background-color-hover: defines the background color of a button in the component when it's hovered. + * @prop --calcite-input-button-icon-color: defines the icon color of a button in the component. + * @prop --calcite-input-button-icon-color-hover: defines the icon color of a button in the component when it's hovered. + * @prop --calcite-input-button-icon-color-active: defines the icon color of a button in the component when it's active. + */ + +@mixin inputTextSpacing() { + font-size: var(--calcite-internal-input-font-size); + line-height: var(--calcite-internal-input-line-height); + padding-inline: var(--calcite-internal-input-spacing-inline); +} +@mixin baseInputStyles() { + transition: + var(--calcite-animation-timing), + block-size 0, + outline-offset 0s; + -webkit-appearance: none; + @apply font-inherit + relative + m-0 + box-border + flex + flex-1 + font-normal + text-ellipsis + focus-base; + border-style: solid; + border-color: var(--calcite-input-border-color, var(--calcite-color-border-input)); + border-width: var(--calcite-border-width-sm); + border-radius: var(--calcite-input-corner-radius, var(--calcite-corner-radius)); + inline-size: var(--calcite-container-size-content-fluid); + max-inline-size: var(--calcite-container-size-content-fluid); + max-block-size: var(--calcite-container-size-content-fluid); + color: var(--calcite-input-text-color, var(--calcite-color-text-1)); + background-color: var(--calcite-input-background-color, var(--calcite-color-foreground-1)); + text-align: var(--calcite-internal-input-text-align); + + &::placeholder, + &:-ms-input-placeholder, + &::-ms-input-placeholder { + @apply font-normal; + color: var(--calcite-input-placeholder-text-color, var(--calcite-color-text-3)); + } + &:placeholder-shown { + @apply text-ellipsis; + } + + &:focus { + @apply focus-inset; + border-color: var(--calcite-input-border-color, var(--calcite-color-brand)); + } + + &[readonly] { + @apply font-medium; + background-color: var(--calcite-input-background-color, var(--calcite-color-background)); + } +} +@mixin inputInvalidStyles() { + border-color: var(--calcite-input-border-color, var(--calcite-color-status-danger)); + + &:focus { + @apply focus-inset-danger; + } +} + +@mixin addInputStyles($type: "*") { + @include form-validation-message(); + @include hidden-form-input(); + @include base-component(); + + :host { + @apply block; + box-shadow: var(--calcite-input-shadow, var(--calcite-shadow-none)); + } + + @if not($type == "textarea") { + input { + @include baseInputStyles(); + @include inputTextSpacing; + } + } + @if ($type == "*") or ($type == "textarea") { + textarea { + @include baseInputStyles(); + @include inputTextSpacing; + } + } + @if not($type == "textarea") { + :host(.no-bottom-border) input { + border-block-end-width: var(--calcite-border-width-none); + } + + // Can this be deprecated? + :host(.border-top-color-one) input { + border-block-start-color: var(--calcite-color-border-1); + } + + // input needed for higher specificity of these overrides + input { + &.inline-child { + @apply transition-default; + background-color: var(--calcite-color-transparent); + + .editing-enabled { + background-color: inherit; + } + } + + &.inline-child:not(.editing-enabled) { + @apply border-color-transparent + flex + cursor-pointer text-ellipsis; + padding-inline-start: 0; + } + } + } + + @if not(type == "text") { + .prefix, + .suffix { + @apply box-border + flex + select-none + content-center + items-center + break-words + border-solid + font-medium + leading-none; + + @include inputTextSpacing; + align-self: stretch; + block-size: auto; + min-block-size: var(--calcite-container-size-content-fluid); + border-width: var(--calcite-border-width-sm); + border-color: var(--calcite-input-border-color, var(--calcite-color-border-input)); + } + .prefix { + @apply order-2; + color: var(--calcite-input-prefix-text-color, var(--calcite-color-text-2)); + background-color: var(--calcite-input-prefix-background-color, var(--calcite-color-background)); + border-inline-end-width: var(--calcite-border-width-none); + } + .suffix { + @apply order-5; + color: var(--calcite-input-prefix-text-color, var(--calcite-color-text-2)); + background-color: var(--calcite-input-suffix-background-color, var(--calcite-color-background)); + border-inline-start-width: var(--calcite-border-width-none); + } + } + + @if ($type == "*") or ($type == "textarea") { + textarea { + min-block-size: var(--calcite-internal-input-block-size); + block-size: auto; + padding-block: var(--calcite-internal-input-spacing-block); + + &::-webkit-resizer { + @apply absolute + bottom-0 + box-border + py-0 + px-1; + inset-inline-end: 0; + } + } + + .resize-icon-wrapper { + position: absolute; + z-index: var(--calcite-z-index); // needed for firefox to display the icon properly + inset-block-end: 2px; + inset-inline-end: 2px; + background-color: var(--calcite-input-background-color, var(--calcite-color-foreground-1)); + color: var(--calcite-input-icon-color, var(--calcite-color-text-3)); + block-size: 0.75rem /* 12px */; + inline-size: 0.75rem /* 12px */; + + calcite-icon { + inset-block-end: 0.25rem; + inset-inline-end: 0.25rem; + transform: rotate(-45deg); + } + } + + .calcite--rtl { + .resize-icon-wrapper { + calcite-icon { + transform: rotate(45deg); + } + } + } + } + + @if ($type == "*") or ($type == "text") { + input[type="text"]::-ms-clear, + input[type="text"]::-ms-reveal { + @apply hidden + h-0 + w-0; + } + } + + @if ($type == "*") or ($type == "search") { + input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; + } + + input[type="search"]::-webkit-search-decoration, + input[type="search"]::-webkit-search-cancel-button, + input[type="search"]::-webkit-search-results-button, + input[type="search"]::-webkit-search-results-decoration { + @apply hidden; + } + } + @if ($type == "*") or ($type == "date") { + input[type="date"]::-webkit-clear-button { + @apply hidden; + } + + // hide the default date picker + :input::-webkit-calendar-picker-indicator { + @apply hidden; + } + + input[type="date"]::-webkit-input-placeholder { + visibility: hidden !important; + } + } + @if ($type == "*") or ($type == "time") { + input[type="time"]::-webkit-clear-button { + @apply hidden; + } + } + + @if ($type == "*") or ($type == "number") { + // number buttons + input[type="number"] { + -moz-appearance: textfield; + + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + -moz-appearance: textfield; + @apply m-0; + } + } + + .number-button-wrapper { + @apply transition-default + pointer-events-none + order-6 + box-border + flex + flex-col; + block-size: var(--calcite-internal-input-block-size); + } + + .number-button-item { + padding-block: 0px; + padding-inline: 0.5rem /* 8px */; + } + } + + input, + ::slotted(calcite-button) { + block-size: var(--calcite-internal-input-block-size); + } + + .clear-button { + @apply order-4; + border-inline-start-width: var(--calcite-border-width-none); + inline-size: var(--calcite-internal-input-inline-size); + } + + .icon { + @apply transition-default + pointer-events-none + absolute + block + z-default; // needed for firefox to display the icon properly + inset-inline-start: var(--calcite-internal-input-spacing-inline); + } + + .loader { + @apply pointer-events-none + absolute + block; + inset-block-start: var(--calcite-size-px); + inset-inline: var(--calcite-size-px); + } + + .wrapper { + @apply relative + flex + flex-row + items-center; + } + + .element-wrapper { + @apply relative + order-3 + inline-flex + flex-1 + items-center; + } + + // slotted action + .action-wrapper { + @apply order-7 flex; + } + + @include disabled() { + @if ($type == "*") or ($type == "textarea") { + textarea { + resize: none; + } + } + } + + calcite-icon { + --calcite-icon-color: var(--calcite-input-icon-color, var(--calcite-color-text-3)); + } + + button { + @apply focus-base + m-0 + box-border + flex + cursor-pointer + items-center + justify-center + self-center + transition-default; + max-block-size: var(--calcite-container-size-content-fluid); + border-style: solid; + pointer-events: initial; + border-width: var(--calcite-border-width-sm); + border-color: var(--calcite-input-border-color, var(--calcite-color-border-input)); + min-block-size: var(--calcite-internal-input-block-size); + background-color: var(--calcite-input-button-background-color, var(--calcite-color-foreground-1)); + + calcite-icon { + @apply transition-default pointer-events-none; + } + + &:hover { + @apply transition-default; + background-color: var(--calcite-input-button-background-color-hover, var(--calcite-color-foreground-2)); + + calcite-icon { + @apply transition-default; + color: var(--calcite-input-button-icon-color-hover, var(--calcite-color-text-1)); + } + } + &:active { + background-color: var(--calcite-input-button-background-color-active, var(--calcite-color-foreground-3)); + + calcite-icon { + color: var(--calcite-input-button-icon-color-active, var(--calcite-color-text-1)); + } + } + &:focus { + @apply focus-inset; + } + &:disabled { + @apply pointer-events-none; + } + } + + // Changes by host attributes + + // Scale + :host([scale="s"]) { + --calcite-internal-input-font-size: var(--calcite-font-size--2); + --calcite-internal-input-line-height: 1rem /*16px*/; + --calcite-internal-input-block-size: 1.5rem /* 24px */; + --calcite-internal-input-inline-size: 1.5rem /* 24px */; + --calcite-internal-input-spacing-inline: 0.5rem /* 8px */; + --calcite-internal-input-spacing-block: 0.25rem /* 4px */; + --calcite-internal-input-spacing-inline-start: 2rem /* 32px */; + } + :host([scale="m"]) { + --calcite-internal-input-font-size: var(--calcite-font-size--1); + --calcite-internal-input-line-height: 1rem /*16px*/; + --calcite-internal-input-block-size: 2rem /* 24px */; + --calcite-internal-input-inline-size: 2rem /* 24px */; + --calcite-internal-input-spacing-inline: 0.75rem /* 12px */; + --calcite-internal-input-spacing-block: 0.5rem /* 8px */; + --calcite-internal-input-spacing-inline-start: 2.5rem /* 40px */; + } + :host([scale="l"]) { + --calcite-internal-input-font-size: var(--calcite-font-size-0); + --calcite-internal-input-line-height: 1.25rem /* 20px */; + --calcite-internal-input-block-size: 2.75rem /* 44px */; + --calcite-internal-input-inline-size: 2.75rem /* 44px */; + --calcite-internal-input-spacing-inline: 1rem /* 16px */; + --calcite-internal-input-spacing-block: 0.75rem /* 12px */; + --calcite-internal-input-spacing-inline-start: 3.5rem /* 56px */; + } + + //Icon + :host([icon]) { + input { + padding-inline-start: var(--calcite-internal-input-spacing-inline-start); + } + } + + // Status + :host([status="invalid"]) { + @if ($type == "*") or not($type == "textarea") { + input { + @include inputInvalidStyles; + } + } + @if ($type == "*") or ($type == "textarea") { + textarea { + @include inputInvalidStyles; + } + } + } + + // Alignment + :host([alignment="start"]) { + --calcite-internal-input-text-align: start; + } + :host([alignment="end"]) { + --calcite-internal-input-text-align: end; + } + + // Number step buttons "up" "down" + @if ($type == "*") or ($type == "number") { + :host([number-button-type="vertical"]) { + input { + @apply order-2; + } + .wrapper { + flex-direction: row; + display: flex; + } + .number-button-item { + max-block-size: 50%; + min-block-size: 50%; + border-inline-start-width: var(--calcite-border-width-none); + + &[data-adjustment="down"] { + border-block-start-width: var(--calcite-border-width-none); + } + } + } + :host([number-button-type="horizontal"]) { + .number-button-item { + &[data-adjustment="down"] { + @apply order-1; + border-inline-end-width: var(--calcite-border-width-none); + } + &[data-adjustment="up"] { + @apply order-5; + border-inline-start-width: var(--calcite-border-width-none); + } + + calcite-icon { + transform: rotate(90deg); + } + } + + .calcite--rtl { + .number-button-item { + &[data-adjustment="down"] calcite-icon { + transform: rotate(-90deg); + } + &[data-adjustment="up"] calcite-icon { + transform: rotate(-90deg); + } + } + } + } + } + + @if ($type == "*") or ($type == "color") { + :host([type="color"]) input { + @apply p-1; + } + } + + @if ($type == "*") or ($type == "file") { + :host([type="file"]) { + input { + @apply cursor-pointer + border-dashed + text-center; + border-width: var(--calcite-border-width-sm); + padding-inline: var(--calcite-internal-input-file-padding-inline); + padding-block: var(--calcite-internal-input-file-padding-block); + } + } + :host([type="file"][scale="s"]) { + --calcite-internal-input-file-padding-inline: 0.5rem /* 8px */; + --calcite-internal-input-file-padding-block: var(--calcite-size-px); + } + :host([type="file"][scale="m"]) { + --calcite-internal-input-file-padding-inline: 0.75rem; + --calcite-internal-input-file-padding-block: 0.25rem /* 4px */; + } + :host([type="file"][scale="l"]) { + --calcite-internal-input-file-padding-inline: 1rem; + --calcite-internal-input-file-padding-block: 0.5rem /* 8px */; + } + } +} diff --git a/packages/calcite-components/src/components/input/input.scss b/packages/calcite-components/src/components/input/input.scss index b6e19c31ee6..dd474bf1c54 100755 --- a/packages/calcite-components/src/components/input/input.scss +++ b/packages/calcite-components/src/components/input/input.scss @@ -1,718 +1,27 @@ +@import "./common/mixins"; + /** * CSS Custom Properties * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-input-corner-radius: defines the border radius of the component. - * @prop --calcite-input-text-color: defines the text color of the component. - * @prop --calcite-input-border-color: defines the border color of the component. * @prop --calcite-input-background-color: defines the background color of the component. - * @prop --calcite-input-button-background-color: defines the background color of a button element in the component. - * @prop --calcite-input-button-background-color-hover: defines the background color of a :hover-ed button element in the component. - * @prop --calcite-input-button-background-color-active: defines the background color of an :active button element in the component. - * @prop --calcite-input-icon-color: defines the color of an icon element in the component. - * @prop --calcite-input-button-icon-color-hover: defines the color of an icon element when it's parent is hovered in the component. - * @prop --calcite-input-prefix-text-color: defines the prefix text color in the component. - * @prop --calcite-input-prefix-background-color: defines the prefix background color in the component. - * @prop --calcite-input-suffix-text-color: defines the suffix text color in the component. - * @prop --calcite-input-suffix-background-color: defines the suffix background color in the component. + * @prop --calcite-input-border-color: defines the border color of the component. + * @prop --calcite-input-button-background-color: defines the background color of a button in the component. + * @prop --calcite-input-button-background-color-hover: defines the background color of a button in the component when it's hovered. + * @prop --calcite-input-button-border-color: defines the border color of a button in the component. + * @prop --calcite-input-button-icon-color: defines the icon color of a button in the component. + * @prop --calcite-input-button-icon-color-active: defines the icon color of a button in the component when it's active. + * @prop --calcite-input-button-icon-color-hover: defines the icon color of a button in the component when it's hovered. + * @prop --calcite-input-corner-radius: defines the corner radius of the component. + * @prop --calcite-input-icon-color: defines the icon color of the component. * @prop --calcite-input-placeholder-text-color: defines the color of placeholder text in the component. - * @prop --calcite-input-shadow: defines the box-shadow of the component. + * @prop --calcite-input-prefix-background-color: defines the background color of the prefix sub-component. + * @prop --calcite-input-prefix-text-color: defines the text color of the prefix sub-component. + * @prop --calcite-input-suffix-background-color: defines the background color of the suffix sub-component. + * @prop --calcite-input-suffix-text-color: defines the text color of the suffix sub-component. + * @prop --calcite-input-text-color: defines the text color of the component. * */ -:host { - --calcite-input-corner-radius: var(--calcite-corner-radius); - --calcite-input-text-color: var(--calcite-color-text-1); - --calcite-input-border-color: var(--calcite-color-border-input); - --calcite-input-background-color: var(--calcite-color-foreground-1); - --calcite-input-shadow: none; - // Button - --calcite-input-button-background-color: var(--calcite-color-foreground-1); - --calcite-input-button-background-color-hover: var(--calcite-color-foreground-2); - --calcite-input-button-background-color-active: var(--calcite-color-foreground-3); - - // Icon - --calcite-input-icon-color: var(--calcite-color-text-3); - --calcite-input-button-icon-color-hover: var(--calcite-color-text-1); - - // Prefix/Suffix - --calcite-input-prefix-text-color: var(--calcite-color-text-2); - --calcite-input-prefix-background-color: var(--calcite-color-background); - --calcite-input-suffix-text-color: var(--calcite-color-text-2); - --calcite-input-suffix-background-color: var(--calcite-color-background); - - // Placeholder - --calcite-input-placeholder-text-color: var(--calcite-color-text-3); - - // For props that should follow the initial border-color but not change on statechange. - --calcite-internal-input-border-color-base: var(--calcite-color-border-input); - - @apply block; - box-shadow: var(--calcite-input-shadow); -} - -// scales -:host([scale="s"]) { - & input, - & .prefix, - & .suffix { - @apply text-n2h h-6 px-2; - } - & textarea { - @apply h-6; - min-block-size: theme("spacing.6"); - } - & .number-button-wrapper, - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-6; - } - & input[type="file"] { - @apply h-6; - } - & .clear-button { - min-block-size: theme("spacing.6"); - min-inline-size: theme("spacing.6"); - } - & textarea { - @apply text-n2h - h-auto - py-1 - px-2; - } -} - -:host([scale="m"]) { - & input, - & .prefix, - & .suffix { - @apply text-n1h h-8 px-3; - } - & textarea { - min-block-size: theme("spacing.8"); - } - & .number-button-wrapper, - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-8; - } - & input[type="file"] { - @apply h-8; - } - & .clear-button { - min-block-size: theme("spacing.8"); - min-inline-size: theme("spacing.8"); - } - & textarea { - @apply text-n1h - h-auto - py-2 - px-3; - } -} - -:host([scale="l"]) { - & input, - & .prefix, - & .suffix { - @apply text-0h h-11 px-4; - } - & textarea { - min-block-size: theme("spacing.11"); - } - & .number-button-wrapper, - & .action-wrapper calcite-button, - & .action-wrapper calcite-button button { - @apply h-11; - } - & input[type="file"] { - @apply h-11; - } - & .clear-button { - min-block-size: theme("spacing.11"); - min-inline-size: theme("spacing.11"); - } - & textarea { - @apply text-0h - h-auto - py-3 - px-4; - } -} - -@include disabled() { - & textarea { - resize: none; - } -} - -textarea, -input { - @apply border font-inherit relative m-0 - box-border flex max-h-full w-full max-w-full flex-1 font-normal; - - transition: - var(--calcite-animation-timing), - block-size 0, - outline-offset 0s; - -webkit-appearance: none; - border-radius: var(--calcite-input-corner-radius); - color: var(--calcite-input-text-color); - border-color: var(--calcite-input-border-color); - background-color: var(--calcite-input-background-color); -} - -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -// states -input, -textarea { - @apply border-spacing-1 - border-solid - text-ellipsis; - &::placeholder, - &:-ms-input-placeholder, - &::-ms-input-placeholder { - @apply font-normal; - color: var(--calcite-input-placeholder-text-color); - } - &:placeholder-shown { - @apply text-ellipsis; - } -} -input[readonly], -textarea[readonly] { - @apply font-medium; -} -calcite-icon { - color: var(--calcite-input-icon-color); -} -button:hover, -button:active { - --calcite-input-icon-color: var(--calcite-input-button-icon-color-hover); -} - -//focus -textarea, -input { - @apply focus-base; -} -textarea:focus, -input:focus { - @apply focus-inset; -} - -:host([status="invalid"]) { - --calcite-input-border-color: var(--calcite-color-status-danger); - - & input:focus, - & textarea:focus { - @apply focus-inset-danger; - } -} - -// ICONS - -// position icons - -:host([scale="s"]) .icon { - inset-inline-start: theme("spacing.2"); -} - -:host([scale="m"]) .icon { - inset-inline-start: theme("spacing.3"); -} - -:host([scale="l"]) .icon { - inset-inline-start: theme("spacing.4"); -} - -// position placeholder/value text in relation to icons - -:host([icon][scale="s"]) input { - padding-inline-start: theme("padding.8"); -} - -:host([icon][scale="m"]) input { - padding-inline-start: theme("padding.10"); -} - -:host([icon][scale="l"]) input { - padding-inline-start: theme("padding.14"); -} - -// positioning wrapper for icon and loader - -.element-wrapper { - @apply relative - order-3 - inline-flex - flex-1 - items-center; -} - -.icon { - @apply transition-default - pointer-events-none - absolute - block; -} - -.icon, -.resize-icon-wrapper { - @apply z-default; // needed for firefox to display the icon properly -} - -// hide browser default clear - -input[type="text"]::-ms-clear, -input[type="text"]::-ms-reveal { - @apply hidden - h-0 - w-0; -} -input[type="search"]::-webkit-search-decoration, -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-results-button, -input[type="search"]::-webkit-search-results-decoration, -input[type="date"]::-webkit-clear-button, -input[type="time"]::-webkit-clear-button { - @apply hidden; -} - -.clear-button { - pointer-events: initial; - @apply focus-base - order-4 - m-0 - box-border - flex - min-h-full - cursor-pointer - items-center - justify-center - self-stretch - border - border-solid; - - background-color: var(--calcite-input-button-background-color); - border-color: var(--calcite-internal-input-border-color-base); - border-inline-start-width: theme("borderWidth.0"); - - &:hover { - @apply transition-default; - --calcite-input-button-background-color: var(--calcite-input-button-background-color-hover); - - calcite-icon { - @apply transition-default; - } - } - &:active { - --calcite-input-button-background-color: var(--calcite-input-button-background-color-active); - } - &:focus { - @apply focus-inset; - } - &:disabled { - @apply opacity-disabled; - } -} - -// loading -.loader { - inset-block-start: 1px; - inset-inline: 1px; - @apply pointer-events-none - absolute - block; -} - -:host(:not([suffix-text], [type="number"]):empty) { - .loader { - inset-inline-end: calc(var(--calcite-input-corner-radius, 0px) / 2); - } -} -:host(:not([prefix-text], [number-button-type="horizontal"][type="number"])) { - .loader { - inset-inline-start: calc(var(--calcite-input-corner-radius, 0px) / 2); - } -} - -// slotted action -.action-wrapper { - @apply order-7 flex; -} - -// prefix and suffix -.prefix, -.suffix { - @apply box-border - flex - h-auto - min-h-full - select-none - content-center - items-center - break-words - border - border-solid - font-medium - leading-none; - border-color: var(--calcite-internal-input-border-color-base); -} -.prefix { - color: var(--calcite-input-prefix-text-color); - background-color: var(--calcite-input-prefix-background-color); -} -.suffix { - color: var(--calcite-input-suffix-text-color); - background-color: var(--calcite-input-suffix-background-color); -} - -.prefix { - @apply order-2; - border-inline-end-width: theme("borderWidth.0"); -} -.suffix { - @apply order-5; - border-inline-start-width: theme("borderWidth.0"); -} - -// alignment type -:host([alignment="start"]) { - & textarea, - & input { - text-align: start; - } -} - -:host([alignment="end"]) { - & textarea, - & input { - text-align: end; - } -} - -// number buttons -input[type="number"] { - -moz-appearance: textfield; - - &::-webkit-inner-spin-button, - &::-webkit-outer-spin-button { - -webkit-appearance: none; - -moz-appearance: textfield; - @apply m-0; - } -} - -.number-button-wrapper { - @apply transition-default - pointer-events-none - order-6 - box-border - flex - flex-col; -} - -:host([number-button-type="vertical"]) .wrapper { - flex-direction: row; - display: flex; -} - -:host([number-button-type="vertical"]) { - & input, - textarea { - @apply order-2; - } -} - -:host([number-button-type="horizontal"]) .calcite--rtl { - & .number-button-item[data-adjustment="down"] calcite-icon { - transform: rotate(-90deg); - } - & .number-button-item[data-adjustment="up"] calcite-icon { - transform: rotate(-90deg); - } -} - -.number-button-item.number-button-item--horizontal { - &[data-adjustment="down"], - &[data-adjustment="up"] { - @apply order-1 - max-h-full - min-h-full - self-stretch; - & calcite-icon { - transform: rotate(90deg); - } - } -} - -.number-button-item.number-button-item--horizontal[data-adjustment="down"] { - @apply border - border-solid; - border-inline-end-width: theme("borderWidth.0"); -} - -.number-button-item.number-button-item--horizontal[data-adjustment="up"] { - @apply order-5; -} - -:host([number-button-type="vertical"]) .number-button-item[data-adjustment="down"] { - @apply border-t-0; -} - -.number-button-item { - max-block-size: 50%; - min-block-size: 50%; - pointer-events: initial; - @apply transition-default - m-0 - box-border - flex - cursor-pointer - items-center - self-center - border - border-solid - py-0 - px-2; - border-color: var(--calcite-internal-input-border-color-base); - border-inline-start-width: theme("borderWidth.0"); - background-color: var(--calcite-input-button-background-color); - & calcite-icon { - @apply transition-default pointer-events-none; - } - &:focus, - &:hover { - --calcite-input-button-background-color: var(--calcite-input-button-background-color-hover); - } - &:active { - --calcite-input-button-background-color: var(--calcite-input-button-background-color-active); - } - &:disabled { - @apply pointer-events-none; - } -} - -.wrapper { - @apply relative - flex - flex-row - items-center; -} - -// hide the default date picker -:input::-webkit-calendar-picker-indicator { - @apply hidden; -} - -input[type="date"]::-webkit-input-placeholder { - visibility: hidden !important; -} - -// textarea resize icon -textarea::-webkit-resizer { - @apply absolute - bottom-0 - box-border - py-0 - px-1; - inset-inline-end: 0; -} - -.resize-icon-wrapper { - inset-block-end: 2px; - inset-inline-end: 2px; - background-color: var(--calcite-input-button-background-color); - - @apply pointer-events-none - absolute - h-3 - w-3; - - & calcite-icon { - inset-block-end: theme("spacing.1"); - inset-inline-end: theme("spacing.1"); - transform: rotate(-45deg); - } -} - -.calcite--rtl { - .resize-icon-wrapper { - & calcite-icon { - transform: rotate(45deg); - } - } -} - -:host([type="color"]) input { - @apply p-1; -} - -// file input -:host([type="file"]) input { - @apply cursor-pointer - border - border-dashed - text-center; -} - -:host([type="file"][scale="s"]) input { - @apply py-px px-2; -} - -:host([type="file"][scale="m"]) input { - @apply py-1 px-3; -} - -:host([type="file"][scale="l"]) input { - @apply py-2 px-4; -} - -:host(.no-bottom-border) input { - @apply border-b-0; -} - -:host(.border-top-color-one) input { - @apply border-t-color-1; -} - -// input needed for higher specificity of these overrides -input { - &.inline-child { - @apply transition-default bg-transparent; - .editing-enabled { - background-color: inherit; - } - } - - &.inline-child:not(.editing-enabled) { - @apply border-color-transparent - flex - cursor-pointer text-ellipsis; - padding-inline-start: 0; - } -} - -@include form-validation-message(); -@include hidden-form-input(); -@include base-component(); - -:host(:focus) { - --calcite-input-border-color: var(--calcite-color-brand); -} - -:host([read-only]) { - --calcite-input-background-color: var(--calcite-color-background); -} - -:host([number-button-type="horizontal"][type="number"]) { - .wrapper { - > button:first-child { - border-start-start-radius: var(--calcite-input-corner-radius); - border-end-start-radius: var(--calcite-input-corner-radius); - } - - > button:last-of-type { - border-start-end-radius: var(--calcite-input-corner-radius); - border-end-end-radius: var(--calcite-input-corner-radius); - } - } - - button + button, - input + button, - button + div, - input + div { - border-start-start-radius: 0; - border-end-start-radius: 0; - } - input { - border-radius: 0; - } -} -:host([number-button-type="vertical"][type="number"]) { - input, - textarea { - border-start-end-radius: 0; - border-end-end-radius: 0; - } - .number-button-item { - &:first-child { - border-start-end-radius: var(--calcite-input-corner-radius); - } - &:last-child { - border-end-end-radius: var(--calcite-input-corner-radius); - } - } -} -:host([prefix-text]) { - input, - textarea { - border-start-start-radius: 0; - border-end-start-radius: 0; - } -} -:host([suffix-text]) { - input, - textarea { - border-start-end-radius: 0; - border-end-end-radius: 0; - } -} -:host([suffix-text][type="number"]:not([read-only])) { - .suffix { - border-start-end-radius: 0; - border-end-end-radius: 0; - } -} - -:host([scale="l"]) { - .resize-icon-wrapper { - block-size: 18px; - inline-size: 18px; - } -} -:host(:not([clearable], [suffix-text], [type="number"]:not([read-only]))) { - .wrapper:has(+ .validation-container) { - input { - border-start-end-radius: var(--calcite-input-corner-radius); - border-end-end-radius: var(--calcite-input-corner-radius); - } - } -} -:host(:not([type="number"], [suffix-text])) { - input:has(+ .clear-button) { - border-start-end-radius: 0; - border-end-end-radius: 0; - } - .clear-button { - border-start-end-radius: var(--calcite-input-corner-radius); - border-end-end-radius: var(--calcite-input-corner-radius); - } -} - -.prefix { - border-start-start-radius: var(--calcite-input-corner-radius); - border-end-start-radius: var(--calcite-input-corner-radius); -} -.suffix { - border-start-end-radius: var(--calcite-input-corner-radius); - border-end-end-radius: var(--calcite-input-corner-radius); -} -:host(:not(:empty)) { - input, - .clear-button { - border-start-end-radius: 0; - border-end-end-radius: 0; - } -} -::slotted(*) { - border-start-start-radius: 0; - border-end-start-radius: 0; - border-start-end-radius: var(--calcite-input-corner-radius); - border-end-end-radius: var(--calcite-input-corner-radius); -} +@include addInputStyles; diff --git a/packages/calcite-components/src/components/list-item/list-item.scss b/packages/calcite-components/src/components/list-item/list-item.scss index 0bcca595fed..4451b59edfe 100755 --- a/packages/calcite-components/src/components/list-item/list-item.scss +++ b/packages/calcite-components/src/components/list-item/list-item.scss @@ -1,3 +1,19 @@ +/** +* CSS Custom Properties +* +* These properties can be overridden using the component's tag as selector. +* +* @prop --calcite-list-item-handle-icon-color: Specifies the icon color of the sub-component. +* @prop --calcite-list-item-handle-icon-color-hover: Specifies the icon color of the sub-component when in hover state. +* @prop --calcite-list-item-handle-icon-color-focus: Specifies the icon color of the sub-component when in focus state. +* @prop --calcite-list-item-handle-icon-color-selected: Specifies the icon color of the sub-component when selected. + +* @prop --calcite-list-item-handle-background-color: Specifies the background color of the sub-component. +* @prop --calcite-list-item-handle-background-color-hover: Specifies the background color of the sub-component when in hover state. +* @prop --calcite-list-item-handle-background-color-focus: Specifies the background color of the sub-component when in focus state. +* @prop --calcite-list-item-handle-background-color-selected: Specifies the background color of the sub-component when selected. +*/ + :host { @apply flex flex-col; --calcite-list-item-icon-color: theme("colors.brand"); @@ -210,4 +226,18 @@ td:focus { @apply py-3; } +calcite-handle { + --calcite-handle-icon-color: var(--calcite-list-item-handle-icon-color); + --calcite-handle-background-color: var(--calcite-list-item-handle-background-color); + + --calcite-handle-icon-color-hover: var(--calcite-list-item-handle-icon-color-hover); + --calcite-handle-background-color-hover: var(--calcite-list-item-handle-background-color-hover); + + --calcite-handle-icon-color-focus: var(--calcite-list-item-handle-icon-color-focus); + --calcite-handle-background-color-focus: var(--calcite-list-item-handle-background-color-focus); + + --calcite-handle-icon-color-selected: var(--calcite-list-item-handle-icon-color-selected); + --calcite-handle-background-color-selected: var(--calcite-list-item-handle-background-color-selected); +} + @include base-component(); diff --git a/packages/calcite-components/src/components/loader/loader.scss b/packages/calcite-components/src/components/loader/loader.scss index 58da4bcb141..7363729a1ae 100644 --- a/packages/calcite-components/src/components/loader/loader.scss +++ b/packages/calcite-components/src/components/loader/loader.scss @@ -9,8 +9,6 @@ * @prop --calcite-loader-padding : Specifies the padding of the loader. */ -@import "../../assets/styles/animation"; - $stroke-width: 3; $loader-scale: 54; $loader-circumference: ($loader-scale - (2 * $stroke-width)) * 3.14159; diff --git a/packages/calcite-components/src/components/menu-item/menu-item.scss b/packages/calcite-components/src/components/menu-item/menu-item.scss index 749bb6c93cf..9f274ab0b62 100644 --- a/packages/calcite-components/src/components/menu-item/menu-item.scss +++ b/packages/calcite-components/src/components/menu-item/menu-item.scss @@ -1,3 +1,16 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-menu-item-action-background-color: defines the background color of an action sub-component inside a menu-item. + * @prop --calcite-menu-item-action-background-color-hover: defines the background color of an action sub-component when hovered or focused. + * @prop --calcite-menu-item-action-background-color-active: defines the background color of an action sub-component when active. + * @prop --calcite-menu-item-action-menu-border-color: The border color of the sub-component. + * @prop --calcite-menu-item-action-menu-text-color: The text color of the sub-component. + * + */ + :host { @apply flex items-center @@ -126,6 +139,8 @@ } calcite-action { + --calcite-action-background-color: var(--calcite-menu-item-action-background-color); + @apply relative h-auto; border-inline-start: 1px solid var(--calcite-color-foreground-1); &:after { @@ -138,6 +153,15 @@ calcite-action { @apply h-full; inset-block: 0; } + + &:hover, + &:focus { + --calcite-action-background-color: var(--calcite-menu-item-action-background-color-hover); + } + + &:active { + --calcite-action-background-color: var(--calcite-menu-item-action-background-color-active); + } } .content:focus ~ calcite-action, @@ -206,4 +230,9 @@ calcite-action { @apply opacity-100 -end-1; } +calcite-action-menu { + --calcite-action-menu-border-color: var(--calcite-menu-item-action-menu-border-color); + --calcite-action-menu-text-color: var(--calcite-menu-item-action-menu-text-color); +} + @include base-component(); diff --git a/packages/calcite-components/src/components/modal/modal.scss b/packages/calcite-components/src/components/modal/modal.scss index 2df1d60f7db..5fb26a60d67 100644 --- a/packages/calcite-components/src/components/modal/modal.scss +++ b/packages/calcite-components/src/components/modal/modal.scss @@ -1,5 +1,3 @@ -@import "~@esri/calcite-design-tokens/dist/scss/core"; - /** * CSS Custom Properties * diff --git a/packages/calcite-components/src/components/navigation-logo/navigation-logo.scss b/packages/calcite-components/src/components/navigation-logo/navigation-logo.scss index 2143fb04c4c..933ce57961c 100644 --- a/packages/calcite-components/src/components/navigation-logo/navigation-logo.scss +++ b/packages/calcite-components/src/components/navigation-logo/navigation-logo.scss @@ -12,6 +12,7 @@ focus-base no-underline text-0h; + color: inherit; border-block-end: 2px solid transparent; } diff --git a/packages/calcite-components/src/components/navigation-logo/navigation-logo.stories.ts b/packages/calcite-components/src/components/navigation-logo/navigation-logo.stories.ts index 4c893de611d..b501a2413cd 100644 --- a/packages/calcite-components/src/components/navigation-logo/navigation-logo.stories.ts +++ b/packages/calcite-components/src/components/navigation-logo/navigation-logo.stories.ts @@ -61,3 +61,17 @@ export const slottedInNav_TestOnly = (): string => html` /> `; + +export const withHref_TestOnly = (): string => html` + + + + +`; diff --git a/packages/calcite-components/src/components/navigation-user/navigation-user.scss b/packages/calcite-components/src/components/navigation-user/navigation-user.scss index c365a545112..63479494566 100644 --- a/packages/calcite-components/src/components/navigation-user/navigation-user.scss +++ b/packages/calcite-components/src/components/navigation-user/navigation-user.scss @@ -1,3 +1,13 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-navigation-user-avatar-corner-radius: defines the corner radius of the component. + * @prop --calcite-navigation-user-avatar-text-color: defines the text color of the component. + * + */ + :host { @apply inline-flex outline-none; & .button { @@ -62,4 +72,9 @@ calcite-avatar ~ .text-container { font-size: var(--calcite-font-size--1); } +calcite-avatar { + --calcite-avatar-corner-radius: var(--calcite-navigation-user-avatar-corner-radius); + --calcite-avatar-text-color: var(--calcite-navigation-user-avatar-text-color); +} + @include base-component(); diff --git a/packages/calcite-components/src/components/navigation/navigation.scss b/packages/calcite-components/src/components/navigation/navigation.scss index 43869df8eb3..96d12a3c42e 100644 --- a/packages/calcite-components/src/components/navigation/navigation.scss +++ b/packages/calcite-components/src/components/navigation/navigation.scss @@ -3,9 +3,14 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-navigation-width: Specifies the width of the component's content area. + * @prop --calcite-navigation-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-navigation-action-background-color-hover: defines the background color of an action sub-component when hovered or focused. + * @prop --calcite-navigation-action-background-color-active: defines the background color of an action sub-component when active. + * @prop --calcite-navigation-background: Specifies the background color of the component. * @prop --calcite-navigation-border-color: Specifies the border color of the component. + * @prop --calcite-navigation-width: Specifies the width of the component's content area. + * */ @include base-component(); @@ -108,3 +113,16 @@ slot[name="content-center"] ~ slot[name="user"], slot[name="content-end"] ~ slot[name="user"] { @apply m-0; } + +calcite-action { + --calcite-action-background-color: var(--calcite-navigation-action-background-color); + + &:hover, + &:focus { + --calcite-action-background-color: var(--calcite-navigation-action-background-color-hover); + } + + &:active { + --calcite-action-background-color: var(--calcite-navigation-action-background-color-active); + } +} diff --git a/packages/calcite-components/src/components/pagination/pagination.scss b/packages/calcite-components/src/components/pagination/pagination.scss index 3b34d4e7e6e..397c9dfeedb 100644 --- a/packages/calcite-components/src/components/pagination/pagination.scss +++ b/packages/calcite-components/src/components/pagination/pagination.scss @@ -1,5 +1,3 @@ -@import "~@esri/calcite-design-tokens/dist/scss/core"; - /** * CSS Custom Properties * @@ -32,20 +30,6 @@ :host { @apply flex; writing-mode: horizontal-tb; - --calcite-pagination-background-color-active: transparent; - --calcite-pagination-background-color-hover: transparent; - --calcite-pagination-background-color: transparent; - --calcite-pagination-border-color-hover: var(--calcite-color-border-2); - --calcite-pagination-border-color-selected: var(--calcite-color-brand); - --calcite-pagination-text-color-hover: var(--calcite-color-text-1); - --calcite-pagination-text-color-selected: var(--calcite-color-text-1); - --calcite-pagination-text-color: var(--calcite-color-text-3); - --calcite-pagination-arrow-background-color-active: var(--calcite-color-foreground-3); - --calcite-pagination-arrow-background-color-hover: var(--calcite-color-foreground-2); - --calcite-pagination-arrow-background-color: var(--calcite-pagination-background-color); - --calcite-pagination-arrow-icon-color-active: var(--calcite-color-brand); - --calcite-pagination-arrow-icon-color-hover: var(--calcite-color-brand); - --calcite-pagination-arrow-icon-color: var(--calcite-pagination-text-color); } :host([scale="s"]) { @@ -110,8 +94,8 @@ min-inline-size: var(--calcite-internal-pagination-item-size); block-size: var(--calcite-internal-pagination-item-size); padding-inline: var(--calcite-internal-pagination-item-space); - color: var(--calcite-pagination-text-color); - background-color: var(--calcite-pagination-background-color); + color: var(--calcite-pagination-text-color, var(--calcite-color-text-3)); + background-color: var(--calcite-pagination-background-color, transparent); } .chevron, @@ -121,35 +105,39 @@ &:hover { @apply transition-default; - color: var(--calcite-pagination-text-color-hover); - background-color: var(--calcite-pagination-background-color-hover); + color: var(--calcite-pagination-text-color-hover, var(--calcite-color-text-1)); + background-color: var(--calcite-pagination-background-color-hover, transparent); } &:active { - background-color: var(--calcite-pagination-background-color-active); + background-color: var(--calcite-pagination-background-color-active, transparent); } } .page { &:hover { - border-block-end-color: var(--calcite-pagination-border-color-hover); + border-block-end-color: var(--calcite-pagination-border-color-hover, var(--calcite-color-border-2)); } &.selected { font-weight: var(--calcite-font-weight-medium); - color: var(--calcite-pagination-text-color-selected); - border-block-end-color: var(--calcite-pagination-border-color-selected); + color: var(--calcite-pagination-text-color-selected, var(--calcite-color-text-1)); + border-block-end-color: var(--calcite-pagination-border-color-selected, var(--calcite-color-brand)); } } .chevron { - background-color: var(--calcite-pagination-arrow-background-color); + background-color: var( + --calcite-pagination-arrow-background-color, + var(--calcite-pagination-background-color, transparent) + ); + color: var(--calcite-pagination-text-color, var(--calcite-color-text-3)); &:hover { background-color: var(--calcite-pagination-arrow-background-color-hover); - color: var(--calcite-pagination-arrow-icon-color-hover); + color: var(--calcite-pagination-arrow-icon-color-hover, var(--calcite-color-brand)); } &:active { - background-color: var(--calcite-pagination-arrow-background-color-active); - color: var(--calcite-pagination-arrow-icon-color-active); + background-color: var(--calcite-pagination-arrow-background-color-active, var(--calcite-color-foreground-3)); + color: var(--calcite-pagination-arrow-icon-color-active, var(--calcite-color-brand)); } &.disabled { @apply pointer-events-none; diff --git a/packages/calcite-components/src/components/panel/panel.scss b/packages/calcite-components/src/components/panel/panel.scss index 329e9c0af5a..cf6d394849c 100644 --- a/packages/calcite-components/src/components/panel/panel.scss +++ b/packages/calcite-components/src/components/panel/panel.scss @@ -3,6 +3,11 @@ * * These properties can be overridden using the component's tag as selector. * + * @prop --calcite-panel-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-panel-action-background-color-hover: defines the background color of an action sub-component when hovered or focused. + * @prop --calcite-panel-action-background-color-active: defines the background color of an action sub-component when active. + * @prop --calcite-panel-action-menu-border-color: The border color of the sub-component. + * @prop --calcite-panel-action-menu-text-color: The text color of the sub-component. * @prop --calcite-panel-background-color: Specifies the background color of the component. * @prop --calcite-panel-border-color: Specifies the border color of the component. * @prop --calcite-panel-description-text-color: Specifies the text color of the component's description. @@ -12,8 +17,10 @@ * @prop --calcite-panel-footer-space: Specifies the spacing of the component's footer. * @prop --calcite-panel-header-background-color: Specifies the component header's background color. * @prop --calcite-panel-header-border-block-end: [Deprecated] No longer necessary. Specifies the component header's block end border. - * @prop --calcite-panel-heading-text-color: Specifies the component's heading text color. * @prop --calcite-panel-header-z-index: Specifies the component header's z-index. + * @prop --calcite-panel-header-z-index: Specifies the component header's z-index. + * @prop --calcite-panel-heading-text-color: Specifies the component's heading text color. + * */ :host { @@ -24,37 +31,24 @@ block-size: var(--calcite-container-size-content-fluid); flex: 1 1 auto; overflow: hidden; - --calcite-internal-panel-transition: max-block-size var(--calcite-animation-timing), - inline-size var(--calcite-animation-timing); - - --calcite-panel-background-color: var(--calcite-color-background); - --calcite-panel-border-color: var(--calcite-color-border-3); - --calcite-panel-description-text-color: var(--calcite-color-text-2); - --calcite-panel-fab-z-index: var(--calcite-z-index-sticky); - --calcite-panel-footer-background-color: var(--calcite-color-foreground-1); - --calcite-panel-footer-space: var(--calcite-panel-footer-padding, var(--calcite-spacing-sm)); - --calcite-panel-heading-text-color: var(--calcite-color-text-2); - --calcite-panel-header-background-color: var(--calcite-color-foreground-1); - --calcite-panel-header-z-index: var(--calcite-z-index-header); - --calcite-panel-header-border-width: var(--calcite-border-width-sm); - --calcite-panel-header-border-block-end: var(--calcite-border-width-sm) solid var(--calcite-panel-border-color); } @include disabled(); .header { - color: var(--calcite-panel-heading-text-color); + color: var(--calcite-panel-heading-text-color, var(--calcite-color-text-2)); margin: 0; align-content: space-between; align-items: center; display: flex; flex: 1; flex-direction: column; - z-index: var(--calcite-panel-header-z-index); - background-color: var(--calcite-panel-header-background-color); + z-index: var(--calcite-panel-header-z-index, var(--calcite-z-index-header)); + background-color: var(--calcite-panel-header-background-color, var(--calcite-color-foreground-1)); border-block-end: var( --calcite-panel-header-border-block-end, - var(--calcite-panel-header-border-width) solid var(--calcite-panel-border-color) + var(--calcite-panel-header-border-width, var(--calcite-border-width-sm)) solid + var(--calcite-panel-border-color, var(--calcite-color-border-3)) ); } @@ -70,7 +64,7 @@ } .container { - background-color: var(--calcite-panel-background-color); + background-color: var(--calcite-panel-background-color, var(--calcite-color-background)); margin: 0; display: flex; inline-size: var(--calcite-container-size-content-fluid); @@ -78,7 +72,9 @@ flex-direction: column; align-items: stretch; padding: 0; - transition: var(--calcite-internal-panel-transition); + transition: + max-block-size var(--calcite-animation-timing), + inline-size var(--calcite-animation-timing); } .container[hidden] { @@ -95,7 +91,8 @@ } .header-container--border-end { - border-block-end: var(--calcite-border-width-sm) solid var(--calcite-panel-border-color); + border-block-end: var(--calcite-border-width-sm) solid + var(--calcite-panel-border-color, var(--calcite-color-border-3)); } .action-bar-container { @@ -130,14 +127,14 @@ } } .description { - color: var(--calcite-panel-description-text-color); + color: var(--calcite-panel-description-text-color, var(--calcite-color-text-2)); font-size: var(--calcite-font-size--1); line-height: var(--calcite-font-line-height-relative); } } .back-button { - border-color: var(--calcite-panel-border-color); + border-color: var(--calcite-panel-border-color, var(--calcite-color-border-3)); border-style: solid; border-width: 0px; border-inline-end-width: var(--calcite-border-width-sm); @@ -160,19 +157,20 @@ flex-direction: column; flex-wrap: nowrap; align-items: stretch; - background-color: var(--calcite-panel-background-color); + background-color: var(--calcite-panel-background-color, var(--calcite-color-background)); overflow: auto; block-size: var(--calcite-container-size-content-fluid); } .footer { - background-color: var(--calcite-panel-footer-background-color); + background-color: var(--calcite-panel-footer-background-color, var(--calcite-color-foreground-1)); inline-size: var(--calcite-container-size-content-fluid); display: flex; justify-content: space-evenly; flex: 0 0 auto; - padding: var(--calcite-panel-footer-space); - border-block-start: var(--calcite-border-width-sm) solid var(--calcite-panel-border-color); + padding: var(--calcite-panel-footer-space, var(--calcite-panel-footer-padding, var(--calcite-spacing-sm))); + border-block-start: var(--calcite-border-width-sm) solid + var(--calcite-panel-border-color, var(--calcite-color-border-3)); } .fab-container { @@ -181,10 +179,28 @@ margin-inline: auto; margin-block: 0; display: block; - z-index: var(--calcite-panel-fab-z-index); + z-index: var(--calcite-panel-fab-z-index, var(--calcite-z-index-sticky)); padding: var(--calcite-spacing-xxs); inset-inline: 0; inline-size: fit-content; } +calcite-action { + --calcite-action-background-color: var(--calcite-panel-action-background-color); + + &:hover, + &:focus { + --calcite-action-background-color: var(--calcite-panel-action-background-color-hover); + } + + &:active { + --calcite-action-background-color: var(--calcite-panel-action-background-color-active); + } +} + +calcite-action-menu { + --calcite-action-menu-border-color: var(--calcite-panel-action-menu-border-color); + --calcite-action-menu-text-color: var(--calcite-panel-action-menu-text-color); +} + @include base-component(); diff --git a/packages/calcite-components/src/components/pick-list-group/pick-list-group.scss b/packages/calcite-components/src/components/pick-list-group/pick-list-group.scss index d28911e31f7..cde0822c470 100644 --- a/packages/calcite-components/src/components/pick-list-group/pick-list-group.scss +++ b/packages/calcite-components/src/components/pick-list-group/pick-list-group.scss @@ -15,7 +15,7 @@ @apply mb-0; } -@import "../../assets/styles/header"; +@include header(); .heading { @apply text-0h text-n1-wrap diff --git a/packages/calcite-components/src/components/popover/popover.scss b/packages/calcite-components/src/components/popover/popover.scss index a725ec0dba7..1127d074044 100644 --- a/packages/calcite-components/src/components/popover/popover.scss +++ b/packages/calcite-components/src/components/popover/popover.scss @@ -3,30 +3,31 @@ * * These properties can be overridden using the component's tag as selector. * +* @prop --calcite-popover-action-background-color: defines the background color of an action sub-component inside the component. +* @prop --calcite-popover-action-text-color: defines the text color of an action sub-component inside the component. +* @prop --calcite-popover-action-background-color-hover: defines the background color of an action sub-component when hovered or focused inside the component. +* @prop --calcite-popover-action-text-color-hover: defines the text color of an action sub-component when hovered or focused inside the component. +* @prop --calcite-popover-action-background-color-active: defines the background color of an action sub-component when active inside the component. +* @prop --calcite-popover-action-text-color-active: defines the text color of an action sub-component when active inside the component. * @prop --calcite-popover-background-color: Specifies the background color of the component. * @prop --calcite-popover-border-color: The border color of the component. -* @prop --calcite-popover-shadow: The shadow of the component. * @prop --calcite-popover-corner-radius: The corner radius of the component. +* @prop --calcite-popover-shadow: The shadow of the component. * @prop --calcite-popover-text-color: The text color of the component. +* */ :host { @apply pointer-events-none; - --calcite-popover-background-color: var(--calcite-color-foreground-1); - --calcite-popover-border-color: var(--calcite-color-border-3); - --calcite-popover-shadow: var(--calcite-shadow-md); - --calcite-popover-text-color: var(--calcite-color-text-1); - --calcite-popover-corner-radius: var(--calcite-corner-radius-round); - --calcite-popover-z-index: var(--calcite-z-index-popup); } @include floatingUIHost( - "--calcite-popover-z-index", - "--calcite-popover-shadow", - "--calcite-popover-corner-radius", - "--calcite-border-width-sm", - "--calcite-popover-border-color", - "--calcite-popover-background-color" + "var(--calcite-popover-z-index, var(--calcite-z-index-popup))", + "var(--calcite-popover-shadow, var(--calcite-shadow-md))", + "var(--calcite-popover-corner-radius, var(--calcite-corner-radius-round))", + "var(--calcite-border-width-sm)", + "var(--calcite-popover-border-color, var(--calcite-color-border-3))", + "var(--calcite-popover-background-color, var(--calcite-color-foreground-1))" ); .heading { @@ -38,7 +39,7 @@ whitespace-normal font-medium; - color: var(--calcite-popover-text-color); + color: var(--calcite-popover-text-color, var(--calcite-color-text-1)); } :host([scale="s"]) { @@ -78,7 +79,7 @@ border-b border-solid; - border-block-end-color: var(--calcite-popover-border-color); + border-block-end-color: var(--calcite-popover-border-color, var(--calcite-color-border-3)); } .container { @@ -88,8 +89,8 @@ flex-row flex-nowrap; - color: var(--calcite-popover-text-color); - border-radius: var(--calcite-popover-corner-radius); + color: var(--calcite-popover-text-color, var(--calcite-color-text-1)); + border-radius: var(--calcite-popover-corner-radius, var(--calcite-corner-radius-round)); &.has-header { @apply flex-col; @@ -109,8 +110,8 @@ .close-button-container { @apply flex overflow-hidden; flex: 0 0 auto; - border-start-end-radius: var(--calcite-popover-corner-radius); - border-end-end-radius: var(--calcite-popover-corner-radius); + border-start-end-radius: var(--calcite-popover-corner-radius, var(--calcite-corner-radius-round)); + border-end-end-radius: var(--calcite-popover-corner-radius, var(--calcite-corner-radius-round)); } ::slotted(calcite-panel), @@ -118,4 +119,20 @@ @apply h-full; } +calcite-action { + --calcite-action-background-color: var(--calcite-popover-action-background-color); + --calcite-action-text-color: var(--calcite-popover-action-text-color); + + &:hover, + &:focus { + --calcite-action-background-color: var(--calcite-popover-action-background-color-hover); + --calcite-action-text-color: var(--calcite-popover-action-text-color-hover); + } + + &:active { + --calcite-action-background-color: var(--calcite-popover-action-background-color-active); + --calcite-action-text-color: var(--calcite-popover-action-text-color-active); + } +} + @include base-component(); diff --git a/packages/calcite-components/src/components/progress/progress.scss b/packages/calcite-components/src/components/progress/progress.scss index f88cc3ff694..85b37837cab 100644 --- a/packages/calcite-components/src/components/progress/progress.scss +++ b/packages/calcite-components/src/components/progress/progress.scss @@ -1,7 +1,5 @@ -@import "../../assets/styles/animation"; - /** -* CSS Custom Properties +* CSS Custom Properties * * These properties can be overridden using the component's tag as selector. * diff --git a/packages/calcite-components/src/components/rating/rating.scss b/packages/calcite-components/src/components/rating/rating.scss index 39cc7c50502..581e990ae95 100644 --- a/packages/calcite-components/src/components/rating/rating.scss +++ b/packages/calcite-components/src/components/rating/rating.scss @@ -30,14 +30,6 @@ :host { @apply relative flex items-center; inline-size: fit-content; - --calcite-rating-outline-color: var(--calcite-color-border-input); - --calcite-rating-outline-color-hover: var(--calcite-color-brand); - --calcite-rating-value-fill-color: var(--calcite-color-brand); - --calcite-rating-average-fill-color: var(--calcite-color-status-warning); - --calcite-rating-chip-background-color: var(--calcite-color-foreground-2); - --calcite-rating-chip-border-color: var(--calcite-color-foreground-2); - --calcite-rating-chip-shadow: var(--calcite-shadow-none); - --calcite-rating-chip-corner-radius: 9999px; } :host([scale="s"]) { @@ -92,8 +84,7 @@ flex-direction: column; align-self: center; cursor: pointer; - box-shadow: var(--calcite-rating-shadow); - color: var(--calcite-rating-outline-color); + color: var(--calcite-rating-outline-color, var(--calcite-color-border-input)); &:focus { @apply focus-outset; } @@ -101,12 +92,15 @@ .average, .fraction { - color: var(--calcite-rating-average-fill-color); + color: var(--calcite-rating-average-fill-color, var(--calcite-color-status-warning)); +} + +.hovered { + color: var(--calcite-rating-outline-color-hover, var(--calcite-color-brand)); } -.hovered, .selected { - color: var(--calcite-rating-value-fill-color); + color: var(--calcite-rating-value-fill-color, var(--calcite-color-brand)); } .fraction { @@ -122,12 +116,11 @@ calcite-chip { pointer-events: none; cursor: default; - --calcite-chip-background-color: var(--calcite-rating-chip-background-color); - --calcite-chip-border-color: var(--calcite-rating-chip-border-color); - --calcite-chip-shadow: var(--calcite-rating-chip-shadow); - --calcite-chip-corner-radius: var(--calcite-rating-chip-corner-radius); - --calcite-chip-text-color: var(--calcite-rating-chip-text-color); - color: var(--calcite-color-text-3); + --calcite-chip-background-color: var(--calcite-rating-chip-background-color, var(--calcite-color-foreground-2)); + --calcite-chip-border-color: var(--calcite-rating-chip-border-color, var(--calcite-color-foreground-2)); + --calcite-chip-shadow: var(--calcite-rating-chip-shadow, var(--calcite-shadow-none)); + --calcite-chip-corner-radius: var(--calcite-rating-chip-corner-radius, 9999px); + --calcite-chip-text-color: var(--calcite-rating-chip-text-color, var(--calcite-color-text-3)); } .number--average { diff --git a/packages/calcite-components/src/components/select/select.scss b/packages/calcite-components/src/components/select/select.scss index 89c5131a62d..46c99640372 100644 --- a/packages/calcite-components/src/components/select/select.scss +++ b/packages/calcite-components/src/components/select/select.scss @@ -8,7 +8,6 @@ */ :host { - @extend %component-spacing; @apply flex flex-col; } diff --git a/packages/calcite-components/src/components/stepper/stepper.scss b/packages/calcite-components/src/components/stepper/stepper.scss index 2f70fa621a6..884398a6481 100644 --- a/packages/calcite-components/src/components/stepper/stepper.scss +++ b/packages/calcite-components/src/components/stepper/stepper.scss @@ -1,3 +1,14 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-stepper-action-background-color: defines the background color of an action sub-component inside the component. + * @prop --calcite-stepper-action-background-color-hover: defines the background color of an action sub-component when hovered or focused. + * @prop --calcite-stepper-action-background-color-active: defines the background color of an action sub-component when active. + * + */ + :host([scale="s"]) { --calcite-internal-stepper-item-spacing-unit-s: theme("spacing.1"); --calcite-internal-stepper-action-block-size: theme("spacing.11"); @@ -118,5 +129,17 @@ } } +calcite-action { + --calcite-action-background-color: var(--calcite-stepper-action-background-color); + &:hover, + &:focus { + --calcite-action-background-color: var(--calcite-stepper-action-background-color-hover); + } + + &:active { + --calcite-action-background-color: var(--calcite-stepper-action-background-color-active); + } +} + @include stepBar(); @include base-component(); diff --git a/packages/calcite-components/src/components/switch/switch.scss b/packages/calcite-components/src/components/switch/switch.scss index 372a58475d8..cfd0fa77c7d 100644 --- a/packages/calcite-components/src/components/switch/switch.scss +++ b/packages/calcite-components/src/components/switch/switch.scss @@ -11,8 +11,6 @@ * @prop --calcite-switch-shadow: Specifies the shadow of the component. */ -@import "~@esri/calcite-design-tokens/dist/scss/core"; - :host { --calcite-switch-handle-background-color: var(--calcite-color-foreground-1); --calcite-switch-handle-border-color: var(--calcite-color-border-input); diff --git a/packages/calcite-components/src/components/tab-nav/resources.ts b/packages/calcite-components/src/components/tab-nav/resources.ts index 7f1cc8d7095..ae679c3acbc 100644 --- a/packages/calcite-components/src/components/tab-nav/resources.ts +++ b/packages/calcite-components/src/components/tab-nav/resources.ts @@ -1,3 +1,16 @@ +export const ICON = { + chevronRight: "chevron-right", + chevronLeft: "chevron-left", +}; + export const CSS = { + activeIndicatorContainer: "tab-nav-active-indicator-container", container: "tab-nav", + containerHasEndTabTitleOverflow: "tab-nav--end-overflow", + containerHasStartTabTitleOverflow: "tab-nav--start-overflow", + scrollButton: "scroll-button", + scrollButtonContainer: "scroll-button-container", + scrollBackwardContainerButton: "scroll-button-container--backward", + scrollForwardContainerButton: "scroll-button-container--forward", + tabTitleSlotWrapper: "tab-titles-slot-wrapper", }; diff --git a/packages/calcite-components/src/components/tab-nav/tab-nav.e2e.ts b/packages/calcite-components/src/components/tab-nav/tab-nav.e2e.ts index 9ddf3df4c49..90621edc6d4 100644 --- a/packages/calcite-components/src/components/tab-nav/tab-nav.e2e.ts +++ b/packages/calcite-components/src/components/tab-nav/tab-nav.e2e.ts @@ -1,6 +1,8 @@ -import { newE2EPage } from "@stencil/core/testing"; -import { accessible, defaults, renders, hidden } from "../../tests/commonTests"; +import { E2EElement, E2EPage, newE2EPage } from "@stencil/core/testing"; +import { accessible, defaults, hidden, renders, t9n } from "../../tests/commonTests"; import { html } from "../../../support/formatting"; +import { CSS } from "./resources"; +import { getElementRect } from "../../tests/utils"; describe("calcite-tab-nav", () => { describe("defaults", () => { @@ -19,6 +21,10 @@ describe("calcite-tab-nav", () => { accessible("calcite-tab-nav"); }); + describe("translation support", () => { + t9n("calcite-tab-nav"); + }); + it("emits on user interaction", async () => { const page = await newE2EPage(); await page.setContent( @@ -110,4 +116,169 @@ describe("calcite-tab-nav", () => { await page.keyboard.press("Home"); expect(await page.evaluate(() => document.activeElement.id)).toBe("tab1"); }); + + describe("responsiveness", () => { + const tabsHTML = html` + + + Tab 1 Title + Tab 2 Title + Tab 3 Title + Tab 4 Title + Tab 5 Title + Tab 6 Title + Tab 7 Title + Tab 8 Title + + Tab 1 Content + Tab 2 Content + Tab 3 Content + Tab 4 Content + Tab 5 Content + Tab 6 Content + Tab 7 Content + Tab 8 Content + + `; + const sizeShowingAllTabs = { width: 1200, height: 1200 }; + const sizeShowingSomeTabs = { width: 350, height: 1200 }; + + let page: E2EPage; + let scrollBackButton: E2EElement; + let scrollForwardButton: E2EElement; + let scrollContainer: E2EElement; + + async function assertScrollButtonVisibility( + backExpectedVisibility: boolean, + expectedForwardVisibility: boolean, + ): Promise { + /* we need to find the scroll buttons to ensure visibility */ + expect(await scrollBackButton.isVisible()).toBe(backExpectedVisibility); + expect(await scrollForwardButton.isVisible()).toBe(expectedForwardVisibility); + } + + beforeEach(async () => { + page = await newE2EPage(); + await page.setContent(tabsHTML); + await page.setViewport(sizeShowingSomeTabs); + await page.waitForChanges(); + scrollBackButton = await page.find(`calcite-tab-nav >>> .${CSS.scrollBackwardContainerButton}`); + scrollForwardButton = await page.find(`calcite-tab-nav >>> .${CSS.scrollForwardContainerButton}`); + scrollContainer = await page.find(`calcite-tab-nav >>> .${CSS.tabTitleSlotWrapper}`); + }); + + it("shows scrolling buttons if tab-titles overflow", async () => { + await assertScrollButtonVisibility(false, true); + + await page.click("calcite-tab-title:nth-child(4)"); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, true); + + await page.setViewport(sizeShowingAllTabs); + await page.waitForChanges(); + + await assertScrollButtonVisibility(false, false); + + await page.setViewport(sizeShowingSomeTabs); + await page.waitForChanges(); + + await assertScrollButtonVisibility(false, true); + + await page.click("calcite-tab-title:nth-child(4)"); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, true); + }); + + it("scrolling tabs via buttons", async () => { + await assertScrollButtonVisibility(false, true); + + let scrollEnd = scrollContainer.waitForEvent("scrollend"); + await scrollForwardButton.click(); + await page.waitForChanges(); + await scrollEnd; + + await assertScrollButtonVisibility(true, true); + + scrollEnd = scrollContainer.waitForEvent("scrollend"); + await scrollForwardButton.click(); + await page.waitForChanges(); + await scrollEnd; + + await assertScrollButtonVisibility(true, false); + + scrollEnd = scrollContainer.waitForEvent("scrollend"); + await scrollBackButton.click(); + await page.waitForChanges(); + await scrollEnd; + + await assertScrollButtonVisibility(true, true); + + scrollEnd = scrollContainer.waitForEvent("scrollend"); + await scrollBackButton.click(); + await page.waitForChanges(); + await scrollEnd; + + await assertScrollButtonVisibility(false, true); + }); + + it("scrolling tabs via mouse wheel", async () => { + await assertScrollButtonVisibility(false, true); + + const tabNavBounds = await getElementRect(page, "calcite-tab-nav"); + await page.mouse.move(tabNavBounds.x + tabNavBounds.width / 2, tabNavBounds.y + tabNavBounds.height / 2); + await page.mouse.wheel({ deltaY: 200 }); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, true); + + await page.mouse.wheel({ deltaY: 200 }); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, false); + + await page.mouse.wheel({ deltaY: -200 }); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, true); + + await page.mouse.wheel({ deltaY: -200 }); + await page.waitForChanges(); + + await assertScrollButtonVisibility(false, true); + }); + + it("scrolls into view clipped start or end tab-title when selected", async () => { + const tabNavBounds = await getElementRect(page, "calcite-tab-nav"); + await page.mouse.move(tabNavBounds.x + tabNavBounds.width / 2, tabNavBounds.y + tabNavBounds.height / 2); + await page.waitForChanges(); + + await page.mouse.wheel({ deltaY: 1 }); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, true); + + let scrollEnd = scrollContainer.waitForEvent("scrollend"); + const firstTab = await page.find("calcite-tab-title:first-child"); + await firstTab.callMethod("click"); // we call method to avoid having E2E click element in the middle, which would hit the scroll button + await page.waitForChanges(); + await scrollEnd; + + await assertScrollButtonVisibility(false, true); + + await page.mouse.wheel({ deltaY: 180 }); + await page.waitForChanges(); + + await assertScrollButtonVisibility(true, true); + + scrollEnd = scrollContainer.waitForEvent("scrollend"); + const lastTab = await page.find("calcite-tab-title:last-child"); + await lastTab.callMethod("click"); // we call method to avoid having E2E click element in the middle, which would hit the scroll button + await page.waitForChanges(); + await scrollEnd; + + await assertScrollButtonVisibility(true, false); + }); + }); }); diff --git a/packages/calcite-components/src/components/tab-nav/tab-nav.scss b/packages/calcite-components/src/components/tab-nav/tab-nav.scss index 368fd06a1d1..27969104bf6 100644 --- a/packages/calcite-components/src/components/tab-nav/tab-nav.scss +++ b/packages/calcite-components/src/components/tab-nav/tab-nav.scss @@ -1,78 +1,221 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-tab-nav-indicator-color: Specifies the color of the active tab indicator. + * @prop --calcite-tab-nav-button-background-color: Specifies the background color of the scroll buttons. + * @prop --calcite-tab-nav-button-background-color-active: Specifies the background color of the scroll buttons when active. + * @prop --calcite-tab-nav-button-background-color-focus: Specifies the background color of the scroll buttons when focused. + * @prop --calcite-tab-nav-button-background-color-hover: Specifies the background color of the scroll buttons when hovered. + * @prop --calcite-tab-nav-button-border-color: Specifies the border color of the scroll buttons. + * @prop --calcite-tab-nav-button-border-color-active: Specifies the border color of the scroll buttons when active. + * @prop --calcite-tab-nav-button-border-color-focus: Specifies the border color of the scroll buttons when focused. + * @prop --calcite-tab-nav-button-border-color-hover: Specifies the border color of the scroll buttons when hovered. + * @prop --calcite-tab-nav-button-icon-color: Specifies the color of the scroll buttons' icon. + * @prop --calcite-tab-nav-button-icon-color-active: Specifies the color of the scroll buttons' icon when active. + * @prop --calcite-tab-nav-button-icon-color-focus: Specifies the color of the scroll buttons' icon when focused. + * @prop --calcite-tab-nav-button-icon-color-hover: Specifies the color of the scroll buttons' icon when hovered. + */ + :host { + --calcite-internal-tab-nav-gradient-start-side: left; + --calcite-internal-tab-nav-gradient-end-side: right; + @apply relative flex; } .scale-s { + --calcite-internal-tab-nav-scroller-button-width: #{$calcite-size-24}; min-block-size: theme("spacing.6"); } .scale-m { + --calcite-internal-tab-nav-scroller-button-width: #{$calcite-size-32}; min-block-size: theme("spacing.8"); } .scale-l { + --calcite-internal-tab-nav-scroller-button-width: #{$calcite-size-44}; min-block-size: theme("spacing.11"); } -:host([layout="center"]:not([bordered])) { - // `tab-nav` in all scales in layout="center" has a padding of 20px on both ends - padding-inline: theme("margin.5"); +.calcite--rtl { + --calcite-internal-tab-nav-gradient-start-side: right; + --calcite-internal-tab-nav-gradient-end-side: left; +} - //override margin-inline-end for the last child for tab-nav to implement the 20px padding on both ends instead - .tab-nav { - ::slotted(calcite-tab-title:last-child) { - margin-inline-end: theme("margin.0"); - } +$last-mask-color-stop-position: 51%; // we go beyond the half point to ensure the mask color stops overlap when both start and end are overflowing + +.tab-nav--start-overflow { + .tab-nav-active-indicator-container, + .tab-titles-slot-wrapper { + mask-image: linear-gradient( + to var(--calcite-internal-tab-nav-gradient-end-side), + transparent, + transparent var(--calcite-internal-tab-nav-scroller-button-width), + white var(--calcite-internal-tab-nav-scroller-button-width), + white $last-mask-color-stop-position + ); + } +} + +.tab-nav--end-overflow { + .tab-nav-active-indicator-container, + .tab-titles-slot-wrapper { + mask-image: linear-gradient( + to var(--calcite-internal-tab-nav-gradient-start-side), + transparent, + transparent var(--calcite-internal-tab-nav-scroller-button-width), + white var(--calcite-internal-tab-nav-scroller-button-width), + white $last-mask-color-stop-position + ); + } +} + +.tab-nav--start-overflow.tab-nav--end-overflow { + .tab-nav-active-indicator-container, + .tab-titles-slot-wrapper { + mask-image: linear-gradient( + to var(--calcite-internal-tab-nav-gradient-end-side), + transparent, + transparent var(--calcite-internal-tab-nav-scroller-button-width), + white var(--calcite-internal-tab-nav-scroller-button-width), + white $last-mask-color-stop-position, + transparent $last-mask-color-stop-position + ), + linear-gradient( + to var(--calcite-internal-tab-nav-gradient-start-side), + transparent, + transparent var(--calcite-internal-tab-nav-scroller-button-width), + white var(--calcite-internal-tab-nav-scroller-button-width), + white $last-mask-color-stop-position, + transparent $last-mask-color-stop-position + ); + } +} + +.tab-nav::-webkit-scrollbar { + display: none; + -ms-overflow-style: none; + scrollbar-width: none; +} + +:host([layout="center"]) { + ::slotted(calcite-tab-title) { + display: flex; + flex-grow: 1; + flex-shrink: 0; + min-inline-size: auto; + white-space: nowrap; + } + + ::slotted(calcite-tab-title[selected]) { + overflow: unset; } } :host(:not([bordered])) { .scale-l { - ::slotted(calcite-tab-title) { - margin-inline-end: theme("margin.6"); - } + --calcite-internal-tab-nav-gap: var(--calcite-size-xxl); } .scale-m { - ::slotted(calcite-tab-title) { - margin-inline-end: theme("margin.5"); - } + --calcite-internal-tab-nav-gap: var(--calcite-size-xl); } .scale-s { - ::slotted(calcite-tab-title) { - margin-inline-end: theme("margin.4"); - } + --calcite-internal-tab-nav-gap: var(--calcite-size-lg); + } + + .tab-titles-slot-wrapper { + gap: var(--calcite-internal-tab-nav-gap); + } +} + +:host([layout="center"]:not([bordered])) { + .tab-titles-slot-wrapper { + padding-inline: var(--calcite-spacing-xxl); } } -.tab-nav { +.tab-nav, +.tab-titles-slot-wrapper { @apply flex w-full justify-start - overflow-auto; + whitespace-nowrap + overflow-hidden; } // prevent indicator overflow in horizontal scrolling situations .tab-nav-active-indicator-container { @apply absolute - inset-x-0 bottom-0 h-0.5 - w-full - overflow-hidden; + inset-x-0 + overflow-hidden + w-full; } .tab-nav-active-indicator { - @apply bg-brand - absolute + @apply absolute bottom-0 block h-0.5 - transition-all - ease-out; + ease-out + transition-all; + + background-color: var(--calcite-tab-nav-indicator-color, var(--calcite-color-brand)); +} + +.scroll-button-container { + @apply absolute bottom-0 top-0; + + calcite-button { + // TODO: replace with updated sub-component tokens (see prop doc for reference) + + --calcite-offset-invert-focus: 1; + --calcite-color-text-1: var(--calcite-color-text-3); + + block-size: 100%; + + &:hover { + --calcite-color-text-1: unset; + --calcite-color-foreground-1: var(--calcite-color-transparent-hover); + --calcite-color-foreground-3: var(--calcite-color-transparent); + } + } +} + +.scroll-button-container--forward { + inset-inline-end: 0; + z-index: var(--calcite-z-index); } -:host([layout="center"]) .tab-nav { - @apply justify-evenly; +.scroll-button-container--backward { + inset-inline-start: 0; + z-index: var(--calcite-z-index); +} + +:host(:not([bordered])) { + .scroll-button-container--backward, + .scroll-button-container--forward { + &::before { + background-color: var(--calcite-color-border-3); + content: ""; + inline-size: var(--calcite-border-width-sm); + inset-block-start: var(--calcite-border-width-md); + inset-block-end: var(--calcite-border-width-md); + position: absolute; + } + } + + .scroll-button-container--backward::before { + inset-inline-end: 0; + } + + .scroll-button-container--forward::before { + inset-inline-start: 0; + } } :host .position-bottom .tab-nav-active-indicator { diff --git a/packages/calcite-components/src/components/tab-nav/tab-nav.tsx b/packages/calcite-components/src/components/tab-nav/tab-nav.tsx index 9b49fcd7053..7cc7c681213 100644 --- a/packages/calcite-components/src/components/tab-nav/tab-nav.tsx +++ b/packages/calcite-components/src/components/tab-nav/tab-nav.tsx @@ -7,11 +7,14 @@ import { Host, Listen, Prop, + readTask, State, VNode, Watch, } from "@stencil/core"; + import { + Direction, filterDirectChildren, focusElementInGroup, FocusElementInGroupDestination, @@ -21,7 +24,22 @@ import { createObserver } from "../../utils/observers"; import { Scale } from "../interfaces"; import { TabChangeEventDetail, TabCloseEventDetail } from "../tab/interfaces"; import { TabID, TabLayout, TabPosition } from "../tabs/interfaces"; -import { CSS } from "./resources"; +import { CSS, ICON } from "./resources"; +import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../../utils/locale"; +import { + connectMessages, + disconnectMessages, + setUpMessages, + T9nComponent, + updateMessages, +} from "../../utils/t9n"; +import { TabNavMessages } from "./assets/tab-nav/t9n"; +import { + calciteSize24, + calciteSize32, + calciteSize44, +} from "@esri/calcite-design-tokens/dist/es6/core"; +import { CSS_UTILITY } from "../../utils/resources"; /** * @slot - A slot for adding `calcite-tab-title`s. @@ -30,8 +48,9 @@ import { CSS } from "./resources"; tag: "calcite-tab-nav", styleUrl: "tab-nav.scss", shadow: true, + assetsDirs: ["assets"], }) -export class TabNav { +export class TabNav implements LocalizedComponent, T9nComponent { //-------------------------------------------------------------------------- // // Properties @@ -89,6 +108,25 @@ export class TabNav { */ @Prop({ mutable: true }) indicatorWidth: number; + /** + * Made into a prop for testing purposes only. + * + * @internal + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messages: TabNavMessages; + + /** + * Use this property to override individual strings used by the component. + */ + // eslint-disable-next-line @stencil-community/strict-mutable -- updated by t9n module + @Prop({ mutable: true }) messageOverrides: Partial; + + @Watch("messageOverrides") + onMessagesChange(): void { + /* wired up by t9n util */ + } + @Watch("selectedTabId") async selectedTabIdChanged(): Promise { if ( @@ -109,10 +147,7 @@ export class TabNav { @Watch("selectedTitle") selectedTitleChanged(): void { - this.updateOffsetPosition(); - this.updateActiveWidth(); - // reset the animation time on tab selection - this.activeIndicatorEl.style.transitionDuration = `${this.animationActiveDuration}s`; + this.updateActiveIndicator(); } //-------------------------------------------------------------------------- @@ -124,14 +159,21 @@ export class TabNav { connectedCallback(): void { this.parentTabsEl = this.el.closest("calcite-tabs"); this.resizeObserver?.observe(this.el); + connectLocalized(this); + connectMessages(this); } - componentWillLoad(): void { + async componentWillLoad(): Promise { const storageKey = `calcite-tab-nav-${this.storageId}`; if (localStorage && this.storageId && localStorage.getItem(storageKey)) { const storedTab = JSON.parse(localStorage.getItem(storageKey)); this.selectedTabId = storedTab; } + await setUpMessages(this); + } + + componentDidLoad(): void { + this.scrollTabTitleIntoView(this.selectedTitle, "instant"); } componentWillRender(): void { @@ -139,9 +181,10 @@ export class TabNav { this.layout = parentTabsEl?.layout; this.bordered = parentTabsEl?.bordered; - // fix issue with active tab-title not lining up with blue indicator + this.dir = getElementDir(this.el); + if (this.selectedTitle) { - this.updateOffsetPosition(); + this.updateActiveIndicator(); } } @@ -162,28 +205,51 @@ export class TabNav { disconnectedCallback(): void { this.resizeObserver?.disconnect(); + disconnectLocalized(this); + disconnectMessages(this); } + //-------------------------------------------------------------------------- + // + // Render Methods + // + //-------------------------------------------------------------------------- + render(): VNode { - const dir = getElementDir(this.el); const width = `${this.indicatorWidth}px`; const offset = `${this.indicatorOffset}px`; - const indicatorStyle = dir !== "rtl" ? { width, left: offset } : { width, right: offset }; + const indicatorStyle = this.dir !== "rtl" ? { width, left: offset } : { width, right: offset }; + return (
(this.tabNavEl = el)} + ref={this.storeContainerRef} > - + {this.renderScrollButton("start")}
+ +
+
(this.activeIndicatorContainerEl = el)} > @@ -194,6 +260,7 @@ export class TabNav { ref={(el) => (this.activeIndicatorEl = el as HTMLElement)} />
+ {this.renderScrollButton("end")}
); @@ -227,10 +294,55 @@ export class TabNav { @Listen("calciteInternalTabsActivate") internalActivateTabHandler(event: CustomEvent): void { + const activatedTabTitle = event.target as HTMLCalciteTabTitleElement; + this.selectedTabId = event.detail.tab ? event.detail.tab - : this.getIndexOfTabTitle(event.target as HTMLCalciteTabTitleElement); + : this.getIndexOfTabTitle(activatedTabTitle); event.stopPropagation(); + + this.scrollTabTitleIntoView(activatedTabTitle); + } + + private scrollTabTitleIntoView( + activatedTabTitle: HTMLCalciteTabTitleElement, + behavior: ScrollBehavior = "smooth", + ): void { + if (!activatedTabTitle) { + return; + } + + readTask(() => { + const isLTR = this.dir === "ltr"; + const tabTitleContainer = this.tabTitleContainerEl; + const containerBounds = tabTitleContainer.getBoundingClientRect(); + const tabTitleBounds = activatedTabTitle.getBoundingClientRect(); + const scrollPosition = tabTitleContainer.scrollLeft; + const overflowingStartTabTitle = isLTR + ? this.hasOverflowingStartTabTitle + : this.hasOverflowingEndTabTitle; + const overflowingEndTabTitle = isLTR + ? this.hasOverflowingEndTabTitle + : this.hasOverflowingStartTabTitle; + + if ( + tabTitleBounds.left < + containerBounds.left + (overflowingStartTabTitle ? this.scrollerButtonWidth : 0) + ) { + const left = + scrollPosition + (tabTitleBounds.left - containerBounds.left) - this.scrollerButtonWidth; + tabTitleContainer.scrollTo({ left, behavior }); + } else if ( + tabTitleBounds.right > + containerBounds.right - (overflowingEndTabTitle ? this.scrollerButtonWidth : 0) + ) { + const left = + scrollPosition + + (tabTitleBounds.right - containerBounds.right) + + this.scrollerButtonWidth; + tabTitleContainer.scrollTo({ left, behavior }); + } + }); } @Listen("calciteTabsActivate") @@ -273,8 +385,7 @@ export class TabNav { @Listen("calciteInternalTabIconChanged") iconStartChangeHandler(): void { - this.updateActiveWidth(); - this.updateOffsetPosition(); + this.updateActiveIndicator(); } //-------------------------------------------------------------------------- @@ -301,71 +412,237 @@ export class TabNav { @Element() el: HTMLCalciteTabNavElement; - @State() selectedTabId: TabID; + @State() defaultMessages: TabNavMessages; + + @State() effectiveLocale = ""; + + @Watch("effectiveLocale") + effectiveLocaleChange(): void { + updateMessages(this, this.effectiveLocale); + } + + @State() private hasOverflowingStartTabTitle = false; + + @State() private hasOverflowingEndTabTitle = false; + + @State() private selectedTabId: TabID; + + private activeIndicatorEl: HTMLElement; - parentTabsEl: HTMLCalciteTabsElement; + private activeIndicatorContainerEl: HTMLDivElement; - tabNavEl: HTMLDivElement; + private dir: Direction = "ltr"; - activeIndicatorEl: HTMLElement; + private containerEl: HTMLDivElement; - activeIndicatorContainerEl: HTMLDivElement; + private lastScrollWheelAxis: "x" | "y" = "x"; - animationActiveDuration = 0.3; + private parentTabsEl: HTMLCalciteTabsElement; + + private tabTitleContainerEl: HTMLDivElement; + + private intersectionObserver: IntersectionObserver; + + private resizeObserver = createObserver("resize", () => { + this.updateScrollingState(); - resizeObserver = createObserver("resize", () => { if (!this.activeIndicatorEl) { return; } - // remove active indicator transition duration during resize to prevent wobble - this.activeIndicatorEl.style.transitionDuration = "0s"; - this.updateActiveWidth(); - this.updateOffsetPosition(); + this.updateActiveIndicator(); }); + private get scrollerButtonWidth(): number { + const { scale } = this; + return parseInt(scale === "s" ? calciteSize24 : scale === "m" ? calciteSize32 : calciteSize44); + } + //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- + private updateActiveIndicator(): void { + const tabTitleScrollLeft = this.tabTitleContainerEl?.scrollLeft; + const containerScrollLeft = this.containerEl?.scrollLeft; + const navWidth = this.activeIndicatorContainerEl?.offsetWidth; + const tabLeft = this.selectedTitle?.offsetLeft; + const tabWidth = this.selectedTitle?.offsetWidth; + const offsetRight = navWidth - tabLeft - tabWidth; + const offsetBase = this.dir === "ltr" ? tabLeft : offsetRight; + const multiplier = this.dir === "ltr" ? -1 : 1; + + this.indicatorOffset = offsetBase + multiplier * (containerScrollLeft + tabTitleScrollLeft); + this.indicatorWidth = this.selectedTitle?.offsetWidth; + } + + private onTabTitleWheel = (event: WheelEvent): void => { + event.preventDefault(); + + const { deltaX, deltaY } = event; + const x = Math.abs(deltaX); + const y = Math.abs(deltaY); + + let scrollBy: number; + + if (x === y) { + scrollBy = this.lastScrollWheelAxis === "x" ? deltaX : deltaY; + } else if (x > y) { + scrollBy = deltaX; + this.lastScrollWheelAxis = "x"; + } else { + scrollBy = deltaY; + this.lastScrollWheelAxis = "y"; + } + + const scrollByX = (this.dir === "rtl" ? -1 : 1) * scrollBy; + (event.currentTarget as HTMLDivElement).scrollBy(scrollByX, 0); + requestAnimationFrame(() => this.updateActiveIndicator()); + }; + + private onSlotChange = (event: Event): void => { + this.intersectionObserver?.disconnect(); + + const slottedChildren = (event.target as HTMLSlotElement).assignedElements(); + slottedChildren.forEach((child) => { + this.intersectionObserver?.observe(child); + }); + }; + + private storeContainerRef = (el: HTMLDivElement) => (this.containerEl = el); + + private storeTabTitleWrapperRef = (el: HTMLDivElement) => { + this.tabTitleContainerEl = el; + this.intersectionObserver = createObserver("intersection", () => this.updateScrollingState(), { + root: el, + threshold: [0, 0.5, 1], + }); + }; + + private updateScrollingState(): void { + const tabTitleContainer = this.tabTitleContainerEl; + + if (!tabTitleContainer) { + return; + } + + let isOverflowStart: boolean; + let isOverflowEnd: boolean; + + const scrollPosition = tabTitleContainer.scrollLeft; + const visibleWidth = tabTitleContainer.clientWidth; + const totalContentWidth = tabTitleContainer.scrollWidth; + + if (this.dir === "ltr") { + isOverflowStart = scrollPosition > 0; + isOverflowEnd = scrollPosition + visibleWidth < totalContentWidth; + } else { + isOverflowStart = scrollPosition < 0; + isOverflowEnd = scrollPosition !== -(totalContentWidth - visibleWidth); + } + + this.hasOverflowingStartTabTitle = isOverflowStart; + this.hasOverflowingEndTabTitle = isOverflowEnd; + } + + private scrollToTabTitles = (direction: "forward" | "backward"): void => { + readTask(() => { + const tabTitleContainer = this.tabTitleContainerEl; + const containerBounds = tabTitleContainer.getBoundingClientRect(); + const tabTitles = Array.from(this.el.querySelectorAll("calcite-tab-title")); + const { dir } = this; + + if (direction === "forward") { + tabTitles.reverse(); + } + + let closestToEdge: HTMLCalciteTabTitleElement = null; + + tabTitles.forEach((tabTitle) => { + const tabTitleBounds = tabTitle.getBoundingClientRect(); + const containerEndX = containerBounds.x + containerBounds.width; + const tabTitleEndX = tabTitleBounds.x + tabTitleBounds.width; + + if ( + (direction === "forward" && dir === "ltr") || + (direction === "backward" && dir === "rtl") + ) { + const afterContainerEnd = tabTitleBounds.x > containerEndX; + + if (afterContainerEnd) { + closestToEdge = tabTitle; + } else { + const crossingContainerEnd = + tabTitleEndX > containerEndX && tabTitleBounds.x > containerBounds.x; + + if (crossingContainerEnd) { + closestToEdge = tabTitle; + } + } + } else { + const beforeContainerStart = tabTitleEndX < containerBounds.x; + + if (beforeContainerStart) { + closestToEdge = tabTitle; + } else { + const crossingContainerStart = + tabTitleEndX < containerEndX && tabTitleBounds.x < containerBounds.x; + + if (crossingContainerStart) { + closestToEdge = tabTitle; + } + } + } + }); + + if (closestToEdge) { + const { scrollerButtonWidth } = this; + const offsetAdjustment = + (direction === "forward" && dir === "ltr") || (direction === "backward" && dir === "rtl") + ? -scrollerButtonWidth + : closestToEdge.offsetWidth - tabTitleContainer.clientWidth + scrollerButtonWidth; + const scrollTo = closestToEdge.offsetLeft + offsetAdjustment; + + tabTitleContainer.scrollTo({ + left: scrollTo, + behavior: "smooth", + }); + } + }); + }; + + private scrollToNextTabTitles = (): void => this.scrollToTabTitles("forward"); + + private scrollToPreviousTabTitles = (): void => this.scrollToTabTitles("backward"); + handleTabFocus = ( event: CustomEvent, el: HTMLCalciteTabTitleElement, destination: FocusElementInGroupDestination, ): void => { - focusElementInGroup(this.enabledTabTitles, el, destination); + const focused = focusElementInGroup( + this.enabledTabTitles, + el, + destination, + ); + this.scrollTabTitleIntoView(focused, "instant"); event.stopPropagation(); }; - handleContainerScroll = (): void => { - // remove active indicator transition duration while container is scrolling to prevent wobble - this.activeIndicatorEl.style.transitionDuration = "0s"; - this.updateOffsetPosition(); - }; - - updateOffsetPosition(): void { - const dir = getElementDir(this.el); - const navWidth = this.activeIndicatorContainerEl?.offsetWidth; - const tabLeft = this.selectedTitle?.offsetLeft; - const tabWidth = this.selectedTitle?.offsetWidth; - const offsetRight = navWidth - (tabLeft + tabWidth); - this.indicatorOffset = - dir !== "rtl" ? tabLeft - this.tabNavEl?.scrollLeft : offsetRight + this.tabNavEl?.scrollLeft; - } - - updateActiveWidth(): void { - this.indicatorWidth = this.selectedTitle?.offsetWidth; - } - getIndexOfTabTitle(el: HTMLCalciteTabTitleElement, tabTitles = this.tabTitles): number { // In most cases, since these indexes correlate with tab contents, we want to consider all tab titles. // However, when doing relative index operations, it makes sense to pass in this.enabledTabTitles as the 2nd arg. return tabTitles.indexOf(el); } + private onTabTitleScroll = (): void => { + this.updateActiveIndicator(); + this.updateScrollingState(); + }; + async getTabTitleById(id: TabID): Promise { return Promise.all(this.tabTitles.map((el) => el.getTabIdentifier())).then((ids) => { return this.tabTitles[ids.indexOf(id)]; @@ -403,6 +680,7 @@ export class TabNav { } } else if (totalVisibleTabTitles > 1) { const closedTabTitleIndex = tabTitles.findIndex((el) => el === closedTabTitleEl); + const nextTabTitleIndex = visibleTabTitlesIndices.find( (value) => value > closedTabTitleIndex, ); @@ -414,9 +692,40 @@ export class TabNav { } requestAnimationFrame(() => { - this.updateOffsetPosition(); - this.updateActiveWidth(); + this.updateActiveIndicator(); tabTitles[this.selectedTabId].focus(); }); } + + private renderScrollButton = (overflowDirection: "start" | "end"): VNode => { + const { bordered, messages, hasOverflowingStartTabTitle, hasOverflowingEndTabTitle, scale } = + this; + const isEnd = overflowDirection === "end"; + + return ( + + ); + }; } diff --git a/packages/calcite-components/src/components/tab-title/tab-title.scss b/packages/calcite-components/src/components/tab-title/tab-title.scss index ca3a7f3feed..3f698ce7d25 100644 --- a/packages/calcite-components/src/components/tab-title/tab-title.scss +++ b/packages/calcite-components/src/components/tab-title/tab-title.scss @@ -11,47 +11,68 @@ @apply flex-auto; } -:host([layout="center"]) .scale-s, -:host([layout="center"]) .scale-m, -:host([layout="center"]) .scale-l { - @apply my-0 text-center; - flex-basis: theme("spacing.48"); - .content { - @apply m-auto; - } +.content { + @apply flex items-center justify-center; } -// center the text visually and not affected by the x button so as to avoid moving when on or off -:host([layout="center"][closable]) { +.scale-s { .content { - padding-inline-start: 32px; //28px button width + 0.25rem padding + @apply text-n2h py-1; } } -:host([layout="center"][bordered][closable]) .scale-s { +.scale-m { .content { - padding-inline-start: 36px; //28px button width + 0.5rem padding + @apply text-n1h py-2; } } -:host([layout="center"][bordered][closable]) .scale-m { +.scale-l { .content { - padding-inline-start: 40px; //28px button width + 0.75rem padding + @apply text-0h py-2.5; } } -:host([layout="center"][closable]) .scale-l { +:host([closable]) .content { + @apply h-full box-border border-b-color-transparent; +} + +:host([layout="inline"]), +:host([layout="center"]) { .content { - padding-inline-start: 40px; //36px button width + .25 padding + @apply px-1; } } -:host([layout="center"][closable][bordered]) .scale-s { +:host([layout="center"]) .scale-s, +:host([layout="center"]) .scale-m, +:host([layout="center"]) .scale-l { + @apply justify-center my-0 text-center; + .content { - padding-inline-start: 52px; //36px button width + 1rem padding + @apply flex-auto flex-grow; } } +.container { + @apply border-b-2 + border-b-color-transparent + box-border + content-center + cursor-pointer + flex + focus-base + h-full + justify-between + px-0 + text-color-3 + text-n1h + transition-default + w-full; + + border-block-end-style: solid; +} + :host([position="bottom"]) .container { @apply border-t-color-transparent border-b-0 @@ -63,8 +84,8 @@ @apply hidden; } -.container { - @apply focus-base; +:host([selected]) .container { + @apply text-color-1 border-color-transparent; } :host(:focus) .container { @@ -83,56 +104,12 @@ } } -:host([selected]) .container { - @apply text-color-1 border-color-transparent; -} - @include disabled() { .container { @apply pointer-events-none opacity-50; } } -.scale-s { - .content { - @apply text-n2h py-1; - } -} - -.scale-m { - .content { - @apply text-n1h py-2; - } -} - -.scale-l { - .content { - @apply text-0h py-2.5; - } -} - -.container { - @apply border-b-2 - border-b-color-transparent - box-border - content-center - cursor-pointer - flex - h-full - justify-between - px-0 - text-color-3 - text-n1h - transition-default - w-full; - - border-block-end-style: solid; -} - -.content { - @apply flex items-center justify-center; -} - .calcite-tab-title--icon { @apply relative m-0 @@ -165,11 +142,11 @@ focus-base items-center justify-center + p-1 self-center text-color-3 transition-default; - block-size: calc(100% - 2px); // fit within top/bottom borders background-color: var(--calcite-button-transparent-1); margin-inline-start: auto; @@ -216,14 +193,6 @@ box-shadow: inset 0 2px 0 var(--calcite-color-foreground-1); } -:host([bordered]:hover), -:host([bordered]:focus), -:host([bordered]:active) { - .container { - @apply relative; - } -} - :host([bordered]:hover) { .container { background-color: var(--calcite-color-transparent-hover); @@ -239,10 +208,6 @@ } } -:host([closable]) .content { - @apply h-full box-border border-b-color-transparent; -} - :host([closable][position="bottom"]) .container, :host([bordered][position="bottom"]) .container { border-block-start-style: unset; @@ -253,21 +218,34 @@ border-inline-end-color: var(--calcite-color-border-1); } -:host([bordered]) { - .content { - @apply px-3; +:host([layout="inline"][bordered]), +:host([layout="center"][bordered]) { + .scale-m { + .content { + @apply px-3; + } } -} -:host([bordered]) .scale-s { - .content { - @apply px-2; + .scale-s { + .content { + @apply px-2; + } + } + + .scale-l { + .content { + @apply px-4; + } } } -:host([bordered]) .scale-l { - .content { - @apply px-4; +:host([layout="inline"][closable]) { + .scale-s, + .scale-m, + .scale-l { + .content { + padding-inline-end: 0; + } } } diff --git a/packages/calcite-components/src/components/tab/tab.scss b/packages/calcite-components/src/components/tab/tab.scss index c30289b2445..cc0789a618e 100644 --- a/packages/calcite-components/src/components/tab/tab.scss +++ b/packages/calcite-components/src/components/tab/tab.scss @@ -3,7 +3,8 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-tab-content-block-padding: Specifies the block padding of the component's content in the `default` slot. + * @prop --calcite-tab-content-block-padding: [Deprecated] Use `--calcite-tab-content-space-y` instead. Specifies the block padding of the component's content in the `default` slot. + * @prop --calcite-tab-content-space-y: Specifies the vertical space of the component's content in the `default` slot. */ :host([selected]) { @@ -23,22 +24,33 @@ .content { @apply box-border; - padding-block: var(--calcite-internal-tab-content-block-padding); } .scale-s { - --calcite-internal-tab-content-block-padding: var(--calcite-tab-content-block-padding, theme("spacing.1")); - @apply text-n2h; + font-size: var(--calcite-font-size-sm); + line-height: 1rem; + + .content { + padding-block: var(--calcite-tab-content-space-y, var(--calcite-tab-content-block-padding, theme("spacing.1"))); + } } .scale-m { - --calcite-internal-tab-content-block-padding: var(--calcite-tab-content-block-padding, theme("spacing.2")); - @apply text-n1h; + font-size: var(--calcite-font-size); + line-height: 1rem; + + .content { + padding-block: var(--calcite-tab-content-space-y, var(--calcite-tab-content-block-padding, theme("spacing.2"))); + } } .scale-l { - --calcite-internal-tab-content-block-padding: var(--calcite-tab-content-block-padding, theme("spacing.[2.5]")); - @apply text-0h; + font-size: var(--calcite-font-size-md); + line-height: 1.25rem; + + .content { + padding-block: var(--calcite-tab-content-space-y, var(--calcite-tab-content-block-padding, theme("spacing.[2.5]"))); + } } section, diff --git a/packages/calcite-components/src/components/tabs/tabs.scss b/packages/calcite-components/src/components/tabs/tabs.scss index ed21a427295..a9fbd8f159c 100644 --- a/packages/calcite-components/src/components/tabs/tabs.scss +++ b/packages/calcite-components/src/components/tabs/tabs.scss @@ -1,14 +1,26 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-tabs-background-color: The background color of the component. + * @prop --calcite-tabs-border-color: The border color of the component. + */ + :host { @apply flex flex-col; + + --calcite-tabs-background-color: var(--calcite-color-foreground-1); + --calcite-tabs-border-color: var(--calcite-color-border-1); } :host([bordered]) { box-shadow: inset 0 1px 0 var(--calcite-color-border-1); - background-color: var(--calcite-color-foreground-1); + background-color: var(--calcite-tabs-background-color); } -:host([bordered]:not([position="bottom"])) ::slotted(calcite-tab-nav) { - margin-block-end: -1px; +section { + @apply border-color-1 border border-solid; } :host([bordered][position="bottom"]) { @@ -17,8 +29,8 @@ inset 0 -1px 0 var(--calcite-color-border-1); } -:host([bordered]) section { - @apply border-color-1 border border-solid; +:host([bordered]:not([position="bottom"])) ::slotted(calcite-tab-nav) { + margin-block-end: -1px; } :host([bordered][scale="s"]) section { @@ -38,19 +50,21 @@ } section { - @apply border-t-color-1 - flex + @apply flex flex-grow overflow-hidden border-t; + border-block-start-style: solid; + border-block-start-color: var(--calcite-tabs-border-color); } :host([position="bottom"]) section { - @apply border-b-color-1 - flex-col-reverse + @apply flex-col-reverse border-t-0 border-b; + + border-block-end-color: var(--calcite-tabs-border-color); } :host([position="bottom"]:not([bordered])) section { diff --git a/packages/calcite-components/src/components/tabs/tabs.stories.ts b/packages/calcite-components/src/components/tabs/tabs.stories.ts index f767a33501f..7870c683028 100644 --- a/packages/calcite-components/src/components/tabs/tabs.stories.ts +++ b/packages/calcite-components/src/components/tabs/tabs.stories.ts @@ -1,7 +1,7 @@ import { select } from "@storybook/addon-knobs"; import { boolean, iconNames, storyFilters } from "../../../.storybook/helpers"; import { placeholderImage } from "../../../.storybook/placeholderImage"; -import { modesDarkDefault } from "../../../.storybook/utils"; +import { createBreakpointStories, modesDarkDefault } from "../../../.storybook/utils"; import { html } from "../../../support/formatting"; import readme3 from "../tab-nav/readme.md"; import readme4 from "../tab-title/readme.md"; @@ -462,6 +462,101 @@ export const noVerticalScrollbarInsideShellPanel_TestOnly = (): string => html` `; +export const responsiveTabs = (): string => + createBreakpointStories(html` + + + + Tab 1 Title + Tab 2 Title + An Ultramarathon of a Tab Title, why not. + Tab 4 Title + Tab 5 Title + Tab 6 Title + Tab 7 Title + Tab 8 Title + + Tab 1 Content + Tab 2 Content + Tab 3 Content + Tab 4 Content + Tab 5 Content + Tab 6 Content + Tab 7 Content + Tab 8 Content + + + + + + Tab 1 Title + Tab 2 Title + An Ultramarathon of a Tab Title, why not. + Tab 4 Title + Tab 5 Title + Tab 6 Title + Tab 7 Title + Tab 8 Title + + Tab 1 Content + Tab 2 Content + Tab 3 Content + Tab 4 Content + Tab 5 Content + Tab 6 Content + Tab 7 Content + Tab 8 Content + + + + + + Tab 1 Title + Tab 2 Title + An Ultramarathon of a Tab Title, why not. + Tab 4 Title + Tab 5 Title + Tab 6 Title + Tab 7 Title + Tab 8 Title + + Tab 1 Content + Tab 2 Content + Tab 3 Content + Tab 4 Content + Tab 5 Content + Tab 6 Content + Tab 7 Content + Tab 8 Content + + + + + + Tab 1 Title + Tab 2 Title + An Ultramarathon of a Tab Title, why not. + Tab 4 Title + Tab 5 Title + Tab 6 Title + Tab 7 Title + Tab 8 Title + + Tab 1 Content + Tab 2 Content + Tab 3 Content + Tab 4 Content + Tab 5 Content + Tab 6 Content + Tab 7 Content + Tab 8 Content + + + `); export const paddingPropOverrideAtRootLevel = (): string => html` @@ -41,202 +50,641 @@

Card

+
+
Adjust selection mode of demo Chip Groups
+
+ + none + multiple + single + single-persist + + + --calcite-card-group-gap + + +
+
+
+
Group disabled within group
+
+ + +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+
+
+
-
+
Disabled within group
-

Themed

- -

ArcGIS Online: Gallery and Organization pages

- A great example of a study description that might wrap to a line or two, but isn't overly verbose. -
+ + +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+
+
- +
+
Preserving deprecated `selectable` property on individual Card
-

No buttons, not selectable

- -

ArcGIS Online: Gallery and Organization pages

- +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly verbose.
- -
- +
Not a lot of content
-

Card with several buttons

- - - Sample image alt -

Portland Businesses

- by - example_user - -
- Created: Apr 22, 2019 -
- Updated: Dec 9, 2019 -
- View Count: 0 -
- -
- - - - - - - - - View details - Duplicate - Delete - - -
-
+ + +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+
+
- - My great tooltip example - - - Sharing level: 2 - - More... - - More options - +
Not a lot of content and themed
+
+ + +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+ +

ArcGIS Online: Gallery and Organization pages

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. +
+
- +
Cards with several buttons
-

Card with two buttons

- - Sample image alt -

My great Workforce project that might wrap two lines

- Johnathan Smith - Nov 25, 2018 -
+ + + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
- + id="card-icon-test-1" + icon-start="check" + > +
+ + + + + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
- -
-
+ icon-start="check" + > +
+ + + + + + View details + Duplicate + Delete + + +
+ + + Sample image alt + by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+
- - +
Cards with uneven height
-

Selectable and selected card

- - Sample image alt + + + Sample image alt +

Portland Businesses

-

ArcGIS Online Sign In and Sign Up

- A great example of a study description that might wrap to a line or two, but isn't overly verbose. - Lead füt - Trail füt -
+
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + + + + View details + Duplicate + Delete + + +
+
+ +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt + by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+ +
+ + + + + + View details + Duplicate + Delete + + +
+
+ +
+
Card with two buttons
+
+ + + Sample image alt +

My great Workforce project that might wrap two lines

+ Johnathan Smith + Nov 25, 2018 +
+ + + + +
+
+ + Sample image alt +

My great Workforce project that might wrap two lines

+ Johnathan Smith + Nov 25, 2018 +
+ + + + +
+
+ + Sample image alt +

My great Workforce project that might wrap two lines

+ Johnathan Smith + Nov 25, 2018 +
+ + + + +
+
+
- +
Simple footer
-

Deselect example

- - Sample image alt + + + Sample image alt +

ArcGIS Online Sign In and Sign Up

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. + Lead füt + Trail füt +
+ + Sample image alt -

Translated select and deselect example

- A great example of a study description that might wrap to a line or two, but isn't overly verbose. - Lead füt - Trail füt -
+

ArcGIS Online Sign In and Sign Up

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. + Lead füt + Trail füt +
+
-
- -
+
Loading
-

Loading

- - Sample image alt + + + Sample image alt -

Loading example

- A great example of a study description that might wrap to a line or two, but isn't overly verbose. - Lead füt - Trail füt -
+

Loading example

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. + Lead füt + Trail füt +
+ + Sample image alt + +

Loading example

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. + Lead füt + Trail füt +
+ + Sample image alt + +

Loading example

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. + Lead füt + Trail füt +
+ + Sample image alt + +

Loading example

+ A great example of a study description that might wrap to a line or two, but isn't overly + verbose. + Lead füt + Trail füt +
+
+
Footer configuration
-

Submit button

- - Sample image alt -

Untitled experience

- Subtext - Go -
+ + + Sample image alt +

Untitled experience

+ Subtext + Go +
+ + Sample image alt +

Untitled experience

+ Subtext + Go +
+
-
- -
+
thumbnail-position="block-start"
-

thumbnail-position="block-start"

- Sample image alt -

Portland Businesses

- +

Portland Businesses

+ by example_user @@ -273,12 +721,12 @@

Portland Businesses

+
thumbnail-position="block-end"
-

thumbnail-position="block-end"

- Sample image alt -

Portland Businesses

- +

Portland Businesses

+ by example_user @@ -314,57 +762,95 @@

Portland Businesses

- -
+
thumbnail-position="inline-start"
-

thumbnail-position="inline-start"

- - Sample image alt -

Portland Businesses

- by - example_user - -
- Created: Apr 22, 2019 -
- Updated: Dec 9, 2019 -
- View Count: 0 -
- -
- - - - - - + + + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ - - View details - Duplicate - Delete - - -
-
+ + + + + + + View details + Duplicate + Delete + + +
+
+ + Sample image alt +

Portland Businesses

+ by + example_user + +
+ Created: Apr 22, 2019 +
+ Updated: Dec 9, 2019 +
+ View Count: 0 +
+ +
+ + + + + + + + + View details + Duplicate + Delete + + +
+
+
+
thumbnail-position="inline-end"
-

thumbnail-position="inline-end"

- Sample image alt -

Portland Businesses

- +

Portland Businesses

+ by example_user @@ -401,6 +887,37 @@

Portland Businesses

+
diff --git a/packages/calcite-components/src/index.html b/packages/calcite-components/src/index.html index a1dfc41d1ae..67f4edc7139 100644 --- a/packages/calcite-components/src/index.html +++ b/packages/calcite-components/src/index.html @@ -103,7 +103,8 @@

Calcite demo

diff --git a/packages/calcite-components/src/utils/dom.ts b/packages/calcite-components/src/utils/dom.ts index dcd5aa3d8c3..90ff0203544 100644 --- a/packages/calcite-components/src/utils/dom.ts +++ b/packages/calcite-components/src/utils/dom.ts @@ -595,12 +595,12 @@ export type FocusElementInGroupDestination = "first" | "last" | "next" | "previo * @param {boolean} cycle Should navigation cycle through elements or stop at extent - defaults to true. * @returns {Element} The focused element */ -export const focusElementInGroup = ( +export const focusElementInGroup = ( elements: Element[], currentElement: Element, destination: FocusElementInGroupDestination, cycle = true, -): Element => { +): T => { const currentIndex = elements.indexOf(currentElement); const isFirstItem = currentIndex === 0; const isLastItem = currentIndex === elements.length - 1; diff --git a/packages/calcite-components/stencil.config.ts b/packages/calcite-components/stencil.config.ts index 13a50b00eb7..22f2096c1f2 100644 --- a/packages/calcite-components/stencil.config.ts +++ b/packages/calcite-components/stencil.config.ts @@ -23,6 +23,7 @@ export const create: () => Config = () => ({ { components: ["calcite-block", "calcite-block-section"] }, { components: ["calcite-button"] }, { components: ["calcite-card"] }, + { components: ["calcite-card-group"] }, { components: ["calcite-checkbox"] }, { components: ["calcite-chip"] }, { components: ["calcite-chip-group"] }, @@ -78,6 +79,7 @@ export const create: () => Config = () => ({ { components: ["calcite-tab", "calcite-tab-title", "calcite-tab-nav", "calcite-tabs"] }, { components: ["calcite-text-area"] }, { components: ["calcite-tile"] }, + { components: ["calcite-tile-group"] }, { components: ["calcite-tile-select-group", "calcite-tile-select"] }, { components: ["calcite-tip", "calcite-tip-group", "calcite-tip-manager"] }, { components: ["calcite-tooltip"] },