diff --git a/src/components/combo-box/combo-box.hbs b/src/components/combo-box/combo-box.hbs index feceb1badd91..e686647e577b 100644 --- a/src/components/combo-box/combo-box.hbs +++ b/src/components/combo-box/combo-box.hbs @@ -1,31 +1,29 @@
-
-
- -
- - Close menu - - -
-
-
- {{#each items}} -
{{label}}
- {{/each}} +
+ + Close menu + +
+
+ {{#each items}} +
{{label}}
+ {{/each}} +
+
diff --git a/src/components/dropdown/_dropdown.scss b/src/components/dropdown/_dropdown.scss index b1d01aa0d6f0..3e72b372adc2 100644 --- a/src/components/dropdown/_dropdown.scss +++ b/src/components/dropdown/_dropdown.scss @@ -14,7 +14,7 @@ @import '../../globals/scss/css--typography'; @import '../../globals/scss/layout'; -@include exports('dropdown') { +@mixin dropdown { .#{$prefix}--dropdown { @include reset; @include font-family; @@ -224,3 +224,242 @@ @include skeleton; } } + +@mixin dropdown--x { + .#{$prefix}--dropdown { + @include reset; + @include font-family; + @include typescale('zeta'); + position: relative; + list-style: none; + display: block; + background-color: $field-01; + border: none; + box-shadow: 0 1px 0 0 $ui-04; + order: 1; + width: 100%; + height: rem(32px); + cursor: pointer; + color: $text-01; + outline: 2px solid transparent; + transition: $transition--base all; + + &:focus { + outline: 2px solid $brand-01; + } + + &:hover { + background-color: $ui-03; + } + } + + .#{$prefix}--dropdown--open:focus { + outline: 1px solid transparent; + box-shadow: none; + } + + .#{$prefix}--dropdown--open .#{$prefix}--dropdown-list { + @include layer('overlay'); + } + + .#{$prefix}--dropdown--light { + background-color: $field-02; + } + + .#{$prefix}--dropdown--up .#{$prefix}--dropdown-list { + bottom: 2rem; + } + + .#{$prefix}--dropdown__arrow { + fill: $ui-05; + position: absolute; + right: 1rem; + top: rem(13px); + width: rem(10px); + height: rem(5px); + pointer-events: none; + transition: transform $transition--expansion $carbon--standard-easing; + transform-origin: 50% 45%; + } + + .#{$prefix}--dropdown[data-value=''] .#{$prefix}--dropdown-text { + color: $text-01; + } + + .#{$prefix}--dropdown-text { + height: rem(32px); + padding-top: rem(9px); + padding-bottom: rem(9px); + padding-left: rem(15px); + padding-right: $spacing-2xl; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + border: $input-border; + } + + .#{$prefix}--dropdown-list { + @include reset; + @include layer('overlay'); + @include typescale('zeta'); + background-color: $ui-01; + display: flex; + flex-direction: column; + width: 100%; + list-style: none; + position: absolute; + z-index: z('dropdown'); + max-height: 0; + transition: max-height $transition--expansion cubic-bezier(0, 0, 0.38, 0.9); + overflow: hidden; + } + + .#{$prefix}--dropdown-item { + transition: opacity $transition--expansion cubic-bezier(0, 0, 0.38, 0.9); + opacity: 0; + margin-top: -1px; + + &:hover { + background-color: $ui-03; + } + + &:first-of-type { + padding-top: 3px; + } + } + + .#{$prefix}--dropdown-item:last-of-type .#{$prefix}--dropdown-link { + border-bottom: none; + margin-bottom: 2px; + } + + .#{$prefix}--dropdown-link { + display: block; + color: currentColor; + text-decoration: none; + font-weight: normal; + line-height: rem(16px); + padding: $spacing-xs 0; + margin: 0 rem(16px); + border: 1px solid transparent; + border-bottom: 1px solid $ui-03; + text-overflow: ellipsis; + overflow: hidden; + + &:focus { + outline: 2px solid $brand-01; + margin: 0 rem(2px); + padding: $spacing-xs rem(14px); + } + + &:hover { + color: $text-01; + } + } + + .#{$prefix}--dropdown--selected { + display: none; + } + + .#{$prefix}--dropdown--open .#{$prefix}--dropdown__arrow { + transform: rotate(-180deg); + } + + .#{$prefix}--dropdown--open .#{$prefix}--dropdown-list { + max-height: 15rem; + + &:hover { + overflow: auto; + } + } + + .#{$prefix}--dropdown--open .#{$prefix}--dropdown-item { + opacity: 1; + } + + .#{$prefix}--dropdown--disabled { + opacity: 0.5; + cursor: not-allowed; + + &:focus { + outline: none; + } + } + + .#{$prefix}--dropdown--auto-width { + width: auto; + max-width: rem(400px); + } + + .#{$prefix}--dropdown--inline { + background-color: transparent; + box-shadow: none; + transition-property: none; + + &:focus { + outline: none; + box-shadow: none; + } + + &:hover { + background-color: transparent; + } + } + + .#{$prefix}--dropdown--inline:focus .#{$prefix}--dropdown-text { + outline: 2px solid $brand-01; + } + + .#{$prefix}--dropdown--inline[data-value=''] .#{$prefix}--dropdown-text { + color: $text-01; + } + + .#{$prefix}--dropdown--inline .#{$prefix}--dropdown-text { + display: inline-block; + padding-right: 1.5rem; + overflow: visible; + color: $text-01; + } + + .#{$prefix}--dropdown--inline.#{$prefix}--dropdown--open:focus { + box-shadow: none; + } + + .#{$prefix}--dropdown--inline.#{$prefix}--dropdown--open:focus .#{$prefix}--dropdown-list { + @include layer('overlay'); + } + + .#{$prefix}--dropdown--inline.#{$prefix}--dropdown--open:focus .#{$prefix}--dropdown-text { + outline: none; + } + + .#{$prefix}--dropdown--inline .#{$prefix}--dropdown-item:first-of-type { + padding-top: 3px; + } + + .#{$prefix}--dropdown--inline .#{$prefix}--dropdown__arrow { + position: relative; + top: -2px; + left: 8px; + right: 0; + bottom: 0; + } + + .#{$prefix}--dropdown--inline .#{$prefix}--dropdown-link { + font-weight: normal; + } + + // Skeleton State + .#{$prefix}--dropdown-v2.#{$prefix}--skeleton, + .#{$prefix}--dropdown.#{$prefix}--skeleton { + @include skeleton; + } +} + +@include exports('dropdown') { + @if feature-flag-enabled('components-x') { + @include dropdown--x; + } @else { + @include dropdown; + } +} diff --git a/src/components/list-box/_list-box.scss b/src/components/list-box/_list-box.scss index a725911a1473..9bdca1c7ebf9 100644 --- a/src/components/list-box/_list-box.scss +++ b/src/components/list-box/_list-box.scss @@ -16,7 +16,7 @@ $list-box-height: rem(40px); $list-box-inline-height: rem(32px); $list-box-menu-width: rem(300px); -@include exports('list-box') { +@mixin listbox { // The overall container element for a `list-box`. Has two variants, // `disabled` and `inline`. .#{$prefix}--list-box { @@ -254,11 +254,11 @@ $list-box-menu-width: rem(300px); color: $text-01; } - .bx--list-box__menu-item .bx--checkbox-label { + .#{$prefix}--list-box__menu-item .#{$prefix}--checkbox-label { width: 100%; } - .bx--list-box__menu-item .bx--checkbox-label-text { + .#{$prefix}--list-box__menu-item .#{$prefix}--checkbox-label-text { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -291,3 +291,328 @@ $list-box-menu-width: rem(300px); opacity: 1; } } + +@mixin listbox--x { + // The overall container element for a `list-box`. Has two variants, + // `disabled` and `inline`. + .#{$prefix}--list-box { + position: relative; + width: $list-box-width; + height: rem(32px); + max-height: rem(32px); + background-color: $field-01; + border: none; + box-shadow: 0 1px 0 0 $ui-05; + order: 1; + cursor: pointer; + color: $text-01; + transition: all $transition--expansion $carbon--standard-easing; + + &:hover { + background-color: $ui-03; + } + } + + .#{$prefix}--list-box .#{$prefix}--text-input { + height: 100%; + } + + // invalid states + .#{$prefix}--list-box[data-invalid], + .#{$prefix}--list-box[data-invalid] .#{$prefix}--list-box__field:focus { + box-shadow: 0 2px 0 0 $support-01; + } + + .#{$prefix}--list-box[data-invalid] .#{$prefix}--list-box__field:focus .#{$prefix}--list-box__label { + color: $support-01; + } + + .#{$prefix}--list-box ~ .#{$prefix}--form-requirement { + order: 3; + color: $support-01; + font-weight: 400; + margin-top: $spacing-2xs; + + &::before { + display: none; + } + } + + // Light variation for 'list-box' + .#{$prefix}--list-box--light { + background-color: $field-02; + } + + // Disabled state for a `list-box` + .#{$prefix}--list-box--disabled { + opacity: 0.5; + } + + .#{$prefix}--list-box--disabled, + .#{$prefix}--list-box--disabled .#{$prefix}--list-box__field, + .#{$prefix}--list-box--disabled .#{$prefix}--list-box__menu-icon { + cursor: not-allowed; + } + + .#{$prefix}--list-box--disabled .#{$prefix}--list-box__menu-item:hover, + .#{$prefix}--list-box--disabled .#{$prefix}--list-box__menu-item--highlighted { + background-color: transparent; + text-decoration: none; + } + + // Inline variant for a `list-box` + .#{$prefix}--list-box.#{$prefix}--list-box--inline { + background-color: inherit; + width: auto; + height: $list-box-inline-height; + box-shadow: none; + } + + .#{$prefix}--list-box--inline .#{$prefix}--list-box__label { + color: $text-01; + } + + .#{$prefix}--list-box--inline .#{$prefix}--list-box__field { + padding: 0 0 0 rem(10px); + } + + .#{$prefix}--list-box--inline .#{$prefix}--list-box__menu-icon { + position: static; + padding: 0 $spacing-xs; + } + + .#{$prefix}--list-box--inline > .#{$prefix}--list-box__menu { + top: $list-box-inline-height; + width: $list-box-menu-width; + } + + .#{$prefix}--list-box--inline > .#{$prefix}--list-box__field[aria-expanded='true'] ~ .#{$prefix}--list-box__menu { + outline: 1px solid $brand-01; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.1); + } + + .#{$prefix}--list-box--inline > .#{$prefix}--list-box__field[aria-expanded='true'], + .#{$prefix}--list-box.#{$prefix}--list-box--inline > .#{$prefix}--list-box__field:focus { + outline: none; + box-shadow: none; + } + + // The field we use for input, showing selection, etc. + .#{$prefix}--list-box__field { + @include button-reset; + position: relative; + display: inline-flex; + align-items: center; + height: rem(32px); + padding: 0 1rem; + cursor: pointer; + } + + .#{$prefix}--list-box__field:focus, + .#{$prefix}--list-box__field[aria-expanded='true'] { + background: $ui-03; + } + + .#{$prefix}--list-box__field[disabled] { + outline: none; + opacity: 0.5; + } + + // Label for a `list-box__field` + .#{$prefix}--list-box__label { + @include typescale('zeta'); + + color: $text-01; + font-weight: 600; + user-select: none; + } + + // Menu status inside of a `list-box__field` + .#{$prefix}--list-box__menu-icon { + display: inline-block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + height: 100%; + padding: 0 1rem; + transition: transform 200ms $carbon--standard-easing; + cursor: pointer; + } + + .#{$prefix}--list-box__menu-icon > svg { + fill: $ui-05; + height: 100%; + } + + .#{$prefix}--list-box__menu-icon--open { + transform: rotate(180deg); + } + + // Selection indicator for a `list-box__field` + .#{$prefix}--list-box__selection { + display: inline-block; + position: absolute; + top: 0; + right: rem(26px); + bottom: 0; + height: rem(40px); + padding: 0 1rem; + cursor: pointer; + user-select: none; + transition: background-color 200ms $carbon--standard-easing; + } + + .#{$prefix}--list-box__selection > svg { + fill: $ui-05; + height: 100%; + } + + .#{$prefix}--list-box__selection:focus { + outline: 1px solid $brand-01; + } + + // Modifier for a selection to show that multiple selections have been made + .#{$prefix}--list-box__selection--multi { + @include typescale('omega'); + + position: static; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0; + background-color: $brand-01; + height: rem(24px); + width: rem(42px); + color: white; + line-height: 0; + padding: 0.5rem; + margin-right: rem(10px); + border-radius: rem(12px); + } + + .#{$prefix}--list-box__selection--multi > svg { + fill: white; + width: 0.5rem; + height: 0.5rem; + margin-left: rem(5px); + } + + .#{$prefix}--list-box__selection--multi:focus, + .#{$prefix}--list-box__selection--multi:hover { + outline: none; + } + + // Descendant of a `list-box` that displays a list of options to select + .#{$prefix}--list-box__menu { + @include box-shadow(); + + position: absolute; + left: 0; + right: 0; + top: rem(32px); + width: $list-box-width; + background-color: $ui-01; + max-height: rem(120px); + overflow-y: auto; + z-index: z('dropdown'); + } + + // Descendant of a `list-box__menu` that represents a selection for a control + .#{$prefix}--list-box__menu-item { + @include typescale('zeta'); + + display: flex; + align-items: center; + height: rem(32px); + margin-top: -1px; + padding: 0 1rem; + cursor: pointer; + user-select: none; + border-bottom: 1px solid $ui-03; + position: relative; + + &::before, + &::after { + content: ''; + display: block; + position: absolute; + height: calc(100% + 1px); + width: 1rem; + background-color: $ui-01; + } + + &::before { + left: 0; + } + + &::after { + right: 0; + } + + &:last-of-type { + border-bottom: 1px solid $ui-01; + } + + &:last-of-type:hover { + border-bottom: 1px solid $ui-03; + } + } + + .#{$prefix}--list-box__menu-item:hover, + .#{$prefix}--list-box__menu-item--highlighted { + background-color: $ui-03; + color: $text-01; + border-bottom: 1px solid $ui-01; + + &::before, + &::after { + background-color: $ui-03; + } + } + + .#{$prefix}--list-box__menu-item .#{$prefix}--checkbox-label { + width: 100%; + } + + .#{$prefix}--list-box__menu-item .#{$prefix}--checkbox-label-text { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + // Tweaks for descendants + + // In multi-select scenarios, we need to target checkbox inputs + .#{$prefix}--list-box__menu-item > .#{$prefix}--form-item.#{$prefix}--checkbox-wrapper { + margin: 0; + width: 100%; + } + + // When handling input, we need to target nodes that specifically opt-in to + // the `combobox` role in order to make sure the text input is styled + // correctly. + .#{$prefix}--list-box input[role='combobox'] { + background-color: inherit; + font-weight: 600; + outline-offset: -1px; + min-width: 0; + } + + .#{$prefix}--list-box input[role='combobox']::placeholder { + color: $text-01; + font-weight: 400; + } + + .#{$prefix}--list-box--disabled input[role='combobox'] { + opacity: 1; + } +} + +@include exports('list-box') { + @if feature-flag-enabled('components-x') { + @include listbox--x; + } @else { + @include listbox; + } +}