diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-01-15 22:45:57 +0100 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-01-15 22:51:30 +0100 | 
| commit | aa1ca65e7c9807c6d6020e39166536297fe1cdae (patch) | |
| tree | 2648da350fec3b71ab7f575d63e4c63ba08248b1 /src/components/WidgetParts | |
| parent | 16dbb4742264edac82fa6bb8e461259d097f4437 (diff) | |
chore: update sidebar and widgets styles
I'm now using a widget that can be expanded/collapsed. It also allows
me to handle more effectively widgets overflow and to avoid styles
repetitions.
However, with stylelint rule "no-descending-specificity", I'm not sure
if the stylesheets are really logical... Maybe I should deactivate this
rule.
Diffstat (limited to 'src/components/WidgetParts')
7 files changed, 325 insertions, 0 deletions
| diff --git a/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss b/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss new file mode 100644 index 0000000..058d805 --- /dev/null +++ b/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss @@ -0,0 +1,146 @@ +@use "@styles/abstracts/functions" as fun; +@use "@styles/abstracts/mixins" as mix; + +.title { +  margin: 0; +  padding: 0; +  background: none; +  font-size: var(--font-size-xl); +  text-align: left; +} + +.icon { +  display: flex; +  flex-flow: row nowrap; +  align-items: center; +  justify-content: center; +  width: fun.convert-px(30); +  height: fun.convert-px(30); +  background: var(--color-bg); +  border: fun.convert-px(1) solid var(--color-primary); +  border-radius: fun.convert-px(3); +  color: var(--color-primary); +  font-weight: 800; +  transition: all 0.25s ease-in-out 0s; + +  &::before, +  &::after { +    content: ""; +    background: var(--color-primary); +    transition: all 0.4s ease-out 0s; +  } + +  &::before { +    width: 10%; +    height: 60%; +    position: relative; +    left: 30%; +  } + +  &::after { +    width: 60%; +    height: 10%; +    position: relative; +    left: -5%; +  } +} + +.body { +  width: 100%; +  margin: 0; +  overflow-y: auto; +  visibility: hidden; +  opacity: 0; +  height: 0; +  transition: all 0.5s ease-in-out 0s; + +  &--borders { +    border: fun.convert-px(2) solid var(--color-primary-dark); +  } + +  > *:last-child { +    margin-bottom: 0; +  } + +  @include mix.media("screen") { +    @include mix.dimensions("md") { +      font-size: var(--font-size-sm); +      font-weight: 500; +    } +  } +} + +.header { +  flex: 0 0 auto; +  display: flex; +  flex-flow: row nowrap; +  align-items: center; +  justify-content: space-between; +  gap: var(--spacing-md); +  width: 100%; +  min-height: var(--header-height); +  position: sticky; +  top: 0; +  background: var(--color-bg); +  border: none; +  border-bottom: fun.convert-px(2) solid var(--color-primary-dark); +  cursor: pointer; +  transition: background 0.2s ease-in-out 0s; + +  > button { +    padding: 0 var(--spacing-xs); +  } +} + +.wrapper--expanded .icon { +  &::before { +    height: 0; +  } +} + +.header:hover, +.header:focus { +  .icon { +    background: var(--color-primary-light); +    color: var(--color-fg-inverted); +    transform: scale(1.2); + +    &::before, +    &::after { +      background: var(--color-bg); +    } +  } +} + +.wrapper { +  --header-height: #{fun.convert-px(65)}; + +  display: flex; +  flex-flow: column; +  min-height: var(--header-height); +  position: relative; +  overflow-y: hidden; + +  &:first-of-type { +    .header { +      border-top: fun.convert-px(2) solid var(--color-primary-dark); +    } +  } +} + +.wrapper--expanded { +  ~ .wrapper { +    .header { +      border-top: fun.convert-px(2) solid var(--color-primary-dark); +    } +  } + +  .body { +    visibility: visible; +    opacity: 1; +    min-height: inherit; +    height: 100%; +    margin: var(--spacing-sm) 0; +    transition: all 0.45s ease-in-out 0s; +  } +} diff --git a/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx b/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx new file mode 100644 index 0000000..52b5c06 --- /dev/null +++ b/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx @@ -0,0 +1,47 @@ +import { t } from '@lingui/macro'; +import { TitleLevel } from '@ts/types/app'; +import { ReactNode, useState } from 'react'; +import styles from './ExpandableWidget.module.scss'; + +const ExpandableWidget = ({ +  children, +  title, +  titleLevel = 2, +  expand = false, +  withBorders = false, +}: { +  children: ReactNode; +  title: string; +  titleLevel?: TitleLevel; +  expand?: boolean; +  withBorders?: boolean; +}) => { +  const [isExpanded, setIsExpanded] = useState<boolean>(expand); + +  const handleExpanse = () => setIsExpanded((prev) => !prev); + +  const TitleTag = `h${titleLevel}` as keyof JSX.IntrinsicElements; + +  const wrapperClasses = `${styles.wrapper} ${ +    isExpanded ? styles['wrapper--expanded'] : '' +  }`; + +  const bodyClasses = `${styles.body} ${ +    withBorders ? styles['body--borders'] : '' +  }`; + +  return ( +    <div className={wrapperClasses}> +      <button type="button" className={styles.header} onClick={handleExpanse}> +        <span className="screen-reader-text"> +          {isExpanded ? t`Collapse` : t`Expand`} +        </span> +        <TitleTag className={styles.title}>{title}</TitleTag> +        <span className={styles.icon} aria-hidden={true}></span> +      </button> +      <div className={bodyClasses}>{children}</div> +    </div> +  ); +}; + +export default ExpandableWidget; diff --git a/src/components/WidgetParts/List/List.module.scss b/src/components/WidgetParts/List/List.module.scss new file mode 100644 index 0000000..5da5741 --- /dev/null +++ b/src/components/WidgetParts/List/List.module.scss @@ -0,0 +1,47 @@ +@use "@styles/abstracts/functions" as fun; + +.list { +  margin: 0; +  padding: 0; +  list-style-type: none; + +  .list { +    border: none; +    border-top: fun.convert-px(1) solid var(--color-primary-dark); +  } + +  li { +    margin: 0; + +    &:not(:last-of-type) { +      border-bottom: fun.convert-px(1) solid var(--color-primary-dark); +    } +  } + +  a { +    display: flex; +    flex-flow: row nowrap; +    width: 100%; +    padding: var(--spacing-2xs) var(--spacing-xs); +    background: none; +    text-decoration: underline solid transparent 0; +    transition: all 0.16s ease-in-out 0s, text-decoration-color 0s; + +    &:hover, +    &:focus { +      background: var(--color-bg-secondary); +    } + +    &:focus { +      color: var(--color-primary); +      text-decoration-color: var(--color-primary-light); +      text-decoration-thickness: 0.25ex; +    } + +    &:active { +      background: var(--color-bg-tertiary); +      text-decoration-color: transparent; +      text-decoration-thickness: 0; +    } +  } +} diff --git a/src/components/WidgetParts/List/List.tsx b/src/components/WidgetParts/List/List.tsx new file mode 100644 index 0000000..317c4d1 --- /dev/null +++ b/src/components/WidgetParts/List/List.tsx @@ -0,0 +1,7 @@ +import styles from './List.module.scss'; + +const List = ({ items }: { items: Array<any> }) => { +  return <ul className={styles.list}>{items}</ul>; +}; + +export default List; diff --git a/src/components/WidgetParts/OrderedList/OrderedList.module.scss b/src/components/WidgetParts/OrderedList/OrderedList.module.scss new file mode 100644 index 0000000..a286932 --- /dev/null +++ b/src/components/WidgetParts/OrderedList/OrderedList.module.scss @@ -0,0 +1,66 @@ +@use "@styles/abstracts/functions" as fun; +@use "@styles/abstracts/placeholders"; + +.list { +  @extend %reset-ordered-list; +  counter-reset: link; + +  .list { +    border-top: fun.convert-px(1) solid var(--color-primary-dark); +  } + +  a { +    display: flex; +    flex-flow: row nowrap; +    width: 100%; +    padding: var(--spacing-2xs) var(--spacing-xs); +    background: none; +    text-decoration: underline solid transparent 0; +    transition: all 0.16s ease-in-out 0s, text-decoration-color 0s; +    counter-increment: link; + +    &:hover, +    &:focus { +      background: var(--color-bg-secondary); +    } + +    &:focus { +      color: var(--color-primary); +      text-decoration-color: var(--color-primary-light); +      text-decoration-thickness: 0.25ex; +    } + +    &:active { +      background: var(--color-bg-tertiary); +      text-decoration-color: transparent; +      text-decoration-thickness: 0; +    } + +    &::before { +      content: counters(link, ".") ". "; +      color: var(--color-secondary); +      padding-right: var(--spacing-2xs); +    } +  } + +  li { +    width: 100%; +    margin: 0; + +    &:not(:last-of-type) { +      border-bottom: fun.convert-px(1) solid var(--color-primary-dark); +    } + +    &::before { +      display: none; +    } +  } + +  li li a::before { +    padding-left: var(--spacing-sm); +  } + +  li li li a::before { +    padding-left: var(--spacing-lg); +  } +} diff --git a/src/components/WidgetParts/OrderedList/OrderedList.tsx b/src/components/WidgetParts/OrderedList/OrderedList.tsx new file mode 100644 index 0000000..a12ec06 --- /dev/null +++ b/src/components/WidgetParts/OrderedList/OrderedList.tsx @@ -0,0 +1,7 @@ +import styles from './OrderedList.module.scss'; + +const OrderedList = ({ items }: { items: Array<any> }) => { +  return <ol className={styles.list}>{items}</ol>; +}; + +export default OrderedList; diff --git a/src/components/WidgetParts/index.tsx b/src/components/WidgetParts/index.tsx new file mode 100644 index 0000000..59df3bd --- /dev/null +++ b/src/components/WidgetParts/index.tsx @@ -0,0 +1,5 @@ +import ExpandableWidget from './ExpandableWidget/ExpandableWidget'; +import List from './List/List'; +import OrderedList from './OrderedList/OrderedList'; + +export { ExpandableWidget, List, OrderedList }; | 
