From bd3015dcf05b4827de66da5796bf57cec7cd8ea8 Mon Sep 17 00:00:00 2001
From: Maximus <ten.maksim97@gmail.com>
Date: Mon, 29 Aug 2022 13:37:46 +0300
Subject: [PATCH] added article with parts uses dot notation

---
 package.json                                  |  5 +-
 src/components/Article/Article.tsx            | 75 ++++++++++++++++
 .../Article/ArticleParts/ArticleAuthors.tsx   | 53 +++++++++++
 .../ArticleParts/ArticleBreadcumbs.tsx        | 31 +++++++
 .../ArticleParts/ArticleDescription.tsx       | 38 ++++++++
 .../ArticleInteractionButtons.tsx             | 87 +++++++++++++++++++
 .../Article/ArticleParts/ArticleKeywords.tsx  | 55 ++++++++++++
 .../ArticleSubscriptionsButton.tsx            | 39 +++++++++
 .../Article/ArticleParts/ArticleTitle.tsx     | 24 +++++
 src/components/Article/ArticleSearch.tsx      | 45 ++++++++++
 tailwind.config.js                            | 77 ++++++++--------
 11 files changed, 491 insertions(+), 38 deletions(-)
 create mode 100644 src/components/Article/Article.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleAuthors.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleBreadcumbs.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleDescription.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleInteractionButtons.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleKeywords.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx
 create mode 100644 src/components/Article/ArticleParts/ArticleTitle.tsx
 create mode 100644 src/components/Article/ArticleSearch.tsx

diff --git a/package.json b/package.json
index 3ba8665..0273842 100644
--- a/package.json
+++ b/package.json
@@ -27,11 +27,11 @@
     "react-scripts": "5.0.1",
     "react-scrollbars-custom": "^4.1.0",
     "storybook-addon-pseudo-states": "^1.15.1",
+    "tailwindcss": "^3.1.7",
     "tsconfig-paths-webpack-plugin": "^4.0.0",
     "typescript": "^4.7.4",
     "web-vitals": "^2.1.4",
