Skip to content
Open
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
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,89 @@ HyperTableV2 supports several named blocks that allow you to customize specific
</HyperTableV2>
```

#### HyperTableV2 options

`@options` lets you configure optional component behaviors.

##### selectionIntlKeyPath

- Type: `string`
- Required: no

Custom base i18n key path used by `HyperTableV2::Selection` for:

- `<path>.all_records_selected`
- `<path>.records_selected`
- `<path>.select_all`

Default path: `hypertable.selection`.
Note: the clear action label currently uses `hypertable.selection.clear` directly.

```ts
options = {
selectionIntlKeyPath: 'my.table.selection'
};
```

##### delegatedFiltering

- Type: `boolean`
- Required: no

Disables built-in column filter UI and ordering indicators in `HyperTableV2::Column`.
Use this when filtering and sorting are handled by external controls.

```ts
options = {
delegatedFiltering: true
};
```

##### initialRowsAnimation

- Type: `object`
- Required: no

Enables a one-time animation sequence on the first successful non-empty rows load.

Behavior:

- Base behavior: rows are revealed with a staggered sequence across all non-loading cells.
- Extra class behavior: when `extraColumnCellEffectClass` is set, that class is added on top of the base sequence on each cell that matches the columns specified in `columns`.
- Selection column behavior: by default, the extra class does not apply on selection checkbox cells. Set `includeSelectionColumnInExtraEffect` to `true` to include them.
- Extra class delay behavior: `extraColumnCellEffectDelayMs` adds an extra delay before the extra class effect starts.
- If `columns` is omitted or empty, `extraColumnCellEffectClass` is applied to cells from all columns.


```ts
options = {
initialRowsAnimation: {
delayMs: 300,
staggerMs: 40,
maxAnimationDurationMs: 1500,
extraColumnCellEffectDelayMs: 120,
extraColumnCellEffectClass: 'smart-rotating-gradient',
columns: ['foo', 'bar'],
includeSelectionColumnInExtraEffect: false
}
};
```

Fields:

- `delayMs` (number): Delay before the sequence starts. Default: `300`.
- `staggerMs` (number): Extra delay applied per row (`rowIndex * staggerMs`). Default: `40`.
- `maxAnimationDurationMs` (number): Max duration used by the animation window timing. Default: `1500`.
- `extraColumnCellEffectDelayMs` (number, optional): Extra delay applied before the `extraColumnCellEffectClass` effect starts. Default: `0`.
- `extraColumnCellEffectClass` (string): Optional extra CSS class added to targeted cells while animation is active.
- `columns` (string[]): Column keys that receive `extraColumnCellEffectClass`. If omitted or empty, the extra class is applied to all columns.
- `includeSelectionColumnInExtraEffect` (boolean): Whether the extra class should also be applied on selection checkbox cells when selection is enabled. Default: `false`.

Notes:

- The sequence runs once per component lifecycle.
- The active window is capped internally to avoid overly long animations on large datasets.

## Core Concepts

### Column Definitions
Expand Down
4 changes: 4 additions & 0 deletions addon/components/hyper-table-v2/cell.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
"hypertable__cell"
(if this.loading " hypertable__cell--loading")
(if @row.hovered " hypertable__cell--hovered")
(if this.initialRowsAnimationSequenceClass (concat " " this.initialRowsAnimationSequenceClass))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not for this time but this just made the cooldown list, so we can put this behind a computed classes getter or something 👀

Image

@Miexil Miexil Aug 19, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you be a bit more explicit on what you mean here ? ^^ Not sure about the "cooldown list"?

Something like:
class={{this.computedClasses}} ??

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep ^^

(if this.initialRowsAnimationCellClass (concat " " this.initialRowsAnimationCellClass))
}}
style={{this.initialRowsAnimationCellStyle}}
role="button"
{{will-destroy this.teardown}}
{{on "click" this.clickedCell}}
{{on "mouseenter" (fn this.toggleHover @row true)}}
{{on "mouseleave" (fn this.toggleHover @row false)}}
Expand Down
115 changes: 115 additions & 0 deletions addon/components/hyper-table-v2/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import type { InitialRowsAnimationContext } from '@upfluence/hypertable/components/hyper-table-v2';
import TableHandler from '@upfluence/hypertable/core/handler';
import { Column, ResolvedRenderingComponent, Row } from '@upfluence/hypertable/core/interfaces';

interface HyperTableV2CellArgs {
handler: TableHandler;
column: Column;
row: Row;
rowIndex?: number;
initialRowsAnimation?: InitialRowsAnimationContext | null;
disableInitialRowsAnimationExtraEffect?: boolean;
loading: boolean;
onClick?(row: Row): void;
onHover?(row: Row, hovered: boolean): void;
Expand All @@ -17,6 +21,9 @@ interface HyperTableV2CellArgs {
export default class HyperTableV2Cell extends Component<HyperTableV2CellArgs> {
@tracked loadingCellComponent: boolean = true;
@tracked cellComponent?: ResolvedRenderingComponent;
@tracked extraEffectReady: boolean = false;

private extraEffectTimeout?: number;

constructor(owner: unknown, args: HyperTableV2CellArgs) {
super(owner, args);
Expand All @@ -37,6 +44,109 @@ export default class HyperTableV2Cell extends Component<HyperTableV2CellArgs> {
return this.args.loading || this.loadingCellComponent;
}

get initialRowsAnimationCellClass(): string {
const extraColumnCellEffectClass = this.args.initialRowsAnimation?.extraColumnCellEffectClass;

if (!this.shouldApplyInitialRowsAnimationCustomEffect || !extraColumnCellEffectClass) {
this.resetExtraEffectState();
return '';
}

if (this.extraEffectActivationDelayMs <= 0) {
return extraColumnCellEffectClass;
}

this.scheduleExtraEffectIfNeeded();

return this.extraEffectReady ? extraColumnCellEffectClass : '';
}

get initialRowsAnimationSequenceClass(): string {
if (!this.shouldApplyInitialRowsAnimationSequence) {
return '';
}

return 'hypertable__cell--initial-load-sequence';
}

get initialRowsAnimationCellStyle(): string | undefined {
if (!this.shouldApplyInitialRowsAnimationSequence) {
return undefined;
}

const extraColumnCellEffectDelayMs = this.args.initialRowsAnimation?.extraColumnCellEffectDelayMs ?? 0;
const staggeredDelayMs = this.rowAnimationDelayMs;
const extraEffectDelayMs = staggeredDelayMs + extraColumnCellEffectDelayMs;

return `--hypertable-initial-rows-animation-delay: ${staggeredDelayMs}ms; --hypertable-initial-rows-extra-effect-delay: ${extraEffectDelayMs}ms;`;
}

private get rowAnimationDelayMs(): number {
const delayMs = this.args.initialRowsAnimation?.delayMs ?? 0;
const staggerMs = this.args.initialRowsAnimation?.staggerMs ?? 0;
const rowIndex = this.args.rowIndex ?? 0;

return delayMs + rowIndex * staggerMs;
}

private get isInitialRowsAnimationEnabled(): boolean {
return this.args.initialRowsAnimation?.active === true;
}

private get isInitialRowsAnimationTargetedColumn(): boolean {
const columns = this.args.initialRowsAnimation?.columns;

if (!columns || columns.length === 0) {
return true;
}

return columns.includes(this.args.column.definition.key);
}

private get shouldApplyInitialRowsAnimationSequence(): boolean {
return this.isInitialRowsAnimationEnabled && !this.loading;
}

private get shouldApplyInitialRowsAnimationCustomEffect(): boolean {
if (this.args.disableInitialRowsAnimationExtraEffect) {
return false;
}

return this.shouldApplyInitialRowsAnimationSequence && this.isInitialRowsAnimationTargetedColumn;
}

private get extraEffectActivationDelayMs(): number {
const extraColumnCellEffectDelayMs = this.args.initialRowsAnimation?.extraColumnCellEffectDelayMs ?? 0;
return this.rowAnimationDelayMs + extraColumnCellEffectDelayMs;
}

private scheduleExtraEffectIfNeeded(): void {
if (this.extraEffectReady || this.extraEffectTimeout) {
return;
}

const activationDelayMs = this.extraEffectActivationDelayMs;

if (activationDelayMs <= 0) {
this.extraEffectReady = true;
return;
}

this.extraEffectTimeout = window.setTimeout(() => {
this.extraEffectReady = true;
this.extraEffectTimeout = undefined;
}, activationDelayMs);
}

private resetExtraEffectState(): void {
if (this.extraEffectTimeout) {
window.clearTimeout(this.extraEffectTimeout);
this.extraEffectTimeout = undefined;
}

this.extraEffectReady = false;
}

@action
clickedCell(event: MouseEvent) {
event.stopPropagation();
Expand All @@ -50,4 +160,9 @@ export default class HyperTableV2Cell extends Component<HyperTableV2CellArgs> {
toggleHover(row: Row, hovered: boolean) {
this.args.onHover?.(row, hovered);
}

@action
teardown() {
this.resetExtraEffectState();
}
}
13 changes: 10 additions & 3 deletions addon/components/hyper-table-v2/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,14 @@
/>
</header>

{{#each @handler.rows as |row|}}
{{#each @handler.rows as |row rowIndex|}}
<HyperTableV2::Cell
@handler={{@handler}}
@column={{column}}
@row={{row}}
@rowIndex={{rowIndex}}
@initialRowsAnimation={{this.initialRowsAnimationContext}}
@disableInitialRowsAnimationExtraEffect={{this.disableInitialRowsAnimationExtraEffectOnSelectionCells}}
@onClick={{fn this.toggleRowSelection row}}
@onHover={{this.onRowHover}}
@loading={{row._isLoading}}
Expand Down Expand Up @@ -142,11 +145,13 @@
@column={{column}}
@delegatedFiltering={{@options.delegatedFiltering}}
>
{{#each @handler.rows as |row|}}
{{#each @handler.rows as |row rowIndex|}}
<HyperTableV2::Cell
@handler={{@handler}}
@column={{column}}
@row={{row}}
@rowIndex={{rowIndex}}
@initialRowsAnimation={{this.initialRowsAnimationContext}}
@onClick={{this.onRowClick}}
@onHover={{this.onRowHover}}
@loading={{row._isLoading}}
Expand Down Expand Up @@ -175,11 +180,13 @@
disabled=column.definition.position.sticky
}}
>
{{#each @handler.rows as |row|}}
{{#each @handler.rows as |row rowIndex|}}
<HyperTableV2::Cell
@handler={{@handler}}
@column={{column}}
@row={{row}}
@rowIndex={{rowIndex}}
@initialRowsAnimation={{this.initialRowsAnimationContext}}
@onClick={{this.onRowClick}}
@onHover={{this.onRowHover}}
@loading={{row._isLoading}}
Expand Down
Loading
Loading