From 1bb6f062ba0309e6a91ebb2fce6046b65250bed0 Mon Sep 17 00:00:00 2001 From: "dave.snider@gmail.com" Date: Mon, 21 Aug 2017 10:45:31 -0700 Subject: [PATCH] [UI FRAMEWORK][K7]: Flex Group and Flex Grid components (#13603) FlexGroup and FlexGrid allows for layout and positioning inside content. --- ui_framework/dist/ui_framework_theme_dark.css | 183 +++++++++++++++ .../dist/ui_framework_theme_light.css | 183 +++++++++++++++ .../components/guide_demo/_guide_demo.scss | 14 ++ .../doc_site/src/services/routes/routes.js | 7 + .../doc_site/src/views/flex/flex_example.js | 210 ++++++++++++++++++ .../doc_site/src/views/flex/flex_group.js | 18 ++ .../doc_site/src/views/flex/flex_grow.js | 16 ++ .../doc_site/src/views/flex/flex_gutter.js | 57 +++++ .../doc_site/src/views/flex/flex_items.js | 16 ++ .../doc_site/src/views/flex/flex_justify.js | 21 ++ .../doc_site/src/views/flex/flex_nest.js | 25 +++ .../doc_site/src/views/flex/flex_wrap.js | 21 ++ .../src/components/flex/_flex_grid.scss | 40 ++++ .../src/components/flex/_flex_group.scss | 56 +++++ .../src/components/flex/_flex_item.scss | 10 + ui_framework/src/components/flex/_index.scss | 3 + ui_framework/src/components/flex/flex_grid.js | 52 +++++ .../src/components/flex/flex_grid.test.js | 16 ++ .../src/components/flex/flex_group.js | 70 ++++++ .../src/components/flex/flex_group.test.js | 16 ++ ui_framework/src/components/flex/flex_item.js | 20 ++ .../src/components/flex/flex_item.test.js | 16 ++ ui_framework/src/components/flex/index.js | 11 + ui_framework/src/components/index.js | 6 + ui_framework/src/components/index.scss | 1 + 25 files changed, 1088 insertions(+) create mode 100644 ui_framework/doc_site/src/views/flex/flex_example.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_group.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_grow.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_gutter.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_items.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_justify.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_nest.js create mode 100644 ui_framework/doc_site/src/views/flex/flex_wrap.js create mode 100644 ui_framework/src/components/flex/_flex_grid.scss create mode 100644 ui_framework/src/components/flex/_flex_group.scss create mode 100644 ui_framework/src/components/flex/_flex_item.scss create mode 100644 ui_framework/src/components/flex/_index.scss create mode 100644 ui_framework/src/components/flex/flex_grid.js create mode 100644 ui_framework/src/components/flex/flex_grid.test.js create mode 100644 ui_framework/src/components/flex/flex_group.js create mode 100644 ui_framework/src/components/flex/flex_group.test.js create mode 100644 ui_framework/src/components/flex/flex_item.js create mode 100644 ui_framework/src/components/flex/flex_item.test.js create mode 100644 ui_framework/src/components/flex/index.js diff --git a/ui_framework/dist/ui_framework_theme_dark.css b/ui_framework/dist/ui_framework_theme_dark.css index 78901293152f56..0e9dc9881f579a 100644 --- a/ui_framework/dist/ui_framework_theme_dark.css +++ b/ui_framework/dist/ui_framework_theme_dark.css @@ -729,6 +729,189 @@ table { transform: translateY(2px); /* 1 */ } +.kuiFlexGroup { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; } + .kuiFlexGroup .kuiFlexItem { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-flex-basis: 0; + -ms-flex-preferred-size: 0; + flex-basis: 0; } + .kuiFlexGroup.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem { + margin-left: 8px; } + .kuiFlexGroup.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { + margin-left: 16px; } + .kuiFlexGroup.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { + margin-left: 24px; } + .kuiFlexGroup.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { + margin-left: 40px; } + .kuiFlexGroup.kuiFlexGroup--flexGrowZero > .kuiFlexItem { + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-flex-basis: auto; + -ms-flex-preferred-size: auto; + flex-basis: auto; } + .kuiFlexGroup.kuiFlexGroup--justifyContentSpaceBetween { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; } + .kuiFlexGroup.kuiFlexGroup--justifyContentSpaceAround { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around; } + .kuiFlexGroup.kuiFlexGroup--justifyContentCenter { + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; } + .kuiFlexGroup.kuiFlexGroup--justifyContentFlexEnd { + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; } + +@media only screen and (max-width: 768px) { + .kuiFlexGroup { + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; } } + +.kuiFlexGrid { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-bottom: 0; } + .kuiFlexGrid > .kuiFlexItem { + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; } + +.kuiFlexGrid--gutterSmall { + margin: -4px; } + .kuiFlexGrid--gutterSmall.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 4px; + -webkit-flex-basis: calc(25% - 8px); + -ms-flex-preferred-size: calc(25% - 8px); + flex-basis: calc(25% - 8px); } + +.kuiFlexGrid--gutterSmall { + margin: -4px; } + .kuiFlexGrid--gutterSmall.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 4px; + -webkit-flex-basis: calc(33.3% - 8px); + -ms-flex-preferred-size: calc(33.3% - 8px); + flex-basis: calc(33.3% - 8px); } + +.kuiFlexGrid--gutterSmall { + margin: -4px; } + .kuiFlexGrid--gutterSmall.kuiFlexGrid--halves > .kuiFlexItem { + margin: 4px; + -webkit-flex-basis: calc(50% - 8px); + -ms-flex-preferred-size: calc(50% - 8px); + flex-basis: calc(50% - 8px); } + +.kuiFlexGrid--gutterMedium { + margin: -8px; } + .kuiFlexGrid--gutterMedium.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 8px; + -webkit-flex-basis: calc(25% - 16px); + -ms-flex-preferred-size: calc(25% - 16px); + flex-basis: calc(25% - 16px); } + +.kuiFlexGrid--gutterMedium { + margin: -8px; } + .kuiFlexGrid--gutterMedium.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 8px; + -webkit-flex-basis: calc(33.3% - 16px); + -ms-flex-preferred-size: calc(33.3% - 16px); + flex-basis: calc(33.3% - 16px); } + +.kuiFlexGrid--gutterMedium { + margin: -8px; } + .kuiFlexGrid--gutterMedium.kuiFlexGrid--halves > .kuiFlexItem { + margin: 8px; + -webkit-flex-basis: calc(50% - 16px); + -ms-flex-preferred-size: calc(50% - 16px); + flex-basis: calc(50% - 16px); } + +.kuiFlexGrid--gutterLarge { + margin: -12px; } + .kuiFlexGrid--gutterLarge.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 12px; + -webkit-flex-basis: calc(25% - 24px); + -ms-flex-preferred-size: calc(25% - 24px); + flex-basis: calc(25% - 24px); } + +.kuiFlexGrid--gutterLarge { + margin: -12px; } + .kuiFlexGrid--gutterLarge.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 12px; + -webkit-flex-basis: calc(33.3% - 24px); + -ms-flex-preferred-size: calc(33.3% - 24px); + flex-basis: calc(33.3% - 24px); } + +.kuiFlexGrid--gutterLarge { + margin: -12px; } + .kuiFlexGrid--gutterLarge.kuiFlexGrid--halves > .kuiFlexItem { + margin: 12px; + -webkit-flex-basis: calc(50% - 24px); + -ms-flex-preferred-size: calc(50% - 24px); + flex-basis: calc(50% - 24px); } + +.kuiFlexGrid--gutterXLarge { + margin: -16px; } + .kuiFlexGrid--gutterXLarge.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 16px; + -webkit-flex-basis: calc(25% - 32px); + -ms-flex-preferred-size: calc(25% - 32px); + flex-basis: calc(25% - 32px); } + +.kuiFlexGrid--gutterXLarge { + margin: -16px; } + .kuiFlexGrid--gutterXLarge.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 16px; + -webkit-flex-basis: calc(33.3% - 32px); + -ms-flex-preferred-size: calc(33.3% - 32px); + flex-basis: calc(33.3% - 32px); } + +.kuiFlexGrid--gutterXLarge { + margin: -16px; } + .kuiFlexGrid--gutterXLarge.kuiFlexGrid--halves > .kuiFlexItem { + margin: 16px; + -webkit-flex-basis: calc(50% - 32px); + -ms-flex-preferred-size: calc(50% - 32px); + flex-basis: calc(50% - 32px); } + +@media only screen and (max-width: 768px) { + .kuiFlexItem { + width: 100% !important; + -webkit-flex-basis: 100% !important; + -ms-flex-preferred-size: 100% !important; + flex-basis: 100% !important; + margin-left: 0 !important; + margin-bottom: 16px !important; } } + /** * 1. Override invalid state with focus state. */ diff --git a/ui_framework/dist/ui_framework_theme_light.css b/ui_framework/dist/ui_framework_theme_light.css index ee255f148dea8b..c6d2b871cdecfb 100644 --- a/ui_framework/dist/ui_framework_theme_light.css +++ b/ui_framework/dist/ui_framework_theme_light.css @@ -729,6 +729,189 @@ table { transform: translateY(2px); /* 1 */ } +.kuiFlexGroup { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; } + .kuiFlexGroup .kuiFlexItem { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-flex-basis: 0; + -ms-flex-preferred-size: 0; + flex-basis: 0; } + .kuiFlexGroup.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem { + margin-left: 8px; } + .kuiFlexGroup.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { + margin-left: 16px; } + .kuiFlexGroup.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { + margin-left: 24px; } + .kuiFlexGroup.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { + margin-left: 40px; } + .kuiFlexGroup.kuiFlexGroup--flexGrowZero > .kuiFlexItem { + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-flex-basis: auto; + -ms-flex-preferred-size: auto; + flex-basis: auto; } + .kuiFlexGroup.kuiFlexGroup--justifyContentSpaceBetween { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; } + .kuiFlexGroup.kuiFlexGroup--justifyContentSpaceAround { + -webkit-justify-content: space-around; + -ms-flex-pack: distribute; + justify-content: space-around; } + .kuiFlexGroup.kuiFlexGroup--justifyContentCenter { + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; } + .kuiFlexGroup.kuiFlexGroup--justifyContentFlexEnd { + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; } + +@media only screen and (max-width: 768px) { + .kuiFlexGroup { + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; } } + +.kuiFlexGrid { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: stretch; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-bottom: 0; } + .kuiFlexGrid > .kuiFlexItem { + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; } + +.kuiFlexGrid--gutterSmall { + margin: -4px; } + .kuiFlexGrid--gutterSmall.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 4px; + -webkit-flex-basis: calc(25% - 8px); + -ms-flex-preferred-size: calc(25% - 8px); + flex-basis: calc(25% - 8px); } + +.kuiFlexGrid--gutterSmall { + margin: -4px; } + .kuiFlexGrid--gutterSmall.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 4px; + -webkit-flex-basis: calc(33.3% - 8px); + -ms-flex-preferred-size: calc(33.3% - 8px); + flex-basis: calc(33.3% - 8px); } + +.kuiFlexGrid--gutterSmall { + margin: -4px; } + .kuiFlexGrid--gutterSmall.kuiFlexGrid--halves > .kuiFlexItem { + margin: 4px; + -webkit-flex-basis: calc(50% - 8px); + -ms-flex-preferred-size: calc(50% - 8px); + flex-basis: calc(50% - 8px); } + +.kuiFlexGrid--gutterMedium { + margin: -8px; } + .kuiFlexGrid--gutterMedium.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 8px; + -webkit-flex-basis: calc(25% - 16px); + -ms-flex-preferred-size: calc(25% - 16px); + flex-basis: calc(25% - 16px); } + +.kuiFlexGrid--gutterMedium { + margin: -8px; } + .kuiFlexGrid--gutterMedium.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 8px; + -webkit-flex-basis: calc(33.3% - 16px); + -ms-flex-preferred-size: calc(33.3% - 16px); + flex-basis: calc(33.3% - 16px); } + +.kuiFlexGrid--gutterMedium { + margin: -8px; } + .kuiFlexGrid--gutterMedium.kuiFlexGrid--halves > .kuiFlexItem { + margin: 8px; + -webkit-flex-basis: calc(50% - 16px); + -ms-flex-preferred-size: calc(50% - 16px); + flex-basis: calc(50% - 16px); } + +.kuiFlexGrid--gutterLarge { + margin: -12px; } + .kuiFlexGrid--gutterLarge.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 12px; + -webkit-flex-basis: calc(25% - 24px); + -ms-flex-preferred-size: calc(25% - 24px); + flex-basis: calc(25% - 24px); } + +.kuiFlexGrid--gutterLarge { + margin: -12px; } + .kuiFlexGrid--gutterLarge.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 12px; + -webkit-flex-basis: calc(33.3% - 24px); + -ms-flex-preferred-size: calc(33.3% - 24px); + flex-basis: calc(33.3% - 24px); } + +.kuiFlexGrid--gutterLarge { + margin: -12px; } + .kuiFlexGrid--gutterLarge.kuiFlexGrid--halves > .kuiFlexItem { + margin: 12px; + -webkit-flex-basis: calc(50% - 24px); + -ms-flex-preferred-size: calc(50% - 24px); + flex-basis: calc(50% - 24px); } + +.kuiFlexGrid--gutterXLarge { + margin: -16px; } + .kuiFlexGrid--gutterXLarge.kuiFlexGrid--fourths > .kuiFlexItem { + margin: 16px; + -webkit-flex-basis: calc(25% - 32px); + -ms-flex-preferred-size: calc(25% - 32px); + flex-basis: calc(25% - 32px); } + +.kuiFlexGrid--gutterXLarge { + margin: -16px; } + .kuiFlexGrid--gutterXLarge.kuiFlexGrid--thirds > .kuiFlexItem { + margin: 16px; + -webkit-flex-basis: calc(33.3% - 32px); + -ms-flex-preferred-size: calc(33.3% - 32px); + flex-basis: calc(33.3% - 32px); } + +.kuiFlexGrid--gutterXLarge { + margin: -16px; } + .kuiFlexGrid--gutterXLarge.kuiFlexGrid--halves > .kuiFlexItem { + margin: 16px; + -webkit-flex-basis: calc(50% - 32px); + -ms-flex-preferred-size: calc(50% - 32px); + flex-basis: calc(50% - 32px); } + +@media only screen and (max-width: 768px) { + .kuiFlexItem { + width: 100% !important; + -webkit-flex-basis: 100% !important; + -ms-flex-preferred-size: 100% !important; + flex-basis: 100% !important; + margin-left: 0 !important; + margin-bottom: 16px !important; } } + /** * 1. Override invalid state with focus state. */ diff --git a/ui_framework/doc_site/src/components/guide_demo/_guide_demo.scss b/ui_framework/doc_site/src/components/guide_demo/_guide_demo.scss index c151616b235136..077f9300af8672 100644 --- a/ui_framework/doc_site/src/components/guide_demo/_guide_demo.scss +++ b/ui_framework/doc_site/src/components/guide_demo/_guide_demo.scss @@ -18,3 +18,17 @@ background: transparentize(#0096CC, .9); } } + +.guideDemo__highlightGrid { + .kuiFlexItem { + background: transparentize(#0096CC, .9); + padding: 16px; + } +} + +.guideDemo__highlightGridWrap { + .kuiFlexItem div { + background: transparentize(#0096CC, .9); + padding: 16px; + } +} diff --git a/ui_framework/doc_site/src/services/routes/routes.js b/ui_framework/doc_site/src/services/routes/routes.js index a83232d0f6c2ce..455945ce7561b2 100644 --- a/ui_framework/doc_site/src/services/routes/routes.js +++ b/ui_framework/doc_site/src/services/routes/routes.js @@ -12,6 +12,9 @@ import ButtonExample import CallOutExample from '../../views/call_out/call_out_example'; +import FlexExample + from '../../views/flex/flex_example'; + import FormExample from '../../views/form/form_example'; @@ -74,6 +77,10 @@ const components = [{ name: 'CallOut', component: CallOutExample, hasReact: true, +}, { + name: 'Flex', + component: FlexExample, + hasReact: true, }, { name: 'Form', component: FormExample, diff --git a/ui_framework/doc_site/src/views/flex/flex_example.js b/ui_framework/doc_site/src/views/flex/flex_example.js new file mode 100644 index 00000000000000..831bec5a077903 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_example.js @@ -0,0 +1,210 @@ +import React from 'react'; + +import { renderToHtml } from '../../services'; + +import { + GuideCode, + GuideDemo, + GuidePage, + GuideSection, + GuideSectionTypes, + GuideText, +} from '../../components'; + +import { + KuiCallOut, + KuiText, +} from '../../../../components'; + +import FlexGroup from './flex_group'; +const flexGroupSource = require('!!raw!./flex_group'); +const flexGroupHtml = renderToHtml(FlexGroup); + +import FlexItems from './flex_items'; +const flexItemsSource = require('!!raw!./flex_items'); +const flexItemsHtml = renderToHtml(FlexItems); + +import FlexGutter from './flex_gutter'; +const flexGutterSource = require('!!raw!./flex_gutter'); +const flexGutterHtml = renderToHtml(FlexGutter); + +import FlexGrow from './flex_grow'; +const flexGrowSource = require('!!raw!./flex_grow'); +const flexGrowHtml = renderToHtml(FlexGrow); + +import FlexJustify from './flex_justify'; +const flexJustifySource = require('!!raw!./flex_justify'); +const flexJustifyHtml = renderToHtml(FlexJustify); + +import FlexWrap from './flex_wrap'; +const flexWrapSource = require('!!raw!./flex_wrap'); +const flexWrapHtml = renderToHtml(FlexWrap); + +import FlexNest from './flex_nest'; +const flexNestSource = require('!!raw!./flex_nest'); +const flexNestHtml = renderToHtml(FlexNest); + +export default props => ( + + + +

+ Padding and background-color are added to all the FlexItem components on this + documentation page for illustrative purposes only. You will need to add padding through additional + components or classes if you need it. +

+
+
+
+
+ + + + FlexGroup is useful for setting up layouts for a single row of + content. By default any FlexItem within FlexGroup will + stretch and grow to match their siblings. + + + +
+
+
+ + + + Same code as above. Notice that FlexItem creates equal width items + no matter the number of siblings. FlexGroup never wraps. + + + +
+
+
+ + + + You can disable the growth of FlexItem components within + FlexGroup if you need, but it is set to true by default. + + + +
+
+
+ + + + FlexGroups can also use a justifyContent prop + that accepts normal flex-box paramenters. Below are two common scenarios, where you need to + separate two items, or center align a single one. + + + +
+
+
+ + + + FlexGrid is a more rigid component that sets multiple, wrapping + rows of same width items. It only accpets a columns and + gutterSize prop. You can have anywhere between 2-4 columns. Any + more would likely break on laptop screens. + + + +
+
+
+ + + + FlexGroup and FlexGrid can nest + within themselves indefinitely. For example, here we turn off the growth on a + FlexGroup, then nest a grid inside of it. + + + +
+
+
+ + + + The gutterSize prop can be applied to either a + FlexGroup or a FlexGrid to adjust the + spacing between FlexItems. + + + +
+
+
+
+); diff --git a/ui_framework/doc_site/src/views/flex/flex_group.js b/ui_framework/doc_site/src/views/flex/flex_group.js new file mode 100644 index 00000000000000..a2fdd3b54e80e5 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_group.js @@ -0,0 +1,18 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( + + Content grid item + +

Another content grid item

+
+
+

Note how both of these are the same width and height despite having different content?

+
+
+); diff --git a/ui_framework/doc_site/src/views/flex/flex_grow.js b/ui_framework/doc_site/src/views/flex/flex_grow.js new file mode 100644 index 00000000000000..1c1d9000389772 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_grow.js @@ -0,0 +1,16 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( +
+ + These items... + ... will auto size to the size of their content. + +
+); + diff --git a/ui_framework/doc_site/src/views/flex/flex_gutter.js b/ui_framework/doc_site/src/views/flex/flex_gutter.js new file mode 100644 index 00000000000000..eaee7eccb7e7f4 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_gutter.js @@ -0,0 +1,57 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( +
+ + None + None + None + None + + +
+
+ + + Small + Small + Small + Small + + +
+
+ + + Medium + Medium + Medium + Medium + + +
+
+ + + Large (default) + Large (default) + Large (default) + Large (default) + + +
+
+ + + Extra Large + Extra Large + Extra Large + Extra Large + +
+); diff --git a/ui_framework/doc_site/src/views/flex/flex_items.js b/ui_framework/doc_site/src/views/flex/flex_items.js new file mode 100644 index 00000000000000..12ce5b9c702389 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_items.js @@ -0,0 +1,16 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( + + One + Two + Three + Four + Five + +); diff --git a/ui_framework/doc_site/src/views/flex/flex_justify.js b/ui_framework/doc_site/src/views/flex/flex_justify.js new file mode 100644 index 00000000000000..31e18e976dbd44 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_justify.js @@ -0,0 +1,21 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( +
+ + One here on the left + The other over here on the right. + + +

+ + + I’m a single centered item! + +
+); diff --git a/ui_framework/doc_site/src/views/flex/flex_nest.js b/ui_framework/doc_site/src/views/flex/flex_nest.js new file mode 100644 index 00000000000000..2eaf4f49733e57 --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_nest.js @@ -0,0 +1,25 @@ +import React from 'react'; + +import { + KuiFlexGroup, + KuiFlexItem, + KuiFlexGrid, +} from '../../../../components'; + +export default () => ( +
+ + Group One + +
Group Two
+

+ + Nested Grid One + Nested Grid Two + Nested Grid Three + Nested Grid Four + +
+
+
+); diff --git a/ui_framework/doc_site/src/views/flex/flex_wrap.js b/ui_framework/doc_site/src/views/flex/flex_wrap.js new file mode 100644 index 00000000000000..0b76bcbe5dd82b --- /dev/null +++ b/ui_framework/doc_site/src/views/flex/flex_wrap.js @@ -0,0 +1,21 @@ +import React from 'react'; + +import { + KuiFlexGrid, + KuiFlexItem, +} from '../../../../components'; + +export default () => ( +
+ +
One
+
Two
+
Three
+
Four
+
Five
+
Six
+
Seven
+
+
+); + diff --git a/ui_framework/src/components/flex/_flex_grid.scss b/ui_framework/src/components/flex/_flex_grid.scss new file mode 100644 index 00000000000000..1177c5e6b28aa7 --- /dev/null +++ b/ui_framework/src/components/flex/_flex_grid.scss @@ -0,0 +1,40 @@ +.kuiFlexGrid { + display: flex; + align-items: stretch; + flex-wrap: wrap; + margin-bottom: 0; + + > .kuiFlexItem { + flex-grow: 0; + } +} + +$gutterTypes: ( + gutterSmall: $kuiSizeS, + gutterMedium: $kuiSize, + gutterLarge: $kuiSizeL, + gutterXLarge: $kuiSizeXL, +); + +$fractions: ( + fourths: 25%, + thirds: 33.3%, + halves: 50%, +); + +@each $gutterName, $gutterSize in $gutterTypes { + @each $fraction, $percentage in $fractions { + $halfGutterSize: $gutterSize * 0.5; + + .kuiFlexGrid--#{$gutterName} { + margin: -$halfGutterSize; + + &.kuiFlexGrid--#{$fraction} { + > .kuiFlexItem { + margin: $halfGutterSize; + flex-basis: calc(#{$percentage} - #{$gutterSize}); + } + } + } + } +} diff --git a/ui_framework/src/components/flex/_flex_group.scss b/ui_framework/src/components/flex/_flex_group.scss new file mode 100644 index 00000000000000..78519f9b61c12a --- /dev/null +++ b/ui_framework/src/components/flex/_flex_group.scss @@ -0,0 +1,56 @@ +.kuiFlexGroup { + display: flex; + align-items: stretch; + + .kuiFlexItem { + flex-grow: 1; + flex-basis: 0; + } + + // Gutter Sizes + &.kuiFlexGroup--gutterSmall > .kuiFlexItem + .kuiFlexItem { + margin-left: $kuiSizeS; + } + + &.kuiFlexGroup--gutterMedium > .kuiFlexItem + .kuiFlexItem { + margin-left: $kuiSize; + } + + &.kuiFlexGroup--gutterLarge > .kuiFlexItem + .kuiFlexItem { + margin-left: $kuiSizeL; + } + + &.kuiFlexGroup--gutterExtraLarge > .kuiFlexItem + .kuiFlexItem { + margin-left: $kuiSizeXXL; + } + + // Turn off item growth + &.kuiFlexGroup--flexGrowZero > .kuiFlexItem { + flex-grow: 0; + flex-basis: auto; + } + + // Justify the grid + &.kuiFlexGroup--justifyContentSpaceBetween { + justify-content: space-between; + } + + &.kuiFlexGroup--justifyContentSpaceAround { + justify-content: space-around; + } + + &.kuiFlexGroup--justifyContentCenter { + justify-content: center; + } + + &.kuiFlexGroup--justifyContentFlexEnd { + justify-content: flex-end; + } +} + + +@include screenXSmall { + .kuiFlexGroup { + flex-wrap: wrap; + } +} diff --git a/ui_framework/src/components/flex/_flex_item.scss b/ui_framework/src/components/flex/_flex_item.scss new file mode 100644 index 00000000000000..2300f2896e28f5 --- /dev/null +++ b/ui_framework/src/components/flex/_flex_item.scss @@ -0,0 +1,10 @@ +// FlexItems are normally defined by Groups or Grids. +// On mobile we force them to stack and act the same. +@include screenXSmall { + .kuiFlexItem { + width: 100% !important; + flex-basis: 100% !important; + margin-left: 0 !important; + margin-bottom: $kuiSize !important; + } +} diff --git a/ui_framework/src/components/flex/_index.scss b/ui_framework/src/components/flex/_index.scss new file mode 100644 index 00000000000000..42ce128e9cf348 --- /dev/null +++ b/ui_framework/src/components/flex/_index.scss @@ -0,0 +1,3 @@ +@import 'flex_group'; +@import 'flex_grid'; +@import 'flex_item'; diff --git a/ui_framework/src/components/flex/flex_grid.js b/ui_framework/src/components/flex/flex_grid.js new file mode 100644 index 00000000000000..67ecd601036988 --- /dev/null +++ b/ui_framework/src/components/flex/flex_grid.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +const gutterSizeToClassNameMap = { + none: '', + small: 'kuiFlexGrid--gutterSmall', + medium: 'kuiFlexGrid--gutterMedium', + large: 'kuiFlexGrid--gutterLarge', + extraLarge: 'kuiFlexGrid--gutterXLarge', +}; + +export const GUTTER_SIZES = Object.keys(gutterSizeToClassNameMap); + +const columnsToClassNameMap = { + 2: 'kuiFlexGrid--halves', + 3: 'kuiFlexGrid--thirds', + 4: 'kuiFlexGrid--fourths', +}; + +export const COLUMNS = Object.keys(columnsToClassNameMap); + +export const KuiFlexGrid = ({ children, className, gutterSize, columns, ...rest }) => { + const classes = classNames( + 'kuiFlexGrid', + gutterSizeToClassNameMap[gutterSize], + columnsToClassNameMap[columns], + className + ); + + return ( +
+ {children} +
+ ); +}; + +KuiFlexGrid.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + gutterSize: PropTypes.oneOf(GUTTER_SIZES), + columns: PropTypes.oneOf(COLUMNS).isRequired, +}; + +KuiFlexGrid.defaultProps = { + gutterSize: 'large', + columns: null, +}; + diff --git a/ui_framework/src/components/flex/flex_grid.test.js b/ui_framework/src/components/flex/flex_grid.test.js new file mode 100644 index 00000000000000..c1cd5b244aad1f --- /dev/null +++ b/ui_framework/src/components/flex/flex_grid.test.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { KuiFlexGrid } from './flex_grid'; + +describe('KuiFlexGrid', () => { + test('is rendered', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); +}); diff --git a/ui_framework/src/components/flex/flex_group.js b/ui_framework/src/components/flex/flex_group.js new file mode 100644 index 00000000000000..2f742622800061 --- /dev/null +++ b/ui_framework/src/components/flex/flex_group.js @@ -0,0 +1,70 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +const gutterSizeToClassNameMap = { + none: '', + small: 'kuiFlexGroup--gutterSmall', + medium: 'kuiFlexGroup--gutterMedium', + large: 'kuiFlexGroup--gutterLarge', + extraLarge: 'kuiFlexGroup--gutterExtraLarge', +}; + +export const GUTTER_SIZES = Object.keys(gutterSizeToClassNameMap); + +const alignItemsToClassNameMap = { + stretch: '', + flexStart: 'kuiFlexGroup--alignItemsFlexStart', + flexEnd: 'kuiFlexGroup--alignItemsFlexEnd', + center: 'kuiFlexGroup--alignItemsFlexCenter', +}; + +export const ALIGN_ITEMS = Object.keys(alignItemsToClassNameMap); + +const justifyContentToClassNameMap = { + flexStart: '', + flexEnd: 'kuiFlexGroup--justifyContentFlexEnd', + center: 'kuiFlexGroup--justifyContentCenter', + spaceBetween: 'kuiFlexGroup--justifyContentSpaceBetween', + spaceAround: 'kuiFlexGroup--justifyContentSpaceAround', +}; + +export const JUSTIFY_CONTENTS = Object.keys(justifyContentToClassNameMap); + +export const KuiFlexGroup = ({ children, className, gutterSize, alignItems, justifyContent, growItems, ...rest }) => { + const classes = classNames( + 'kuiFlexGroup', + gutterSizeToClassNameMap[gutterSize], + alignItemsToClassNameMap[alignItems], + justifyContentToClassNameMap[justifyContent], + { + 'kuiFlexGroup--flexGrowZero': !growItems, + }, + className + ); + + return ( +
+ {children} +
+ ); +}; + +KuiFlexGroup.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + gutterSize: PropTypes.oneOf(GUTTER_SIZES), + alignItems: PropTypes.oneOf(ALIGN_ITEMS), + justifyContent: PropTypes.oneOf(JUSTIFY_CONTENTS), + growItems: PropTypes.bool, +}; + +KuiFlexGroup.defaultProps = { + gutterSize: 'large', + alignItems: 'stretch', + justifyContent: 'flexStart', + growItems: true, +}; diff --git a/ui_framework/src/components/flex/flex_group.test.js b/ui_framework/src/components/flex/flex_group.test.js new file mode 100644 index 00000000000000..302b539c431092 --- /dev/null +++ b/ui_framework/src/components/flex/flex_group.test.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { KuiFlexGroup } from './flex_group'; + +describe('KuiFlexGroup', () => { + test('is rendered', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); +}); diff --git a/ui_framework/src/components/flex/flex_item.js b/ui_framework/src/components/flex/flex_item.js new file mode 100644 index 00000000000000..cae99fd556cf03 --- /dev/null +++ b/ui_framework/src/components/flex/flex_item.js @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const KuiFlexItem = ({ children, className, ...rest }) => { + const classes = classNames('kuiFlexItem', className); + + return ( +
+ {children} +
+ ); +}; + +KuiFlexItem.propTypes = { + children: PropTypes.node, +}; diff --git a/ui_framework/src/components/flex/flex_item.test.js b/ui_framework/src/components/flex/flex_item.test.js new file mode 100644 index 00000000000000..fad345933de451 --- /dev/null +++ b/ui_framework/src/components/flex/flex_item.test.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { KuiFlexItem } from './flex_item'; + +describe('KuiFlexItem', () => { + test('is rendered', () => { + const component = render( + + ); + + expect(component) + .toMatchSnapshot(); + }); +}); diff --git a/ui_framework/src/components/flex/index.js b/ui_framework/src/components/flex/index.js new file mode 100644 index 00000000000000..58d7d3fb8df32e --- /dev/null +++ b/ui_framework/src/components/flex/index.js @@ -0,0 +1,11 @@ +export { + KuiFlexGroup, +} from './flex_group'; + +export { + KuiFlexGrid, +} from './flex_grid'; + +export { + KuiFlexItem, +} from './flex_item'; diff --git a/ui_framework/src/components/index.js b/ui_framework/src/components/index.js index 30d3a5c884df02..14bcbc59c9f4e1 100644 --- a/ui_framework/src/components/index.js +++ b/ui_framework/src/components/index.js @@ -16,6 +16,12 @@ export { KuiCallOut, } from './call_out'; +export { + KuiFlexGroup, + KuiFlexGrid, + KuiFlexItem, +} from './flex'; + export { KuiCheckboxGroup, KuiFieldNumber, diff --git a/ui_framework/src/components/index.scss b/ui_framework/src/components/index.scss index ee99d84a4c735a..a1a0112d9e5dc2 100644 --- a/ui_framework/src/components/index.scss +++ b/ui_framework/src/components/index.scss @@ -5,6 +5,7 @@ @import 'badge/index'; @import 'button/index'; @import 'call_out/index'; +@import 'flex/index'; @import 'form/index'; @import 'header/index'; @import 'icon/index';