From 724de6836c6aa554330ef6cd413ed69a15d0218e Mon Sep 17 00:00:00 2001 From: Alison Joseph Date: Tue, 15 May 2018 19:30:04 -0500 Subject: [PATCH] feat(code-snippet): Add new Code snippet variation and styles (#761) * feat(code-snippet): Update code snippet styles and names * feat(code-snippet): Update font size * chore: cleanup html files * feat(code-snippet): Update feedback copy tooltip to match new styles * chore: cleanup javascript * feat(code-snippet): Update copy tooltip styles * chore: cleanup js * feat(code-snippet): Update click event to only happen on button * chore: Add back support for deprecated class names * feat(code-snippet): Update tooltip styles and positioning * fix: update tooltip positioning for inline snippet --- demo/scss/_page.scss | 8 +- src/components/code-snippet/README.md | 7 +- .../code-snippet/_code-snippet.scss | 179 +++++++++++++++--- src/components/code-snippet/_mixins.scss | 6 +- src/components/code-snippet/code-snippet.js | 75 ++++++++ .../inline-code-snippet-light.html | 6 + .../code-snippet/inline-code-snippet.html | 7 + src/components/code-snippet/migrate-to-9.x.md | 16 ++ .../code-snippet/multi-line-code-snippet.html | 40 ++++ .../single-line-code-snippet.html | 12 ++ src/components/copy-button/_copy-button.scss | 22 ++- src/globals/js/components.js | 1 + src/globals/scss/_vars.scss | 3 +- 13 files changed, 334 insertions(+), 48 deletions(-) create mode 100644 src/components/code-snippet/code-snippet.js create mode 100644 src/components/code-snippet/inline-code-snippet-light.html create mode 100644 src/components/code-snippet/inline-code-snippet.html create mode 100644 src/components/code-snippet/migrate-to-9.x.md create mode 100644 src/components/code-snippet/multi-line-code-snippet.html create mode 100644 src/components/code-snippet/single-line-code-snippet.html diff --git a/demo/scss/_page.scss b/demo/scss/_page.scss index e9aca5fb302c..54801ad61312 100644 --- a/demo/scss/_page.scss +++ b/demo/scss/_page.scss @@ -75,12 +75,12 @@ td { } } - button { - border-radius: 0; - } - & > *:not(.component-example):not(.component-variation), & > { + button { + border-radius: 0; + } + .page__divider-heading { @include typescale('zeta'); font-weight: 600; diff --git a/src/components/code-snippet/README.md b/src/components/code-snippet/README.md index 5495eeaa706e..df4a6407152d 100644 --- a/src/components/code-snippet/README.md +++ b/src/components/code-snippet/README.md @@ -11,9 +11,10 @@ Mixins specific to Code Snippet are located in [src/components/code-snippet/_mix #### Modifiers -Use these modifiers with `.bx--root-class` class. +Use these modifiers with `.bx--snippet` class. | Selector | Description | |----------------------------------|------------------------------------| -| .bx--snippet--code | Styles for multiple lines of code | -| .bx--snippet--terminal | Styles for single lines of code | +| .bx--snippet--single | Styles for multiple lines of code | +| .bx--snippet--multi | Styles for single lines of code | +| .bx--snippet--inline | Styles for code inline inside text | \ No newline at end of file diff --git a/src/components/code-snippet/_code-snippet.scss b/src/components/code-snippet/_code-snippet.scss index 33f7bd89206a..65063658652a 100644 --- a/src/components/code-snippet/_code-snippet.scss +++ b/src/components/code-snippet/_code-snippet.scss @@ -9,60 +9,94 @@ @import 'mixins'; @include exports('snippet') { - .#{$prefix}--snippet--code { - @include bx--snippet; - background-color: $snippet-background-color; - padding: $spacing-md $spacing-2xl $spacing-md $spacing-md; - } - .#{$prefix}--snippet code { - @include typescale('epsilon'); + font-family: $font-family-mono; } - .#{$prefix}--snippet--code .#{$prefix}--snippet-container { - max-height: rem(192px); - overflow-y: scroll; - margin-right: $spacing-md; + // Inline Code Snippet + .#{$prefix}--snippet--inline { + @include reset; position: relative; + display: inline; + padding: 0; + background: transparent; + font-size: inherit; + border: 1px solid transparent; + border-radius: 4px; + background-color: $field-01; + font-size: 85%; + cursor: pointer; + + &:hover { + background-color: darken($field-01, 10%); + } + + &:focus { + @include focus-outline; + outline: none; + border: 1px solid $brand-01; + } } - .#{$prefix}--snippet--code .#{$prefix}--snippet-container pre { - white-space: pre-wrap; + .#{$prefix}--snippet--inline code { + padding: rem(1px) $spacing-xs; } - .#{$prefix}--snippet--terminal { + .#{$prefix}--snippet--inline.#{$prefix}--snippet--light { + background-color: $field-02; + + &:hover { + background-color: rgba($brand-02, 0.1); + } + } + + // Single Line Snippet + .#{$prefix}--snippet--single { @include bx--snippet; - background-color: $text-01; - color: $inverse-01; - height: 3.5rem; + height: rem(56px); padding: 0 $spacing-2xl 0 $spacing-md; } - .#{$prefix}--snippet--terminal .#{$prefix}--snippet-container { + .#{$prefix}--snippet--single .#{$prefix}--snippet-container { display: flex; align-items: center; height: 130%; white-space: nowrap; overflow-x: scroll; position: relative; + padding-bottom: $spacing-md; + } + + // Multi Line Snippet + .#{$prefix}--snippet--multi { + @include bx--snippet; + padding: $spacing-md $spacing-3xl $spacing-md $spacing-lg; + } + + .#{$prefix}--snippet--multi .#{$prefix}--snippet-container { + overflow: hidden; + position: relative; + max-height: rem(254px); + min-height: rem(56px); + transition: all $transition--base $carbon--standard-easing; + } + + .#{$prefix}--snippet--expand.#{$prefix}--snippet--multi .#{$prefix}--snippet-container { + max-height: rem(1500px); + } + + .#{$prefix}--snippet--multi .#{$prefix}--snippet-container pre { + white-space: pre-wrap; } .#{$prefix}--snippet__icon { fill: $brand-01; height: rem(24px); width: rem(18px); - transition: $transition--base; + transition: all $transition--base $carbon--standard-easing; &:hover { - fill: $brand-02; - } - } - - .#{$prefix}--snippet--terminal .#{$prefix}--snippet__icon { - fill: $inverse-01; - - &:hover { - fill: $brand-02; + fill: $hover-primary; } } @@ -70,7 +104,7 @@ @include reset; cursor: pointer; position: absolute; - top: 0.5rem; + top: 0.675rem; right: 0.5rem; border: none; background-color: transparent; @@ -87,9 +121,9 @@ .#{$prefix}--snippet .#{$prefix}--btn--copy__feedback { @include font-family; z-index: z('overlay'); - font-weight: 600; + font-weight: 400; left: inherit; - top: 1rem; + top: 0.75rem; right: 1.25rem; } @@ -112,4 +146,87 @@ height: 1rem; display: block; } + + .#{$prefix}--snippet-button .#{$prefix}--btn--copy__feedback { + top: rem(25px); + left: 1rem; + right: auto; + } + + .#{$prefix}--snippet--inline .#{$prefix}--btn--copy__feedback { + right: auto; + left: 50%; + } + + // Show more / less button + .#{$prefix}--snippet-btn--expand { + position: absolute; + right: 0; + bottom: 0; + } + + .#{$prefix}--snippet-btn--expand--hide.#{$prefix}--snippet-btn--expand { + display: none; + } + + .#{$prefix}--snippet-btn--expand .#{$prefix}--icon-chevron--down { + fill: $brand-01; + margin-left: $spacing-2xs; + transform: rotate(0deg); + transition: $transition--base; + } + + .#{$prefix}--snippet-btn--expand:hover .#{$prefix}--icon-chevron--down, + .#{$prefix}--snippet-btn--expand:focus .#{$prefix}--icon-chevron--down { + fill: $inverse-01; + } + + .#{$prefix}--snippet--expand .#{$prefix}--snippet-btn--expand .#{$prefix}--icon-chevron--down { + transform: rotate(180deg); + } + + /* + Deprecated class names will be removed in v10.x + */ + + .#{$prefix}--snippet--terminal { + @include bx--snippet; + height: rem(56px); + padding: 0 $spacing-2xl 0 $spacing-md; + } + + .#{$prefix}--snippet--terminal .#{$prefix}--snippet-container { + display: flex; + align-items: center; + height: 130%; + white-space: nowrap; + overflow-x: scroll; + position: relative; + padding-bottom: $spacing-md; + } + + .#{$prefix}--snippet--code { + @include bx--snippet; + padding: $spacing-md $spacing-3xl $spacing-md $spacing-lg; + } + + .#{$prefix}--snippet--code .#{$prefix}--snippet-container { + overflow: hidden; + position: relative; + max-height: rem(254px); + min-height: rem(56px); + transition: all $transition--base $carbon--standard-easing; + } + + .#{$prefix}--snippet--expand.#{$prefix}--snippet--code .#{$prefix}--snippet-container { + max-height: rem(1500px); + } + + .#{$prefix}--snippet--code .#{$prefix}--snippet-container pre { + white-space: pre-wrap; + } + + .#{$prefix}--snippet--inline .#{$prefix}--btn--copy__feedback { + right: auto; + } } diff --git a/src/components/code-snippet/_mixins.scss b/src/components/code-snippet/_mixins.scss index f761a4461aec..6fa3e2d7797f 100644 --- a/src/components/code-snippet/_mixins.scss +++ b/src/components/code-snippet/_mixins.scss @@ -1,6 +1,10 @@ @mixin bx--snippet { + @include typescale('omega'); + line-height: 1.45; + background: $snippet-background-color; + border: 1px solid $snippet-border-color; font-family: $font-family-mono; position: relative; - max-width: rem(640px); + max-width: rem(600px); width: 100%; } diff --git a/src/components/code-snippet/code-snippet.js b/src/components/code-snippet/code-snippet.js new file mode 100644 index 000000000000..1be63e2ccdbf --- /dev/null +++ b/src/components/code-snippet/code-snippet.js @@ -0,0 +1,75 @@ +import mixin from '../../globals/js/misc/mixin'; +import createComponent from '../../globals/js/mixins/create-component'; +import initComponentBySearch from '../../globals/js/mixins/init-component-by-search'; +import handles from '../../globals/js/mixins/handles'; + +class CodeSnippet extends mixin(createComponent, initComponentBySearch, handles) { + /** + * CodeSnippet UI. + * @extends CreateComponent + * @extends InitComponentBySearch + * @extends Handles + * @param {HTMLElement} element The element working as a CodeSnippet UI. + */ + + constructor(element, options) { + super(element, options); + + this._initCodeSnippet(); + this.element.querySelector(this.options.classExpandText).addEventListener('click', evt => this._handleClick(evt)); + } + + _handleClick() { + const expandBtn = this.element.querySelector(this.options.classExpandText); + this.element.classList.toggle(this.options.classExpanded); + + if (this.element.classList.contains(this.options.classExpanded)) { + expandBtn.textContent = expandBtn.getAttribute(this.options.attribShowLessText); + } else { + expandBtn.textContent = expandBtn.getAttribute(this.options.attribShowMoreText); + } + } + + _initCodeSnippet() { + const expandBtn = this.element.querySelector(this.options.classExpandText); + if (!expandBtn) { + throw new TypeError('Cannot find the expand button.'); + } + + expandBtn.textContent = expandBtn.getAttribute(this.options.attribShowMoreText); + + if (this.element.offsetHeight < this.options.minHeight) { + this.element.classList.add(this.options.classHideExpand); + this.element.classList.add(this.options.classExpanded); + } + } + + /** + * The map associating DOM element and code snippet UI instance. + * @member CodeSnippet.components + * @type {WeakMap} + */ + static components = new WeakMap(); + + /** + * The component options. + * If `options` is specified in the constructor, {@linkcode CodeSnippet.create .create()}, + * or {@linkcode CodeSnippet.init .init()}, + * properties in this object are overriden for the instance being create and how {@linkcode CodeSnippet.init .init()} works. + * @member CodeSnippet.options + * @type {Object} + * @property {string} selectorInit The data attribute to find code snippet UIs. + */ + static options = { + selectorInit: '[data-code-snippet]', + attribShowMoreText: 'data-show-more-text', + attribShowLessText: 'data-show-less-text', + minHeight: 288, + classExpanded: 'bx--snippet--expand', + classExpandBtn: 'bx--snippet-btn--expand', + classExpandText: '.bx--snippet-btn--text', + classHideExpand: 'bx--snippet-btn--expand--hide', + }; +} + +export default CodeSnippet; diff --git a/src/components/code-snippet/inline-code-snippet-light.html b/src/components/code-snippet/inline-code-snippet-light.html new file mode 100644 index 000000000000..0ee65a2dec6b --- /dev/null +++ b/src/components/code-snippet/inline-code-snippet-light.html @@ -0,0 +1,6 @@ +

Here is an example of a text that a user would be reading. In this paragraph would be + +that the user could look at and copy in to their code editor.

diff --git a/src/components/code-snippet/inline-code-snippet.html b/src/components/code-snippet/inline-code-snippet.html new file mode 100644 index 000000000000..3f681c6145d4 --- /dev/null +++ b/src/components/code-snippet/inline-code-snippet.html @@ -0,0 +1,7 @@ + +

Here is an example of a text that a user would be reading. In this paragraph would be + + that the user could look at and copy in to their code editor.

diff --git a/src/components/code-snippet/migrate-to-9.x.md b/src/components/code-snippet/migrate-to-9.x.md new file mode 100644 index 000000000000..ac6218e6c87f --- /dev/null +++ b/src/components/code-snippet/migrate-to-9.x.md @@ -0,0 +1,16 @@ +### HTML + +Terminal snippet has been renamed to Single line snippet, Code snippet has been renamed to Multi-line snippet. We have also added a new variation, Inline snippet. + +### SCSS + +|Old Class |New Class | Note | +|------------------------|------------------------|---------| +|`bx--snippet--terminal` |`bx--snippet--single` | Changed | +|`bx--snippet--code` |`bx--snippet--multi` | Changed | +| |`bx--snippet--inline` | New | + + +### Javascript + +The multi-line code snippet now has javascript. Be sure to add the data attribute `[data-code-snippet]` for it to work. \ No newline at end of file diff --git a/src/components/code-snippet/multi-line-code-snippet.html b/src/components/code-snippet/multi-line-code-snippet.html new file mode 100644 index 000000000000..bcd0f0a33dee --- /dev/null +++ b/src/components/code-snippet/multi-line-code-snippet.html @@ -0,0 +1,40 @@ +
+
+ +
+@mixin grid-container {
+  width: 100%;
+  padding-right: padding(mobile);
+  padding-left: padding(mobile);
+
+  @include breakpoint(bp--xs--major) {
+    padding-right: padding(xs);
+    padding-left: padding(xs);
+  }
+}
+
+$z-indexes: (
+  modal : 9000,
+  overlay : 8000,
+  dropdown : 7000,
+  header : 6000,
+  footer : 5000,
+  hidden : - 1,
+  overflowHidden: - 1,
+  floating: 10000
+);       
+      
+
+
+ + +
diff --git a/src/components/code-snippet/single-line-code-snippet.html b/src/components/code-snippet/single-line-code-snippet.html new file mode 100644 index 000000000000..a58f52427106 --- /dev/null +++ b/src/components/code-snippet/single-line-code-snippet.html @@ -0,0 +1,12 @@ +
+
+
node -v Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis, veritatis voluptate id incidunt molestiae officia possimus, quasi itaque alias, architecto hic, dicta fugit? Debitis delectus quidem explicabo vitae fuga laboriosam!
+
+ +
diff --git a/src/components/copy-button/_copy-button.scss b/src/components/copy-button/_copy-button.scss index a9ffa6aadb37..5c2eaa7f9cef 100644 --- a/src/components/copy-button/_copy-button.scss +++ b/src/components/copy-button/_copy-button.scss @@ -23,25 +23,31 @@ top: 1.2rem; left: 50%; + &:focus { + border: 2px solid red; + } + &:before { @include layer('overlay'); @include typescale('omega'); top: 1.1rem; - padding: 0.5rem 1rem; - border: 1px solid $ui-03; - color: $text-01; + padding: $spacing-2xs; + color: $inverse-01; content: attr(data-feedback); transform: translateX(-50%); white-space: nowrap; pointer-events: none; + border-radius: 4px; + font-weight: 400; } &:after { top: 0.85rem; - width: 0.5rem; - height: 0.5rem; - border-right: 1px solid $ui-03; - border-bottom: 1px solid $ui-03; + width: 0.6rem; + height: 0.6rem; + left: -0.3rem; + border-right: 1px solid $inverse-02; + border-bottom: 1px solid $inverse-02; content: ''; transform: rotate(-135deg); } @@ -50,7 +56,7 @@ &:after { position: absolute; display: block; - background: $ui-01; + background: $inverse-02; } &--displayed { diff --git a/src/globals/js/components.js b/src/globals/js/components.js index ae93e3ae32d2..b29f32c0ff93 100644 --- a/src/globals/js/components.js +++ b/src/globals/js/components.js @@ -30,3 +30,4 @@ export { default as Slider } from '../../components/slider/slider'; export { default as Tile } from '../../components/tile/tile'; export { default as Carousel } from '../../components/carousel/carousel'; export { default as Lightbox } from '../../components/lightbox/lightbox'; +export { default as CodeSnippet } from '../../components/code-snippet/code-snippet'; diff --git a/src/globals/scss/_vars.scss b/src/globals/scss/_vars.scss index 7c0df12a3fd6..a5eef158d188 100644 --- a/src/globals/scss/_vars.scss +++ b/src/globals/scss/_vars.scss @@ -83,7 +83,8 @@ $card-flex-align: center !default; $checkbox-border-width: 2px !default; // Code Snippet -$snippet-background-color: rgba($color__blue-51, 0.1) !default; +$snippet-background-color: $ui-01 !default; +$snippet-border-color: $ui-03 !default; // Content Switcher $content-switcher-border-radius: 8px !default;