Skip to content
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
58 changes: 58 additions & 0 deletions table/src/-private/-type-tests/any-column.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { expectTypeOf } from 'expect-type';

import { headlessTable } from '../../index.ts';
import { isVisible, hide } from '../../plugins/column-visibility/index.ts';
import { sort } from '../../plugins/data-sorting/index.ts';

import type { AnyColumn, AnyTable, CellContext, Column } from '../../index.ts';
import type { ComponentLike } from '@glint/template';

interface Person {
name: string;
}
declare const people: Person[];

declare const GroupedCell: ComponentLike<
CellContext<Person> & { groupBy: 'day' | 'week' }
>;

const table = headlessTable(
{},
{
columns: () => [{ key: 'name', Cell: GroupedCell, meta: { width: 120 } }],
data: () => people,
},
);

/////////////////////////////////////////////
// `Column` keeps a Cell that is checked: a Cell of a column written by hand
// still takes `@row` and `@column`, and nothing else.
expectTypeOf<NonNullable<Column<Person>['Cell']>>().toEqualTypeOf<
ComponentLike<CellContext<Person, unknown, any>>
>();

/////////////////////////////////////////////
// `AnyColumn` is the column of any table: what its Cell takes is not known here.
expectTypeOf<NonNullable<AnyColumn<Person>['Cell']>>().toEqualTypeOf<
ComponentLike<any>
>();

// Code that reads a column takes any column, whatever its Cells ask for.
function keyOf(column: AnyColumn<Person>) {
return column.key;
}
function widthOf(column: AnyColumn<Person, { width?: number }>) {
return column.meta?.width;
}
function columnCount(table: AnyTable<Person>) {
return table.columns.length;
}

keyOf(table.columns[0]!);
widthOf(table.columns[0]!);
columnCount(table);

