Skip to content

Commit

Permalink
Merge pull request #1 from surplex/feature/Adjust_tabula_concept
Browse files Browse the repository at this point in the history
Feature/adjust tabula concept
  • Loading branch information
fourhundredfour authored Feb 9, 2021
2 parents cc44e3a + 747c1f4 commit b4c896f
Show file tree
Hide file tree
Showing 16 changed files with 201 additions and 101 deletions.
66 changes: 65 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,68 @@ A Table Component with some batteries included.
## Installation


## Examples
## Examples

### Simple usage
```svelte
<script lang="ts">
import { Table, Header, Row } from "surplex/svelte-tabula";
const data: any[] = [
{id: 1, name: "Daniel", jobTitle: "Software craftsman"},
{id: 3, name: "Max", jobTitle: "Head of Development"},
{id: 2, name: "Mustermann", jobTitle: "Tester"},
];
const headerDefinitions: HeaderDefinition[] = [
{prop: "id", name: "Id", editable: false, sortable: true, filterable: true},
{prop: "name", name: "Name", editable: true, sortable: true, filterable: true},
{prop: "jobTitle", name: "Job Title", editable: false, sortable: false, filterable: false},
];
</script>
<Table>
<Header slot="head" {headerDefinitions}/>
{#each data as row}
<Row data={row} />
{/each}
</Table>
```

### Modify the editable property at column level
```svelte
<script lang="ts">
import { Table, Header, Row, Column, FilterEvent } from "surplex/svelte-tabula";
const data: any[] = [
{id: 1, name: "Daniel", jobTitle: "Software craftsman"},
{id: 3, name: "Max", jobTitle: "Head of Development"},
{id: 2, name: "Mustermann", jobTitle: "Tester"},
];
const headerDefinitions: HeaderDefinition[] = [
{prop: "id", name: "Id", editable: false, sortable: true, filterable: true},
{prop: "name", name: "Name", editable: true, sortable: true, filterable: true},
{prop: "jobTitle", name: "Job Title", editable: false, sortable: false, filterable: false},
];
</script>
<Table>
<Header slot="head" on:sort={() => data.sort()} on:filter={(event: FilterEvent) => {}} {headerDefinitions}/>
{#each data as row}
<Row>
<!-- slot name equals prop value in HeaderDefinition -->
<td slot="name">Daniel</td>
</Row>
{/each}
</Table>
```

