diff --git a/components/Blocks/Table/index.tsx b/components/Blocks/Table/index.tsx
new file mode 100644
index 000000000..1a84cae17
--- /dev/null
+++ b/components/Blocks/Table/index.tsx
@@ -0,0 +1,97 @@
+"use client"
+
+import {
+ flexRender,
+ getCoreRowModel,
+ getPaginationRowModel,
+ useReactTable,
+} from "@tanstack/react-table"
+import { useState } from "react"
+
+import ScrollWrapper from "@/components/TempDesignSystem/ScrollWrapper"
+import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
+import Table from "@/components/TempDesignSystem/Table"
+
+import styles from "./table.module.css"
+
+import type { TableProps } from "@/types/components/blocks/table"
+
+export default function TableBlock({ data }: TableProps) {
+ const { columns, rows, totalWidth } = data
+ const initialPageSize = 5
+ const [pageSize, setPageSize] = useState(initialPageSize)
+ const showMoreVisible = rows.length > initialPageSize
+ const showLessVisible = pageSize >= rows.length
+ const columnDefs = columns.map((col) => ({
+ accessorKey: col.id,
+ header: col.header,
+ size: col.width,
+ }))
+
+ const table = useReactTable({
+ columns: columnDefs,
+ data: rows,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ state: {
+ pagination: {
+ pageIndex: 0,
+ pageSize,
+ },
+ },
+ })
+
+ function handleShowMore() {
+ setPageSize(showLessVisible ? initialPageSize : rows.length)
+ }
+
+ return (
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))}
+
+
+
+ {showMoreVisible ? (
+
+ ) : null}
+
+ )
+}
diff --git a/components/Blocks/Table/table.module.css b/components/Blocks/Table/table.module.css
new file mode 100644
index 000000000..805cb9935
--- /dev/null
+++ b/components/Blocks/Table/table.module.css
@@ -0,0 +1,6 @@
+.tableWrapper {
+ display: grid;
+ border: 1px solid var(--Base-Border-Subtle);
+ border-radius: var(--Corner-radius-Small);
+ overflow: hidden;
+}
diff --git a/components/Blocks/index.tsx b/components/Blocks/index.tsx
index efd0f2bcb..2423508d1 100644
--- a/components/Blocks/index.tsx
+++ b/components/Blocks/index.tsx
@@ -5,7 +5,7 @@ import TextCols from "@/components/Blocks/TextCols"
import UspGrid from "@/components/Blocks/UspGrid"
import JsonToHtml from "@/components/JsonToHtml"
-import Table from "../Table"
+import Table from "./Table"
import type { BlocksProps } from "@/types/components/blocks"
import { BlocksEnums } from "@/types/enums/blocks"
@@ -50,7 +50,7 @@ export default function Blocks({ blocks }: BlocksProps) {
/>
)
case BlocksEnums.block.Table:
- return
+ return
case BlocksEnums.block.TextCols:
return
case BlocksEnums.block.TextContent:
diff --git a/components/ContentType/ContentPage/contentPage.module.css b/components/ContentType/ContentPage/contentPage.module.css
index 026aa6065..aa0036f83 100644
--- a/components/ContentType/ContentPage/contentPage.module.css
+++ b/components/ContentType/ContentPage/contentPage.module.css
@@ -40,6 +40,8 @@
}
.mainContent {
+ display: grid;
+ gap: var(--Spacing-x4);
width: 100%;
}
diff --git a/components/TempDesignSystem/ScrollWrapper/index.tsx b/components/TempDesignSystem/ScrollWrapper/index.tsx
new file mode 100644
index 000000000..1eb341f90
--- /dev/null
+++ b/components/TempDesignSystem/ScrollWrapper/index.tsx
@@ -0,0 +1,35 @@
+"use client"
+
+import { useMemo } from "react"
+
+import useScrollShadows from "@/hooks/useScrollShadows"
+
+import { ScrollWrapperProps } from "./scrollWrapper"
+
+import styles from "./scrollWrapper.module.css"
+
+export default function ScrollWrapper({
+ className,
+ children,
+}: ScrollWrapperProps) {
+ const { containerRef, showLeftShadow, showRightShadow } = useScrollShadows()
+
+ const classNames = useMemo(() => {
+ const cls = [styles.scrollWrapper, className]
+ if (showLeftShadow) {
+ cls.push(styles.leftShadow)
+ }
+ if (showRightShadow) {
+ cls.push(styles.rightShadow)
+ }
+ return cls.join(" ")
+ }, [showLeftShadow, showRightShadow, className])
+
+ return (
+
+ )
+}
diff --git a/components/TempDesignSystem/ScrollWrapper/scrollWrapper.module.css b/components/TempDesignSystem/ScrollWrapper/scrollWrapper.module.css
new file mode 100644
index 000000000..b604a3c24
--- /dev/null
+++ b/components/TempDesignSystem/ScrollWrapper/scrollWrapper.module.css
@@ -0,0 +1,33 @@
+.scrollWrapper {
+ position: relative;
+ overflow: hidden;
+}
+
+.scrollWrapper::before,
+.scrollWrapper::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ height: 100%;
+ pointer-events: none;
+ z-index: 1;
+ transition: opacity 0.2s ease;
+ opacity: 0;
+ width: 50px;
+}
+
+.scrollWrapper.leftShadow::before {
+ left: 0;
+ background: linear-gradient(to right, rgba(0, 0, 0, 0.3), transparent);
+ opacity: 1;
+}
+
+.scrollWrapper.rightShadow::after {
+ right: 0;
+ background: linear-gradient(to left, rgba(0, 0, 0, 0.3), transparent);
+ opacity: 1;
+}
+
+.content {
+ overflow-x: auto;
+}
diff --git a/components/TempDesignSystem/ScrollWrapper/scrollWrapper.ts b/components/TempDesignSystem/ScrollWrapper/scrollWrapper.ts
new file mode 100644
index 000000000..8440abfa5
--- /dev/null
+++ b/components/TempDesignSystem/ScrollWrapper/scrollWrapper.ts
@@ -0,0 +1,2 @@
+export interface ScrollWrapperProps
+ extends React.PropsWithChildren> {}
diff --git a/components/TempDesignSystem/ShowMoreButton/button.module.css b/components/TempDesignSystem/ShowMoreButton/button.module.css
deleted file mode 100644
index f55557f33..000000000
--- a/components/TempDesignSystem/ShowMoreButton/button.module.css
+++ /dev/null
@@ -1,4 +0,0 @@
-.container {
- display: flex;
- justify-content: center;
-}
diff --git a/components/TempDesignSystem/ShowMoreButton/index.tsx b/components/TempDesignSystem/ShowMoreButton/index.tsx
index 6e9cae7bf..ca8a24dcb 100644
--- a/components/TempDesignSystem/ShowMoreButton/index.tsx
+++ b/components/TempDesignSystem/ShowMoreButton/index.tsx
@@ -5,18 +5,29 @@ import { useIntl } from "react-intl"
import { ChevronDownIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
-import styles from "./button.module.css"
+import { showMoreButtonVariants } from "./variants"
-import type { ShowMoreButtonParams } from "@/types/components/myPages/stays/button"
+import styles from "./showMoreButton.module.css"
+
+import type { ShowMoreButtonProps } from "./showMoreButton"
export default function ShowMoreButton({
+ className,
+ intent,
disabled,
+ showLess,
loadMoreData,
-}: ShowMoreButtonParams) {
- const { formatMessage } = useIntl()
+}: ShowMoreButtonProps) {
+ const intl = useIntl()
+ const classNames = showMoreButtonVariants({
+ className,
+ intent,
+ })
+
return (
-
+
)
diff --git a/components/TempDesignSystem/ShowMoreButton/showMoreButton.module.css b/components/TempDesignSystem/ShowMoreButton/showMoreButton.module.css
new file mode 100644
index 000000000..32299ec80
--- /dev/null
+++ b/components/TempDesignSystem/ShowMoreButton/showMoreButton.module.css
@@ -0,0 +1,23 @@
+.container {
+ display: flex;
+ justify-content: center;
+}
+
+.table {
+ display: grid;
+ justify-content: stretch;
+ border-top: 1px solid var(--Base-Border-Subtle);
+ background-color: var(--Base-Surface-Primary-light-Normal);
+}
+
+.table .button {
+ border-radius: 0;
+}
+
+.icon {
+ transition: transform 0.3s;
+}
+
+.showLess .icon {
+ transform: rotate(180deg);
+}
diff --git a/components/TempDesignSystem/ShowMoreButton/showMoreButton.ts b/components/TempDesignSystem/ShowMoreButton/showMoreButton.ts
new file mode 100644
index 000000000..b6ad929f5
--- /dev/null
+++ b/components/TempDesignSystem/ShowMoreButton/showMoreButton.ts
@@ -0,0 +1,11 @@
+import { showMoreButtonVariants } from "./variants"
+
+import type { VariantProps } from "class-variance-authority"
+
+export interface ShowMoreButtonProps
+ extends React.PropsWithChildren
>,
+ VariantProps {
+ disabled?: boolean
+ showLess?: boolean
+ loadMoreData: () => void
+}
diff --git a/components/TempDesignSystem/ShowMoreButton/variants.ts b/components/TempDesignSystem/ShowMoreButton/variants.ts
new file mode 100644
index 000000000..17f8620f1
--- /dev/null
+++ b/components/TempDesignSystem/ShowMoreButton/variants.ts
@@ -0,0 +1,11 @@
+import { cva } from "class-variance-authority"
+
+import styles from "./showMoreButton.module.css"
+
+export const showMoreButtonVariants = cva(styles.container, {
+ variants: {
+ intent: {
+ table: styles.table,
+ },
+ },
+})
diff --git a/components/TempDesignSystem/Table/TH.tsx b/components/TempDesignSystem/Table/TH.tsx
index ca2377193..657dc2b1d 100644
--- a/components/TempDesignSystem/Table/TH.tsx
+++ b/components/TempDesignSystem/Table/TH.tsx
@@ -1,7 +1,13 @@
import styles from "./table.module.css"
-function TH({ children }: React.PropsWithChildren) {
- return {children} |
+import type { THeadProps } from "./table"
+
+function TH({ children, width = "auto", ...props }: THeadProps) {
+ return (
+
+ {children}
+ |
+ )
}
export default TH
diff --git a/components/TempDesignSystem/Table/index.tsx b/components/TempDesignSystem/Table/index.tsx
index 5ba745df5..fb5c4fde6 100644
--- a/components/TempDesignSystem/Table/index.tsx
+++ b/components/TempDesignSystem/Table/index.tsx
@@ -1,13 +1,34 @@
+import { TableProps } from "./table"
import TBody from "./TBody"
import TD from "./TD"
import TH from "./TH"
import THead from "./THead"
import TR from "./TR"
+import { tableVariants } from "./variants"
-import styles from "./table.module.css"
+function Table({
+ className,
+ intent,
+ borderRadius,
+ variant,
+ layout,
+ width = "100%",
+ children,
+ ...props
+}: TableProps) {
+ const classNames = tableVariants({
+ className,
+ borderRadius,
+ intent,
+ layout,
+ variant,
+ })
-function Table({ children }: React.PropsWithChildren) {
- return
+ return (
+
+ )
}
Table.THead = THead
diff --git a/components/TempDesignSystem/Table/table.module.css b/components/TempDesignSystem/Table/table.module.css
index fcbb3fec2..2296b2ecd 100644
--- a/components/TempDesignSystem/Table/table.module.css
+++ b/components/TempDesignSystem/Table/table.module.css
@@ -1,20 +1,20 @@
.table {
- border-radius: var(--Corner-radius-Medium);
border-collapse: collapse;
overflow: hidden;
- width: 100%;
+ min-width: 100%;
}
.thead {
- background-color: var(--Base-Background-Secondary-Normal, #f7e1d5);
+ color: var(--Base-Text-High-contrast);
+ background-color: var(--Base-Surface-Primary-dark-Normal);
}
.tbody {
- background-color: var(--Base-Surface-Primary-light-Normal, #fff);
+ background-color: var(--Base-Surface-Primary-light-Normal);
}
.tr:not(:last-of-type) {
- border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider, #f0c1b6);
+ border-bottom: 1px solid var(--Base-Border-Subtle);
}
.th {
@@ -28,6 +28,35 @@
padding: var(--Spacing-x2);
}
+.fixed {
+ table-layout: fixed;
+}
+
+.smallRadius {
+ border-radius: var(--Corner-radius-Small);
+}
+.mediumRadius {
+ border-radius: var(--Corner-radius-Medium);
+}
+.largeRadius {
+ border-radius: var(--Corner-radius-Large);
+}
+
+.content .thead {
+ background-color: var(--Base-Surface-Subtle-Hover);
+}
+
+.content .tbody {
+ background-color: var(--Base-Background-Primary-Normal);
+}
+
+.content.striped .tbody .tr:nth-child(odd) {
+ background-color: var(--Base-Surface-Subtle-Normal);
+}
+.content.striped .tbody .tr:nth-child(even) {
+ background-color: var(--Base-Background-Primary-Normal);
+}
+
@media screen and (min-width: 768px) {
.th {
padding: var(--Spacing-x2) var(--Spacing-x3);
diff --git a/components/TempDesignSystem/Table/table.ts b/components/TempDesignSystem/Table/table.ts
new file mode 100644
index 000000000..f2759484d
--- /dev/null
+++ b/components/TempDesignSystem/Table/table.ts
@@ -0,0 +1,14 @@
+import { tableVariants } from "./variants"
+
+import type { VariantProps } from "class-variance-authority"
+
+export interface TableProps
+ extends React.PropsWithChildren>,
+ VariantProps {
+ width?: string
+}
+
+export interface THeadProps
+ extends React.PropsWithChildren> {
+ width?: string
+}
diff --git a/components/TempDesignSystem/Table/variants.ts b/components/TempDesignSystem/Table/variants.ts
new file mode 100644
index 000000000..c70202dcf
--- /dev/null
+++ b/components/TempDesignSystem/Table/variants.ts
@@ -0,0 +1,27 @@
+import { cva } from "class-variance-authority"
+
+import styles from "./table.module.css"
+
+export const tableVariants = cva(styles.table, {
+ variants: {
+ intent: {
+ light: styles.light,
+ striped: styles.striped,
+ },
+ variant: {
+ content: styles.content,
+ },
+ borderRadius: {
+ none: "",
+ small: styles.smallRadius,
+ medium: styles.mediumRadius,
+ large: styles.largeRadius,
+ },
+ layout: {
+ fixed: styles.fixed,
+ },
+ },
+ defaultVariants: {
+ borderRadius: "medium",
+ },
+})
diff --git a/hooks/useScrollShadows.ts b/hooks/useScrollShadows.ts
new file mode 100644
index 000000000..7073d110e
--- /dev/null
+++ b/hooks/useScrollShadows.ts
@@ -0,0 +1,36 @@
+import { useEffect, useRef, useState } from "react"
+
+const useScrollShadows = () => {
+ const containerRef = useRef(null)
+ const [showLeftShadow, setShowLeftShadow] = useState(false)
+ const [showRightShadow, setShowRightShadow] = useState(false)
+
+ useEffect(() => {
+ const handleScroll = () => {
+ const container = containerRef.current
+ if (!container) return
+
+ setShowLeftShadow(container.scrollLeft > 0)
+ setShowRightShadow(
+ container.scrollLeft < container.scrollWidth - container.clientWidth
+ )
+ }
+
+ const container = containerRef.current
+ if (container) {
+ container.addEventListener("scroll", handleScroll)
+ }
+
+ handleScroll()
+
+ return () => {
+ if (container) {
+ container.removeEventListener("scroll", handleScroll)
+ }
+ }
+ }, [])
+
+ return { containerRef, showLeftShadow, showRightShadow }
+}
+
+export default useScrollShadows
diff --git a/package-lock.json b/package-lock.json
index 35805d69e..c12152ff6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,7 @@
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
"@t3-oss/env-nextjs": "^0.9.2",
"@tanstack/react-query": "^5.28.6",
+ "@tanstack/react-table": "^8.20.5",
"@trpc/client": "^11.0.0-rc.467",
"@trpc/react-query": "^11.0.0-rc.467",
"@trpc/server": "^11.0.0-rc.467",
@@ -5971,6 +5972,37 @@
"react": "^18.0.0"
}
},
+ "node_modules/@tanstack/react-table": {
+ "version": "8.20.5",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz",
+ "integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==",
+ "dependencies": {
+ "@tanstack/table-core": "8.20.5"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/@tanstack/table-core": {
+ "version": "8.20.5",
+ "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz",
+ "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz",
diff --git a/package.json b/package.json
index 2be8bd832..ab79709d3 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
"@t3-oss/env-nextjs": "^0.9.2",
"@tanstack/react-query": "^5.28.6",
+ "@tanstack/react-table": "^8.20.5",
"@trpc/client": "^11.0.0-rc.467",
"@trpc/react-query": "^11.0.0-rc.467",
"@trpc/server": "^11.0.0-rc.467",
diff --git a/server/routers/contentstack/schemas/blocks/table.ts b/server/routers/contentstack/schemas/blocks/table.ts
index 74f144608..5109d487b 100644
--- a/server/routers/contentstack/schemas/blocks/table.ts
+++ b/server/routers/contentstack/schemas/blocks/table.ts
@@ -28,17 +28,20 @@ export const tableSchema = z.object({
}),
})
.transform((data) => {
+ const totalWidth = data.column_widths.reduce(
+ (acc, width) => acc + width,
+ 0
+ )
const columns = data.table.tableState.columns.map((col, idx) => ({
id: col.id,
- Header: col.label || "",
- accessor: col.accessor,
- columnWidth: data.column_widths[idx] || 0,
+ header: col.label || "",
+ width: data.column_widths[idx] || 0,
}))
const rows = data.table.tableState.data.map((rowData) => {
const transformedRow: Record = {}
- columns.forEach((col) => {
- transformedRow[col.accessor] = rowData[col.accessor] || ""
+ columns.forEach((header) => {
+ transformedRow[header.id] = rowData[header.id] || ""
})
return transformedRow
})
@@ -46,6 +49,7 @@ export const tableSchema = z.object({
return {
columns,
rows,
+ totalWidth,
}
}),
})
diff --git a/types/components/blocks/table.ts b/types/components/blocks/table.ts
new file mode 100644
index 000000000..ee7d961d0
--- /dev/null
+++ b/types/components/blocks/table.ts
@@ -0,0 +1,5 @@
+import type { TableData } from "@/types/trpc/routers/contentstack/blocks"
+
+export interface TableProps {
+ data: TableData
+}
diff --git a/types/trpc/routers/contentstack/blocks.ts b/types/trpc/routers/contentstack/blocks.ts
index e1ff7e923..1c46dda93 100644
--- a/types/trpc/routers/contentstack/blocks.ts
+++ b/types/trpc/routers/contentstack/blocks.ts
@@ -14,6 +14,6 @@ export interface DynamicContent extends z.output {}
export interface Shortcuts extends z.output {}
export type Shortcut = Shortcuts["shortcuts"]
export interface TableBlock extends z.output {}
-export type Table = TableBlock["table"]
+export type TableData = TableBlock["table"]
export interface TextCols extends z.output {}
export interface UspGrid extends z.output {}