Skip to content

Commit

Permalink
feat: allow to resize grid view columns
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Sep 13, 2024
1 parent 075bc69 commit da211ae
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<script lang="ts">
import * as ContextMenu from "$lib/components/ui/context-menu"
import { Render, Subscribe, createRender, createTable } from "svelte-headless-table"
import { writable, type Readable, type Writable } from "svelte/store"
import { derived, writable, type Readable, type Writable } from "svelte/store"
import GridViewCheckbox from "./grid-view-checkbox.svelte"
import * as Table from "$lib/components/ui/table/index.js"
import { addResizedColumns, addSelectedRows } from "svelte-headless-table/plugins"
import { copyToClipboard } from "@svelte-put/copy"
import { toast } from "svelte-sonner"
import { cn } from "$lib/utils.js"
import { getRecordsStore } from "$lib/store/records.store"
import { type Field } from "@undb/table"
import { GridView, type Field } from "@undb/table"
import { getTable } from "$lib/store/table.store"
import GridViewActions from "./grid-view-actions.svelte"
import GridViewActionHeader from "./grid-view-action-header.svelte"
Expand All @@ -30,7 +30,9 @@
import { gridViewStore, isRowSelected, isSelectedCell } from "./grid-view.store"
import SelectedRecordsButton from "./selected-records-button.svelte"
import { aggregatesStore } from "$lib/store/aggregates.store"
import ViewPagination from "../view/view-pagination.svelte"
import ViewPagination from "../view/view-pagination.svelte"
import { createMutation } from "@tanstack/svelte-query"
import { trpc } from "$lib/trpc/client"
export let readonly = false
export let viewId: Readable<string>
Expand All @@ -41,6 +43,8 @@
const t = getTable()
let fields = derived(t, ($t) => $t?.getOrderedVisibleFields($viewId) ?? ([] as Field[]))
const r = queryParam("r")
const deleteRecordId = queryParam("deleteRecordId")
const duplicateRecordId = queryParam("duplicateRecordId")
Expand All @@ -50,11 +54,11 @@
toast.success("Copied record ID to clipboard")
}
$: view = $t.views.getViewById($viewId)
$: viewFilter = view.filter.into(undefined)
let view = derived(t, ($t) => $t.views.getViewById($viewId) as GridView)
$: viewFilter = $view.filter.into(undefined)
$: hasFilterFieldIds = viewFilter?.fieldIds
$: colorSpec = view.color.into(undefined)?.getSpec($t.schema).into(undefined)
$: colorSpec = $view.color.into(undefined)?.getSpec($t.schema).into(undefined)
$: getTableAggregates = aggregatesStore.getTableAggregates
$: aggregates = $getTableAggregates($t.id.value)
Expand All @@ -63,14 +67,17 @@
let hasRecord = store.hasRecord
let count = store.count
const setFieldWidth = createMutation({
mutationFn: trpc.table.view.setFieldWidth.mutate,
})
const table = createTable(store.data, {
select: addSelectedRows(),
resize: addResizedColumns(),
})
$: fields = $t?.getOrderedVisibleFields($viewId) ?? ([] as Field[])
$: columns =
table.createColumns([
let columns = derived([fields, view], ([$fields, $view]) => {
return table.createColumns([
table.column({
accessor: "$select",
header: (_, { pluginStates }) => {
Expand Down Expand Up @@ -107,7 +114,7 @@
},
},
}),
...fields.map((field, index) =>
...$fields.map((field, index) =>
table.column({
header: () => createRender(GridViewHeader, { field }),
accessor: field.id.value,
Expand All @@ -131,7 +138,8 @@
}),
plugins: {
resize: {
initialWidth: 200,
initialWidth: $view.getFieldWidth(field.id.value),
disable: readonly,
},
},
}),
Expand All @@ -149,10 +157,10 @@
},
},
}),
]) ?? []
])
})
const viewModel = writable(table.createViewModel(columns ?? []))
$: columns, viewModel.set(table.createViewModel(columns))
const viewModel = derived(columns, ($columns) => table.createViewModel($columns))
$: visibleColumns = $viewModel.visibleColumns
$: headerRows = $viewModel.headerRows
Expand All @@ -166,7 +174,7 @@
.map(Number)
.map((index) => $store.ids[index])
$: resize = $viewModel.pluginStates.resize.columnWidths
$: columnWidths = $viewModel.pluginStates.resize.columnWidths
</script>