### Styling
Tabula uses the defined table structure in [html53](https://www.w3.org/TR/html53/tabular-data.html#tabular-data).
Pass an `options` object to the `<Table>` component to modify the different elements inside the table.
```svelte
<script lang="ts">
// ...
const styling: Object = {
table: ['table-class'],
td: ['bg-gray-500', 'hover:bg-gray-700']
};
</script>
<Table {options}>
</Table>
```
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 0 additions & 26 deletions src/Column.svelte

This file was deleted.

12 changes: 0 additions & 12 deletions src/Factory/RowDefinitionFactory.ts

This file was deleted.

2 changes: 0 additions & 2 deletions src/Misc/Sort.svelte → src/Filters/SortFilter.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import { createEventDispatcher } from "svelte";
import { Sort } from "../Interfaces/Events/SortEvent";
import { HeaderDefinition } from "../Interfaces/HeaderDefinition";
export let headerDefinition: HeaderDefinition;
const dispatch = createEventDispatcher();
let currentSort: Sort = "normal";
const getNewSort = () => {
Expand Down
4 changes: 0 additions & 4 deletions src/Misc/Filter.svelte → src/Filters/TextFilter.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { HeaderDefinition } from "../Interfaces/HeaderDefinition";
export let headerDefinition: HeaderDefinition;
let dropDownVisible: boolean = false;
const dispatch = createEventDispatcher();
</script>

Expand Down
56 changes: 40 additions & 16 deletions src/Header.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
<script lang="ts">
import { getContext, onMount } from "svelte";
import { optionsKey } from "./context";
import SortFilter from "./Filters/SortFilter.svelte";
import TextFilter from "./Filters/TextFilter.svelte";
import { HeaderDefinition } from "./Interfaces/HeaderDefinition";
import Filter from "./Misc/Filter.svelte";
import Sort from "./Misc/Sort.svelte";
import { headerDefinitions as hdStore } from "./stores";
export let headerDefinitions: HeaderDefinition[] = [];
onMount(() => {
hdStore.set(headerDefinitions);
});
export let headerDefinition: HeaderDefinition;
const { getClassesForElement } = getContext(optionsKey);
let dropDownVisible: HeaderDefinition | undefined = undefined;
let dropDownVisible: boolean = false;
const setDropDown = (headerDefinition: HeaderDefinition) => {
if (headerDefinition === dropDownVisible) {
dropDownVisible = undefined;
}
dropDownVisible = headerDefinition;
};
</script>

<th on:click={() => (dropDownVisible = !dropDownVisible)}>
{headerDefinition.text}
<section style={dropDownVisible ? "display: block;" : "display: none"}>
{#if headerDefinition.sortable}
<Sort {headerDefinition} on:sort />
{/if}
{#if headerDefinition.filterable}
<Filter {headerDefinition} on:filter />
{/if}
</section>
</th>
<tr class={getClassesForElement("tr")}>
{#each headerDefinitions as headerDefinition}
<th
on:click={() => setDropDown(headerDefinition)}
class={getClassesForElement("th")}
>
{headerDefinition.name}
<section
class={getClassesForElement("dropdown")}
style={dropDownVisible === headerDefinition
? "display: block;"
: "display: none"}
>
{#if headerDefinition.sortable}
<SortFilter {headerDefinition} on:sort />
{/if}
{#if headerDefinition.filterable}
<TextFilter {headerDefinition} on:filter />
{/if}
</section>
</th>
{/each}
</tr>

<style>
th {
position: relative;
display: inline-block;
}
section {
position: absolute;
background-color: #f9f9f9;
Expand Down
4 changes: 0 additions & 4 deletions src/Interfaces/ColumnDefinition.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/Interfaces/Events/EditEvent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ColumnDefinition } from "../ColumnDefinition";
import { RowDefinition } from "../RowDefinition";

export interface EditEvent {
columnDefinition: ColumnDefinition;
rowDefinition: RowDefinition;
value: string;
rawEvent: Event;
}
10 changes: 6 additions & 4 deletions src/Interfaces/HeaderDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export interface HeaderDefinition {
text: string;
sortable: boolean;
filterable: boolean;
}
prop: string;
name: string;
sortable: boolean;
filterable: boolean;
editable: boolean;
}
5 changes: 3 additions & 2 deletions src/Interfaces/RowDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ColumnDefinition } from "./ColumnDefinition";
export interface RowDefinition {
columns: ColumnDefinition[];
prop: string;
value: string;
editable: boolean;
}
53 changes: 49 additions & 4 deletions src/Row.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,57 @@
<script lang="ts">
import Column from "./Column.svelte";
import { onMount } from "svelte";
import { HeaderDefinition } from "./Interfaces/HeaderDefinition";
import { RowDefinition } from "./Interfaces/RowDefinition";
import { headerDefinitions } from "./stores";
import { createEventDispatcher } from "svelte";
export let row: Object;
const dispatch = createEventDispatcher();
let tableData: RowDefinition[];
let editActive: boolean = false;
export let rowDefinition: RowDefinition;
headerDefinitions.subscribe((headerDefinitionsValue: HeaderDefinition[]) => {
tableData = headerDefinitionsValue.map(
(headerDefinition: HeaderDefinition) => {
if (!Object.keys(row).includes(headerDefinition.prop)) {
return;
}
return {
prop: headerDefinition.prop,
value: row[headerDefinition.prop],
editable: headerDefinition.editable,
};
}
);
});
const triggerEditEvent = (
event: KeyboardEvent,
rowDefinition: RowDefinition
) => {
if (event.code != "Enter") {
return;
}
editActive = false;
dispatch("edit", { rowDefinition, rawEvent: event });
};
</script>

<tr>
{#each rowDefinition.columns as column}
<Column on:edit columnDefinition={column} />
{#each tableData as rowDefinition}
{#if $$slots[rowDefinition.prop]}
<!-- TODO: Implement dynamic slots or another solution -->
{:else}
<td
on:click={() => (rowDefinition.editable ? (editActive = true) : null)}
>
{#if rowDefinition.editable && editActive}
<textarea
on:keydown={(event) => triggerEditEvent(event, rowDefinition)}
/>
{:else}
{rowDefinition.value}
{/if}
</td>
{/if}
{/each}
</tr>
43 changes: 25 additions & 18 deletions src/Table.svelte
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
<script lang="ts">
import Row from "./Row.svelte";
import Header from "./Header.svelte";
import { HeaderDefinition } from "./Interfaces/HeaderDefinition";
import { RowDefinition } from "./Interfaces/RowDefinition";
export let headers: HeaderDefinition[] = [];
export let data: RowDefinition[] = [];
import { getContext, setContext } from "svelte";
import { optionsKey } from "./context";
export let options: any = undefined;
setContext(optionsKey, {
options,
getClassesForElement: (element: string) => {
if (!Object.keys(options).includes(element)) {
return "default_style";
}
return options[element].join(" ");
}
});
const { getClassesForElement } = getContext(optionsKey);
</script>

<table>
<thead>
<tr>
{#each headers as header}
<Header on:sort on:filter headerDefinition={header} />
{/each}
</tr>
<table class={getClassesForElement("table")}>
<thead class={getClassesForElement("thead")}>
<slot name="head" />
</thead>
<tbody>
{#each data as row}
<Row on:edit rowDefinition={row} />
{/each}
<tbody class={getClassesForElement("tbody")}>
<slot name="body" />
</tbody>
</table>

<style>
.default_style {
border: 1px solid black;
padding: .5rem;
}
</style>
1 change: 1 addition & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const optionsKey = {};
9 changes: 4 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
export { RowDefinitionFactory } from "./Factory/RowDefinitionFactory";
export type { RowDefinition } from "./Interfaces/RowDefinition";
export type { ColumnDefinition } from "./Interfaces/ColumnDefinition";
export type { HeaderDefinition } from "./Interfaces/HeaderDefinition";
export type { RowDefinition } from "./Interfaces/RowDefinition";
export type { EditEvent } from "./Interfaces/Events/EditEvent";
export type { FilterEvent } from "./Interfaces/Events/FilterEvent";
export type { Sort, SortEvent } from "./Interfaces/Events/SortEvent";
export { default as Table } from "./Table.svelte";
export { default as Row } from "./Row.svelte";
export { default as Column } from "./Column.svelte";
export { default as Header } from "./Header.svelte";
export { default as Header } from "./Header.svelte";
export { default as SortFilter } from "./Filters/SortFilter.svelte";
export { default as TextFilter } from "./Filters/TextFilter.svelte";
4 changes: 4 additions & 0 deletions src/stores.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Writable, writable } from "svelte/store";
import { HeaderDefinition } from "./Interfaces/HeaderDefinition";

export const headerDefinitions: Writable<Array<HeaderDefinition>> = writable(new Array<HeaderDefinition>());

0 comments on commit b4c896f

Please sign in to comment.