diff --git a/app/build/list/page.tsx b/app/build/list/page.tsx index 4e94b0f..ce1616e 100644 --- a/app/build/list/page.tsx +++ b/app/build/list/page.tsx @@ -3,6 +3,8 @@ import { Platform, PlatformCode, PlatformQuery } from "@prisma/client"; import { templates, samples } from "@/platforms"; import Link from "next/link"; +export const dynamic = "force-static"; + export default async function QueryList() { const platforms = await prisma.platform.findMany({ include: { diff --git a/lib/@dsvgui/.storybook/main.ts b/lib/@dsvgui/.storybook/main.ts index 0450a88..d4ec618 100644 --- a/lib/@dsvgui/.storybook/main.ts +++ b/lib/@dsvgui/.storybook/main.ts @@ -3,7 +3,7 @@ import webpack from "webpack"; const defaultConfig = { - stories: ["../components/**/*.stories.@(js|jsx|ts|tsx)"], + stories: ["../(components|grid)/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ "@storybook/addon-links", "@storybook/addon-essentials", diff --git a/lib/@dsvgui/.storybook/preview.ts b/lib/@dsvgui/.storybook/preview.ts index 0f07f76..467f08d 100644 --- a/lib/@dsvgui/.storybook/preview.ts +++ b/lib/@dsvgui/.storybook/preview.ts @@ -1,4 +1,7 @@ /** @type { import('@storybook/react').Preview } */ + +import "react-grid-layout/css/styles.css"; + const preview = { parameters: { actions: { argTypesRegex: "^on[A-Z].*" }, diff --git a/lib/@dsvgui/components/article/index.stories.tsx b/lib/@dsvgui/components/article/index.stories.tsx index 1259112..cf3ba37 100644 --- a/lib/@dsvgui/components/article/index.stories.tsx +++ b/lib/@dsvgui/components/article/index.stories.tsx @@ -1,25 +1,203 @@ +import React from "react"; + import type { Meta, StoryObj } from "@storybook/react"; -import { Article } from "./index"; -import { readImageURL } from "@/lib/@dsvgui/utils"; +import { Article, articleDocumentPreferences, IArticle } from "./index"; +import { readImageURL } from "../../utils"; +import { Grid } from "../../utils/grid"; const meta: Meta = { title: "Article", component: Article, - render: ({ articles }, { loaded: { thumbnails } }) => { + render: ({ articles, document }, { loaded: { thumbnails } }) => { articles = articles.map((article, key) => ({ ...article, thumbnail: thumbnails[key], })); - return
; + return
; }, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Base: Story = { args: { + document: { + w: 5, + h: 4, + ...articleDocumentPreferences, + }, + articles: [ + { + meta: { + title: "Why JS?", + description: "I can't imagine a word without Javascript.", + author: "sametcodes", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--uJ_UDNhq--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2q16fucjwb4cla0ho05.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + { + meta: { + title: "Why not PHP?", + description: "I can't imagine a word with PHP.", + author: "eminkokdemir", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--r3He9vYK--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fuc2yb3yi1680dgmfj25.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + { + meta: { + title: "Go is the best", + description: "What do and what do not do with Go.", + author: "ekurt", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--Wzj_6u7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53d24e36pofszh6shj6t.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + { + meta: { + title: "Launching my TodoList App", + description: + "You can find the source code on my Github. I will be happy if you contribute to the project.", + author: "aydinfz", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--_hLjVEgA--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewoetr6z83in1o5iu5uc.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + ], + }, + loaders: [ + async ({ args: { articles } }) => { + const thumbnails = await Promise.all( + articles.map((article) => readImageURL(article.thumbnail.value)) + ); + return { thumbnails }; + }, + ], +}; + +export const Compact: Story = { + args: { + document: { + w: 3, + h: 3, + ...articleDocumentPreferences, + }, + articles: [ + { + meta: { + title: "Why JS?", + description: "I can't imagine a word without Javascript.", + author: "sametcodes", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--uJ_UDNhq--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2q16fucjwb4cla0ho05.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + { + meta: { + title: "Why not PHP?", + description: "I can't imagine a word with PHP.", + author: "eminkokdemir", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--r3He9vYK--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fuc2yb3yi1680dgmfj25.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + { + meta: { + title: "Go is the best", + description: "What do and what do not do with Go.", + author: "ekurt", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--Wzj_6u7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53d24e36pofszh6shj6t.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + { + meta: { + title: "Launching my TodoList App", + description: + "You can find the source code on my Github. I will be happy if you contribute to the project.", + author: "aydinfz", + }, + thumbnail: { + value: + "https://res.cloudinary.com/practicaldev/image/fetch/s--_hLjVEgA--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewoetr6z83in1o5iu5uc.png", + width: 150, + height: 150, + }, + publish_date: "20.03.2023", + like_count: 3, + reading_time_minutes: 4, + }, + ], + }, + loaders: [ + async ({ args: { articles } }) => { + const thumbnails = await Promise.all( + articles.map((article) => readImageURL(article.thumbnail.value)) + ); + return { thumbnails }; + }, + ], +}; + +export const WithoutImage: Story = { + args: { + document: { + w: 4, + h: 3, + ...articleDocumentPreferences, + }, articles: [ { meta: { @@ -97,3 +275,7 @@ export const Base: Story = { }, ], }; + +export const WithGrid = () => { + return ; +}; diff --git a/lib/@dsvgui/components/article/index.tsx b/lib/@dsvgui/components/article/index.tsx index aad6558..a6a0abc 100644 --- a/lib/@dsvgui/components/article/index.tsx +++ b/lib/@dsvgui/components/article/index.tsx @@ -1,11 +1,14 @@ -import { Document, Text } from "@/lib/@dsvgui"; -import { wrapText } from "@/lib/@dsvgui/utils"; -import { getTextWidth } from "@/lib/@dsvgui/utils/index"; +import React from "react"; + +import { Document, Text, getDocumentSize } from "../../document"; +import { wrapText, getTextWidth } from "../../utils"; import { AiOutlineCalendar, AiOutlineLike } from "react-icons/ai"; import { BiTimeFive } from "react-icons/bi"; import { IoPersonCircleOutline } from "react-icons/io5"; +import { DocumentMeta } from "../../document/type"; + export type IArticle = { articles: Array<{ meta: { @@ -22,39 +25,49 @@ export type IArticle = { like_count: number; reading_time_minutes: number; }>; +} & DocumentMeta; + +export const articleDocumentPreferences = { + minW: 3, + minH: 1, + maxW: 5, + maxH: 4, + default: { + w: 5, + h: 3, + }, }; -export const Article: React.FC = ({ articles }) => { +export const Article: React.FC = ({ articles, document }) => { + document = document || articleDocumentPreferences.default; + const { width, height } = getDocumentSize(document); + + const withImage = document.w > 4; const documentId = Math.random().toString(36).substr(2, 9); + const rowHeight = height / articles.length; const innerPadding = 30; - const thumbnailWidth = 100; - const titleWidth = - Math.max( - ...articles.map((article) => - getTextWidth(article.meta.title, { fontSize: 16, fontWeight: 700 }) - ), - 400 - ) + 20; + const thumbnailWidth = withImage ? 100 : 0; + const titleWidth = Math.max( + ...articles.map((article) => + getTextWidth(article.meta.title, { fontSize: 16, fontWeight: 700 }) + ), + width - thumbnailWidth - 45 + ); const containerWidth = titleWidth + thumbnailWidth + innerPadding; return ( - + {articles.map((article, key: number) => { - const yOffset = 100 * key; - const xOffset = 120; + const yOffset = rowHeight * key; + const xOffset = thumbnailWidth + 20; const title = wrapText( article.meta.title, { maxLineWidth: titleWidth, fontSize: 16, fontWeight: 700 }, (line: string, index: number) => ( - + {line} ) @@ -64,7 +77,8 @@ export const Article: React.FC = ({ articles }) => { <> {key !== 0 && ( = ({ articles }) => { stroke="#dddddd" /> )} - - + + {title} {wrapText( article.meta.description.replace(/\n/gm, " "), { maxLineWidth: titleWidth - 20, fontSize: 12, - maxLines: 3, + maxLines: 2, }, (line: string, index) => ( = ({ articles }) => { )} - + = ({ articles }) => { {article.publish_date} - - - - {article.like_count} - - - - - - {article.reading_time_minutes} - - - + {(document?.w || articleDocumentPreferences.default.w) > + 3 && ( + <> + + + + {article.like_count} + + + + + + {article.reading_time_minutes} + + + + )} + = ({ articles }) => { - - - - - - + + + + + + + - - - + + )} ); })} @@ -183,12 +213,7 @@ export const Article: React.FC = ({ articles }) => { - + diff --git a/lib/@dsvgui/components/barstats/index.stories.tsx b/lib/@dsvgui/components/barstats/index.stories.tsx index 1eac1b1..322a8b7 100644 --- a/lib/@dsvgui/components/barstats/index.stories.tsx +++ b/lib/@dsvgui/components/barstats/index.stories.tsx @@ -1,16 +1,79 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { BarStats } from "./index"; +import { Grid } from "../../utils/grid"; +import { BarStats, barstatsDocumentPreferences, IBarStats } from "./index"; const meta: Meta = { title: "BarStats", component: BarStats, + args: { + title: "Latest efforts", + subtitle: "Last 30 Days", + document: { + w: 4, + h: 2, + ...barstatsDocumentPreferences, + }, + items: [ + { + key: "readme", + name: "readme", + value: 1, + }, + { + key: "bucketlist", + name: "bucketlist", + value: 90, + }, + ], + items_per_row: 2, + }, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Base: Story = { args: { + document: { + w: 4, + h: 2, + ...barstatsDocumentPreferences, + }, + title: "Latest efforts", + subtitle: "Last 30 Days", + items: [ + { + key: "readme", + name: "readme", + value: 1, + }, + { + key: "bucketlist", + name: "bucketlist", + value: 90, + }, + { + key: "project1", + name: "project1", + value: 90, + }, + { + key: "project2", + name: "project2", + value: 90, + }, + ], + items_per_row: 2, + }, +}; + +export const Compact: Story = { + args: { + document: { + w: 4, + h: 2, + ...barstatsDocumentPreferences, + }, title: "Latest efforts", subtitle: "Last 30 Days", items: [ @@ -24,7 +87,31 @@ export const Base: Story = { name: "bucketlist", value: 90, }, + { + key: "project1", + name: "project1", + value: 90, + }, + { + key: "project2", + name: "project2", + value: 90, + }, + { + key: "readme", + name: "readme", + value: 1, + }, + { + key: "bucketlist", + name: "bucketlist", + value: 90, + }, ], items_per_row: 2, }, }; + +export const WithGrid = () => { + return ; +}; diff --git a/lib/@dsvgui/components/barstats/index.tsx b/lib/@dsvgui/components/barstats/index.tsx index ffce66c..8d0d0c4 100644 --- a/lib/@dsvgui/components/barstats/index.tsx +++ b/lib/@dsvgui/components/barstats/index.tsx @@ -1,6 +1,8 @@ -import { Document, Text } from "@/lib/@dsvgui"; -import { stringToColorCode } from "@/lib/@dsvgui/utils"; -import { getTextWidth } from "../../utils/index"; +import React from "react"; + +import { Document, Text, getDocumentSize } from "../.."; +import { stringToColorCode } from "../../utils"; +import { DocumentMeta } from "../../document/type"; export type IBarStats = { title: string; @@ -11,6 +13,17 @@ export type IBarStats = { value: number; }>; items_per_row?: number; +} & DocumentMeta; + +export const barstatsDocumentPreferences = { + minW: 4, + minH: 2, + maxW: 5, + maxH: 2, + default: { + w: 5, + h: 2, + }, }; export const BarStats: React.FC = ({ @@ -18,17 +31,22 @@ export const BarStats: React.FC = ({ subtitle, items, items_per_row = 2, + document, }) => { - const width = Math.max( - getTextWidth(title, { fontSize: 22, fontWeight: 700 }), - 330 - ); - const totalBarWidth = width; - const legendMy = 20; + document = document || barstatsDocumentPreferences.default; + const { width, height } = getDocumentSize(document); + const totalBarWidth = width - 50; - const height = 70 + Math.ceil(items.length / items_per_row) * legendMy; + const legendMy = 20; const totalValue = items.reduce((acc, item) => acc + item.value, 0); + items_per_row = document.w <= 2 ? 1 : items_per_row; + const barHeight = 20; + + // if items_per_row is 2, slice it to 6 items + // if items_per_row is 1, slice it to 3 items + const slicedItems = items.slice(0, items_per_row * 3); + let tempBarWidth = 0; return ( @@ -53,11 +71,11 @@ export const BarStats: React.FC = ({ : 0; tempBarWidth += prevBarWidth; return ( - + @@ -67,15 +85,17 @@ export const BarStats: React.FC = ({ - {items.map((item, index) => { + {slicedItems.map((item, index) => { const circleSize = 6; const x = circleSize + (index % items_per_row ? width / items_per_row : 0); const y = - 80 + Math.floor(index / items_per_row) * legendMy + 5; + 80 + + (Math.floor(index / items_per_row) * legendMy + 5) + + barHeight; return ( - + = { title: "Calendar", @@ -21,7 +22,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; const produceFakeDates = () => { const today = Date.now(); @@ -35,23 +36,39 @@ const produceFakeDates = () => { }; const dates = produceFakeDates(); -export const Regular: Story = { +export const Base: Story = { args: { + document: { + w: 4, + h: 2, + ...calendarDocumentPreferences, + }, + title: "Calendar", + subtitle: "This is a Calendar component", dates, + weekCount: 26, }, }; -export const WithText: Story = { +export const Compact: Story = { args: { - title: "Calendar", - subtitle: "This is a Calendar component", + document: { + w: 4, + h: 1, + ...calendarDocumentPreferences, + }, dates, - weekCount: 52, + weekCount: 26, }, }; export const WithStreak: Story = { args: { + document: { + w: 4, + h: 2, + ...calendarDocumentPreferences, + }, title: "Calendar", subtitle: "This is a Calendar component", dates, @@ -59,3 +76,7 @@ export const WithStreak: Story = { showStreak: true, }, }; + +export const WithGrid = () => { + return ; +}; diff --git a/lib/@dsvgui/components/calendar/index.tsx b/lib/@dsvgui/components/calendar/index.tsx index 4d94224..6b59373 100644 --- a/lib/@dsvgui/components/calendar/index.tsx +++ b/lib/@dsvgui/components/calendar/index.tsx @@ -1,5 +1,8 @@ -import { Document, Text } from "@/lib/@dsvgui"; -import { generateColorVariations } from "@/lib/@dsvgui/utils"; +import React from "react"; + +import { Document, Text, getDocumentSize } from "../../document"; +import { generateColorVariations } from "../../utils"; +import { DocumentMeta } from "../../document/type"; export type ICalendar = { title?: string; @@ -9,6 +12,17 @@ export type ICalendar = { dates: { [key: string]: number }; showMonthLabels?: boolean; showStreak?: boolean; +} & DocumentMeta; + +export const calendarDocumentPreferences = { + minW: 3, + minH: 1, + maxW: 8, + maxH: 2, + default: { + w: 4, + h: 2, + }, }; export const Calendar: React.FC = ({ @@ -19,10 +33,20 @@ export const Calendar: React.FC = ({ dates, showMonthLabels = true, showStreak = false, + document, }) => { + document = document || calendarDocumentPreferences.default; + const { width, height } = getDocumentSize(document); + const isCompact = document.h === 1; + weekCount = document.w * 6.2; + + title = isCompact ? "" : title; + subtitle = isCompact ? "" : subtitle; + showStreak = isCompact ? false : showStreak; + const dayCount = 7; - const boxSize = 13; - const boxMargin = 5; + const boxMargin = 3; + const boxSize = (width - 30) / weekCount - boxMargin - (isCompact ? 4 : 0); const documentColorId = "_" + Math.random().toString(36).substr(2, 5); const today = Date.now(); @@ -48,10 +72,6 @@ export const Calendar: React.FC = ({ (title || showStreak ? 22 : 0) + ((title && subtitle) || showStreak ? 16 : 0) + (title || subtitle || showStreak ? 15 : 0); - const calendarHeight = dayCount * (boxSize + boxMargin); - - const width = weekCount * (boxSize + boxMargin) - boxMargin; - const height = calendarHeight + headerHeight + ((showMonthLabels && 15) || 0); const totalValue = Object.values(dates).reduce( (acc, value) => acc + value, @@ -125,10 +145,14 @@ export const Calendar: React.FC = ({ key={`${nthDay}`} id={`${dateString}`} data-value={dates[dateString] || 0} - x={(weekIndex - 1) * (boxSize + boxMargin)} - y={(dayIndex - 1) * (boxSize + boxMargin)} + x={ + (weekIndex - 1) * (boxSize + boxMargin) - (isCompact ? 5 : 0) + } + y={ + (dayIndex - 1) * (boxSize + boxMargin) - (isCompact ? 7.5 : 0) + } width={boxSize} - rx="3px" + rx="2px" height={boxSize} className={[color, "d"].filter(Boolean).join(" ")} /> @@ -143,12 +167,12 @@ export const Calendar: React.FC = ({ (_, i) => { const month = new Date(today - i * 30 * 24 * 60 * 60 * 1000); const monthString = month.toLocaleString("default", { month: "short" }); - const x = width - i * 4 * (boxSize + boxMargin + 1) - 25; + const x = (weekCount - i * 4) * (boxSize + boxMargin) - boxSize / 2 - 25; return ( 0 ? x : 0} - y={166} + y={170} option={{ size: 12, weight: 500 }} > {monthString} @@ -177,14 +201,14 @@ export const Calendar: React.FC = ({ {showStreak && ( <> width - textWidth} + x={(textWidth) => width - textWidth - 40} y={18} option="title" > {streak} width - textWidth} + x={(textWidth) => width - textWidth - 40} y={36} option="subtitle" > @@ -194,7 +218,25 @@ export const Calendar: React.FC = ({ )} )} - + {isCompact && ( + <> + width - textWidth - 40} + y={18} + option="title" + > + {streak} + + width - textWidth - 40} + y={36} + option="subtitle" + > + Streak + + + )} + {weeks} {showMonthLabels && ( diff --git a/lib/@dsvgui/components/fallback/index.stories.tsx b/lib/@dsvgui/components/fallback/index.stories.tsx index 4b80952..27b305a 100644 --- a/lib/@dsvgui/components/fallback/index.stories.tsx +++ b/lib/@dsvgui/components/fallback/index.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Fallback } from "./index"; +import { Grid } from "../../utils/grid"; +import { Fallback, IFallback, documentPreferences } from "./index"; const meta: Meta = { title: "Fallback", @@ -7,11 +8,20 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; -export const Warning: Story = { +export const Base: Story = { args: { + document: { + w: 4, + h: 1, + ...documentPreferences, + }, title: "Fallback", message: "This is a fallback component", }, }; + +export const WithGrid = () => { + return ; +}; diff --git a/lib/@dsvgui/components/fallback/index.tsx b/lib/@dsvgui/components/fallback/index.tsx index 4f0e023..8fcfa3b 100644 --- a/lib/@dsvgui/components/fallback/index.tsx +++ b/lib/@dsvgui/components/fallback/index.tsx @@ -1,15 +1,29 @@ import React from "react"; -import { Document, Text } from "@/lib/@dsvgui"; -import { wrapText } from "@/lib/@dsvgui/utils/index"; + +import { Document, Text, getDocumentSize } from "../../document"; +import { wrapText } from "../../utils"; import { IoWarningOutline } from "react-icons/io5"; +import { DocumentMeta } from "../../document/type"; -type IFallback = { +export type IFallback = { title: string; message: string; +} & DocumentMeta; + +export const documentPreferences = { + minW: 3, + minH: 1, + maxW: 4, + maxH: 1, + default: { + w: 3, + h: 1, + }, }; -export const Fallback: React.FC = ({ title, message }) => { - const width = 450; +export const Fallback: React.FC = ({ title, message, document }) => { + document = document || documentPreferences.default; + const { height, width } = getDocumentSize(document); const subtitle = wrapText( message, @@ -21,8 +35,6 @@ export const Fallback: React.FC = ({ title, message }) => { ) ); - const height = 25 + subtitle.length * 16; - return ( = { title: "Flock", @@ -16,7 +19,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; const produceMockImages = (n: number, width: number, height: number) => { return Array.from({ length: n }).map((_, key) => ({ @@ -31,10 +34,14 @@ const produceMockImages = (n: number, width: number, height: number) => { export const Base: Story = { args: { + document: { + w: 4, + h: 1, + ...flockDocumentPreferences, + }, title: "Flock", subtitle: "This is a flock component", - items_per_row: 5, - members: produceMockImages(4, 150, 150), + members: produceMockImages(16, 5, 5), }, loaders: [ async ({ args }) => { @@ -45,3 +52,28 @@ export const Base: Story = { }, ], }; + +export const WithText: Story = { + args: { + document: { + w: 4, + h: 2, + ...flockDocumentPreferences, + }, + title: "Flock", + subtitle: "This is a flock component", + members: produceMockImages(16, 5, 5), + }, + loaders: [ + async ({ args }) => { + const images = await Promise.all( + args.members.map((member) => readImageURL(member.image.value)) + ); + return { images }; + }, + ], +}; + +export const WithGrid = () => { + return ; +}; diff --git a/lib/@dsvgui/components/flock/index.tsx b/lib/@dsvgui/components/flock/index.tsx index 16ae7a1..690f78b 100644 --- a/lib/@dsvgui/components/flock/index.tsx +++ b/lib/@dsvgui/components/flock/index.tsx @@ -1,10 +1,11 @@ -import { Document, Text } from "@/lib/@dsvgui"; -import { getTextWidth } from "@/lib/@dsvgui/utils"; +import React from "react"; + +import { Document, getDocumentSize, Text } from "../../document"; +import { DocumentMeta } from "../../document/type"; export type IFlock = { title?: string; subtitle?: string; - items_per_row: number; members: Array<{ image: { value: string; @@ -13,52 +14,54 @@ export type IFlock = { }; caption: string; }>; +} & DocumentMeta; + +export const flockDocumentPreferences = { + minW: 3, + minH: 1, + maxW: 8, + maxH: 8, + default: { + w: 4, + h: 2, + }, }; export const Flock: React.FC = ({ + document, title, subtitle, - items_per_row, members, }) => { - const circleSize = 50; + document = document || flockDocumentPreferences.default; + const { height, width } = getDocumentSize(document); + const isCompact = document.h === 1; + + title = isCompact ? "" : title; + subtitle = isCompact ? "" : subtitle; + + const circleSize = 45; const circleGap = 5; - const numRows = Math.ceil(members.length / items_per_row); const titleFontSize = title ? 22 : 0; const subtitleFontSize = subtitle ? 16 : 0; - const headStart = { x: 0, y: 20 }; + const headStart = { x: 0, y: isCompact ? 0 : 20 }; const boxStart = { x: circleSize / 2, y: headStart.y + titleFontSize + subtitleFontSize + circleSize / 2, }; - const titleWidth = title - ? getTextWidth(title.trim(), { fontSize: titleFontSize, fontWeight: 700 }) - : 0; - const subtitleWidth = subtitle - ? getTextWidth(subtitle.trim(), { - fontSize: subtitleFontSize, - fontWeight: 500, - }) - : 0; + const itemsPerRow = Math.floor(width / (circleSize + circleGap)); - const documentWidth = Math.max( - titleWidth, - subtitleWidth, - (members.length > items_per_row ? items_per_row : members.length) * - (circleSize + circleGap) + members = members.slice( + 0, + itemsPerRow * Math.floor((height - boxStart.y) / (circleSize + circleGap)) ); - const documentHeight = - headStart.y + - titleFontSize + - subtitleFontSize + - numRows * (circleSize + circleGap); const documentId = Math.random().toString(36).substr(2, 9); return ( - + {title && ( {title.trim()} @@ -70,8 +73,8 @@ export const Flock: React.FC = ({ )} {members.map((member, index) => { - const row = Math.floor(index / items_per_row); - const col = index % items_per_row; + const row = Math.floor(index / itemsPerRow); + const col = index % itemsPerRow; return ( <> diff --git a/lib/@dsvgui/components/line/index.stories.tsx b/lib/@dsvgui/components/line/index.stories.tsx index 7e378e8..762e0e1 100644 --- a/lib/@dsvgui/components/line/index.stories.tsx +++ b/lib/@dsvgui/components/line/index.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Line } from "./index"; +import { Grid } from "../../utils/grid"; +import { ILine, Line, documentPreferences } from "./index"; const meta: Meta = { title: "Line", @@ -14,18 +15,23 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; const randomPoints = (length: number) => { - const points = []; + const points: Array = []; for (let i = 0; i < length; i++) { points.push(Math.round(Math.random() * 50)); } return points; }; -export const Minimal: Story = { +export const Compact: Story = { args: { + document: { + w: 4, + h: 1, + ...documentPreferences, + }, items: [ { leftTitle: "Line", @@ -38,6 +44,11 @@ export const Minimal: Story = { export const Colored: Story = { args: { + document: { + w: 4, + h: 2, + ...documentPreferences, + }, items: [ { leftTitle: "Line", @@ -51,6 +62,11 @@ export const Colored: Story = { export const WithSecondaryTitle: Story = { args: { + document: { + w: 4, + h: 2, + ...documentPreferences, + }, items: [ { leftTitle: "Line", @@ -66,6 +82,11 @@ export const WithSecondaryTitle: Story = { export const WithPeriodLabels: Story = { args: { + document: { + w: 4, + h: 2, + ...documentPreferences, + }, items: [ { leftTitle: "Line", @@ -80,6 +101,11 @@ export const WithPeriodLabels: Story = { export const MultipleLines: Story = { args: { + document: { + w: 4, + h: 6, + ...documentPreferences, + }, items: [ { leftTitle: "Line", @@ -111,3 +137,18 @@ export const MultipleLines: Story = { ], }, }; + +export const WithGrid = () => { + return ( + + ); +}; diff --git a/lib/@dsvgui/components/line/index.tsx b/lib/@dsvgui/components/line/index.tsx index 4f4ad8d..55ca8f2 100644 --- a/lib/@dsvgui/components/line/index.tsx +++ b/lib/@dsvgui/components/line/index.tsx @@ -1,6 +1,10 @@ -import { Document } from "@/lib/@dsvgui"; -import { getTextWidth, hexToRgb } from "@/lib/@dsvgui/utils/index"; +import React from "react"; + +import { Document } from "../../document"; +import { hexToRgb } from "../../utils"; import { Text } from "../../document/text"; +import { DocumentMeta } from "../../document/type"; +import { getDocumentSize } from "../../document/document"; export type ILineItem = { leftTitle?: string; @@ -12,18 +16,30 @@ export type ILineItem = { period?: "month" | "weekday" | "day"; }; -type ILine = { +export type ILine = { items: Array; +} & DocumentMeta; + +export const documentPreferences = { + minW: 3, + minH: 1, + maxW: 9, + maxH: 6, + default: { + w: 5, + h: 2, + }, }; -export const Line: React.FC = ({ items }) => { +export const Line: React.FC = ({ items, document }) => { + document = document || documentPreferences.default; + const { height, width } = getDocumentSize(document); + const documentPadding = 40; const today = Date.now(); - const pointGap = 30; - const rowHeight = 140; - const rowGap = 60; - const height = rowHeight * items.length + rowGap * (items.length - 1); + const rowHeight = height / items.length; + const rowGap = 60; function createDynamicSvgPath( item: ILine["items"][0], @@ -66,41 +82,11 @@ export const Line: React.FC = ({ items }) => { return path; } - const widths = items.map((item) => { - const leftTitleWidth = getTextWidth(item.leftTitle || "", { - fontSize: 22, - fontWeight: 700, - }); - const leftSubtitleWidth = getTextWidth(item.leftSubtitle || "", { - fontSize: 16, - fontWeight: 500, - }); - const leftTitlesWidth = Math.max(leftTitleWidth, leftSubtitleWidth); - - const rightTitleWidth = getTextWidth(item.rightTitle || "", { - fontSize: 22, - fontWeight: 700, - }); - const rightSubtitleWidth = getTextWidth(item.rightSubtitle || "", { - fontSize: 16, - fontWeight: 500, - }); - const rightTitlesWidth = Math.max(rightTitleWidth, rightSubtitleWidth); - - const totalTextWidth = leftTitlesWidth + rightTitlesWidth + 50; - const totalPointWidth = - item.points.length * pointGap < 100 ? 100 : item.points.length * pointGap; - const totalWidth = - totalPointWidth > totalTextWidth ? totalPointWidth : totalTextWidth; - return totalWidth + documentPadding; - }); - - const width = Math.max(...widths); const _items = items.map((item, index) => { const pathValue = createDynamicSvgPath(item, { - lineHeight: rowHeight, - lineWidth: width, - ratio: 0.6, + lineHeight: rowHeight - 40, + lineWidth: width - 30, + ratio: 0.65, }); let labels: Array = []; @@ -137,7 +123,7 @@ export const Line: React.FC = ({ items }) => { }); } return ( - + = ({ items }) => { - width - textWidth} y={15}> + width - textWidth - documentPadding} + y={15} + > {item.rightTitle} - width - textWidth} y={34}> + width - textWidth - documentPadding} + y={34} + > {item.rightSubtitle} + {item.period && ( - + {labels} )} @@ -189,7 +187,7 @@ export const Line: React.FC = ({ items }) => { = { title: "Metrics", @@ -10,14 +10,51 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Base: Story = { args: { + document: { + w: 3, + h: 1, + ...metricsDocumentPreferences, + }, icon: SiCodewars, data: [ - { title: "Honor", value: 4 }, - { title: "Rank", value: 24 }, + { title: "Honor", value: "347" }, + { title: "Rank", value: "5 kyu" }, ], }, }; + +export const Multiple: Story = { + args: { + document: { + w: 4, + h: 1, + ...metricsDocumentPreferences, + }, + icon: SiGithub, + data: [ + { title: "Commits", value: 846 }, + { title: "PRs", value: 143 }, + { title: "Issues", value: 92 }, + ], + }, +}; + +export const Long: Story = { + args: { + document: { + w: 4, + h: 1, + ...metricsDocumentPreferences, + }, + icon: SiWakatime, + data: [{ title: "All time since today", value: "997 hrs 49 mins" }], + }, +}; + +export const WithGrid = () => { + return ; +}; diff --git a/lib/@dsvgui/components/metrics/index.tsx b/lib/@dsvgui/components/metrics/index.tsx index 59b567a..1e9129b 100644 --- a/lib/@dsvgui/components/metrics/index.tsx +++ b/lib/@dsvgui/components/metrics/index.tsx @@ -1,65 +1,56 @@ -import { Document } from "@/lib/@dsvgui"; -import { Text } from "@/lib/@dsvgui/document/text"; -import { Fragment } from "react"; -import { getTextWidth } from "@/lib/@dsvgui/utils"; +import React, { Fragment } from "react"; + +import { Document, getDocumentSize } from "../../document"; +import { Text } from "../../document/text"; import { IconType } from "react-icons/lib"; +import { DocumentMeta } from "../../document/type"; -type IMetrics = { +export type IMetrics = { icon: IconType; data: Array<{ title: string; value: number | string; }>; +} & DocumentMeta; + +export const metricsDocumentPreferences = { + minH: 1, + maxH: 1, + minW: 2, + maxW: 5, + default: { + w: 4, + h: 1, + }, }; -export const Metrics: React.FC = ({ data, icon: Icon }) => { - const padding = 35; - let initialPadding = 15; - - const width = data.reduce((acc, item) => { - const wValue = getTextWidth(item.value.toString(), { - fontSize: 22, - fontWeight: 700, - }); - const wTitle = getTextWidth(item.title.toString(), { - fontSize: 16, - fontWeight: 500, - }); - const wText = Math.max(wValue, wTitle); +export const Metrics: React.FC = ({ data, document, icon: Icon }) => { + document = document || { h: 1, w: data.length + 1 }; - return acc + wText + padding; - }, padding + initialPadding); + const { width, height } = getDocumentSize(document); + let initialPadding = 20; + const padding = (width - 45 - 20) / data.length; return ( - - - - + + + {data.map((item, index) => { - const wTitle = getTextWidth(item.title.toString(), { - fontSize: 16, - fontWeight: 500, - }); - const wValue = getTextWidth(item.value.toString(), { - fontSize: 22, - fontWeight: 700, - }); - const wText = Math.max(wValue, wTitle); - const xText = initialPadding; - initialPadding += wText + padding; + initialPadding += padding; + const xLine = xText - 10; return ( - + {item.title} - + {item.value.toString()} {index !== 0 && ( - + )} ); diff --git a/lib/@dsvgui/components/placeholder/index.stories.tsx b/lib/@dsvgui/components/placeholder/index.stories.tsx new file mode 100644 index 0000000..3ca2aff --- /dev/null +++ b/lib/@dsvgui/components/placeholder/index.stories.tsx @@ -0,0 +1,97 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Grid } from "../../utils/grid"; +import { documentPreferences, Placeholder } from "./index"; + +const meta: Meta = { + title: "Placeholder", + component: Placeholder, +}; +export default meta; + +type Story = StoryObj; + +export const Size1x1: Story = { + args: { + document: { + w: 1, + h: 1, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const Size2x1: Story = { + args: { + document: { + w: 2, + h: 1, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const Size1x2: Story = { + args: { + document: { + w: 1, + h: 2, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const Size2x2: Story = { + args: { + document: { + w: 2, + h: 2, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const Size3x2: Story = { + args: { + document: { + w: 3, + h: 2, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const Size2x3: Story = { + args: { + document: { + w: 2, + h: 3, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const Size3x3: Story = { + args: { + document: { + w: 3, + h: 3, + ...documentPreferences, + }, + }, + loaders: [], +}; + +export const WithGrid = () => { + return ( + + ); +}; diff --git a/lib/@dsvgui/components/placeholder/index.tsx b/lib/@dsvgui/components/placeholder/index.tsx new file mode 100644 index 0000000..edb0197 --- /dev/null +++ b/lib/@dsvgui/components/placeholder/index.tsx @@ -0,0 +1,36 @@ +import React from "react"; + +import { Document, getDocumentSize, Text } from "../../document"; +import { DocumentMeta } from "../../document/type"; +import { getTextWidth } from "../../utils"; + +export type IPlaceholder = {} & DocumentMeta; + +export const documentPreferences = { + minW: 1, + minH: 1, + maxW: 3, + maxH: 3, + default: { + w: 1, + h: 1, + }, +}; + +export const Placeholder: React.FC = ({ document }) => { + document = document || documentPreferences.default; + const { height, width } = getDocumentSize(document); + + const textWidth = getTextWidth(`${document.w}x${document.h}`, { + fontSize: 16, + fontWeight: 500, + }); + + return ( + + + {String(`${document.w}x${document.h}`)} + + + ); +}; diff --git a/lib/@dsvgui/components/progress/index.stories.tsx b/lib/@dsvgui/components/progress/index.stories.tsx index 35125c8..4fa0242 100644 --- a/lib/@dsvgui/components/progress/index.stories.tsx +++ b/lib/@dsvgui/components/progress/index.stories.tsx @@ -1,9 +1,10 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Progress } from "./index"; +import { progressDocumentPreferences, IProgress, Progress } from "./index"; import { AiOutlineCalendar } from "react-icons/ai"; import { HiChartBar } from "react-icons/hi"; import { VscIssues, VscGitPullRequest } from "react-icons/vsc"; +import { Grid } from "../../utils/grid"; const meta: Meta = { title: "Progress", @@ -11,10 +12,15 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Base: Story = { args: { + document: { + w: 4, + h: 2, + ...progressDocumentPreferences, + }, title: "Progress", percent: 55, metrics: [ @@ -25,3 +31,45 @@ export const Base: Story = { ], }, }; + +export const OnlyFirstLine: Story = { + args: { + document: { + w: 3, + h: 2, + ...progressDocumentPreferences, + }, + title: "Progress", + percent: 55, + metrics: [ + { icon: HiChartBar, text: "%51 completed" }, + { icon: AiOutlineCalendar, text: "Due by April 17, 2023" }, + { icon: VscIssues, text: "7/19 issues solved" }, + { icon: VscGitPullRequest, text: "8/10 PRs closed" }, + ], + }, +}; + +export const OnlyProgress: Story = { + args: { + document: { + w: 4, + h: 1, + ...progressDocumentPreferences, + }, + title: "Progress", + percent: 55, + metrics: [ + { icon: HiChartBar, text: "%51 completed" }, + { icon: AiOutlineCalendar, text: "Due by April 17, 2023" }, + { icon: VscIssues, text: "7/19 issues solved" }, + { icon: VscGitPullRequest, text: "8/10 PRs closed" }, + ], + }, +}; + +export const WithGrid = () => { + return ( + + ); +}; diff --git a/lib/@dsvgui/components/progress/index.tsx b/lib/@dsvgui/components/progress/index.tsx index d9838d0..e4b5c03 100644 --- a/lib/@dsvgui/components/progress/index.tsx +++ b/lib/@dsvgui/components/progress/index.tsx @@ -1,5 +1,9 @@ -import { Document, Text } from "@/lib/@dsvgui"; +import React from "react"; + +import { Document, Text } from "../../document"; import { IconType } from "react-icons/lib"; +import { DocumentMeta } from "../../document/type"; +import { getDocumentSize } from "../../document/document"; export type IProgress = { title: string; @@ -8,45 +12,82 @@ export type IProgress = { text: string; icon: IconType; }>; +} & DocumentMeta; + +export const progressDocumentPreferences = { + minW: 3, + minH: 1, + maxW: 5, + maxH: 2, + default: { + w: 5, + h: 2, + }, }; -export const Progress: React.FC = ({ title, percent, metrics }) => { - const metricsOffsetX = 230; +export const Progress: React.FC = ({ + title, + percent, + metrics, + document, +}) => { + document = document || progressDocumentPreferences.default; + const { height, width } = getDocumentSize(document); + + const metricsOffsetX = width / 2 - 20; const metricsOffsetY = 30; - const height = 70 + Math.ceil(metrics.length / 2) * 30; + const showLabels = document.h > 1; + const showAllLabels = document.w > 3; return ( - - + + {title} - + - {metrics.map((metric, index) => ( - - - - {metric.text} - - - ))} + {(showLabels && + metrics.map((metric, index) => { + if (!showAllLabels && index > 1) return <>; + const horizontalSplit = `translate(${ + index % 2 ? metricsOffsetX : 0 + } ${105 + Math.floor(index / 2) * metricsOffsetY})`; + const verticalSplit = `translate(0 ${ + 105 + Math.floor(index) * metricsOffsetY + })`; + + return ( + + + + {metric.text} + + + ); + })) || + ""} ); }; diff --git a/lib/@dsvgui/document/branding.tsx b/lib/@dsvgui/document/branding.tsx index 2d9e7c2..bccaa23 100644 --- a/lib/@dsvgui/document/branding.tsx +++ b/lib/@dsvgui/document/branding.tsx @@ -1,4 +1,6 @@ -import { Text } from "@/lib/@dsvgui/"; +import React from "react"; + +import { Text } from ".."; type IBranding = { x: number; diff --git a/lib/@dsvgui/document/container.tsx b/lib/@dsvgui/document/container.tsx index 6bea9c9..70c8706 100644 --- a/lib/@dsvgui/document/container.tsx +++ b/lib/@dsvgui/document/container.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import React, { ReactNode } from "react"; export type IContainer = { children?: ReactNode; diff --git a/lib/@dsvgui/document/document.tsx b/lib/@dsvgui/document/document.tsx index 9e5f84d..789945f 100644 --- a/lib/@dsvgui/document/document.tsx +++ b/lib/@dsvgui/document/document.tsx @@ -1,5 +1,5 @@ -import { ReactNode, SVGAttributes } from "react"; -import { Style, Container, Branding } from "@/lib/@dsvgui"; +import React, { ReactNode, SVGAttributes } from "react"; +import { Style, Container, Branding } from ".."; import { getTextWidth } from "../utils"; export type IDocumentProps = SVGAttributes & { @@ -11,14 +11,37 @@ export type IDocumentProps = SVGAttributes & { useBranding?: boolean; }; +export const getDocumentSize = ({ + w, + h, + margin = 10, + unitHeight = 90, + unitWidth = 90, +}: { + w: number; + h: number; + margin?: number; + unitHeight?: number; + unitWidth?: number; +}) => { + return { + width: w * unitWidth + margin * (w - 1), + height: h * unitHeight + margin * (h - 1), + }; +}; + export const Document: React.FC = (props) => { - const { w, h, useBranding = true, ...rest } = props; + let { w, h } = props; + const { useBranding = true, ...rest } = props; const documentId = Math.random().toString(36).substr(2, 9); const padding = rest.padding ?? 40; const margin = rest.margin ?? 4; const brand = "readme.rocks"; + w -= margin + padding; + h -= margin + padding; + return ( { return ( diff --git a/lib/@dsvgui/document/text.tsx b/lib/@dsvgui/document/text.tsx index e68b9ad..856f4fc 100644 --- a/lib/@dsvgui/document/text.tsx +++ b/lib/@dsvgui/document/text.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { FontFamily, defaultFont } from "../document/fonts"; import { loadOpenTypeFont } from "../utils/fonts"; diff --git a/lib/@dsvgui/document/type.ts b/lib/@dsvgui/document/type.ts new file mode 100644 index 0000000..74353a5 --- /dev/null +++ b/lib/@dsvgui/document/type.ts @@ -0,0 +1,6 @@ +export type DocumentMeta = { + document?: { + w: number; + h: number; + }; +}; diff --git a/lib/@dsvgui/package-lock.json b/lib/@dsvgui/package-lock.json index cbb63df..9e09a1b 100644 --- a/lib/@dsvgui/package-lock.json +++ b/lib/@dsvgui/package-lock.json @@ -26,7 +26,8 @@ }, "devDependencies": { "@storybook/addon-mdx-gfm": "^7.0.6", - "@types/opentype.js": "^1.3.4" + "@types/opentype.js": "^1.3.4", + "@types/react-grid-layout": "^1.3.5" } }, "node_modules/@ampproject/remapping": { @@ -5016,6 +5017,15 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-grid-layout": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-1.3.5.tgz", + "integrity": "sha512-WH/po1gcEcoR6y857yAnPGug+ZhkF4PaTUxgAbwfeSH/QOgVSakKHBXoPGad/sEznmkiaK3pqHk+etdWisoeBQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", diff --git a/lib/@dsvgui/package.json b/lib/@dsvgui/package.json index af4ca43..ec6a75a 100644 --- a/lib/@dsvgui/package.json +++ b/lib/@dsvgui/package.json @@ -29,6 +29,7 @@ }, "devDependencies": { "@storybook/addon-mdx-gfm": "^7.0.6", - "@types/opentype.js": "^1.3.4" + "@types/opentype.js": "^1.3.4", + "@types/react-grid-layout": "^1.3.5" } } diff --git a/lib/@dsvgui/postcss.config.js b/lib/@dsvgui/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/lib/@dsvgui/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/lib/@dsvgui/tailwind.config.js b/lib/@dsvgui/tailwind.config.js new file mode 100644 index 0000000..9652acf --- /dev/null +++ b/lib/@dsvgui/tailwind.config.js @@ -0,0 +1,20 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./app/**/*.{js,ts,jsx,tsx}", + "./pages/**/*.{js,ts,jsx,tsx}", + "./components/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + backgroundImage: (theme) => ({ + "dark-lp-gradient": + "linear-gradient(38deg, rgba(34,34,34,1) 0%, rgba(19,21,23,1) 12%, rgba(26,25,37,1) 32%, rgba(35,45,64,1) 64%, rgba(50,50,84,1) 100%)", + "light-lp-gradient": + "linear-gradient(38deg, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 21%, rgba(250,251,255,1) 31%, rgba(166,181,255,1) 95%, rgba(172,136,255,1) 100%)", + }), + }, + }, + variants: {}, + plugins: [], +}; diff --git a/lib/@dsvgui/utils/grid.tsx b/lib/@dsvgui/utils/grid.tsx new file mode 100644 index 0000000..8b9a6fe --- /dev/null +++ b/lib/@dsvgui/utils/grid.tsx @@ -0,0 +1,59 @@ +import { ReactRenderer } from "@storybook/react"; +import { StoryAnnotations } from "@storybook/types"; +import { useState } from "react"; + +import GridLayout from "react-grid-layout"; +import { DocumentMeta } from "../document/type"; + +export const Grid = ({ + component: Component, + stories, +}: { + component: React.FC; + stories: Array>>; +}) => { + const componentSizes: Array = stories.map((story, i) => { + const { document } = story.args as T; + return { + ...(document || { w: 4, h: 2 }), + x: 0, + y: stories + .slice(0, i) + .reduce( + (acc, c) => + acc + ((c.args as T).document as { w: number; h: number }).h, + 0 + ), + i: i.toString(), + }; + }, []); + + const [layout, setLayout] = + useState>(componentSizes); + + const onLayoutChange = (newLayout: Array) => { + setLayout(newLayout); + }; + + return ( + + {stories.map((story, i) => { + const document = layout[i]; + return ( +
+ +
+ ); + })} + + ); +}; diff --git a/package-lock.json b/package-lock.json index 721a8bc..bd4cb93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "qs": "^6.11.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-grid-layout": "^1.4.4", "react-icons": "^4.8.0", "tailwind-merge": "^1.10.0", "yup": "^1.0.2" @@ -6261,6 +6262,11 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, + "node_modules/fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==" + }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -10564,6 +10570,19 @@ "react": "^18.2.0" } }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-element-to-jsx-string": { "version": "15.0.0", "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz", @@ -10583,6 +10602,31 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==" }, + "node_modules/react-grid-layout": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.4.4.tgz", + "integrity": "sha512-7+Lg8E8O8HfOH5FrY80GCIR1SHTn2QnAYKh27/5spoz+OHhMmEhU/14gIkRzJOtympDPaXcVRX/nT1FjmeOUmQ==", + "dependencies": { + "clsx": "^2.0.0", + "fast-equals": "^4.0.3", + "prop-types": "^15.8.1", + "react-draggable": "^4.4.5", + "react-resizable": "^3.0.5", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, + "node_modules/react-grid-layout/node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/react-icons": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", @@ -10642,6 +10686,18 @@ } } }, + "node_modules/react-resizable": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.5.tgz", + "integrity": "sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==", + "dependencies": { + "prop-types": "15.x", + "react-draggable": "^4.0.3" + }, + "peerDependencies": { + "react": ">= 16.3" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -10976,6 +11032,11 @@ "lodash": "^4.17.21" } }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", diff --git a/package.json b/package.json index bf8da21..ca51623 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "qs": "^6.11.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-grid-layout": "^1.4.4", "react-icons": "^4.8.0", "tailwind-merge": "^1.10.0", "yup": "^1.0.2" diff --git a/platforms/dailydev/view/index.tsx b/platforms/dailydev/view/index.tsx index aadd083..72e0138 100644 --- a/platforms/dailydev/view/index.tsx +++ b/platforms/dailydev/view/index.tsx @@ -37,5 +37,7 @@ export const listArticles: ViewComponent = async (result, config) => { } ); - return
; + return ( +
+ ); }; diff --git a/platforms/devto/view/index.tsx b/platforms/devto/view/index.tsx index 54eacb8..75bb8d8 100644 --- a/platforms/devto/view/index.tsx +++ b/platforms/devto/view/index.tsx @@ -34,5 +34,7 @@ export const listArticles: ViewComponent = async (result, config) => { } ); - return
; + return ( +
+ ); }; diff --git a/platforms/github/view/index.tsx b/platforms/github/view/index.tsx index d63ea1e..e6a91be 100644 --- a/platforms/github/view/index.tsx +++ b/platforms/github/view/index.tsx @@ -182,7 +182,7 @@ export const getUserCommitStreak: ViewComponent = (result, config) => { }; export const getContributors: ViewComponent = async (result, config) => { - const { title, subtitle, items_per_row } = config.viewConfig as any; + const { title, subtitle } = config.viewConfig as any; const contributors = result.data.repository.mentionableUsers.nodes; @@ -215,18 +215,11 @@ export const getContributors: ViewComponent = async (result, config) => { }) ); - return ( - - ); + return ; }; export const getUserSponsorList: ViewComponent = async (result, config) => { - const { title, subtitle, items_per_row } = config.viewConfig as any; + const { title, subtitle } = config.viewConfig as any; const { nodes: sponsors } = result.data.user.sponsorshipsAsMaintainer; @@ -259,12 +252,5 @@ export const getUserSponsorList: ViewComponent = async (result, config) => { }) ); - return ( - - ); + return ; }; diff --git a/platforms/leetcode/view/index.tsx b/platforms/leetcode/view/index.tsx index c7bcb6b..c8cef46 100644 --- a/platforms/leetcode/view/index.tsx +++ b/platforms/leetcode/view/index.tsx @@ -10,7 +10,7 @@ export const getUser: ViewComponent = (result, config) => { data={[ { title: "Reputation", value: user.profile.reputation }, { title: "Ranking", value: user.profile.ranking }, - { title: "Post view count", value: user.profile.postViewCount }, + { title: "View count", value: user.profile.postViewCount }, ]} /> ); diff --git a/platforms/wakatime/view/index.tsx b/platforms/wakatime/view/index.tsx index 048778d..f0a15fd 100644 --- a/platforms/wakatime/view/index.tsx +++ b/platforms/wakatime/view/index.tsx @@ -5,6 +5,7 @@ import { Metrics, Line, BarStats, IBarStats, ILineItem } from "@/lib/@dsvgui"; export const getAllTimeSinceToday: ViewComponent = (result, config) => { return (