diff --git a/packages/ui/next-env.d.ts b/packages/ui/next-env.d.ts
new file mode 100644
index 0000000..4f11a03
--- /dev/null
+++ b/packages/ui/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/packages/ui/src/ArticleLayout.jsx b/packages/ui/src/ArticleLayout.jsx
deleted file mode 100644
index eeeba39..0000000
--- a/packages/ui/src/ArticleLayout.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-'use client'
-
-import { useContext } from 'react'
-import { useRouter } from 'next/navigation'
-
-import { AppContext } from '@/app/providers'
-import { Container } from '@/components/Container'
-import { Prose } from '@/components/Prose'
-import { formatDate } from '@/lib/formatDate'
-
-function ArrowLeftIcon(props) {
- return (
-
- )
-}
-
-export function ArticleLayout({ children, article, isRssFeed = false }) {
- let router = useRouter()
- let { previousPathname } = useContext(AppContext)
-
- if (isRssFeed) {
- return children
- }
-
- return (
-
-
-
- {previousPathname && (
-
- )}
-
-
-
- {article.title}
-
-
-
-
- {children}
-
-
-
-
-
- )
-}
diff --git a/packages/ui/src/ArticleLayout/ArticleLayout.tsx b/packages/ui/src/ArticleLayout/ArticleLayout.tsx
new file mode 100644
index 0000000..1700390
--- /dev/null
+++ b/packages/ui/src/ArticleLayout/ArticleLayout.tsx
@@ -0,0 +1,60 @@
+import { Container } from "../Container/Container";
+import { Prose } from "../Prose/Prose";
+import { ArticleLayoutBack } from "./ArticleLayoutBack";
+import { FC } from "react";
+
+export function formatDate(dateString: string) {
+ return new Date(`${dateString}T00:00:00Z`).toLocaleDateString("en-US", {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ timeZone: "UTC",
+ });
+}
+
+interface Article {
+ title: string;
+ description: string;
+ author: string;
+ date: string;
+}
+
+export interface ArticleWithSlug extends Article {
+ slug: string;
+}
+
+type ArticleLayoutProps = {
+ article: ArticleWithSlug;
+ children: React.ReactNode;
+};
+export const ArticleLayout: FC = ({
+ article,
+ children,
+}) => {
+ return (
+
+
+
+
+
+
+
+ {article.title}
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+};
diff --git a/packages/ui/src/ArticleLayout/ArticleLayoutBack.tsx b/packages/ui/src/ArticleLayout/ArticleLayoutBack.tsx
new file mode 100644
index 0000000..6218bf5
--- /dev/null
+++ b/packages/ui/src/ArticleLayout/ArticleLayoutBack.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { FC, useEffect, useState } from "react";
+
+const ArrowLeftIcon: FC> = (props) => {
+ return (
+
+ );
+};
+
+export const ArticleLayoutBack: FC = () => {
+ let router = useRouter();
+ const [hasPreviousRoute, setHasPreviousRoute] = useState(false);
+ useEffect(() => {
+ if (window?.history?.length > 0) {
+ setHasPreviousRoute(true);
+ }
+ }, []);
+
+ if (!hasPreviousRoute) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/ui/src/Button.stories.ts b/packages/ui/src/Button/Button.stories.ts
similarity index 56%
rename from packages/ui/src/Button.stories.ts
rename to packages/ui/src/Button/Button.stories.ts
index 01eaa8c..782dba4 100644
--- a/packages/ui/src/Button.stories.ts
+++ b/packages/ui/src/Button/Button.stories.ts
@@ -2,17 +2,13 @@ import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";
-// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta = {
title: "Example/Button2",
component: Button,
parameters: {
- // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: "centered",
},
- // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
tags: ["autodocs"],
- // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {},
} satisfies Meta;
@@ -23,11 +19,13 @@ type Story = StoryObj;
export const Primary: Story = {
args: {
variant: "primary",
+ children: "Awesome primary button",
},
};
export const Secondary: Story = {
args: {
variant: "secondary",
+ children: "Awesome secondary button",
},
};
diff --git a/packages/ui/src/Button.tsx b/packages/ui/src/Button/Button.tsx
similarity index 73%
rename from packages/ui/src/Button.tsx
rename to packages/ui/src/Button/Button.tsx
index c17be50..475e61a 100644
--- a/packages/ui/src/Button.tsx
+++ b/packages/ui/src/Button/Button.tsx
@@ -1,36 +1,35 @@
-import { FC } from "react";
import Link from "next/link";
import clsx from "clsx";
+import { ComponentPropsWithoutRef } from "react";
const variantStyles = {
primary:
"bg-zinc-800 font-semibold text-zinc-100 hover:bg-zinc-700 active:bg-zinc-800 active:text-zinc-100/70 dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:active:bg-zinc-700 dark:active:text-zinc-100/70",
secondary:
"bg-zinc-50 font-medium text-zinc-900 hover:bg-zinc-100 active:bg-zinc-100 active:text-zinc-900/60 dark:bg-zinc-800/50 dark:text-zinc-300 dark:hover:bg-zinc-800 dark:hover:text-zinc-50 dark:active:bg-zinc-800/50 dark:active:text-zinc-50/70",
-} as const;
+};
-/** @todo Сделать нормальный тип */
-type Props = {
- href?: string;
+type ButtonProps = {
variant?: keyof typeof variantStyles;
- className?: string;
-};
+} & (
+ | (ComponentPropsWithoutRef<"button"> & { href?: undefined })
+ | ComponentPropsWithoutRef
+);
-export const Button: FC = ({
+export const Button = ({
variant = "primary",
className,
- href,
...props
-}) => {
+}: ButtonProps) => {
className = clsx(
"inline-flex items-center gap-2 justify-center rounded-md py-2 px-3 text-sm outline-offset-2 transition active:transition-none",
variantStyles[variant],
className,
);
- return href ? (
-
- ) : (
+ return typeof props.href === "undefined" ? (
+ ) : (
+
);
};
diff --git a/packages/ui/src/Card.jsx b/packages/ui/src/Card/Card.tsx
similarity index 54%
rename from packages/ui/src/Card.jsx
rename to packages/ui/src/Card/Card.tsx
index 080566a..7570592 100644
--- a/packages/ui/src/Card.jsx
+++ b/packages/ui/src/Card/Card.tsx
@@ -1,7 +1,13 @@
-import Link from 'next/link'
-import clsx from 'clsx'
+import Link from "next/link";
+import clsx from "clsx";
+import type {
+ ComponentPropsWithoutRef,
+ ElementType,
+ FC,
+ ReactNode,
+} from "react";
-function ChevronRightIcon(props) {
+const ChevronRightIcon: FC> = (props) => {
return (
- )
-}
+ );
+};
+
+export const Card = ({
+ as,
+ className,
+ children,
+}: Omit, "as" | "className"> & {
+ as?: T;
+ className?: string;
+}) => {
+ let Component = as ?? "div";
-export function Card({ as: Component = 'div', className, children }) {
return (
{children}
- )
-}
+ );
+};
-Card.Link = function CardLink({ children, ...props }) {
+const CardLink: FC> = ({
+ children,
+ ...props
+}) => {
return (
<>
@@ -33,26 +51,38 @@ Card.Link = function CardLink({ children, ...props }) {
{children}
>
- )
-}
+ );
+};
+Card.Link = CardLink;
+
+const CardTitle = ({
+ as,
+ href,
+ children,
+}: Omit, "as" | "href"> & {
+ as?: T;
+ href?: string;
+}) => {
+ let Component = as ?? "h2";
-Card.Title = function CardTitle({ as: Component = 'h2', href, children }) {
return (
{href ? {children} : children}
- )
-}
+ );
+};
+Card.Title = CardTitle;
-Card.Description = function CardDescription({ children }) {
+const CardDescription = ({ children }: { children: ReactNode }) => {
return (
{children}
- )
-}
+ );
+};
+Card.Description = CardDescription;
-Card.Cta = function CardCta({ children }) {
+const CardCta = ({ children }: { children: ReactNode }) => {
return (
- )
-}
+ );
+};
+Card.Cta = CardCta;
-Card.Eyebrow = function CardEyebrow({
- as: Component = 'p',
+const CardEyebrow = ({
+ as,
decorate = false,
className,
children,
...props
-}) {
+}: Omit, "as" | "decorate"> & {
+ as?: T;
+ decorate?: boolean;
+}) => {
+ let Component = as ?? "p";
+
return (
@@ -90,5 +126,6 @@ Card.Eyebrow = function CardEyebrow({
)}
{children}
- )
-}
+ );
+};
+Card.Eyebrow = CardEyebrow;
diff --git a/packages/ui/src/Container.jsx b/packages/ui/src/Container.jsx
deleted file mode 100644
index e9956d1..0000000
--- a/packages/ui/src/Container.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { forwardRef } from 'react'
-import clsx from 'clsx'
-
-const OuterContainer = forwardRef(function OuterContainer(
- { className, children, ...props },
- ref
-) {
- return (
-
- )
-})
-
-const InnerContainer = forwardRef(function InnerContainer(
- { className, children, ...props },
- ref
-) {
- return (
-
- )
-})
-
-export const Container = forwardRef(function Container(
- { children, ...props },
- ref
-) {
- return (
-
- {children}
-
- )
-})
-
-Container.Outer = OuterContainer
-Container.Inner = InnerContainer
diff --git a/packages/ui/src/Container/Container.tsx b/packages/ui/src/Container/Container.tsx
new file mode 100644
index 0000000..90081d4
--- /dev/null
+++ b/packages/ui/src/Container/Container.tsx
@@ -0,0 +1,42 @@
+import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
+import clsx from "clsx";
+
+export const ContainerOuter = forwardRef<
+ ElementRef<"div">,
+ ComponentPropsWithoutRef<"div">
+>(({ className, children, ...props }, ref) => {
+ return (
+
+ );
+});
+ContainerOuter.displayName = "ContainerOuter";
+
+export const ContainerInner = forwardRef<
+ ElementRef<"div">,
+ ComponentPropsWithoutRef<"div">
+>(({ className, children, ...props }, ref) => {
+ return (
+
+ );
+});
+ContainerInner.displayName = "ContainerInner";
+
+export const Container = forwardRef<
+ ElementRef,
+ ComponentPropsWithoutRef
+>(({ children, ...props }, ref) => {
+ return (
+
+ {children}
+
+ );
+});
+Container.displayName = "Container";
diff --git a/packages/ui/src/Footer.jsx b/packages/ui/src/Footer/Footer.tsx
similarity index 72%
rename from packages/ui/src/Footer.jsx
rename to packages/ui/src/Footer/Footer.tsx
index efe0549..ebcba4d 100644
--- a/packages/ui/src/Footer.jsx
+++ b/packages/ui/src/Footer/Footer.tsx
@@ -1,8 +1,9 @@
-import Link from 'next/link'
+import Link from "next/link";
-import { Container } from '@/components/Container'
+import { ContainerInner, ContainerOuter } from "../Container/Container";
+import type { ReactNode } from "react";
-function NavLink({ href, children }) {
+const NavLink = ({ href, children }: { href: string; children: ReactNode }) => {
return (
{children}
- )
-}
+ );
+};
-export function Footer() {
+export const Footer = () => {
return (
- {children}
+ {children && {children}
}
- )
-}
+ );
+};
diff --git a/packages/ui/src/SocialIcons.jsx b/packages/ui/src/SocialIcons/SocialIcons.tsx
similarity index 90%
rename from packages/ui/src/SocialIcons.jsx
rename to packages/ui/src/SocialIcons/SocialIcons.tsx
index ff2b66a..db3d709 100644
--- a/packages/ui/src/SocialIcons.jsx
+++ b/packages/ui/src/SocialIcons/SocialIcons.tsx
@@ -1,21 +1,23 @@
-export function TwitterIcon(props) {
+import type { ComponentPropsWithoutRef, FC } from "react";
+
+export const TwitterIcon: FC> = (props) => {
return (
- )
-}
+ );
+};
-export function InstagramIcon(props) {
+export const InstagramIcon: FC> = (props) => {
return (
- )
-}
+ );
+};
-export function GitHubIcon(props) {
+export const GitHubIcon: FC> = (props) => {
return (
- )
-}
+ );
+};
-export function LinkedInIcon(props) {
+export const LinkedInIcon: FC> = (props) => {
return (
- )
-}
+ );
+};
diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json
index 2ed2f12..7a61802 100644
--- a/packages/ui/tsconfig.json
+++ b/packages/ui/tsconfig.json
@@ -1,6 +1,6 @@
{
"extends": "@sneg240/tsconfig/react-library.json",
- "include": [".", "./.storybook"],
+ "include": ["src", ".storybook", "next-env.d.ts"],
"compilerOptions": {
"module": "esnext"
},