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
{children}
; +} + +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 ( + + + {author} + {i != React.Children.count(children) - 1 ? "," : null} + + + ); + }); + + return ( +
+ +
{authors}
+
+ ); +} + +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 ( + + {emphasis === "high" ? : null} + {children} + + ); +} + +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" ? ( +
+ + {children} + +
+ ) : ( +
+ {children} +
+ ); +} + +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: , + title: "Read file", + buttonEmphasis: "high", + iconClassName: "h-6 fill-white stroke-white", + }, + { + icon: , + title: "Download", + buttonEmphasis: "low", + iconClassName: "w-6 fill-gray-900 stroke-gray-900", + }, + { + icon: , + title: "Cite", + buttonEmphasis: "low", + iconClassName: "w-6 fill-gray-900 stroke-gray-900", + }, + { + icon: , + title: "Share", + buttonEmphasis: "low", + iconClassName: "w-6 fill-gray-900 stroke-gray-900", + }, +]; + +type ArticleButtonProps = { + children?: React.ReactNode; + className?: string; + emphasis?: "high" | "low"; +} & Omit, "">; + +export function ArticleInteractionButtons({ + children, + className, + emphasis, //to change displaying of component + ...props +}: ArticleButtonProps) { + const abstractButton = ( + + ); + + const fileInteractionButtons = interactionButtonsStore.map((button) => { + return ( + + ); + }); + + return ( +
+ {emphasis === "low" && !children ? abstractButton : null} + {children ? children : fileInteractionButtons} +
+ ); +} + +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 ( + +
+ + {keyword} + {i != React.Children.count(children) - 1 && emphasis === "low" + ? "," + : null} + +
+
+ ); + }); + return ( +
+ {emphasis === "low" ? ( + + ) : null} +
{keywords}
+
+ ); +} + +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, "">; + +export function ArticleSubscriptionsButtons({ + className, + favorite = false, +}: ArticleSubscriptionsButtonsProps) { + return ( +
+ + +
+ ); +} 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 ( + + + {children} + + + ); +} + +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 ( +
+
+ + {["Yoda", "Lallalla", "Maxim"]} + + +
+
+ Yeanda lacreav +
+ + + {["Reavap", "aldjfoa", "dkfjaoif"]} + + + {["porn", "development", "frontend"]} + + + + low + + + {" "} + 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. + +
+ ); +} 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)", + }, }, }, };