Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add visual cues to FK column header #1388

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions mathesar_ui/src/components/FkReferent.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts">
import {
faExclamationTriangle,
faPlus,
} from '@fortawesome/free-solid-svg-icons';
import { Icon, Spinner } from '@mathesar-component-library';
import { tables } from '@mathesar/stores/tables';
import type { Column } from '@mathesar/api/tables/columns';
import type { Constraint } from '@mathesar/api/tables/constraints';
import type { PaginatedResponse } from '@mathesar/utils/api';
import { getAPI } from '@mathesar/utils/api';
import Identifier from './Identifier.svelte';

export let constraint: Constraint;

$: referentTable =
constraint.type === 'foreignkey'
? $tables.data.get(constraint.referent_table)
: undefined;

async function getReferentColumns(_constraint: Constraint) {
if (_constraint.type !== 'foreignkey') {
return [];
}
const tableId = _constraint.referent_table;
const url = `/api/db/v0/tables/${tableId}/columns/?limit=500`;
const referentTableColumns = await getAPI<PaginatedResponse<Column>>(url);
return referentTableColumns.results.filter((c) =>
_constraint.referent_columns.includes(c.id),
);
}
</script>

<span class="fk-referent">
<Identifier>{referentTable?.name}</Identifier>
<span class="table-column-delimiter">.</span>
{#await getReferentColumns(constraint)}
<Spinner />
{:then referentColumns}
{#each referentColumns as referentColumn, index (referentColumn.id)}
<Identifier>{referentColumn.name}</Identifier>
{#if index < referentColumns.length - 1}
<Icon data={faPlus} />
{/if}
{/each}
{:catch error}
<Icon data={faExclamationTriangle} />
{/await}
</span>
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
position: relative;

.table-content {
--header-height: 32px;
overflow: hidden;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: #f7f8f8;
&.has-foreign-keys {
// We increase the height of the header to account for the description of
// the referent.
--header-height: 42px;
}

.cell {
position: absolute;
Expand Down Expand Up @@ -40,7 +46,7 @@
}

.header {
height: 32px;
height: var(--header-height);
min-width: 100%;
position: relative;
background: #f9f9f9;
Expand All @@ -59,7 +65,7 @@

.body {
position: absolute;
top: 32px;
top: var(--header-height);
left: 0px;
right: 0px;
bottom: 0px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
setTabularDataStoreInContext(tabularDataContextStore);

$: tabularDataContextStore.set(tabularData);
$: ({ processedColumns } = tabularData);
$: ({ processedColumns, constraintsDataStore } = tabularData);
$: hasForeignKeys = $constraintsDataStore.constraints.some(
(c) => c.type === 'foreignkey',
);
</script>

<ActionsPane />

<div class="table-data">
<div class="table-content">
<div class="table-content" class:has-foreign-keys={hasForeignKeys}>
{#if $processedColumns.size}
<Header />
<!-- We'd eventually replace Body with Sheet -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
<script lang="ts">
import { Icon, Button, Spinner } from '@mathesar-component-library';
import {
faArrowRight,
faExclamationTriangle,
faPlus,
faTrash,
} from '@fortawesome/free-solid-svg-icons';
import { Icon, Button } from '@mathesar-component-library';
import { faTrash } from '@fortawesome/free-solid-svg-icons';
import { confirmDelete } from '@mathesar/stores/confirmation';
import { getTabularDataStoreFromContext } from '@mathesar/stores/table-data';
import type { Constraint } from '@mathesar/api/tables/constraints';
import { tables } from '@mathesar/stores/tables';
import Identifier from '@mathesar/components/Identifier.svelte';
import type { Column } from '@mathesar/api/tables/columns';
import type { PaginatedResponse } from '@mathesar/utils/api';
import { getAPI } from '@mathesar/utils/api';
import FkReferent from '@mathesar/components/FkReferent.svelte';

export let constraint: Constraint;
export let drop: () => Promise<void>;
Expand All @@ -35,22 +26,6 @@
);
$: columnNames = columns.map((columnInConstraint) => columnInConstraint.name);
$: columnSummary = columnNames.join(', ');
$: referentTable =
constraint.type === 'foreignkey'
? $tables.data.get(constraint.referent_table)
: undefined;

async function getReferentColumns(_constraint: Constraint) {
if (_constraint.type !== 'foreignkey') {
return [];
}
const tableId = _constraint.referent_table;
const url = `/api/db/v0/tables/${tableId}/columns/?limit=500`;
const referentTableColumns = await getAPI<PaginatedResponse<Column>>(url);
return referentTableColumns.results.filter((c) =>
_constraint.referent_columns.includes(c.id),
);
}
</script>

<div class="table-constraint">
Expand All @@ -62,22 +37,8 @@
<span class="columns">{columnSummary}</span>
</div>
<div class="referent">
{#if referentTable}
References
<Identifier>{referentTable.name}</Identifier>
<Icon data={faArrowRight} />
{#await getReferentColumns(constraint)}
<Spinner />
{:then referentColumns}
{#each referentColumns as referentColumn, index (referentColumn.id)}
<Identifier>{referentColumn.name}</Identifier>
{#if index < referentColumns.length - 1}
<Icon data={faPlus} />
{/if}
{/each}
{:catch error}
<Icon data={faExclamationTriangle} />
{/await}
{#if constraint.type === 'foreignkey'}
References: <FkReferent {constraint} />
{/if}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@

.dropdown.trigger.column-opts {
width: 100%;
height: 100%;
.trigger-content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.column-description {
display: flex;
align-items: center;
}
.constraint-icon {
color: #888;
margin-right: 0.2em;
}
.referent {
margin-left: 2em;
display: block;
font-size: 80%;
color: #555;
.arrow {
color: #aaa;
}
}
}

.dropdown.content.column-opts-content {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
faCog,
faChevronRight,
faChevronLeft,
faLink,
faKey,
faArrowRight,
} from '@fortawesome/free-solid-svg-icons';
import { toast } from '@mathesar/stores/toast';
import {
Expand All @@ -28,6 +31,8 @@
ROW_CONTROL_COLUMN_WIDTH,
} from '@mathesar/stores/table-data/display';
import ColumnName from '@mathesar/components/ColumnName.svelte';
import FkReferent from '@mathesar/components/FkReferent.svelte';
import type { FkConstraint } from '@mathesar/api/tables/constraints';
import { getErrorMessage } from '@mathesar/utils/errors';
import DefaultOptions from './DefaultOptions.svelte';
import AbstractTypeConfiguration from './abstract-type-configuration/AbstractTypeConfiguration.svelte';
Expand All @@ -41,6 +46,9 @@
export let constraintsDataStore: ConstraintsDataStore;

$: ({ column, abstractType } = processedColumn);
$: fkConstraint = $constraintsDataStore.constraints.find(
(c) => c.type === 'foreignkey' && c.columns.includes(column.id),
) as FkConstraint | undefined;
$: ({ display } = $tabularData);
$: ({ columnPlacements } = display);

Expand Down Expand Up @@ -164,58 +172,71 @@
contentClass="no-max-height column-opts-content"
on:close={setDefaultView}
>
<ColumnName slot="trigger" {column} />
<svelte:fragment slot="content">
<div class="container">
<div class="section type-header">
{#if view === 'default'}
<h6 class="category">Data Type</h6>
<span slot="trigger" class="trigger-content">
<span class="column-description">
{#if column.primary_key}
<Icon class="constraint-icon" size="0.9rem" data={faKey} />
{:else if fkConstraint}
<Icon class="constraint-icon" size="0.9rem" data={faLink} />
{/if}
<ColumnName {column} />
</span>
{#if fkConstraint}
<span class="referent">
<Icon class="arrow" data={faArrowRight} />
<FkReferent constraint={fkConstraint} />
</span>
{/if}
</span>
<div class="container" slot="content">
<div class="section type-header">
{#if view === 'default'}
<h6 class="category">Data Type</h6>
<Button
class="type-switch"
appearance="plain"
on:click={setTypeView}
>
<span>{abstractType.name}</span>
<Icon size="0.8em" data={faCog} />
<Icon size="0.7em" data={faChevronRight} />
</Button>
{:else if view === 'type'}
<h6 class="category">
<Button
class="type-switch"
size="small"
appearance="plain"
on:click={setTypeView}
class="padding-zero"
on:click={setDefaultView}
>
<span>{abstractType.name}</span>
<Icon size="0.8em" data={faCog} />
<Icon size="0.7em" data={faChevronRight} />
<Icon data={faChevronLeft} />
Go back
</Button>
{:else if view === 'type'}
<h6 class="category">
<Button
size="small"
appearance="plain"
class="padding-zero"
on:click={setDefaultView}
>
<Icon data={faChevronLeft} />
Go back
</Button>
</h6>
{/if}
</div>

<div class="divider" />

<div class="section">
{#if view === 'default'}
<DefaultOptions
{meta}
{column}
{columnsDataStore}
{constraintsDataStore}
on:close={closeMenu}
on:rename={handleStartRenaming}
/>
{:else if view === 'type'}
<AbstractTypeConfiguration
{processedColumn}
{abstractType}
on:close={closeMenu}
/>
{/if}
</div>
</div></svelte:fragment
>
</h6>
{/if}
</div>

<div class="divider" />

<div class="section">
{#if view === 'default'}
<DefaultOptions
{meta}
{column}
{columnsDataStore}
{constraintsDataStore}
on:close={closeMenu}
on:rename={handleStartRenaming}
/>
{:else if view === 'type'}
<AbstractTypeConfiguration
{processedColumn}
{abstractType}
on:close={closeMenu}
/>
{/if}
</div>
</div>
</Dropdown>
{/if}
<ColumnResizer columnId={column.id} />
Expand Down