diff --git a/.changeset/cruel-ads-march.md b/.changeset/cruel-ads-march.md new file mode 100644 index 0000000..a43019d --- /dev/null +++ b/.changeset/cruel-ads-march.md @@ -0,0 +1,30 @@ +--- +"@musica-sacra/layout": major +--- + +Create new layout King. + +- **what** Create new 3 column layout KIng and refactor old layouts. +- **why** We wanted a 3 column layout. +- **how** Breaking changes! Follow instructions: + +1. **If you are using layout ``** + +This layout was renamed to ``. All classNames derived from `layout-basic` were renamed to `prince`. + +There was change from layout content + sidebar width, from `1200px` to `1280px`. + +2. **If you are using layout ``** + +This layout was renamed to ``. All classNames derived from `layout-with-sidebar` were renamed to `queen`. + +There was added prop `fullWidth`, default set to `false`. With this prop you can make the layout to take full width of the page, and dont have max-width of content. + +There was change from layout content + sidebar width, from `1200px` to `1280px`. + +3. **New layout ``** + +There was added new layout ``. Layout has 3 column, see readme for more information. + + + diff --git a/packages/layout/CHANGELOG.md b/packages/layout/CHANGELOG.md index 49c0804..9266d01 100644 --- a/packages/layout/CHANGELOG.md +++ b/packages/layout/CHANGELOG.md @@ -4,8 +4,8 @@ ### Minor Changes -- 50e606b: Renamed prop `classname` to `className` in Container.tsx and LayoutBasic.tsx - - **What** Renamed prop `classname` to `className` in Container.tsx and LayoutBasic.tsx +- 50e606b: Renamed prop `classname` to `className` in Container.tsx and Prince.tsx + - **What** Renamed prop `classname` to `className` in Container.tsx and Prince.tsx - **Why** Matches React convention, improves consistency. - **How** Rename `classname` prop to `className` in `Container` and `LayoutBasic`. Update your code by replacing all `classname` props with `className`. diff --git a/packages/layout/README.md b/packages/layout/README.md index 1887baa..f7db166 100644 --- a/packages/layout/README.md +++ b/packages/layout/README.md @@ -11,12 +11,13 @@ Layout components for building easy and consistent web app layouts in React. This package provides the following React layout components: - `Container`: Styled wrapper. -- `LayoutBasic`: Single column layout utilizing previously mentioned Container as main wrapper. -- `LayoutWithSidebar`: Double column layout with main content and left sidebar. +- `Prince`: Single column layout utilizing previously mentioned Container as main wrapper. +- `Queen`: Double column layout with main content and left sidebar. +- `King`: Triple column layout with main content and two sidebars. -## \ +### \ -Basic styled wrapper that provides container of with 1200px with auto margins on sides. +Basic styled wrapper that provides container of with 1280px with auto margins on sides. ```jsx import { Container } from '@musica-sacra/layout' @@ -30,41 +31,63 @@ export function ExampleComponent() { } ``` -## \ +### \ Component for creating page layouts. Single column. Provides aditional wrapper around ``. ```jsx -import { LayoutBasic } from '@musica-sacra/layout' +import { Prince } from '@musica-sacra/layout' export function ExampleComponent() { return ( - + {/* Your Content */} - + ) } ``` -## \ +### \ Double column layout with main content and sidebar on the left side. ```jsx -import { LayoutWithSidebar } from '@musica-sacra/layout' +import { Queen } from '@musica-sacra/layout' export function ExampleComponent() { return ( - {/* Your Sidebar Content*/}} > {/* Your Main Content */} - + ) } ``` -Also provides context through hook useSidebar(), which gives you methods: +### \ + +Triple column layout. + +```jsx +import { King } from '@musica-sacra/layout' + +export function ExampleComponent() { + return ( + {/* Your LEFT Sidebar Content*/}} + rightSidebar={
{/* Your RIGHT Sidebar Content*/}
} + > + {/* Your Main Content */} +
+ ) +} +``` + + +### useSidebar + +Layouts with sidebar also provide context through hook useSidebar(), which gives you methods: - closeSidebar() ```jsx diff --git a/packages/layout/src/components/container/container.scss b/packages/layout/src/components/container/container.scss index 5c539e3..a97129b 100644 --- a/packages/layout/src/components/container/container.scss +++ b/packages/layout/src/components/container/container.scss @@ -1,5 +1,5 @@ .ms-container { - max-width: 1200px; + max-width: 1280px; width: 100%; margin: 0 auto; diff --git a/packages/layout/src/components/king/King.tsx b/packages/layout/src/components/king/King.tsx new file mode 100644 index 0000000..7727a5a --- /dev/null +++ b/packages/layout/src/components/king/King.tsx @@ -0,0 +1,94 @@ +import { ReactNode, useState } from 'react'; +import { useBem } from '@musica-sacra/hooks'; +import { SidebarExpandButton } from '../sidebarExpandButton/SidebarExpandButton'; +import { Overlay } from '../overlay/Overlay'; +import { SidebarContext } from '../../context/SidebarContext'; + +type KingProps = { + className?: string; + isPageLayout?: boolean; + children: React.ReactNode; + leftSidebar: ReactNode; + rightSidebar: ReactNode; + mergeSidebarsWhenResponsive?: boolean; +}; + +export function King({ + className = '', + isPageLayout = false, + children, + leftSidebar, + rightSidebar, + mergeSidebarsWhenResponsive = false, +}: KingProps) { + const { bem, base } = useBem('ms-king'); + + const [isSidebarExpanded, setSidebarExpanded] = useState(false); + + const toggleSidebar = () => { + setSidebarExpanded((prev) => !prev); + }; + + const closeSidebar = () => setSidebarExpanded(false); + + return ( +
+ + + +
+
+ +
+ + +
+ {leftSidebar} + {mergeSidebarsWhenResponsive && ( +
+
+ {rightSidebar} +
+ )} +
+ +
+ +
+
{children}
+
+ +
+
{rightSidebar}
+
+
+ ); +} diff --git a/packages/layout/src/components/king/__tests__/King.stories.tsx b/packages/layout/src/components/king/__tests__/King.stories.tsx new file mode 100644 index 0000000..3bfe8fd --- /dev/null +++ b/packages/layout/src/components/king/__tests__/King.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { King } from '../King'; + +const meta: Meta = { + title: '@musica-sacra/Layout/components/King', + component: King, + args: { + children: 'children', + leftSidebar: 'leftSidebar', + rightSidebar: 'rightSidebar', + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const PageLayout: Story = { + args: { + isPageLayout: true, + }, +}; + +export const MergedSidebars: Story = { + args: { + mergeSidebarsWhenResponsive: true, + }, +}; diff --git a/packages/layout/src/components/king/king.scss b/packages/layout/src/components/king/king.scss new file mode 100644 index 0000000..0d2bfe8 --- /dev/null +++ b/packages/layout/src/components/king/king.scss @@ -0,0 +1,149 @@ +.ms-king { + background-color: lightpink; + + width: 100%; + display: flex; + justify-content: space-between; + box-sizing: border-box; + + @media (max-width: 1220px) { + flex-direction: column; + justify-content: flex-start; + } + + * { + box-sizing: border-box; + } + + &__ { + &content { + background-color: white; + + width: 100%; + max-width: 900px; + + @media (max-width: 1540px) { + max-width: 100%; + } + } + &content-inner { + max-width: 900px; + margin: auto; + + @media (max-width: 900px) { + + } + } + + &sidebar { + background-color: lightBlue; + width: 100%; + max-width: 320px; + height: auto; + z-index: 10; + + &--{ + &left { + background-color: rgb(246, 246, 247); + + @media (max-width: 1220px) { + position: fixed; + top: 0; + left: 0; + min-height: 100vh; + height: 100%; + width: 90%; + max-width: 400px; + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); + + transform: translateX(-100%); + transition: transform 0.3s ease; + + overflow: auto; + } + } + + &expanded { + @media (max-width: 1220px) { + transform: translateX(0); + transition: transform 0.3s ease 0.2s; + } + } + + &right { + @media (max-width: 1540px) { + display: none; + } + } + + &merged { + display: none; + + @media (max-width: 1540px) { + display: block; + } + } + } + } + &sidebar-content { + width: 100%; + margin-right: 0; + margin-left: auto; + padding: 1rem; + + @media (max-width: 1220px) { + padding: 0 1rem; + } + } + &sidebar-close-button { + display: none; + width: 100%; + padding: 1rem; + position: sticky; + top: 0; + background-color: rgb(246, 246, 247); + box-shadow: 0 1px 10px rgba(150, 150, 150, 0.1); + + @media (max-width: 1220px) { + display: flex; + } + + button { + margin-right: 0; + margin-left: auto; + + background-color: transparent; + color: rgba(60, 60, 67, 1); + + outline: none; + border: none; + + &:hover { + cursor: pointer; + color: rgba(8, 130, 170, 1); + } + } + } + + + &hr { + background-color: rgba(60, 60, 67, 0.12); + height: 1px; + width: 100%; + margin: 2rem 0; + } + } + + &-- { + &page-layout { + min-height: 100vh; + } + } + + .ms-sidebar-expand-button { + display: none; + @media (max-width: 1220px) { + display: block; + } + } +} \ No newline at end of file diff --git a/packages/layout/src/components/layoutWithSidebar/LayoutWithSidebar.tsx b/packages/layout/src/components/layoutWithSidebar/LayoutWithSidebar.tsx deleted file mode 100644 index b63a6e6..0000000 --- a/packages/layout/src/components/layoutWithSidebar/LayoutWithSidebar.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { ReactNode, useState } from 'react'; -import { useBem } from '@musica-sacra/hooks'; -import { SidebarContext } from '../../context/SidebarContext'; - -type LayoutWithSidebarProps = { - className?: string; - sidebar: ReactNode; - children: ReactNode; - isPageLayout?: boolean; -}; - -export function LayoutWithSidebar({ - className = '', - sidebar, - children, - isPageLayout = false, -}: LayoutWithSidebarProps) { - const { bem, base } = useBem('ms-layout-with-sidebar'); - - const [isSidebarExpanded, setSidebarExpanded] = useState(false); - - const toggleSidebar = () => { - setSidebarExpanded((prev) => !prev); - }; - - const closeSidebar = () => setSidebarExpanded(false); - - return ( -
-
- -
- - {isSidebarExpanded && ( -
- )} - - - -
-
{children}
-
-
- ); -} diff --git a/packages/layout/src/components/overlay/Overlay.tsx b/packages/layout/src/components/overlay/Overlay.tsx new file mode 100644 index 0000000..2564c48 --- /dev/null +++ b/packages/layout/src/components/overlay/Overlay.tsx @@ -0,0 +1,19 @@ +import { useBem } from '@musica-sacra/hooks'; + +type OverlayProps = { + shouldBeVisible: boolean; + onClickCallback?: () => void; +}; + +export function Overlay({ shouldBeVisible, onClickCallback }: OverlayProps) { + const { bem, base } = useBem('ms-overlay'); + + return ( +
+ ); +} diff --git a/packages/layout/src/components/overlay/overlay.scss b/packages/layout/src/components/overlay/overlay.scss new file mode 100644 index 0000000..59cbdce --- /dev/null +++ b/packages/layout/src/components/overlay/overlay.scss @@ -0,0 +1,18 @@ +.ms-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.4); + opacity: 0; + transition: all 0.3s ease; + z-index: -1; + pointer-events: none; + + &-- { + &visible { + opacity: 1; + z-index: 9; + cursor: pointer; + pointer-events: auto; + } + } +} \ No newline at end of file diff --git a/packages/layout/src/components/layoutBasic/LayoutBasic.tsx b/packages/layout/src/components/prince/Prince.tsx similarity index 66% rename from packages/layout/src/components/layoutBasic/LayoutBasic.tsx rename to packages/layout/src/components/prince/Prince.tsx index 6c19680..cea885f 100644 --- a/packages/layout/src/components/layoutBasic/LayoutBasic.tsx +++ b/packages/layout/src/components/prince/Prince.tsx @@ -1,19 +1,19 @@ import { Container } from '../container/Container'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { useBem } from '@musica-sacra/hooks'; -type LayoutBasicProps = { - children?: React.ReactNode; +type PrinceProps = { + children?: ReactNode; className?: string; isPageLayout?: boolean; }; -export function LayoutBasic({ +export function Prince({ children, className = '', isPageLayout = false, -}: LayoutBasicProps) { - const { bem, base } = useBem('ms-layout-basic'); +}: PrinceProps) { + const { bem, base } = useBem('ms-prince'); return (
diff --git a/packages/layout/src/components/layoutBasic/__tests__/LayoutBasic.stories.tsx b/packages/layout/src/components/prince/__tests__/Prince.stories.tsx similarity index 61% rename from packages/layout/src/components/layoutBasic/__tests__/LayoutBasic.stories.tsx rename to packages/layout/src/components/prince/__tests__/Prince.stories.tsx index 38adda8..c3f40d8 100644 --- a/packages/layout/src/components/layoutBasic/__tests__/LayoutBasic.stories.tsx +++ b/packages/layout/src/components/prince/__tests__/Prince.stories.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { LayoutBasic } from '@musica-sacra/layout'; import type { StoryObj } from '@storybook/react'; +import { Prince } from '../Prince'; const meta = { - title: '@musica-sacra/Layout/components/LayoutBasic', - component: LayoutBasic, + title: '@musica-sacra/Layout/components/Prince', + component: Prince, args: { - children:

LayoutBasic content

, + children:

Prince layout content

, }, }; diff --git a/packages/layout/src/components/layoutBasic/layoutBasic.scss b/packages/layout/src/components/prince/prince.scss similarity index 86% rename from packages/layout/src/components/layoutBasic/layoutBasic.scss rename to packages/layout/src/components/prince/prince.scss index e308cf8..d6d9a3c 100644 --- a/packages/layout/src/components/layoutBasic/layoutBasic.scss +++ b/packages/layout/src/components/prince/prince.scss @@ -1,4 +1,4 @@ -.ms-layout-basic { +.ms-prince { width: 100%; padding: 3rem 0 4rem 0; diff --git a/packages/layout/src/components/queen/Queen.tsx b/packages/layout/src/components/queen/Queen.tsx new file mode 100644 index 0000000..186179e --- /dev/null +++ b/packages/layout/src/components/queen/Queen.tsx @@ -0,0 +1,79 @@ +import { ReactNode, useState } from 'react'; +import { useBem } from '@musica-sacra/hooks'; +import { SidebarContext } from '../../context/SidebarContext'; +import { Overlay } from '../overlay/Overlay'; +import { SidebarExpandButton } from '../sidebarExpandButton/SidebarExpandButton'; + +type QueenProps = { + className?: string; + sidebar: ReactNode; + children: ReactNode; + isPageLayout?: boolean; + fullWidth?: boolean; +}; + +export function Queen({ + className = '', + sidebar, + children, + isPageLayout = false, + fullWidth = false, +}: QueenProps) { + const { bem, base } = useBem('ms-queen'); + + const [isSidebarExpanded, setSidebarExpanded] = useState(false); + + const toggleSidebar = () => { + setSidebarExpanded((prev) => !prev); + }; + + const closeSidebar = () => setSidebarExpanded(false); + + return ( +
+ + + +
+
+ +
+ + +
{sidebar}
+
+
+ +
+
{children}
+
+
+ ); +} diff --git a/packages/layout/src/components/layoutWithSidebar/__tests__/LayoutWithSidebar.stories.tsx b/packages/layout/src/components/queen/__tests__/Queen.stories.tsx similarity index 63% rename from packages/layout/src/components/layoutWithSidebar/__tests__/LayoutWithSidebar.stories.tsx rename to packages/layout/src/components/queen/__tests__/Queen.stories.tsx index a9d5212..e51b67b 100644 --- a/packages/layout/src/components/layoutWithSidebar/__tests__/LayoutWithSidebar.stories.tsx +++ b/packages/layout/src/components/queen/__tests__/Queen.stories.tsx @@ -1,7 +1,7 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { LayoutWithSidebar } from '@musica-sacra/layout'; +import { Queen } from '../Queen'; const DummyContent = () => (
@@ -38,9 +38,9 @@ const DummyContent = () => (
); -const meta: Meta = { - title: '@musica-sacra/Layout/components/LayoutWithSidebar', - component: LayoutWithSidebar, +const meta: Meta = { + title: '@musica-sacra/Layout/components/Queen', + component: Queen, argTypes: { className: { @@ -57,9 +57,9 @@ type Story = StoryObj; const Template: Story = { render: (args) => ( -
}> +
}> - + ), }; @@ -69,7 +69,7 @@ export const Default: Story = { export const MixedContentLength: Story = { render: (args) => ( - @@ -104,9 +104,54 @@ export const MixedContentLength: Story = { } > - + ), args: { isPageLayout: true, }, }; + +export const FullWidth: Story = { + render: (args) => ( + +

Long sidebar

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+

Len pár položiek.

+
+ } + > + + + ), + args: { + isPageLayout: true, + fullWidth: true, + }, +}; diff --git a/packages/layout/src/components/layoutWithSidebar/layoutWithSidebar.scss b/packages/layout/src/components/queen/queen.scss similarity index 69% rename from packages/layout/src/components/layoutWithSidebar/layoutWithSidebar.scss rename to packages/layout/src/components/queen/queen.scss index a7dc4f1..8c58397 100644 --- a/packages/layout/src/components/layoutWithSidebar/layoutWithSidebar.scss +++ b/packages/layout/src/components/queen/queen.scss @@ -1,5 +1,5 @@ -.ms-layout-with-sidebar { - $max-width: 1200px; +.ms-queen { + $max-width: 1280px; $sidebar-ratio: 1; $content-ratio: 3; @@ -20,44 +20,6 @@ } &__ { - &overlay { - position: fixed; - inset: 0; - background-color: rgba(0, 0, 0, 0.4); - opacity: 0; - cursor: pointer; - transition: all 0.3s ease; - z-index: -1; - - &--active { - opacity: 1; - z-index: 9; - } - } - - &sidebar-expand { - display: none; - width: 100%; - padding: 1rem; - background-color: rgb(246, 246, 247); // TODO delete - - @media (max-width: $max-width) { - display: block; - } - - button { - background-color: transparent; - outline: none; - border: none; - color: rgba(60, 60, 67, 1); - - &:hover { - cursor: pointer; - color: rgba(8, 130, 170, 1); // TODO delete - } - } - } - &sidebar { width: calc((100% - ($max-width - 64px)) / 2 + $sidebar-min-width - 32px); min-width: $sidebar-min-width; @@ -71,8 +33,9 @@ left: 0; min-height: 100vh; height: 100%; - width: 80%; - max-width: 300px; + width: 90%; + max-width: 400px; + min-width: auto; box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); transform: translateX(-100%); @@ -91,7 +54,7 @@ } } - &sidebar-close { + &sidebar-close-button { display: none; width: 100%; padding: 1rem; @@ -127,7 +90,7 @@ margin-left: auto; padding: 1rem; - @media(max-width: 1200px) { + @media (max-width: $max-width) { width: 100%; padding: 0 1rem; } @@ -142,17 +105,19 @@ width: 100%; min-width: auto; } + } - &-inner { - width: $content-min-width; - margin-left: 0; - margin-right: auto; - padding: 1rem; + &content-inner { + width: $content-min-width; + margin-left: 0; + margin-right: auto; + padding: 1rem; + max-width: 960px; - @media (max-width: $max-width) { - width: 100%; - padding: 2rem; - } + @media (max-width: $max-width) { + width: 100%; + padding: 2rem; + margin: auto; } } } @@ -161,5 +126,26 @@ &page-layout { min-height: 100vh; } + + &full-width { + .ms-queen__content { + width: 100%; + } + + .ms-queen__content-inner { + width: 100%; + padding: 1rem 2rem; + max-width: none; + } + + .ms-queen__sidebar { + width: 400px; + max-width: 100%; + } + + .ms-queen__sidebar-content { + width: 100%; + } + } } } \ No newline at end of file diff --git a/packages/layout/src/components/sidebarExpandButton/SidebarExpandButton.tsx b/packages/layout/src/components/sidebarExpandButton/SidebarExpandButton.tsx new file mode 100644 index 0000000..c1a7abd --- /dev/null +++ b/packages/layout/src/components/sidebarExpandButton/SidebarExpandButton.tsx @@ -0,0 +1,37 @@ +import { useBem } from '@musica-sacra/hooks'; + +type SidebarExpandButtonProps = { + onClickCallback: () => void; +}; + +export function SidebarExpandButton({ + onClickCallback, +}: SidebarExpandButtonProps) { + const { bem } = useBem('ms-sidebar-expand-button'); + + return ( +
+ +
+ ); +} diff --git a/packages/layout/src/components/sidebarExpandButton/sidebarExpandButton.scss b/packages/layout/src/components/sidebarExpandButton/sidebarExpandButton.scss new file mode 100644 index 0000000..7844fbd --- /dev/null +++ b/packages/layout/src/components/sidebarExpandButton/sidebarExpandButton.scss @@ -0,0 +1,22 @@ +.ms-sidebar-expand-button { + display: none; + width: 100%; + padding: 1rem; + background-color: rgb(246, 246, 247); + + @media (max-width: 1280px) { + display: block; + } + + button { + background-color: transparent; + outline: none; + border: none; + color: rgba(60, 60, 67, 1); + + &:hover { + cursor: pointer; + color: rgba(8, 130, 170, 1); + } + } +} \ No newline at end of file diff --git a/packages/layout/src/main.ts b/packages/layout/src/main.ts index d7ef143..005c53b 100644 --- a/packages/layout/src/main.ts +++ b/packages/layout/src/main.ts @@ -1,8 +1,9 @@ import { Container } from './components/container/Container'; -import { LayoutBasic } from './components/layoutBasic/LayoutBasic'; -import { LayoutWithSidebar } from './components/layoutWithSidebar/LayoutWithSidebar'; +import { King } from './components/king/King'; +import { Queen } from './components/queen/Queen'; +import { Prince } from './components/prince/Prince'; -export { Container, LayoutBasic, LayoutWithSidebar }; +export { Container, King, Queen, Prince }; export { useSidebar } from './context/SidebarContext';