From 04315547a89bdf876ecfa445a1799e120ba2388f Mon Sep 17 00:00:00 2001 From: Gavyn McKenzie Date: Fri, 19 Jan 2024 17:15:29 +0000 Subject: [PATCH 1/7] feat: grid --- .storybook/preview.ts | 67 ++++++++++++ components/composition/Grid/Grid.stories.ts | 101 ++++++++++++++++++ components/composition/Grid/Grid.ts | 98 +++++++++++++++++ .../composition/GridItem/GridItem.stories.ts | 81 ++++++++++++++ components/composition/GridItem/GridItem.ts | 95 ++++++++++++++++ 5 files changed, 442 insertions(+) create mode 100644 components/composition/Grid/Grid.stories.ts create mode 100644 components/composition/Grid/Grid.ts create mode 100644 components/composition/GridItem/GridItem.stories.ts create mode 100644 components/composition/GridItem/GridItem.ts diff --git a/.storybook/preview.ts b/.storybook/preview.ts index e91e837..a148c8c 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -11,6 +11,73 @@ const preview: Preview = { date: /Date$/i, }, }, + viewport: { + viewports: { + smallMobile: { + name: 'Small mobile', + styles: { + width: '320px', + height: '568px', + }, + }, + mobile: { + name: 'Average mobile', + styles: { + width: '375px', + height: '667px', + }, + }, + largeMobile: { + name: 'Large mobile', + styles: { + width: '480px', + height: '896px', + }, + }, + smallTablet: { + name: 'Small tablet', + styles: { + width: '600px', + height: '960px', + }, + }, + tablet: { + name: 'Tablet', + styles: { + width: '768px', + height: '1024px', + }, + }, + largeTablet: { + name: 'Large tablet', + styles: { + width: '1024px', + height: '1366px', + }, + }, + smallDesktop: { + name: 'Small desktop', + styles: { + width: '1280px', + height: '800px', + }, + }, + desktop: { + name: 'Desktop', + styles: { + width: '1440px', + height: '900px', + }, + }, + largeDesktop: { + name: 'Large desktop', + styles: { + width: '1920px', + height: '1080px', + }, + }, + } + }, }, }; diff --git a/components/composition/Grid/Grid.stories.ts b/components/composition/Grid/Grid.stories.ts new file mode 100644 index 0000000..7bf37a0 --- /dev/null +++ b/components/composition/Grid/Grid.stories.ts @@ -0,0 +1,101 @@ +import { StoryObj } from '@storybook/web-components'; +import { html } from 'lit'; + +import './Grid'; +import '../GridItem/GridItem'; + +export default { + component: 'diamond-grid', + argTypes: { + direction: { + control: { + type: 'select', + }, + options: ['row', 'column', 'row-reverse', 'column-reverse'], + }, + justifyContent: { + control: { + type: 'select', + }, + options: [ + 'flex-start', + 'flex-end', + 'center', + 'space-between', + 'space-around', + 'space-evenly', + ], + }, + alignItems: { + control: { + type: 'select', + }, + options: ['flex-start', 'flex-end', 'center'], + }, + gap: { + control: { + type: 'select', + }, + options: ['xs', 'sm', 'md', 'lg', 'xl'], + }, + }, +}; + +export const Grid: StoryObj = { + render: ({ + wrap, + direction, + justifyContent, + alignItems, + gap, + inline, + }) => html` + + ${[...Array(12).keys()].map( + (i) => html` + +
+ ${i + 1} +
+
+ `, + )} +
+ `, +}; + +Grid.args = { + wrap: false, + direction: 'row', + justifyContent: 'flex-start', + alignItems: 'flex-start', + gap: 'md', + inline: false, +}; + +export const GridItem: StoryObj = { + render: ({ grow, shrink }) => html` + + ${[...Array(24).keys()].map( + (i) => html` + +
+ ${i + 1} +
+
+ `, + )} +
+ `, +}; diff --git a/components/composition/Grid/Grid.ts b/components/composition/Grid/Grid.ts new file mode 100644 index 0000000..bd2c322 --- /dev/null +++ b/components/composition/Grid/Grid.ts @@ -0,0 +1,98 @@ +import { LitElement, css, html, unsafeCSS } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; + +const directions = ['column', 'row-reverse', 'column-reverse']; + +const justify = [ + 'flex-end', + 'center', + 'space-between', + 'space-around', + 'space-evenly', +]; + +const align = ['flex-end', 'center']; + +const gap = ['xs', 'sm', 'lg', 'xl']; + +@customElement('diamond-grid') +export class Grid extends LitElement { + @property({ type: Boolean }) readonly wrap?: boolean; + @property({ type: Boolean }) readonly inline?: boolean; + @property() readonly direction?: + | 'row' + | 'column' + | 'row-reverse' + | 'column-reverse' = 'row'; + @property() readonly justifyContent?: + | 'flex-start' + | 'flex-end' + | 'center' + | 'space-between' + | 'space-around' + | 'space-evenly' = 'flex-start'; + @property() readonly alignItems?: 'flex-start' | 'flex-end' | 'center' = + 'flex-start'; + + static styles = css` + :host { + --diamond-grid-gap: var(--diamond-spacing); + display: flex; + margin: calc(var(--diamond-grid-gap) * -0.5); + } + + :host([inline]) { + display: inline-flex; + } + + :host([wrap]) { + flex-wrap: wrap; + } + + ${unsafeCSS( + directions + .map( + (direction) => + `:host([direction="${direction}"]) { flex-direction: ${direction}; }`, + ) + .join('\n'), + )} + + ${unsafeCSS( + justify + .map( + (justify) => + `:host([justify-content="${justify}"]) { justify-content: ${justify}; }`, + ) + .join('\n'), + )} + + ${unsafeCSS( + align + .map( + (align) => + `:host([align-items="${align}"]) { align-items: ${align}; }`, + ) + .join('\n'), + )} + + ${unsafeCSS( + gap + .map( + (gap) => + `:host([gap="${gap}"]) { --diamond-grid-gap: var(--diamond-spacing-${gap}); }`, + ) + .join('\n'), + )} + `; + + render() { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'diamond-grid': Grid; + } +} diff --git a/components/composition/GridItem/GridItem.stories.ts b/components/composition/GridItem/GridItem.stories.ts new file mode 100644 index 0000000..cd6230e --- /dev/null +++ b/components/composition/GridItem/GridItem.stories.ts @@ -0,0 +1,81 @@ +import { StoryObj } from '@storybook/web-components'; +import { html } from 'lit'; + +import '../Grid/Grid'; +import './GridItem'; + +const col = { + control: { type: 'select' }, + options: ['auto', ...[...Array(12).keys()].map((i) => `${i + 1}`)], +}; + +export default { + component: 'diamond-grid-item', + argTypes: { + smallMobile: col, + mobile: col, + largeMobile: col, + smallTablet: col, + tablet: col, + largeTablet: col, + smallDesktop: col, + desktop: col, + largeDesktop: col, + }, +}; + +export const GridItem: StoryObj = { + render: ({ + grow, + shrink, + smallMobile, + mobile, + largeMobile, + smallTablet, + tablet, + largeTablet, + smallDesktop, + desktop, + largeDesktop, + }) => html` + + ${[...Array(24).keys()].map( + (i) => html` + +
+ ${i + 1} +
+
+ `, + )} +
+ `, +}; + +GridItem.args = { + grow: false, + shrink: false, + smallMobile: '12', + mobile: '12', + largeMobile: '6', + smallTablet: '6', + tablet: '4', + largeTablet: '3', + smallDesktop: '2', + desktop: '1', + largeDesktop: '1', +}; diff --git a/components/composition/GridItem/GridItem.ts b/components/composition/GridItem/GridItem.ts new file mode 100644 index 0000000..3f0ce3c --- /dev/null +++ b/components/composition/GridItem/GridItem.ts @@ -0,0 +1,95 @@ +import { LitElement, css, html, unsafeCSS } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; + +const breakpoints = [ + { name: 'small-mobile', value: '0' }, + { name: 'mobile', value: '375px' }, + { name: 'large-mobile', value: '480px' }, + { name: 'small-tablet', value: '600px' }, + { name: 'tablet', value: '768px' }, + { name: 'large-tablet', value: '1024px' }, + { name: 'small-desktop', value: '1280px' }, + { name: 'desktop', value: '1440px' }, + { name: 'large-desktop', value: '1920px' }, +]; + +const colWidth = (i: number) => (100 / 12) * (i + 1); + +const columns = (breakpoint: string) => { + return ` + :host([${breakpoint}="auto"]) { width: auto; } + + ${[...Array(12).keys()] + .map( + (i) => ` + :host([${breakpoint}="${i + 1}"]) { width: ${colWidth(i)}%; } + `, + ) + .join('')} + `; +}; + +const columnBreakpoints = () => { + return unsafeCSS( + breakpoints + .map( + (bp) => ` + @media (min-width: ${bp.value}) { + ${columns(bp.name)} + } + `, + ) + .join(''), + ); +}; + +@customElement('diamond-grid-item') +export class GridItem extends LitElement { + @property({ type: Boolean }) readonly grow?: boolean; + @property({ type: Boolean }) readonly shrink?: boolean; + + @property() readonly smallMobile?: string; + @property() readonly mobile?: string; + @property() readonly largeMobile?: string; + + @property() readonly smallTablet?: string; + @property() readonly tablet?: string; + @property() readonly largeTablet?: string; + + @property() readonly smallDesktop?: string; + @property() readonly desktop?: string; + @property() readonly largeDesktop?: string; + + static styles = css` + :host { + display: block; + flex: 0 0 auto; + min-width: 0; + padding: calc(var(--diamond-grid-gap) / 2); + } + + :host([grow]) { + flex-grow: 1; + } + + :host([shrink]) { + flex-shrink: 1; + } + + :host[small-mobile='12'] { + width: 100%; + } + + ${columnBreakpoints()} + `; + + render() { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'diamond-grid-item': GridItem; + } +} From 28f28059e588bbc0357a308b34e39b154be569ad Mon Sep 17 00:00:00 2001 From: Gavyn McKenzie Date: Tue, 23 Jan 2024 05:15:27 +0000 Subject: [PATCH 2/7] refactor: more explicit wrap --- components/composition/Grid/Grid.stories.ts | 28 ++++++------------- components/composition/Grid/Grid.ts | 8 ++++-- .../composition/GridItem/GridItem.stories.ts | 2 +- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/components/composition/Grid/Grid.stories.ts b/components/composition/Grid/Grid.stories.ts index 7bf37a0..25a70e7 100644 --- a/components/composition/Grid/Grid.stories.ts +++ b/components/composition/Grid/Grid.stories.ts @@ -7,6 +7,12 @@ import '../GridItem/GridItem'; export default { component: 'diamond-grid', argTypes: { + wrap: { + control: { + type: 'select', + }, + options: ['wrap', 'nowrap', 'wrap-reverse'], + }, direction: { control: { type: 'select', @@ -51,7 +57,7 @@ export const Grid: StoryObj = { inline, }) => html` html` - - ${[...Array(24).keys()].map( - (i) => html` - -
- ${i + 1} -
-
- `, - )} -
- `, -}; diff --git a/components/composition/Grid/Grid.ts b/components/composition/Grid/Grid.ts index bd2c322..7078552 100644 --- a/components/composition/Grid/Grid.ts +++ b/components/composition/Grid/Grid.ts @@ -17,7 +17,7 @@ const gap = ['xs', 'sm', 'lg', 'xl']; @customElement('diamond-grid') export class Grid extends LitElement { - @property({ type: Boolean }) readonly wrap?: boolean; + @property() readonly wrap?: 'wrap' | 'nowrap' | 'wrap-reverse'; @property({ type: Boolean }) readonly inline?: boolean; @property() readonly direction?: | 'row' @@ -45,10 +45,14 @@ export class Grid extends LitElement { display: inline-flex; } - :host([wrap]) { + :host([wrap='wrap']) { flex-wrap: wrap; } + :host([wrap='wrap-reverse']) { + flex-wrap: wrap-reverse; + } + ${unsafeCSS( directions .map( diff --git a/components/composition/GridItem/GridItem.stories.ts b/components/composition/GridItem/GridItem.stories.ts index cd6230e..b70f676 100644 --- a/components/composition/GridItem/GridItem.stories.ts +++ b/components/composition/GridItem/GridItem.stories.ts @@ -38,7 +38,7 @@ export const GridItem: StoryObj = { desktop, largeDesktop, }) => html` - + ${[...Array(24).keys()].map( (i) => html` Date: Tue, 23 Jan 2024 05:21:50 +0000 Subject: [PATCH 3/7] feat: cssMap for simpler css generation --- components/composition/Grid/Grid.ts | 53 ++++++++------------- components/composition/GridItem/GridItem.ts | 13 ++--- lib/css-map.ts | 5 ++ 3 files changed, 33 insertions(+), 38 deletions(-) create mode 100644 lib/css-map.ts diff --git a/components/composition/Grid/Grid.ts b/components/composition/Grid/Grid.ts index 7078552..0749d8f 100644 --- a/components/composition/Grid/Grid.ts +++ b/components/composition/Grid/Grid.ts @@ -1,9 +1,11 @@ -import { LitElement, css, html, unsafeCSS } from 'lit'; +import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { cssMap } from '../../../lib/css-map'; + const directions = ['column', 'row-reverse', 'column-reverse']; -const justify = [ +const justifys = [ 'flex-end', 'center', 'space-between', @@ -11,9 +13,9 @@ const justify = [ 'space-evenly', ]; -const align = ['flex-end', 'center']; +const aligns = ['flex-end', 'center']; -const gap = ['xs', 'sm', 'lg', 'xl']; +const gaps = ['xs', 'sm', 'lg', 'xl']; @customElement('diamond-grid') export class Grid extends LitElement { @@ -53,40 +55,27 @@ export class Grid extends LitElement { flex-wrap: wrap-reverse; } - ${unsafeCSS( - directions - .map( - (direction) => - `:host([direction="${direction}"]) { flex-direction: ${direction}; }`, - ) - .join('\n'), + ${cssMap( + directions, + (direction) => + `:host([direction="${direction}"]) { flex-direction: ${direction}; }`, )} - ${unsafeCSS( - justify - .map( - (justify) => - `:host([justify-content="${justify}"]) { justify-content: ${justify}; }`, - ) - .join('\n'), + ${cssMap( + justifys, + (justify) => + `:host([justify-content="${justify}"]) { justify-content: ${justify}; }`, )} - ${unsafeCSS( - align - .map( - (align) => - `:host([align-items="${align}"]) { align-items: ${align}; }`, - ) - .join('\n'), + ${cssMap( + aligns, + (align) => `:host([align-items="${align}"]) { align-items: ${align}; }`, )} - ${unsafeCSS( - gap - .map( - (gap) => - `:host([gap="${gap}"]) { --diamond-grid-gap: var(--diamond-spacing-${gap}); }`, - ) - .join('\n'), + ${cssMap( + gaps, + (gap) => + `:host([gap="${gap}"]) { --diamond-grid-gap: var(--diamond-spacing-${gap}); }`, )} `; diff --git a/components/composition/GridItem/GridItem.ts b/components/composition/GridItem/GridItem.ts index 3f0ce3c..522e3bc 100644 --- a/components/composition/GridItem/GridItem.ts +++ b/components/composition/GridItem/GridItem.ts @@ -1,6 +1,8 @@ import { LitElement, css, html, unsafeCSS } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { cssMap } from '../../../lib/css-map'; + const breakpoints = [ { name: 'small-mobile', value: '0' }, { name: 'mobile', value: '375px' }, @@ -19,13 +21,12 @@ const columns = (breakpoint: string) => { return ` :host([${breakpoint}="auto"]) { width: auto; } - ${[...Array(12).keys()] - .map( - (i) => ` + ${cssMap( + [...Array(12).keys()], + (i) => ` :host([${breakpoint}="${i + 1}"]) { width: ${colWidth(i)}%; } - `, - ) - .join('')} + `, + )} `; }; diff --git a/lib/css-map.ts b/lib/css-map.ts new file mode 100644 index 0000000..c2cacdc --- /dev/null +++ b/lib/css-map.ts @@ -0,0 +1,5 @@ +import { unsafeCSS } from 'lit'; + +export function cssMap(arr, callback) { + return unsafeCSS(arr.map(callback).join('\n')); +} From 93902ed8cd7a9680c24f5ade5e2ed79c6b6bdd98 Mon Sep 17 00:00:00 2001 From: Gavyn McKenzie Date: Tue, 23 Jan 2024 05:23:42 +0000 Subject: [PATCH 4/7] refactor: grid item is a sub component --- components/composition/{GridItem => Grid}/GridItem.stories.ts | 2 +- components/composition/{GridItem => Grid}/GridItem.ts | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename components/composition/{GridItem => Grid}/GridItem.stories.ts (98%) rename components/composition/{GridItem => Grid}/GridItem.ts (100%) diff --git a/components/composition/GridItem/GridItem.stories.ts b/components/composition/Grid/GridItem.stories.ts similarity index 98% rename from components/composition/GridItem/GridItem.stories.ts rename to components/composition/Grid/GridItem.stories.ts index b70f676..76a2d4c 100644 --- a/components/composition/GridItem/GridItem.stories.ts +++ b/components/composition/Grid/GridItem.stories.ts @@ -1,7 +1,7 @@ import { StoryObj } from '@storybook/web-components'; import { html } from 'lit'; -import '../Grid/Grid'; +import './Grid'; import './GridItem'; const col = { diff --git a/components/composition/GridItem/GridItem.ts b/components/composition/Grid/GridItem.ts similarity index 100% rename from components/composition/GridItem/GridItem.ts rename to components/composition/Grid/GridItem.ts From 5ae9128313b203ee29a69a85a317b7dc50d41663 Mon Sep 17 00:00:00 2001 From: Gavyn McKenzie Date: Tue, 23 Jan 2024 05:25:22 +0000 Subject: [PATCH 5/7] feat: column types --- components/composition/Grid/GridItem.ts | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/components/composition/Grid/GridItem.ts b/components/composition/Grid/GridItem.ts index 522e3bc..faafb0f 100644 --- a/components/composition/Grid/GridItem.ts +++ b/components/composition/Grid/GridItem.ts @@ -3,6 +3,21 @@ import { customElement, property } from 'lit/decorators.js'; import { cssMap } from '../../../lib/css-map'; +type Column = + | 'auto' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + | '10' + | '11' + | '12'; + const breakpoints = [ { name: 'small-mobile', value: '0' }, { name: 'mobile', value: '375px' }, @@ -49,17 +64,17 @@ export class GridItem extends LitElement { @property({ type: Boolean }) readonly grow?: boolean; @property({ type: Boolean }) readonly shrink?: boolean; - @property() readonly smallMobile?: string; - @property() readonly mobile?: string; - @property() readonly largeMobile?: string; + @property() readonly smallMobile?: Column; + @property() readonly mobile?: Column; + @property() readonly largeMobile?: Column; - @property() readonly smallTablet?: string; - @property() readonly tablet?: string; - @property() readonly largeTablet?: string; + @property() readonly smallTablet?: Column; + @property() readonly tablet?: Column; + @property() readonly largeTablet?: Column; - @property() readonly smallDesktop?: string; - @property() readonly desktop?: string; - @property() readonly largeDesktop?: string; + @property() readonly smallDesktop?: Column; + @property() readonly desktop?: Column; + @property() readonly largeDesktop?: Column; static styles = css` :host { From 347fc25ed8028277114bac0be4947064d438a45e Mon Sep 17 00:00:00 2001 From: Gavyn McKenzie Date: Tue, 23 Jan 2024 05:28:06 +0000 Subject: [PATCH 6/7] fix: linting --- components/composition/Grid/Grid.stories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/composition/Grid/Grid.stories.ts b/components/composition/Grid/Grid.stories.ts index 25a70e7..324eba3 100644 --- a/components/composition/Grid/Grid.stories.ts +++ b/components/composition/Grid/Grid.stories.ts @@ -2,7 +2,7 @@ import { StoryObj } from '@storybook/web-components'; import { html } from 'lit'; import './Grid'; -import '../GridItem/GridItem'; +import './GridItem'; export default { component: 'diamond-grid', From 60a67354f2bf1a7fc828d7252102358611dac66e Mon Sep 17 00:00:00 2001 From: Gavyn McKenzie Date: Tue, 23 Jan 2024 05:28:57 +0000 Subject: [PATCH 7/7] css map typing --- lib/css-map.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/css-map.ts b/lib/css-map.ts index c2cacdc..536a742 100644 --- a/lib/css-map.ts +++ b/lib/css-map.ts @@ -1,5 +1,5 @@ import { unsafeCSS } from 'lit'; -export function cssMap(arr, callback) { +export function cssMap(arr: any[], callback: (any) => string) { return unsafeCSS(arr.map(callback).join('\n')); }