// and so do the plugins
isVisible(table.columns[0]!);
hide(table.columns[0]!);
sort(table.columns[0]!);
13 changes: 10 additions & 3 deletions table/src/-private/-type-tests/cell-args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import {
import { DataSorting, sort } from '../../plugins/data-sorting/index.ts';
import { meta } from '../../plugins/index.ts';

import type { CellContext, Column, ColumnConfig, Table } from '../../index.ts';
import type {
AnyColumn,
AnyTable,
CellContext,
Column,
ColumnConfig,
Table,
} from '../../index.ts';
import type { ComponentLike } from '@glint/template';

interface Person {
Expand Down Expand Up @@ -168,10 +175,10 @@ expectTypeOf<CellArgsOf<(typeof plain.columns)[0]['Cell']>>().toEqualTypeOf<

/////////////////////////////////////////////
// A column whose Cell takes args fits code that knows nothing about them
function takesAnyColumn(column: Column<Person>) {
function takesAnyColumn(column: AnyColumn<Person>) {
return column.key;
}
function takesAnyTable(table: Table<Person>) {
function takesAnyTable(table: AnyTable<Person>) {
return table.columns.length;
}
takesAnyColumn(grouped.columns[0]!);
Expand Down
32 changes: 32 additions & 0 deletions table/src/-private/any.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Column } from './column.ts';
import type { Table } from './table.ts';

/**
* A column of any table, for code that reads a column without rendering its Cell.
*
* The args of a `Cell` are the args of a function, so a column that carries them
* is not a column that carries none. Code that only reads `key`, `name` or `meta`
* would have to name those args and then ignore them. This says "any args" instead.
*
* ```ts
* function widthOf(column: AnyColumn<Person, ExportMeta>) {
* return column.meta?.width;
* }
* ```
*/
export type AnyColumn<T = unknown, ColumnMeta = unknown> = Column<
T,
ColumnMeta,
any,
any
>;

/**
* A table of any shape, the counterpart of `AnyColumn`.
*/
export type AnyTable<T = unknown, ColumnMeta = unknown> = Table<
T,
ColumnMeta,
any,
any
>;
2 changes: 1 addition & 1 deletion table/src/-private/column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class Column<
T = unknown,
ColumnMeta = unknown,
Meta = unknown,
CellArgs = any,
CellArgs = unknown,
> {
get Cell():
| ComponentLike<CellContext<T, unknown, any> & CellArgs>
Expand Down
9 changes: 5 additions & 4 deletions table/src/-private/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { composeFunctionModifiers } from './utils.ts';

import type { BasePlugin, Plugin } from '../plugins/index.ts';
import type { Class } from './private-types.ts';
import type { AnyColumn } from './any.ts';
import type { ColumnConfig, Destructor, TableConfig } from './interfaces';
import type Owner from '@ember/owner';
import { compatOwner } from './ember-compat.ts';
Expand Down Expand Up @@ -55,7 +56,7 @@ export interface Table<
DataType = unknown,
ColumnMeta = unknown,
Meta = unknown,
CellArgs = any,
CellArgs = unknown,
> {
/**
* @private
Expand Down Expand Up @@ -87,7 +88,7 @@ export class Table<
DataType = unknown,
ColumnMeta = unknown,
Meta = unknown,
CellArgs = any,
CellArgs = unknown,
> {
/**
* @private
Expand Down Expand Up @@ -198,7 +199,7 @@ export class Table<
}>;
columnHeader: FunctionBasedModifier<{
Element: HTMLElement;
Args: { Positional: [Column<DataType>]; Named: EmptyObject };
Args: { Positional: [AnyColumn<DataType>]; Named: EmptyObject };
}>;
row: FunctionBasedModifier<{
Element: HTMLElement;
Expand All @@ -225,7 +226,7 @@ export class Table<
// With curried+composed modifiers, only the plugin's headerModifier
// that has tracked changes would run, leaving the other modifiers alone
columnHeader: modifier(
(element: HTMLElement, [column]: [Column<DataType>]): Destructor => {
(element: HTMLElement, [column]: [AnyColumn<DataType>]): Destructor => {
const modifiers = this.plugins.map(
(plugin) => plugin.headerCellModifier,
);
Expand Down
1 change: 1 addition & 0 deletions table/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export { deserializeSorts, serializeSorts } from './utils.ts';
* Public Types
*******************************/
export type { Column } from './-private/column.ts';
export type { AnyColumn, AnyTable } from './-private/any.ts';
export type {
CellContext,
ColumnConfig,
Expand Down
15 changes: 8 additions & 7 deletions table/src/plugins/-private/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { normalizePluginsConfig } from './utils.ts';

import type { Table } from '../../-private/table.ts';
import type { AnyColumn, AnyTable } from '../../-private/any.ts';
import type { ColumnReordering } from '../column-reordering';
import type { ColumnVisibility } from '../column-visibility';
import type { Class, Constructor } from '../../-private/private-types.ts';
Expand Down Expand Up @@ -165,7 +166,7 @@ export const preferences = {
* works, they can access this data, too. No security guaranteed)
*/
forColumn<P extends BasePlugin<any>, Data = unknown>(
column: Column<Data>,
column: AnyColumn<Data>,
klass: Class<P>,
) {
return {
Expand Down Expand Up @@ -328,7 +329,7 @@ function columnsFor<
}

function resolveColumns<DataType = any>(
table: Table<DataType>,
table: AnyTable<DataType>,
requester?: Plugin<any>,
): Column<DataType>[] {
assert(
Expand Down Expand Up @@ -575,7 +576,7 @@ export const meta = {
* Note that this requires the column instance to exist on the table.
*/
forColumn<P extends BasePlugin<any>, Data = unknown>(
column: Column<Data>,
column: AnyColumn<Data>,
klass: Class<P>,
): ColumnMetaFor<SignatureFrom<P>> {
const columnMeta = column.table[COLUMN_META_KEY];
Expand Down Expand Up @@ -686,7 +687,7 @@ export const meta = {
* For example, multiple column-focused plugins may care about width or visibility
*/
forColumn<FeatureName extends string, Data = unknown>(
column: Column<Data>,
column: AnyColumn<Data>,
featureName: FeatureName,
): ColumnFeatures[FeatureName] {
const { plugins } = column.table;
Expand Down Expand Up @@ -793,7 +794,7 @@ export const options = {
},

forColumn<P extends BasePlugin<any>, Data = unknown>(
column: Column<Data>,
column: AnyColumn<Data>,
klass: Class<P>,
): Partial<ColumnOptionsFor<SignatureFrom<P>>> {
const tuple = column.config.pluginOptions?.find(
Expand All @@ -819,13 +820,13 @@ function getPluginInstance<Instance>(
mapKey: Class<Instance>,
factory: () => Instance,
): Instance;
function getPluginInstance<RootKey extends Column<any> | Row<any>, Instance>(
function getPluginInstance<RootKey extends AnyColumn<any> | Row<any>, Instance>(
map: WeakMap<Column | Row, Map<Class<Instance>, Instance>>,
rootKey: RootKey,
mapKey: Class<Instance>,
factory: () => Instance,
): Instance;
function getPluginInstance<RootKey extends Column<any> | Row<any>, Instance>(
function getPluginInstance<RootKey extends AnyColumn<any> | Row<any>, Instance>(
...args:
| [FactoryMap<Instance>, Class<Instance>, () => Instance]
| [
Expand Down
21 changes: 12 additions & 9 deletions table/src/plugins/column-reordering/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@ import { ColumnReordering } from './plugin.ts';

import type { ColumnOrder, TableMeta } from './plugin.ts';
import type { Column, Table } from '../../index.ts';
import type { AnyColumn, AnyTable } from '../../-private/any.ts';

/**
* Move the column one position to the left.
* If the column is first, nothing will happen.
*/
export const moveLeft = <DataType = unknown>(column: Column<DataType>): void =>
meta.forColumn(column, ColumnReordering).moveLeft();
export const moveLeft = <DataType = unknown>(
column: AnyColumn<DataType>,
): void => meta.forColumn(column, ColumnReordering).moveLeft();

/**
* Move the column one position to the right.
* If the column is last, nothing will happen.
*/
export const moveRight = <DataType = unknown>(column: Column<DataType>): void =>
meta.forColumn(column, ColumnReordering).moveRight();
export const moveRight = <DataType = unknown>(
column: AnyColumn<DataType>,
): void => meta.forColumn(column, ColumnReordering).moveRight();

/**
* Override all column positions at once.
*/
export const setColumnOrder = <DataType = unknown>(
table: Table<DataType>,
table: AnyTable<DataType>,
order: ColumnOrder<DataType>,
): void => {
// Note: The meta.forTable API doesn't preserve the DataType generic from the table parameter.
Expand All @@ -38,22 +41,22 @@ export const setColumnOrder = <DataType = unknown>(
* Ask if the column cannot move to the left
*/
export const cannotMoveLeft = <DataType = unknown>(
column: Column<DataType>,
column: AnyColumn<DataType>,
): boolean => meta.forColumn(column, ColumnReordering).cannotMoveLeft;

/**
* Ask if the column cannot move to the right
*/
export const cannotMoveRight = <DataType = unknown>(
column: Column<DataType>,
column: AnyColumn<DataType>,
): boolean => meta.forColumn(column, ColumnReordering).cannotMoveRight;

/**
* Ask if the column can move to the left
* (If your plugin doesn't expose `canMoveLeft`, use `!cannotMoveLeft`.)
*/
export const canMoveLeft = <DataType = unknown>(
column: Column<DataType>,
column: AnyColumn<DataType>,
): boolean =>
// Prefer this if available:
// meta.forColumn(column, ColumnReordering).canMoveLeft
Expand All @@ -64,7 +67,7 @@ export const canMoveLeft = <DataType = unknown>(
* (If your plugin doesn't expose `canMoveRight`, use `!cannotMoveRight`.)
*/
export const canMoveRight = <DataType = unknown>(
column: Column<DataType>,
column: AnyColumn<DataType>,
): boolean =>
// Prefer this if available:
// meta.forColumn(column, ColumnReordering).canMoveRight
Expand Down
8 changes: 5 additions & 3 deletions table/src/plugins/column-resizing/handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Modifier from 'ember-modifier';
import { meta } from '../-private/base.ts';
import { ColumnResizing } from './plugin.ts';

import type { AnyColumn } from '../../-private/any.ts';

import type { ColumnMeta } from './plugin.ts';
import type { Column } from '../../-private/column.ts';

Expand All @@ -25,10 +27,10 @@ const waiter = buildWaiter('ColumnResizing#ResizeHandle');
*/

class ResizeHandle<DataType = unknown> extends Modifier<{
Args: { Positional: [Column<DataType>] };
Args: { Positional: [AnyColumn<DataType>] };
}> {
declare dragHandle: HTMLElement;
declare column: Column<DataType>;
declare column: AnyColumn<DataType>;
declare meta: ColumnMeta;

// Pointer
Expand All @@ -47,7 +49,7 @@ class ResizeHandle<DataType = unknown> extends Modifier<{
token?: unknown;

isSetup = false;
modify(element: Element, [column]: [Column<DataType>]): void {
modify(element: Element, [column]: [AnyColumn<DataType>]): void {
this.column = column;
this.meta = meta.forColumn(column, ColumnResizing);
this.dragHandle = element as HTMLElement;
Expand Down
Loading