-    "yup": "^0.32.11",
-    "tailwindcss": "^3.1.7"
+    "yup": "^0.32.11"
   },
   "scripts": {
     "dev-tools": "redux-devtools --hostname=localhost --port=8000",
@@ -91,6 +91,7 @@
     "@storybook/react": "^6.5.9",
     "@storybook/testing-library": "^0.0.13",
     "@svgr/webpack": "^6.3.1",
+    "@tailwindcss/line-clamp": "^0.4.0",
     "@testing-library/jest-dom": "^5.16.4",
     "@testing-library/react": "^13.3.0",
     "@testing-library/user-event": "^13.5.0",
diff --git a/src/components/Article/Article.tsx b/src/components/Article/Article.tsx
new file mode 100644
index 0000000..9e10af2
--- /dev/null
+++ b/src/components/Article/Article.tsx
@@ -0,0 +1,75 @@
+import React, { useState } from "react";
+/* -------------------------------------------------------------------------- */
+/*                            imports Article parts                           */
+/* -------------------------------------------------------------------------- */
+import { ArticleTitle } from "./ArticleParts/ArticleTitle";
+import { ArticleBreadcumbs } from "./ArticleParts/ArticleBreadcumbs";
+import { ArticleAuthors } from "./ArticleParts/ArticleAuthors";
+import { ArticleKeywords } from "./ArticleParts/ArticleKeywords";
+import { ArticleInteractionButtons } from "./ArticleParts/ArticleInteractionButtons";
+import { ArticleDescription } from "./ArticleParts/ArticleDescription";
+import { ArticleSubscriptionsButtons } from "./ArticleParts/ArticleSubscriptionsButton";
+
+/**
+ * Reduces a sequence of names to initials.
+ * @param  {String} name  Space Delimited sequence of names.
+ * @param  {String} sep   A period separating the initials.
+ * @param  {String} trail A period ending the initials.
+ * @param  {String} hyph  A hypen separating double names.
+ * @return {String}       Properly formatted initials.
+ */
+type ArticleTileExtentions = {
+  Title?: {
+    children?: string;
+    className?: string;
+  };
+  Breadcumbs?: {
+    children?: string[];
+    highlightLAstChild?: boolean;
+  };
+  Authors?: {
+    children: React.ReactNode;
+    className?: string;
+    emphasis?: "low" | "high";
+  };
+  Keywords?: {
+    children?: React.ReactNode;
+    className?: string;
+    emphasis?: "low" | "high";
+  };
+  Description?: {
+    children?: React.ReactNode;
+    emphasis?: "low" | "high";
+    isShowing?: boolean;
+  };
+  InteractionButtons?: {
+    children?: React.ReactNode;
+    className?: string;
+    emphasis?: "high" | "low";
+  };
+  SubscriptionButtons?: {
+    className?: string;
+  };
+};
+
+type ArticleTileProps = {
+  /** Description of prop "foo". */
+  children?: React.ReactNode;
+};
+
+export function Article({
+  /** Description of prop "foo". */
+  children,
+}: ArticleTileProps & ArticleTileExtentions) {
+  const [isShowing, setIsShowing] = useState(false);
+
+  return <div className="flex flex-col w-full">{children}</div>;
+}
+
+Article.Title = ArticleTitle;
+Article.Breadcumbs = ArticleBreadcumbs;
+Article.Authors = ArticleAuthors;
+Article.Keywords = ArticleKeywords;
+Article.InteractionButtons = ArticleInteractionButtons;
+Article.Description = ArticleDescription;
+Article.SubscriptionsButtons = ArticleSubscriptionsButtons;
diff --git a/src/components/Article/ArticleParts/ArticleAuthors.tsx b/src/components/Article/ArticleParts/ArticleAuthors.tsx
new file mode 100644
index 0000000..5e2e16d
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleAuthors.tsx
@@ -0,0 +1,53 @@
+import React, { useMemo } from "react";
+import { RouterLink } from "components/typography/RouterLink";
+import Typography from "components/typography/Typography";
+import classNames from "classnames";
+import { SVGUser } from "components/icons";
+
+type AuthorsProps = {
+  children: React.ReactNode;
+  className?: string;
+  emphasis?: "low" | "high";
+  linkTo?: string;
+};
+
+export function ArticleAuthors({
+  children,
+
+  className,
+  emphasis = "high",
+  linkTo = "#",
+}: AuthorsProps) {
+  const authors = React.Children.map(children, (author, i) => {
+    return (
+      <RouterLink to={linkTo}>
+        <Typography
+          fontWeightVariant={emphasis === "high" ? "medium" : "normal"}
+          className={classNames(
+            "hover:text-blue-600",
+            {
+              "text-xs leading-5 text-gray-500": emphasis === "low",
+              "text-lg leading-6 text-gray-900": emphasis === "high",
+            },
+            {
+              "mr-1": i != React.Children.count(children) - 1,
+            },
+            className
+          )}
+        >
+          {author}
+          {i != React.Children.count(children) - 1 ? "," : null}
+        </Typography>
+      </RouterLink>
+    );
+  });
+
+  return (
+    <div className="flex flex-row items-center">
+      <SVGUser className="w-6 fill-gray-500 stroke-gray-500"></SVGUser>
+      <div className="ml-2 flex flex-row">{authors}</div>
+    </div>
+  );
+}
+
+ArticleAuthors.displayName = "ArticleAuthors";
diff --git a/src/components/Article/ArticleParts/ArticleBreadcumbs.tsx b/src/components/Article/ArticleParts/ArticleBreadcumbs.tsx
new file mode 100644
index 0000000..e52881e
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleBreadcumbs.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import Breadcrumbs from "components/breadcrumbs";
+import Logo from "components/Logo";
+import classNames from "classnames";
+
+type ArticleBreadcumbsProps = {
+  emphasis?: "high" | "low";
+  children?: string[];
+};
+
+export function ArticleBreadcumbs({
+  children,
+  emphasis = "high", //Emphasis high uses when we display article page
+}: ArticleBreadcumbsProps) {
+  return (
+    <Breadcrumbs
+      divider="slash"
+      className={classNames(
+        "text-xs leading-4 text-gray-700 flex flex-row items-center",
+        {
+          "last:text-gray-900": emphasis === "high",
+        }
+      )}
+    >
+      {emphasis === "high" ? <Logo className="w-4" fillColors="gray" /> : null}
+      {children}
+    </Breadcrumbs>
+  );
+}
+
+ArticleBreadcumbs.displayName = "ArticleBreadcumbs";
diff --git a/src/components/Article/ArticleParts/ArticleDescription.tsx b/src/components/Article/ArticleParts/ArticleDescription.tsx
new file mode 100644
index 0000000..119241d
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleDescription.tsx
@@ -0,0 +1,38 @@
+import React from "react";
+import Typography from "components/typography/Typography";
+import { Transition } from "@headlessui/react";
+
+type ArticleDescriptionProps = {
+  children?: React.ReactNode;
+  emphasis?: "low" | "high";
+  isShowing?: boolean;
+};
+
+export function ArticleDescription({
+  children,
+  emphasis = "high",
+  isShowing = false,
+}: ArticleDescriptionProps) {
+  return emphasis === "low" ? (
+    <div className="overflow-hidden">
+      <Transition
+        appear
+        show={isShowing}
+        enter="ease-in-out duration-200"
+        enterFrom="-translate-y-full"
+        enterTo="translate-y-0"
+        leave="ease-in-out duration-200"
+        leaveFrom="translate-y-0"
+        leaveTo="-translate-y-full"
+      >
+        <Typography className="text-base text-gray-900">{children}</Typography>
+      </Transition>
+    </div>
+  ) : (
+    <div>
+      <Typography className="text-base text-gray-900">{children}</Typography>
+    </div>
+  );
+}
+
+ArticleDescription.displayName = "ArticleDescription";
diff --git a/src/components/Article/ArticleParts/ArticleInteractionButtons.tsx b/src/components/Article/ArticleParts/ArticleInteractionButtons.tsx
new file mode 100644
index 0000000..ef69869
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleInteractionButtons.tsx
@@ -0,0 +1,87 @@
+import React from "react";
+import { Button } from "components/Button/Button";
+import Typography from "components/typography/Typography";
+import {
+  SVGArrowDown,
+  SVGArrowUp,
+  SVGCite,
+  SVGFiletext,
+  SVGDownload,
+  SVGShare,
+  SVGFolder,
+} from "components/icons";
+import classNames from "classnames";
+
+const interactionButtonsStore = [
+  {
+    icon: <SVGFiletext />,
+    title: "Read file",
+    buttonEmphasis: "high",
+    iconClassName: "h-6 fill-white stroke-white",
+  },
+  {
+    icon: <SVGDownload />,
+    title: "Download",
+    buttonEmphasis: "low",
+    iconClassName: "w-6 fill-gray-900 stroke-gray-900",
+  },
+  {
+    icon: <SVGCite />,
+    title: "Cite",
+    buttonEmphasis: "low",
+    iconClassName: "w-6 fill-gray-900 stroke-gray-900",
+  },
+  {
+    icon: <SVGShare />,
+    title: "Share",
+    buttonEmphasis: "low",
+    iconClassName: "w-6 fill-gray-900 stroke-gray-900",
+  },
+];
+
+type ArticleButtonProps = {
+  children?: React.ReactNode;
+  className?: string;
+  emphasis?: "high" | "low";
+} & Omit<React.ComponentPropsWithoutRef<"button">, "">;
+
+export function ArticleInteractionButtons({
+  children,
+  className,
+  emphasis, //to change displaying of component
+  ...props
+}: ArticleButtonProps) {
+  const abstractButton = (
+    <Button emphasis="medium" className="text-sm leading-4 items-center px-3">
+      <Typography fontWeightVariant="bold" className="pr-2">
+        Abstract
+      </Typography>
+      <Button.Icon>
+        <SVGArrowDown className="w-4 fill-blue-700 stroke-blue-700" />
+      </Button.Icon>
+    </Button>
+  );
+
+  const fileInteractionButtons = interactionButtonsStore.map((button) => {
+    return (
+      <Button
+        emphasis={button.buttonEmphasis === "high" ? "high" : "low"}
+        className="h-max px-2"
+      >
+        <Button.Icon>
+          {React.cloneElement(button.icon, { className: button.iconClassName })}
+        </Button.Icon>
+        {emphasis === "high" ? <Typography>{button.title}</Typography> : null}
+      </Button>
+    );
+  });
+
+  return (
+    <div className="flex flex-row">
+      {emphasis === "low" && !children ? abstractButton : null}
+      {children ? children : fileInteractionButtons}
+    </div>
+  );
+}
+
+ArticleInteractionButtons.displayName = "ArticleInteractionButtons";
diff --git a/src/components/Article/ArticleParts/ArticleKeywords.tsx b/src/components/Article/ArticleParts/ArticleKeywords.tsx
new file mode 100644
index 0000000..01b5546
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleKeywords.tsx
@@ -0,0 +1,55 @@
+import React from "react";
+import Typography from "components/typography/Typography";
+import { SVGKey } from "components/icons";
+import { RouterLink } from "components/typography/RouterLink";
+import classNames from "classnames";
+
+type KeywordsProps = {
+  children?: React.ReactNode;
+  className?: string;
+  emphasis?: "low" | "high";
+  linkTo?: string;
+};
+
+export function ArticleKeywords({
+  children,
+  className,
+  emphasis = "high",
+  linkTo = "#",
+}: KeywordsProps) {
+  const keywords = React.Children.map(children, (keyword, i) => {
+    return (
+      <RouterLink to={linkTo}>
+        <div
+          className={classNames(
+            "mr-1",
+            "hover:text-blue-600",
+            {
+              "text-xs text-gray-500 leading-5": emphasis === "low",
+              "text-base text-gray-900  px-2 border rounded hover:border-blue-600 border-gray-900":
+                emphasis === "high",
+            },
+            className
+          )}
+        >
+          <Typography>
+            {keyword}
+            {i != React.Children.count(children) - 1 && emphasis === "low"
+              ? ","
+              : null}
+          </Typography>
+        </div>
+      </RouterLink>
+    );
+  });
+  return (
+    <div className="flex flex-row items-center">
+      {emphasis === "low" ? (
+        <SVGKey className="w-6 fill-gray-500 stroke-gray-500" />
+      ) : null}
+      <div className="flex flex-row ml-2">{keywords}</div>
+    </div>
+  );
+}
+
+ArticleKeywords.displayName = "ArticleKeywords";
diff --git a/src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx b/src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx
new file mode 100644
index 0000000..ad6dd09
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx
@@ -0,0 +1,39 @@
+import React, { useState } from "react";
+import { Button } from "components/Button/Button";
+import {
+  SVGFavoriteFilled,
+  SVGFavoriteOutlined,
+  SVGFolder,
+} from "components/icons";
+
+const subscriptionStyles = "fill-gray-500 stroke-gray-500 w-6";
+
+type ArticleSubscriptionsButtonsProps = {
+  className?: string;
+  favorite?: boolean;
+  //Todo create tracking subscriptions and onClick props
+} & Omit<React.ComponentPropsWithoutRef<"button">, "">;
+
+export function ArticleSubscriptionsButtons({
+  className,
+  favorite = false,
+}: ArticleSubscriptionsButtonsProps) {
+  return (
+    <div className="flex flex-row">
+      <Button emphasis="low">
+        <Button.Icon>
+          <SVGFolder className={subscriptionStyles} />
+        </Button.Icon>
+      </Button>
+      <Button emphasis="low" onClick={() => {}}>
+        <Button.Icon>
+          {!favorite ? (
+            <SVGFavoriteOutlined className={subscriptionStyles} />
+          ) : (
+            <SVGFavoriteFilled className="fill-blue-600 stroke-blue-600 w-6" />
+          )}
+        </Button.Icon>
+      </Button>
+    </div>
+  );
+}
diff --git a/src/components/Article/ArticleParts/ArticleTitle.tsx b/src/components/Article/ArticleParts/ArticleTitle.tsx
new file mode 100644
index 0000000..7a5ae89
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleTitle.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import Typography from "components/typography/Typography";
+import { RouterLink } from "components/typography/RouterLink";
+type ArticleTitleProps = {
+  className?: string;
+  children?: string;
+  linkTo?: string;
+};
+
+export function ArticleTitle({
+  className,
+  children,
+  linkTo = "#",
+}: ArticleTitleProps) {
+  return (
+    <RouterLink to={linkTo}>
+      <Typography fontWeightVariant="semibold" className={className}>
+        {children}
+      </Typography>
+    </RouterLink>
+  );
+}
+
+ArticleTitle.displayName = "ArticleTitle";
diff --git a/src/components/Article/ArticleSearch.tsx b/src/components/Article/ArticleSearch.tsx
new file mode 100644
index 0000000..8b0e397
--- /dev/null
+++ b/src/components/Article/ArticleSearch.tsx
@@ -0,0 +1,45 @@
+import React from "react";
+import { Article } from "./Article";
+
+export function ArticleSearch() {
+  return (
+    <Article>
+      <div className="flex flex-row justify-between">
+        <Article.Breadcumbs emphasis="high">
+          {["Yoda", "Lallalla", "Maxim"]}
+        </Article.Breadcumbs>
+        <Article.SubscriptionsButtons />
+      </div>
+      <div className="flex flex-row">
+        <Article.Title className="text-3xl">Yeanda lacreav</Article.Title>
+      </div>
+
+      <Article.Authors emphasis="low">
+        {["Reavap", "aldjfoa", "dkfjaoif"]}
+      </Article.Authors>
+      <Article.Keywords emphasis="low">
+        {["porn", "development", "frontend"]}
+      </Article.Keywords>
+
+      <Article.InteractionButtons emphasis="low">
+        low
+      </Article.InteractionButtons>
+      <Article.Description>
+        {" "}
+        Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere quidem
+        provident temporibus! Fuga dolores placeat at voluptatem quia, vero
+        molestiae animi et itaque a, officia ullam expedita temporibus cum
+        deserunt.Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere
+        quidem provident temporibus! Fuga dolores placeat at voluptatem quia,
+        vero molestiae animi et itaque a, officia ullam expedita temporibus cum
+        deserunt.Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere
+        quidem provident temporibus! Fuga dolores placeat at voluptatem quia,
+        vero molestiae animi et itaque a, officia ullam expedita temporibus cum
+        deserunt.Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere
+        quidem provident temporibus! Fuga dolores placeat at voluptatem quia,
+        vero molestiae animi et itaque a, officia ullam expedita temporibus cum
+        deserunt.
+      </Article.Description>
+    </Article>
+  );
+}
diff --git a/tailwind.config.js b/tailwind.config.js
index f8b96c6..8f4d294 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -12,7 +12,9 @@ function Helper() {
     let obj = {};
     obj[key] = this.withOpacityValue(`--color-${key}-text-base`);
     obj[`${key}-muted`] = this.withOpacityValue(`--color-${key}-text-muted`);
-    obj[`${key}-inverted`] = this.withOpacityValue(`--color-${key}-text-inverted`);
+    obj[`${key}-inverted`] = this.withOpacityValue(
+      `--color-${key}-text-inverted`
+    );
 
     return obj;
   };
@@ -22,7 +24,9 @@ function Helper() {
     obj[key] = this.withOpacityValue(`--color-${key}`);
     obj[`${key}-disabled`] = this.withOpacityValue(`--color-${key}-disabled`);
     obj[`${key}-hover`] = this.withOpacityValue(`--color-${key}-hover`);
-    obj[`${key}-disabled-hover`] = this.withOpacityValue(`--color-${key}-disabled-hover`);
+    obj[`${key}-disabled-hover`] = this.withOpacityValue(
+      `--color-${key}-disabled-hover`
+    );
 
     return obj;
   };
@@ -44,7 +48,7 @@ function Helper() {
 
   this.luminanced = function (key, additionalStops = []) {
     let obj = {};
-    obj['50'] = this.withOpacityValue(`--color-${key}-50`);
+    obj["50"] = this.withOpacityValue(`--color-${key}-50`);
     for (let index = 0; index < additionalStops.length; index++) {
       const element = additionalStops[index];
       obj[element] = this.withOpacityValue(`--color-${key}-${element}`);
@@ -62,64 +66,65 @@ const ColorHelper = new Helper();
  * @type { import('@types/tailwindcss/tailwind-config').TailwindConfig }
  */
 module.exports = {
-  content: ['./src/**/*.{jsx,tsx}'],
+  content: ["./src/**/*.{jsx,tsx}"],
   plugins: [
     function ({ addComponents }) {
       addComponents({
-        '.container': {
-          maxWidth: '840px',
-          paddingLeft: '0.5rem',
-          paddingRight: '0.5rem',
-          '@screen sm': {
-            maxWidth: 'calc(100% - 30px)',
+        ".container": {
+          maxWidth: "840px",
+          paddingLeft: "0.5rem",
+          paddingRight: "0.5rem",
+          "@screen sm": {
+            maxWidth: "calc(100% - 30px)",
           },
-          '@screen md': {
-            maxWidth: 'calc(100% - 30px)',
+          "@screen md": {
+            maxWidth: "calc(100% - 30px)",
           },
-          '@screen lg': {
-            maxWidth: '840px',
+          "@screen lg": {
+            maxWidth: "840px",
           },
-          '@screen xl': {
-            maxWidth: '840px',
+          "@screen xl": {
+            maxWidth: "840px",
           },
-          '@screen 2xl': {
-            maxWidth: '840px',
+          "@screen 2xl": {
+            maxWidth: "840px",
           },
         },
       });
     },
+    require("@tailwindcss/line-clamp"),
   ],
   theme: {
     extend: {
       screens: {
-        tall: { raw: '(min-height: 1200px) and (orientation: portrait)' },
-        skewed: { raw: '(max-height: 600px)' },
-        "1.5xl": '1300px',
+        tall: { raw: "(min-height: 1200px) and (orientation: portrait)" },
+        skewed: { raw: "(max-height: 600px)" },
+        "1.5xl": "1300px",
       },
       height: {
-        'half-screen': '50vh',
+        "half-screen": "50vh",
       },
       minHeight: {
-        'half-screen': '50vh',
+        "half-screen": "50vh",
       },
       colors: {
-        aside: 'var(--color-aside)',
-        main: 'var(--color-body)',
-        header: '#191D2B',
-        transparent: 'transparent',
-        current: 'currentColor',
-        white: '#ffffff',
-        black: '#000000',
-        serv: ColorHelper.luminanced('serv'),
-        blue: ColorHelper.luminanced('blue'),
-        gray: ColorHelper.luminanced('gray', [75]),
+        aside: "var(--color-aside)",
+        main: "var(--color-body)",
+        header: "#191D2B",
+        transparent: "transparent",
+        current: "currentColor",
+        white: "#ffffff",
+        black: "#000000",
+        serv: ColorHelper.luminanced("serv"),
+        blue: ColorHelper.luminanced("blue"),
+        gray: ColorHelper.luminanced("gray", [75]),
       },
       gridTemplateColumns: {
-        'layout': 'min-content 7fr',
+        layout: "min-content 7fr",
       },
       gridTemplateRows: {
-        'page': '3.5rem calc(100vh - 3.5rem)'
-      }
+        page: "3.5rem calc(100vh - 3.5rem)",
+      },
     },
   },
 };