<div class="flex h-full w-full flex-col">
Expand All @@ -186,20 +194,37 @@
{#each headerRow.cells as cell, i (cell.id)}
<Subscribe attrs={cell.attrs()} let:attrs props={cell.props()} let:props>
{@const hasFilter = hasFilterFieldIds?.has(cell.id) ?? false}
<Table.Head
<th
{...attrs}
class={cn(
"h-9 border-r [&:has([role=checkbox])]:pl-3",
"text-muted-foreground relative h-9 border-r px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pl-3 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
i === 0 && "border-r-0",
hasFilter && "bg-orange-50",
)}
use:props.resize
>
{#if cell.id === "$select" && !$hasRecord}
<Checkbox checked={false} disabled />
{:else}
<Render of={cell.render()} />
{#if !props.resize.disabled}
<button
class="absolute bottom-0 right-0 top-0 z-10 w-1 cursor-col-resize bg-transparent transition-colors hover:bg-sky-500/50"
use:props.resize.drag
on:mouseup={() => {
const fieldId = cell.id
const width = $columnWidths[fieldId]
$setFieldWidth.mutate({
tableId: $t.id.value,
viewId: $viewId,
field: fieldId,
width,
})
}}
/>
{/if}
{/if}
</Table.Head>
</th>
</Subscribe>
{/each}
</Table.Row>
Expand Down Expand Up @@ -307,7 +332,7 @@
<tfooter class="text-muted-foreground sticky bottom-0 h-8 w-full border-t bg-white text-sm">
<tr class="flex h-8 w-full">
{#each $visibleColumns as column}
{@const width = $resize[column.id]}
{@const width = $columnWidths[column.id]}
<td
style={`width: ${width}px; min-width: ${width}px; max-width: ${width}px`}
class="h-full overflow-hidden"
Expand Down
4 changes: 4 additions & 0 deletions packages/persistence/src/table/table.filter-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
WithViewAggregate,
WithViewColor,
WithViewFields,
WithViewFieldWidth,
WithViewFilter,
WithViewIdSpecification,
WithViewOption,
Expand Down Expand Up @@ -72,6 +73,9 @@ export class TableFilterVisitor extends AbstractQBVisitor<TableDo> implements IT
),
)
}
withViewFieldWidth(views: WithViewFieldWidth): void {
throw new Error("Method not implemented.")
}
withDuplicatedTable(spec: DuplicatedTableSpecification): void {
throw new Error("Method not implemented.")
}
Expand Down
6 changes: 5 additions & 1 deletion packages/persistence/src/table/table.mutation-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
WithView,
WithViewAggregate,
WithViewColor,
WithViewFieldWidth,
WithViewFields,
WithViewFilter,
WithViewIdSpecification,
Expand All @@ -34,7 +35,7 @@ import type {
WithoutView,
} from "@undb/table"
import { AbstractQBMutationVisitor } from "../abstract-qb.visitor"
import { json,type IQueryBuilder } from "../qb"
import { json, type IQueryBuilder } from "../qb"
import { tables } from "../tables"

export class TableMutationVisitor extends AbstractQBMutationVisitor implements ITableSpecVisitor {
Expand Down Expand Up @@ -151,6 +152,9 @@ export class TableMutationVisitor extends AbstractQBMutationVisitor implements I
withView(views: WithView): void {
this.setData(tables.views.name, json(this.table.views.toJSON()))
}
withViewFieldWidth(views: WithViewFieldWidth): void {
this.setData(tables.views.name, json(this.table.views.toJSON()))
}
withNewView(views: WithNewView): void {
this.setData(tables.views.name, json(this.table.views.toJSON()))

Expand Down
2 changes: 2 additions & 0 deletions packages/persistence/src/table/table.reference-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
WithViewAggregate,
WithViewColor,
WithViewFields,
WithViewFieldWidth,
WithViewFilter,
WithViewIdSpecification,
WithViewOption,
Expand Down Expand Up @@ -73,6 +74,7 @@ export class TableReferenceVisitor implements ITableSpecVisitor {
withFormId(spec: WithFormIdSpecification): void {
this.sqb = this.sqb.leftJoin("undb_table_id_mapping", "undb_table_id_mapping.table_id", "undb_table.id")
}
withViewFieldWidth(spec: WithViewFieldWidth): void {}
withNewForm(views: WithNewFormSpecification): void {}
withoutForm(spec: WithoutFormSpecification): void {}
withForm(views: WithFormSpecification): void {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ReferenceField,
UPDATED_AT_TYPE,
UPDATED_BY_TYPE,
WithViewFieldWidth,
type DuplicatedTableSpecification,
type ITableSpecVisitor,
type SelectField,
Expand Down Expand Up @@ -197,6 +198,7 @@ export class UnderlyingTableSpecVisitor implements ITableSpecVisitor {
}
}
withViewFields(fields: WithViewFields): void {}
withViewFieldWidth(spec: WithViewFieldWidth): void {}
withForm(views: WithFormSpecification): void {}
withForms(views: TableFormsSpecification): void {}
withNewForm(views: WithNewFormSpecification): void {}
Expand Down
8 changes: 7 additions & 1 deletion packages/table/src/methods/set-field-width.method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,11 @@ export function setFieldWidth(this: TableDo, dto: ISetFieldWidthDTO): Option<Tab
throw new Error("View type is not grid")
}

throw new Error("Not implemented")
const spec = view.$setFieldWidthSpec(dto.field, dto.width)

if (spec.isSome()) {
spec.unwrap().mutate(this)
}

return spec
}
1 change: 0 additions & 1 deletion packages/table/src/methods/set-view-filter.method.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Option } from "@undb/domain"
import type { ISetViewFilterDTO } from "../dto"
import { SetViewFilterEvent } from "../events"
import { ViewIdVo } from "../modules"
import type { TableComositeSpecification } from "../specifications"
import type { TableDo } from "../table.do"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export abstract class AbstractView {
abstract $duplicate(dto: IDuplicateViewDTO): Option<WithNewView>

$delete(): Option<WithoutView> {
return Some(new WithoutView(this as View))
return Some(new WithoutView(this as unknown as View))
}

setFilter(filter: IRootViewFilter) {
Expand Down
34 changes: 33 additions & 1 deletion packages/table/src/modules/views/view/variants/grid-view.vo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { None, Option, Some } from "@undb/domain"
import { z } from "@undb/zod"
import type { IDuplicateViewDTO } from "../../../../dto"
import { WithNewView, WithView } from "../../../../specifications/table-view.specification"
import { WithNewView, WithView, WithViewFieldWidth } from "../../../../specifications/table-view.specification"
import { FieldIdVo } from "../../../schema/fields/field-id.vo"
import { ViewIdVo } from "../view-id.vo"
import { AbstractView, baseViewDTO, createBaseViewDTO, updateBaseViewDTO } from "./abstract-view.vo"

Expand Down Expand Up @@ -47,6 +48,29 @@ export class GridView extends AbstractView {
return new GridView({ ...dto, id: ViewIdVo.fromStringOrCreate(dto.id).value })
}

getFieldWidth(fieldId: string) {
return this.grid.into(undefined)?.widths?.[fieldId] ?? 200
}

setFieldWidth(fieldId: string, width: number) {
this.grid = Some({
...this.grid.into(undefined),
widths: {
...this.grid.into(undefined)?.widths,
[fieldId]: width,
},
})
}

$setFieldWidthSpec(fieldId: string, width: number): Option<WithViewFieldWidth> {
const previous = this.grid.into(undefined)?.widths?.[fieldId]
if (previous === width) {
return None
}

return Some(new WithViewFieldWidth(this.id, new FieldIdVo(fieldId), width))
}

override type = GRID_TYPE

override $update(input: IUpdateGridViewDTO): Option<WithView> {
Expand Down Expand Up @@ -78,4 +102,12 @@ export class GridView extends AbstractView {
),
)
}

override toJSON(): IGridViewDTO {
return {
...super.toJSON(),
type: GRID_TYPE,
grid: this.grid.into(undefined),
}
}
}
28 changes: 26 additions & 2 deletions packages/table/src/specifications/table-view.specification.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Ok, Option, WontImplementException, type Result } from "@undb/domain"
import type { IRootViewFilter, View, ViewId } from "../modules"
import { Ok,Option,WontImplementException,type Result } from "@undb/domain"
import type { FieldId,IRootViewFilter,View,ViewId } from "../modules"
import type { IViewAggregate } from "../modules/views/view/view-aggregate/view-aggregate.vo"
import type { IRootViewColor } from "../modules/views/view/view-color"
import type { IViewFields } from "../modules/views/view/view-fields"
Expand Down Expand Up @@ -210,3 +210,27 @@ export class WithViewIdSpecification extends TableComositeSpecification {
return Ok(undefined)
}
}

