diff --git a/frontend/.changeset/heavy-fans-add.md b/frontend/.changeset/heavy-fans-add.md new file mode 100644 index 000000000..d9daf0af2 --- /dev/null +++ b/frontend/.changeset/heavy-fans-add.md @@ -0,0 +1,6 @@ +--- +"@liam-hq/erd-core": patch +"@liam-hq/cli": patch +--- + +Refactored components for better maintainability: TableColumnList, TableColumn, Cardinality. diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx index 6f73d96b4..0f0ebcb80 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx @@ -135,6 +135,7 @@ export const ERDContent: FC = ({ ...node, data: { ...node.data, + highlightedHandles: [], isHighlighted: false, }, } diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/Cardinality.module.css b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/Cardinality.module.css new file mode 100644 index 000000000..67c1d84ea --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/Cardinality.module.css @@ -0,0 +1,21 @@ +.handleCardinality { + top: auto; + position: absolute; + width: 23px; + height: 16px; + color: var(--global-border); +} + +.handleCardinalityHighlighted { + color: var(--node-layout); + transition: color var(--default-hover-animation-duration) + var(--default-timing-function); +} + +.handleCardinalityRight { + right: -23px; +} + +.handleCardinalityLeft { + left: -23px; +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/Cardinality.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/Cardinality.tsx new file mode 100644 index 000000000..34c6f7831 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/Cardinality.tsx @@ -0,0 +1,41 @@ +import { + CardinalityZeroOrManyLeftIcon, + CardinalityZeroOrOneLeftIcon, + CardinalityZeroOrOneRightIcon, +} from '@liam-hq/ui' +import clsx from 'clsx' +import type { FC } from 'react' +import styles from './Cardinality.module.css' + +type CardinalityProps = { + direction: 'left' | 'right' + cardinality: 'ONE_TO_ONE' | 'ONE_TO_MANY' + isHighlighted: boolean +} + +export const Cardinality: FC = ({ + direction, + cardinality, + isHighlighted, +}) => { + const isLeft = direction === 'left' + const iconClass = isLeft + ? styles.handleCardinalityLeft + : styles.handleCardinalityRight + const highlightClass = isHighlighted + ? styles.handleCardinalityHighlighted + : '' + + const Icon = + cardinality === 'ONE_TO_ONE' + ? isLeft + ? CardinalityZeroOrOneLeftIcon + : CardinalityZeroOrOneRightIcon + : CardinalityZeroOrManyLeftIcon + + return ( + + ) +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/index.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/index.ts new file mode 100644 index 000000000..28ab36f50 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/Cardinality/index.ts @@ -0,0 +1 @@ +export * from './Cardinality' diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/CardinalityNotation.module.css b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/CardinalityNotation.module.css new file mode 100644 index 000000000..8880b02b6 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/CardinalityNotation.module.css @@ -0,0 +1,23 @@ +.handleCardinalityNotation { + color: var(--global-border); + opacity: 0; +} + +.handleCardinalityNotationRight { + position: absolute; + right: -20px; + top: -8px; +} + +.handleCardinalityNotationLeft { + position: absolute; + left: -20px; + top: -8px; +} + +.handleCardinalityNotationHighlighted { + color: var(--node-layout); + transition: color var(--default-hover-animation-duration) + var(--default-timing-function); + opacity: 1; +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/CardinalityNotation.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/CardinalityNotation.tsx new file mode 100644 index 000000000..89311915a --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/CardinalityNotation.tsx @@ -0,0 +1,35 @@ +import clsx from 'clsx' +import type { FC } from 'react' +import styles from './CardinalityNotation.module.css' + +type CardinalityNotationProps = { + direction: 'left' | 'right' + notation: '1' | 'n' + isHighlighted: boolean +} + +export const CardinalityNotation: FC = ({ + direction, + notation, + isHighlighted, +}) => { + const isLeft = direction === 'left' + const notationClass = isLeft + ? styles.handleCardinalityNotationLeft + : styles.handleCardinalityNotationRight + const highlightClass = isHighlighted + ? styles.handleCardinalityNotationHighlighted + : '' + + return ( + + {notation} + + ) +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/index.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/index.ts new file mode 100644 index 000000000..7714a3a04 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/CardinalityNotation/index.ts @@ -0,0 +1 @@ +export * from './CardinalityNotation' diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/TableColumn.module.css b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/TableColumn.module.css new file mode 100644 index 000000000..add217722 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/TableColumn.module.css @@ -0,0 +1,68 @@ +.columnWrapper { + display: grid; + grid-template-columns: auto 1fr; + align-items: center; + padding: var(--spacing-2); + gap: var(--spacing-1half); + background: var(--node-background); + color: var(--global-foreground); + font-size: var(--font-size-3); + line-height: normal; + border-bottom: 1px solid var(--node-border); + position: relative; +} + +.columnWrapper:last-child { + border-bottom-left-radius: var(--border-radius-md); + border-bottom-right-radius: var(--border-radius-md); +} + +.primaryKeyIcon { + color: var(--primary-accent); +} + +.diamondIcon { + color: var(--overlay-70); +} + +.columnNameWrapper { + display: inline-flex; + justify-content: space-between; + align-items: center; +} + +.columnName { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.columnType { + color: var(--overlay-70, rgba(255, 255, 255, 0.7)); + text-align: right; + font-family: var(--code-font); + font-size: var(--font-size-1); + font-weight: 400; + line-height: normal; + opacity: 0; + transition: opacity 300ms var(--default-timing-function); +} + +.wrapper:hover .columnType { + opacity: 1; +} + +.handle { + top: auto; + opacity: 0; +} + +.handle[data-handlepos='right'] { + transform: translate(50%, 0%); + right: -20px; +} + +.handle[data-handlepos='left'] { + transform: translate(-50%, 0%); + left: -20px; +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/TableColumn.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/TableColumn.tsx new file mode 100644 index 000000000..bf7ad46e2 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/TableColumn.tsx @@ -0,0 +1,148 @@ +import type { Column, Relationships, Table } from '@liam-hq/db-structure' +import { DiamondFillIcon, DiamondIcon, KeyRound } from '@liam-hq/ui' +import { Handle, Position } from '@xyflow/react' +import clsx from 'clsx' +import type { FC } from 'react' +import { match } from 'ts-pattern' +import { Cardinality } from './Cardinality' +import { CardinalityNotation } from './CardinalityNotation' +import styles from './TableColumn.module.css' + +type TableColumnProps = { + table: Table + column: Column + relationships: Relationships + isHighlighted: boolean + highlightedHandles: string[] +} + +export const TableColumn: FC = ({ + table, + column, + relationships, + isHighlighted, + highlightedHandles, +}) => { + const handleId = `${table.name}-${column.name}` + + const isSource = Object.values(relationships).some( + (relationship) => + relationship.primaryTableName === table.name && + relationship.primaryColumnName === column.name, + ) + const targetCardinality = Object.values(relationships).find( + ({ foreignTableName, foreignColumnName }) => + foreignTableName === table.name && foreignColumnName === column.name, + )?.cardinality + + return ( +
  • + {column.primary && ( + + )} + {!column.primary && + (column.notNull ? ( + + ) : ( + + ))} + + + {column.name} + {column.type} + + + {isSource && ( + <> + + + + + )} + + {targetCardinality && ( + <> + + {match(targetCardinality) + .with('ONE_TO_ONE', () => ( + <> + + + + )) + .with('ONE_TO_MANY', () => ( + <> + + + + )) + .otherwise(() => null)} + + )} +
  • + ) +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/index.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/index.ts new file mode 100644 index 000000000..1340d33d1 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumn/index.ts @@ -0,0 +1 @@ +export * from './TableColumn' diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumnList.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumnList.tsx new file mode 100644 index 000000000..366f7bbd2 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/TableColumnList.tsx @@ -0,0 +1,30 @@ +import type { Relationships, Table } from '@liam-hq/db-structure' +import type { FC } from 'react' +import { TableColumn } from './TableColumn' + +type TableColumnListProps = { + table: Table + relationships: Relationships + isHighlighted: boolean + highlightedHandles: string[] +} + +export const TableColumnList: FC = ({ + table, + relationships, + isHighlighted, + highlightedHandles, +}) => ( +
      + {Object.values(table.columns).map((column) => ( + + ))} +
    +) diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/index.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/index.ts new file mode 100644 index 000000000..5358622c9 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableColumnList/index.ts @@ -0,0 +1 @@ +export * from './TableColumnList' diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.module.css b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.module.css index e8a957777..42f9e938d 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.module.css +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.module.css @@ -17,118 +17,3 @@ border: 2px solid var(--primary-accent); box-shadow: 0px 0px 20px 0px rgba(29, 237, 131, 0.4); } - -.columnWrapper { - display: grid; - grid-template-columns: auto 1fr; - align-items: center; - padding: var(--spacing-2); - gap: var(--spacing-1half); - background: var(--node-background); - color: var(--global-foreground); - font-size: var(--font-size-3); - line-height: normal; - border-bottom: 1px solid var(--node-border); - position: relative; -} - -.columnWrapper:last-child { - border-bottom-left-radius: var(--border-radius-md); - border-bottom-right-radius: var(--border-radius-md); -} - -.primaryKeyIcon { - color: var(--primary-accent); -} - -.diamondIcon { - color: var(--overlay-70); -} - -.columnNameWrapper { - display: inline-flex; - justify-content: space-between; - align-items: center; -} - -.columnName { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.columnType { - color: var(--overlay-70, rgba(255, 255, 255, 0.7)); - text-align: right; - font-family: var(--code-font); - font-size: var(--font-size-1); - font-weight: 400; - line-height: normal; - opacity: 0; - transition: opacity 300ms var(--default-timing-function); -} - -.wrapper:hover .columnType { - opacity: 1; -} - -.handle { - top: auto; - opacity: 0; -} - -.handle[data-handlepos='right'] { - transform: translate(50%, 0%); - right: -20px; -} - -.handle[data-handlepos='left'] { - transform: translate(-50%, 0%); - left: -20px; -} - -.handleCardinality { - top: auto; - position: absolute; - width: 23px; - height: 16px; - color: var(--global-border); -} - -.handleCardinalityHighlighted { - color: var(--node-layout); - transition: color var(--default-hover-animation-duration) - var(--default-timing-function); -} - -.handleCardinalityRight { - right: -23px; -} - -.handleCardinalityLeft { - left: -23px; -} - -.handleCardinalityComment { - color: var(--global-border); - opacity: 0; -} - -.handleCardinalityCommentRight { - position: absolute; - right: -20px; - top: -8px; -} - -.handleCardinalityCommentLeft { - position: absolute; - left: -20px; - top: -8px; -} - -.handleCardinalityCommentHighlighted { - color: var(--node-layout); - transition: color var(--default-hover-animation-duration) - var(--default-timing-function); - opacity: 1; -} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.tsx index 4a67fc35d..115ce2c71 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.tsx +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/TableNode/TableNode.tsx @@ -4,19 +4,11 @@ import { useUserEditingStore, } from '@/stores' import type { Table } from '@liam-hq/db-structure' -import { - CardinalityZeroOrManyLeftIcon, - CardinalityZeroOrOneLeftIcon, - CardinalityZeroOrOneRightIcon, - DiamondFillIcon, - DiamondIcon, - KeyRound, -} from '@liam-hq/ui' -import { Handle, type Node, type NodeProps, Position } from '@xyflow/react' +import type { Node, NodeProps } from '@xyflow/react' import clsx from 'clsx' import { type FC, useCallback } from 'react' -import { match } from 'ts-pattern' import { isRelatedToTable } from '../ERDContent' +import { TableColumnList } from './TableColumnList' import { TableHeader } from './TableHeader' import styles from './TableNode.module.css' @@ -64,152 +56,12 @@ export const TableNode: FC = ({ > {showMode === 'ALL_FIELDS' && ( -
      - {Object.values(table.columns).map((column) => { - const handleId = `${table.name}-${column.name}` - const isSource = Object.values(relationships).some( - (relationship) => - relationship.primaryTableName === table.name && - relationship.primaryColumnName === column.name, - ) - const targetCardinality = Object.values(relationships).find( - ({ foreignTableName, foreignColumnName }) => - foreignTableName === table.name && - foreignColumnName === column.name, - )?.cardinality - - return ( -
    • - {column.primary && ( - - )} - {!column.primary && - (column.notNull ? ( - - ) : ( - - ))} - - - {column.name} - {column.type} - - - {isSource && ( - <> - - - - 1 - - - )} - - {targetCardinality && ( - <> - - {match(targetCardinality) - .with('ONE_TO_ONE', () => ( - <> - - - - 1 - - - )) - .with('ONE_TO_MANY', () => ( - <> - - - - n - - - )) - .otherwise(() => null)} - - )} -
    • - ) - })} -
    + )} )