diff --git a/apps/cyberstorm-remix/app/Markdown.module.css b/apps/cyberstorm-remix/app/Markdown.module.css index ef2dcc0fe..0bd24531d 100644 --- a/apps/cyberstorm-remix/app/Markdown.module.css +++ b/apps/cyberstorm-remix/app/Markdown.module.css @@ -103,6 +103,8 @@ font-family: var(--font-family-monospace); font-style: normal; line-height: 150%; + white-space: break-spaces; + overflow-wrap: break-word; background: #0d0d21; } diff --git a/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css b/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css index 01dc213d6..495c68501 100644 --- a/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css +++ b/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css @@ -12,12 +12,6 @@ .nimbus-commoncomponents-listingdependency__image { flex-shrink: 0; width: 80px; - - > svg { - width: 40px; - height: 40px; - color: var(--color-surface-a10); - } } .nimbus-commoncomponents-listingdependency__info { diff --git a/apps/cyberstorm-remix/app/p/packageListing.css b/apps/cyberstorm-remix/app/p/packageListing.css index b3cd5c7f7..96639601a 100644 --- a/apps/cyberstorm-remix/app/p/packageListing.css +++ b/apps/cyberstorm-remix/app/p/packageListing.css @@ -10,6 +10,7 @@ flex-direction: column; flex-grow: 1; gap: var(--space-32); + min-width: 0; } .nimbus-packagelisting__sidebar { diff --git a/apps/cyberstorm-remix/app/p/tabs/Required/Required.css b/apps/cyberstorm-remix/app/p/tabs/Required/Required.css index f54a62539..26193817e 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Required/Required.css +++ b/apps/cyberstorm-remix/app/p/tabs/Required/Required.css @@ -1,29 +1,29 @@ .nimbus-packagelisting__tabs__required { display: flex; flex-direction: column; - gap: 1rem; + gap: var(--gap-md); } .nimbus-packagelisting__tabs__required__title { display: flex; flex-direction: column; - gap: 16px; + gap: var(--gap-md); align-items: flex-start; align-self: stretch; - padding-bottom: 24px; + padding-bottom: var(--space-24); } .nimbus-packagelisting__tabs__required__title__description { color: var(--color-text-secondary); - font-weight: 400; - line-height: 150%; + font-weight: var(--font-weight-regular); + line-height: var(--line-height-md); } .nimbus-packagelisting__tabs__required__body { display: flex; flex-direction: column; - gap: 2px; + gap: var(--gap-xxxs); align-items: flex-start; align-self: stretch; - padding-bottom: 32px; + padding-bottom: var(--space-32); } diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.css b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.css new file mode 100644 index 000000000..95e62de8c --- /dev/null +++ b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.css @@ -0,0 +1,34 @@ +.nimbus-packagelisting__tabs__versions { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.nimbus-packagelisting__tabs__versions__actionscell { + display: flex; + gap: 24px; + align-items: flex-start; + align-self: stretch; + justify-content: flex-end; +} + +.nimbus-packagelisting__tabs__versions__columns__version { + color: var(--color-text-primary); + font-weight: var(--font-weight-bold); + font-size: var(--font-size-body-md); + line-height: var(--line-height-md); +} + +.nimbus-packagelisting__tabs__versions__columns__uploaddate { + color: var(--color-text-tertiary); + font-weight: var(--font-weight-regular); + font-size: var(--font-size-body-md); + line-height: var(--line-height-md); +} + +.nimbus-packagelisting__tabs__versions__columns__downloads { + color: var(--color-text-tertiary); + font-weight: var(--font-weight-regular); + font-size: var(--font-size-body-md); + line-height: var(--line-height-md); +} diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.module.css b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.module.css deleted file mode 100644 index ba22c2837..000000000 --- a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.main { - display: flex; - flex-direction: column; - gap: 1rem; -} diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx index 2690ffe18..febadeffc 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx @@ -1,9 +1,18 @@ -import { faCircleExclamation } from "@fortawesome/free-solid-svg-icons"; +import { faDownload } from "@fortawesome/free-solid-svg-icons"; import { faBoltLightning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import styles from "./Versions.module.css"; -import { Table, Sort, Alert, Button } from "@thunderstore/cyberstorm"; +import "./Versions.css"; +import { + NewTableSort, + NewButton, + NewIcon, + NewTable, + NewTableRows, + NewTableLabels, + Heading, + NewAlert, +} from "@thunderstore/cyberstorm"; import { LoaderFunctionArgs } from "@remix-run/node"; import { getDapper } from "cyberstorm/dapper/sessionUtils"; import { ApiError } from "@thunderstore/thunderstore-api"; @@ -83,73 +92,99 @@ export default function Versions() { return
{message}
; } - const tableRows = versions.map((v) => [ + const tableRows: NewTableRows = versions.map((v) => [ { value: v.version_number, sortValue: v.version_number }, { value: new Date(v.datetime_created).toUTCString(), sortValue: v.datetime_created, }, { value: v.download_count.toLocaleString(), sortValue: v.download_count }, - { value: , sortValue: 0 }, - { value: , sortValue: 0 }, + { + value: ( +
+ + +
+ ), + sortValue: 0, + }, ]); return ( -
+
- + Versions + + } headers={columns} rows={tableRows} - sortDirection={Sort.ASC} - variant="itemListSmall" + sortDirection={NewTableSort.ASC} /> ); } -const columns = [ - { value: "Version", disableSort: false }, - { value: "Upload date", disableSort: false }, - { value: "Downloads", disableSort: false }, - { value: "", disableSort: true }, - { value: "", disableSort: true }, +const columns: NewTableLabels = [ + { + value: "Version", + disableSort: false, + columnClasses: "nimbus-packagelisting__tabs__versions__columns__version", + }, + { + value: "Upload date", + disableSort: false, + columnClasses: "nimbus-packagelisting__tabs__versions__columns__uploaddate", + }, + { + value: "Downloads", + disableSort: false, + columnClasses: "nimbus-packagelisting__tabs__versions__columns__downloads", + }, + { value: "Actions", disableSort: true }, ]; const ModManagerBanner = () => ( - - Please note that the install buttons only work if you have compatible - client software installed, such as the{" "} - - Thunderstore Mod Manager. - {" "} - Otherwise use the zip download links instead. - - } - variant={"info"} - icon={} - /> + + Please note that the install buttons only work if you have compatible client + software installed, such as the{" "} + + Thunderstore Mod Manager. + {" "} + Otherwise use the zip download links instead. + ); const DownloadLink = (props: { download_url: string }) => ( - - - Download - - + + + + + Download + ); const InstallLink = (props: { install_url: string }) => ( - - - - - - Install - - + + + + + Install + ); diff --git a/packages/cyberstorm-theme/src/components.tsx b/packages/cyberstorm-theme/src/components.tsx index 8382dac42..2d2f2509a 100644 --- a/packages/cyberstorm-theme/src/components.tsx +++ b/packages/cyberstorm-theme/src/components.tsx @@ -50,6 +50,14 @@ export { type SelectModifiers, SelectModifiersList, } from "./components/Select/Select"; +export { + type TableVariants, + TableVariantsList, + type TableSizes, + TableSizesList, + type TableModifiers, + TableModifiersList, +} from "./components/Table/Table"; export { type TextInputVariants, TextInputVariantsList, @@ -75,6 +83,12 @@ export { type TagModifiers, TagModifiersList, } from "./components/Tag/Tag"; +export { + type AlertVariants, + AlertVariantsList, + type AlertSizes, + AlertSizesList, +} from "./components/Alert/Alert"; export { type MetaItemVariants, MetaItemVariantsList, diff --git a/packages/cyberstorm-theme/src/components/Alert/Alert.css b/packages/cyberstorm-theme/src/components/Alert/Alert.css new file mode 100644 index 000000000..95aa04044 --- /dev/null +++ b/packages/cyberstorm-theme/src/components/Alert/Alert.css @@ -0,0 +1,72 @@ +@layer theme-components { + /* SIZES */ + .ts-alert:where(.ts-size--small) { + --alert-gap: var(--alert-sm-gap); + --alert-padding-block: var(--alert-sm-padding-block); + --alert-padding-inline: var(--alert-sm-padding-inline); + --alert-font-size: var(--alert-sm-font-size); + --alert-font-weight: var(--alert-sm-font-weight); + --alert-line-height: var(--alert-sm-line-height); + --alert-icon-width: var(--alert-sm-icon-width); + --alert-icon-height: var(--alert-sm-icon-height); + --alert-border-radius: var(--alert-sm-border-radius); + } + + /* VARIANTS */ + .ts-alert:where(.ts-variant--danger) { + --alert-background-color: var(--alert-danger-bg-color--default); + --alert-border-color: var(--alert-danger-border-color--default); + --alert-icon-color: var(--alert-danger-icon-color--default); + --alert-color: var(--alert-danger-text-color--default); + } + + .ts-alert:where(.ts-variant--info) { + --alert-background-color: var(--alert-info-bg-color--default); + --alert-border-color: var(--alert-info-border-color--default); + --alert-icon-color: var(--alert-info-icon-color--default); + --alert-color: var(--alert-info-text-color--default); + } + + .ts-alert:where(.ts-variant--success) { + --alert-background-color: var(--alert-success-bg-color--default); + --alert-border-color: var(--alert-success-border-color--default); + --alert-icon-color: var(--alert-success-icon-color--default); + --alert-color: var(--alert-success-text-color--default); + } + + .ts-alert:where(.ts-variant--warning) { + --alert-background-color: var(--alert-warning-bg-color--default); + --alert-border-color: var(--alert-warning-border-color--default); + --alert-icon-color: var(--alert-warning-icon-color--default); + --alert-color: var(--alert-warning-text-color--default); + } + + /* TOKENS */ + :root { + --alert-danger-bg-color--default: var(--color-accent-red-2); + --alert-danger-border-color--default: var(--color-accent-red-7); + --alert-danger-icon-color--default: var(--color-accent-red-7); + --alert-danger-text-color--default: var(--color-accent-red-11); + --alert-info-bg-color--default: var(--color-accent-blue-2); + --alert-info-border-color--default: var(--color-accent-blue-7); + --alert-info-icon-color--default: var(--color-accent-blue-7); + --alert-info-text-color--default: var(--color-accent-blue-11); + --alert-success-bg-color--default: var(--color-accent-green-2); + --alert-success-border-color--default: var(--color-accent-green-7); + --alert-success-icon-color--default: var(--color-accent-green-7); + --alert-success-text-color--default: var(--color-accent-green-11); + --alert-warning-bg-color--default: var(--color-accent-yellow-2); + --alert-warning-border-color--default: var(--color-accent-yellow-7); + --alert-warning-icon-color--default: var(--color-accent-yellow-7); + --alert-warning-text-color--default: var(--color-accent-yellow-11); + --alert-sm-gap: var(--space-16); + --alert-sm-padding-block: var(--space-16); + --alert-sm-padding-inline: var(--space-24); + --alert-sm-font-size: var(--font-size-body-md); + --alert-sm-font-weight: var(--font-weight-regular); + --alert-sm-line-height: var(--line-height-md); + --alert-sm-icon-width: 1.125rem; + --alert-sm-icon-height: 1.375rem; + --alert-sm-border-radius: var(--radius-sm); + } +} diff --git a/packages/cyberstorm-theme/src/components/Alert/Alert.ts b/packages/cyberstorm-theme/src/components/Alert/Alert.ts new file mode 100644 index 000000000..6bbaf2f86 --- /dev/null +++ b/packages/cyberstorm-theme/src/components/Alert/Alert.ts @@ -0,0 +1,12 @@ +// Variants +export const AlertVariantsList = [ + "info", + "success", + "warning", + "danger", +] as const; +export type AlertVariants = "info" | "success" | "warning" | "danger"; + +// Sizes +export const AlertSizesList = ["small"] as const; +export type AlertSizes = "small"; diff --git a/packages/cyberstorm-theme/src/components/Table/Table.css b/packages/cyberstorm-theme/src/components/Table/Table.css new file mode 100644 index 000000000..8a31800a7 --- /dev/null +++ b/packages/cyberstorm-theme/src/components/Table/Table.css @@ -0,0 +1,49 @@ +@layer theme-components { + /* SELECT */ + + /* VARIANTS */ + .ts-table:where(.ts-variant--default) { + --select-background-color: var(--dropdown-bg-color--default); + --select-border-color: var(--dropdown-border-color--default); + --select-box-shadow: var(--shadow-lg); + --select-animation: fade-in var(--animation-duration-md) ease; + --select-border: var(--space-px) var(--select-border-color) solid; + } + + /* SIZES */ + .ts-table:where(.ts-size--medium) { + --select-border-radius: var(--dropdown-md-border-radius); + --select-padding: var(--dropdown-md-padding); + } + + :root { + --dropdown-bg-color--default: var(--color-surface-4); + --dropdown-border-color--default: var(--color-surface-a6); + --dropdown-item-bg-color--default: #0000; + --dropdown-item-bg-color--hover: var(--color-surface-a7); + --dropdown-item-icon-color--default: var(--color-text-tertiary); + --dropdown-item-icon-color--hover: var(--color-text-primary); + --dropdown-item-text-color--default: var(--color-text-primary); + --dropdown-item-text-color--hover: var(--color-text-primary); + --dropdown-item-danger-bg-color--default: #0000; + --dropdown-item-danger-bg-color--hover: var(--color-accent-red-6); + --dropdown-item-danger-icon-color--default: var(--color-accent-red-7); + --dropdown-item-danger-icon-color--hover: #fff; + --dropdown-item-danger-text-color--default: var(--color-accent-red-7); + --dropdown-item-danger-text-color--hover: #fff; + --dropdown-item-disabled-bg-color--default: #0000; + --dropdown-item-disabled-bg-color--hover: #0000; + --dropdown-item-disabled-icon-color--default: var(--color-text-tertiary); + --dropdown-item-disabled-icon-color--hover: var(--color-text-tertiary); + --dropdown-item-disabled-text-color--default: var(--color-text-tertiary); + --dropdown-item-disabled-text-color--hover: var(--color-text-tertiary); + --dropdown-md-border-radius: var(--radius-md); + --dropdown-md-padding: var(--space-8) 0; + --dropdown-item-md-gap: var(--gap-md); + --dropdown-item-md-padding: var(--space-12) var(--space-16); + --dropdown-item-md-font-weight: var(--font-weight-regular); + --dropdown-item-md-font-size: var(--font-size-body-md); + --select-trigger-md-font-weight: var(--font-weight-bold); + --select-trigger-md-font-size: var(--font-size-body-md); + } +} diff --git a/packages/cyberstorm-theme/src/components/Table/Table.ts b/packages/cyberstorm-theme/src/components/Table/Table.ts new file mode 100644 index 000000000..9806c0546 --- /dev/null +++ b/packages/cyberstorm-theme/src/components/Table/Table.ts @@ -0,0 +1,15 @@ +// Table ROOT +// Variants +export const TableVariantsList = ["default"] as const; +export type TableVariants = "default"; + +// Sizes +export const TableSizesList = ["medium"] as const; +export type TableSizes = "medium"; + +// Modifiers +export const TableModifiersList = [] as const; +// There is an issue with Typescript (eslint) and prettier disagreeing if +// the type should have parentheses +// prettier-ignore +export type TableModifiers = typeof TableModifiersList[number]; diff --git a/packages/cyberstorm-theme/src/index.tsx b/packages/cyberstorm-theme/src/index.tsx index ff3bd0386..34efd92de 100644 --- a/packages/cyberstorm-theme/src/index.tsx +++ b/packages/cyberstorm-theme/src/index.tsx @@ -10,7 +10,9 @@ import "./components/TextInput/TextInput.css"; import "./components/Icon/Icon.css"; import "./components/BreadCrumbs/BreadCrumbs.css"; import "./components/Select/Select.css"; +import "./components/Table/Table.css"; import "./components/Tag/Tag.css"; +import "./components/Alert/Alert.css"; import "./components/MetaItem/MetaItem.css"; import "./components/Link/Link.css"; import "./components/Image/Image.css"; diff --git a/packages/cyberstorm/src/index.ts b/packages/cyberstorm/src/index.ts index 3a806b85a..cd240ab5b 100644 --- a/packages/cyberstorm/src/index.ts +++ b/packages/cyberstorm/src/index.ts @@ -113,6 +113,7 @@ export { } from "./newComponents/Select/Select"; export { Icon as NewIcon } from "./newComponents/Icon/Icon"; export { Tag as NewTag } from "./newComponents/Tag/Tag"; +export { Alert as NewAlert } from "./newComponents/Alert/Alert"; export { MetaItem as NewMetaItem } from "./newComponents/MetaItem/MetaItem"; export { Pagination as NewPagination } from "./newComponents/Pagination/Pagination"; export { @@ -123,3 +124,9 @@ export { export { Image } from "./newComponents/Image/Image"; export { AdContainer } from "./newComponents/AdContainer/AdContainer"; export { Tabs } from "./newComponents/Tabs/Tabs"; +export { + Table as NewTable, + TableSort as NewTableSort, + type TableRows as NewTableRows, + type TableLabels as NewTableLabels, +} from "./newComponents/Table/Table"; diff --git a/packages/cyberstorm/src/newComponents/Alert/Alert.css b/packages/cyberstorm/src/newComponents/Alert/Alert.css new file mode 100644 index 000000000..24985637b --- /dev/null +++ b/packages/cyberstorm/src/newComponents/Alert/Alert.css @@ -0,0 +1,30 @@ +@layer components { + .ts-alert { + display: flex; + + gap: var(--alert-gap); + align-items: flex-start; + align-self: stretch; + + border-left: 3px solid var(--alert-border-color); + border-radius: var(--alert-border-radius); + background-color: var(--alert-background-color); + padding-block: var(--alert-padding-block); + padding-inline: var(--alert-padding-inline); + + > .ts-alert__icon { + width: var(--alert-icon-width); + height: var(--alert-icon-height); + + --icon-color: var(--alert-icon-color); + } + + > .ts-alert__content { + min-width: 0; + color: var(--alert-color); + font-weight: var(--alert-font-weight); + font-size: var(--alert-font-size); + line-height: var(--alert-line-height); + } + } +} diff --git a/packages/cyberstorm/src/newComponents/Alert/Alert.tsx b/packages/cyberstorm/src/newComponents/Alert/Alert.tsx new file mode 100644 index 000000000..91c5bfe19 --- /dev/null +++ b/packages/cyberstorm/src/newComponents/Alert/Alert.tsx @@ -0,0 +1,62 @@ +import "./Alert.css"; +import React from "react"; +import { classnames, componentClasses } from "../../utils/utils"; +import { + AlertSizes, + AlertVariants, +} from "@thunderstore/cyberstorm-theme/src/components"; +import { + faCheckCircle, + faExclamationCircle, + faExclamationTriangle, +} from "@fortawesome/free-solid-svg-icons"; +import { faOctagonExclamation } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { NewIcon, PrimitiveComponentDefaultProps } from "../.."; + +interface AlertProps extends PrimitiveComponentDefaultProps { + csVariant?: AlertVariants; + csSize?: AlertSizes; +} + +export const Alert = React.forwardRef( + (props: AlertProps, forwardedRef) => { + const { + children, + rootClasses, + csVariant = "info", + csSize = "small", + ...forwardedProps + } = props; + + const icon = getIcon(csVariant); + + return ( +
+ + + + {children} +
+ ); + } +); + +Alert.displayName = "Alert"; + +const getIcon = (scheme: AlertProps["csVariant"] = "info") => { + return { + info: faExclamationCircle, + success: faCheckCircle, + warning: faExclamationTriangle, + danger: faOctagonExclamation, + }[scheme]; +}; diff --git a/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.css b/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.css index 619b6f9bb..86e4fabda 100644 --- a/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.css +++ b/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.css @@ -8,7 +8,7 @@ font-size: var(--font-size-body-lg); } - .ts-cardcommunity__imagewrapper:hover { + .ts-cardcommunity__image:hover { filter: brightness(1.2); } @@ -18,12 +18,12 @@ outline-offset: 0.5rem; } - .ts-cardcommunity .ts-cardcommunity__imagewrapper > div { + .ts-cardcommunity .ts-cardcommunity__image > div { transform-origin: bottom; } - .ts-cardcommunity:hover .ts-cardcommunity__imagewrapper > div, - .ts-cardcommunity:focus-within .ts-cardcommunity__imagewrapper > div { + .ts-cardcommunity:hover .ts-cardcommunity__image > div, + .ts-cardcommunity:focus-within .ts-cardcommunity__image > div { transform: scale(1.035); } diff --git a/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx b/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx index 9834b5e82..01b4296c2 100644 --- a/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx +++ b/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx @@ -53,7 +53,7 @@ export function CardCommunity(props: Props) { diff --git a/packages/cyberstorm/src/newComponents/Image/Image.css b/packages/cyberstorm/src/newComponents/Image/Image.css index 0a024a630..7ce80fc79 100644 --- a/packages/cyberstorm/src/newComponents/Image/Image.css +++ b/packages/cyberstorm/src/newComponents/Image/Image.css @@ -44,6 +44,8 @@ } .ts-image__icon { - width: 100%; + width: 50px; + height: 50px; + color: var(--color-surface-a10); } } diff --git a/packages/cyberstorm/src/newComponents/Table/Table.css b/packages/cyberstorm/src/newComponents/Table/Table.css new file mode 100644 index 000000000..b11b9f97f --- /dev/null +++ b/packages/cyberstorm/src/newComponents/Table/Table.css @@ -0,0 +1,98 @@ +@layer components { + .ts-table { + --table-border: 1px solid var(--color-surface-a8); + + width: 100%; + border-collapse: separate; + border-spacing: 0; + + tr > th, + tr > td { + padding: 8px 16px; + border-bottom: var(--table-border); + } + + tr > td:first-child, + tr > th:first-child { + border-left: var(--table-border); + } + + tr > td:last-child, + tr > th:last-child { + border-right: var(--table-border); + } + + tr:first-child > th { + border-top: var(--table-border); + } + + tr:first-child > th:first-child { + border-top-left-radius: var(--radius-md); + } + + tr:first-child > th:last-child { + border-top-right-radius: var(--radius-md); + } + + tr:first-child > th:only-child { + border-top-left-radius: var(--radius-md); + border-top-right-radius: var(--radius-md); + } + + tr:last-child > td:first-child, + tr:last-child > th:first-child { + border-bottom-left-radius: var(--radius-md); + } + + tr:last-child > td:last-child, + tr:last-child > th:last-child { + border-bottom-right-radius: var(--radius-md); + } + } + + .ts-table__caption { + padding: 1rem; + border-top: var(--table-border); + border-right: var(--table-border); + border-left: var(--table-border); + border-top-left-radius: var(--radius-md); + border-top-right-radius: var(--radius-md); + text-align: left; + } + + .ts-table__header { + color: var(--color-text-tertiary); + font-weight: 700; + + font-size: var(--font-size-body-md); + line-height: 150%; /* 21px */ + text-align: left; + background: var(--color-surface-a4); + } + + .ts-table__sortbutton { + display: flex; + gap: 1rem; + align-items: center; + background: transparent; + } + + .ts-table__row { + :last-child { + text-align: right; + } + + :last-child > .ts-table__sortbutton { + float: right; + text-align: right; + } + } + + .ts-table__item { + padding: 8px 16px; + } + + :root { + --table-border: 1px solid var(--color-surface-a8); + } +} diff --git a/packages/cyberstorm/src/newComponents/Table/Table.tsx b/packages/cyberstorm/src/newComponents/Table/Table.tsx new file mode 100644 index 000000000..3037baede --- /dev/null +++ b/packages/cyberstorm/src/newComponents/Table/Table.tsx @@ -0,0 +1,199 @@ +import { CSSProperties, ReactNode, useState } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faSort, + faSortDown, + faSortUp, +} from "@fortawesome/free-solid-svg-icons"; +import "./Table.css"; +import { NewIcon } from "../.."; +import { classnames, componentClasses } from "../../utils/utils"; +import { + TableVariants, + TableSizes, + TableModifiers, +} from "@thunderstore/cyberstorm-theme/src/components"; +import React from "react"; + +interface SortButtonProps { + identifier: number; + current: number; + direction: TableSort; + hook: React.Dispatch< + React.SetStateAction<{ identifier: number; direction: TableSort }> + >; + label: string; +} + +export enum TableSort { + DESC = -1, + OFF, + ASC, +} + +interface TableItem { + value: ReactNode; + sortValue: string | number; +} + +export type TableLabels = { + value: string; + disableSort: boolean; + columnClasses?: string; +}[]; + +type TableRow = TableItem[]; + +export type TableRows = TableRow[]; + +export interface TableProps { + titleRowContent?: ReactNode; + headers: TableLabels; + rows: TableRows; + disableSort?: boolean; + sortByHeader?: number; + sortDirection?: TableSort; + gridTemplateColumns?: string; + rootClasses?: string; + csVariant?: TableVariants; + csSize?: TableSizes; + csModifiers?: TableModifiers[]; +} + +function SortButton(props: SortButtonProps) { + const { identifier, current, direction = TableSort.OFF, hook, label } = props; + + const hookParams = { identifier, direction: TableSort.DESC }; + let icon = faSort; + + if (identifier === current) { + hookParams.direction = + direction === TableSort.ASC ? TableSort.DESC : TableSort.ASC; + icon = direction === TableSort.ASC ? faSortDown : faSortUp; + } + + return ( + + ); +} + +export const Table = React.forwardRef( + (props: TableProps, forwardedRef) => { + const { + titleRowContent, + headers, + rows, + sortByHeader = 0, + sortDirection = TableSort.DESC, + disableSort = false, + gridTemplateColumns, + rootClasses, + csVariant = "default", + csSize = "medium", + csModifiers = [], + } = props; + + const [sortVariables, setSortVariables] = useState({ + identifier: sortByHeader, + direction: sortDirection, + }); + + function compare(a: TableRow, b: TableRow) { + const column = sortVariables.identifier; + if (a[column] && b[column] && a[column].sortValue < b[column].sortValue) { + return sortVariables.direction; + } + if (a[column] && b[column] && a[column].sortValue > b[column].sortValue) { + return -sortVariables.direction; + } + return 0; + } + + if (!disableSort) { + rows.sort(compare); + } + + // const rowCount = { "--row-count": rows.length } as CSSProperties; + let columnCSSProps = {}; + if (gridTemplateColumns) { + columnCSSProps = { + "--column-count": headers.length, + "--dynamic-grid-template-columns": gridTemplateColumns, + } as CSSProperties; + } else { + columnCSSProps = { "--column-count": headers.length } as CSSProperties; + } + + return ( +
+ + + + {titleRowContent ? ( + + ) : null} + + + {headers.map((header, headerI) => ( + + ))} + + + + {rows.map((row, rowI) => ( + + {row.map((col, colI) => ( + + ))} + + ))} + +
{titleRowContent}
+ {header.disableSort ? ( + header.value + ) : ( + + )} +
+ {col.value} +
+ ); + } +); + +Table.displayName = "Table"; diff --git a/packages/cyberstorm/src/utils/utils.ts b/packages/cyberstorm/src/utils/utils.ts index 2dca67f05..54cfa0dc9 100644 --- a/packages/cyberstorm/src/utils/utils.ts +++ b/packages/cyberstorm/src/utils/utils.ts @@ -30,6 +30,7 @@ export const formatFileSize = (bytes: number) => { }).format(bytes); }; +// TODO: FIX: Adds a empty space at the end when last item is undefined export const classnames = ( ...classnames: (string | null | undefined)[] ): string => {