export class WithViewFieldWidth extends TableComositeSpecification {
constructor(
public readonly viewId: ViewId,
public readonly fieldId: FieldId,
public readonly width: number,
) {
super()
}
isSatisfiedBy(t: TableDo): boolean {
throw new WontImplementException(WithViewFieldWidth.name + ".isSatisfiedBy")
}
mutate(t: TableDo): Result<TableDo, string> {
const view = t.views.getViewById(this.viewId.value)
if (view.type === 'grid') {
view.setFieldWidth(this.fieldId.value, this.width)
}
return Ok(t)
}
accept(v: ITableSpecVisitor): Result<void, string> {
v.withViewFieldWidth(this)
return Ok(undefined)
}
}
2 changes: 2 additions & 0 deletions packages/table/src/specifications/table-visitor.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
WithViewAggregate,
WithViewColor,
WithViewFields,
WithViewFieldWidth,
WithViewFilter,
WithViewIdSpecification,
WithViewOption,
Expand Down Expand Up @@ -67,4 +68,5 @@ export interface ITableSpecVisitor extends ISpecVisitor {
withForeignRollupField(spec: WithForeignRollupFieldSpec): void
withTableForeignTables(spec: WithTableForeignTablesSpec): void
withTableUnqueName(spec: TableUniqueNameSpecification): void
withViewFieldWidth(spec: WithViewFieldWidth): void
}

0 comments on commit da211ae

Please sign in to comment.