|
|
|
|
@ -5,13 +5,13 @@ import { readableActive, type Active, type ActiveProps, type StateProps } from '
|
|
|
|
|
import type { OnChangeFn, Direction, SomaKeyboardEvent, SomaMouseEvent } from '../../types';
|
|
|
|
|
import { KEYS, getDirectionalKeys } from '../../keyboard';
|
|
|
|
|
import { Soma } from '../../core/soma.svelte';
|
|
|
|
|
import { TypeaheadBuffer } from '../../typeahead';
|
|
|
|
|
import { FieldProvider } from '../field/field-provider.svelte';
|
|
|
|
|
import { GRID_LIST_LANGS } from './langs';
|
|
|
|
|
import type { GridListSelectionMode } from './types';
|
|
|
|
|
|
|
|
|
|
import { gridListMorfo } from '../../../morfo/components/grid-list';
|
|
|
|
|
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
|
|
|
|
|
import type { TimerHandle } from '$timer';
|
|
|
|
|
|
|
|
|
|
const attrs = createAttrs(gridListMorfo);
|
|
|
|
|
|
|
|
|
|
@ -55,14 +55,19 @@ export class GridListProvider {
|
|
|
|
|
|
|
|
|
|
readonly field = FieldProvider.get();
|
|
|
|
|
|
|
|
|
|
private typeaheadBuffer = '';
|
|
|
|
|
private typeaheadTimer: TimerHandle | null = null;
|
|
|
|
|
private readonly typeahead: TypeaheadBuffer;
|
|
|
|
|
/** Anchor for Shift+Click range selection. */
|
|
|
|
|
private anchor: string | null = null;
|
|
|
|
|
|
|
|
|
|
private constructor(opts: GridListOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.soma = Soma.require();
|
|
|
|
|
this.typeahead = new TypeaheadBuffer({
|
|
|
|
|
timers: this.soma.uix.timers,
|
|
|
|
|
key: () => `soma:grid-list:${this.opts.id.current}:typeahead`,
|
|
|
|
|
timeout: () => this.opts.typeaheadTimeout.current,
|
|
|
|
|
meta: { component: 'grid-list', action: 'typeahead-reset' }
|
|
|
|
|
});
|
|
|
|
|
this.runtime = this.soma.runtime(gridListMorfo, {});
|
|
|
|
|
this.runtimePart = this.runtime.part('provider', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
@ -72,7 +77,7 @@ export class GridListProvider {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
return () => this.clearTypeaheadTimer();
|
|
|
|
|
return () => this.typeahead.destroy();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -365,21 +370,11 @@ export class GridListProvider {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private handleTypeahead(char: string, fromIndex: number) {
|
|
|
|
|
this.typeaheadBuffer += char.toLowerCase();
|
|
|
|
|
this.clearTypeaheadTimer();
|
|
|
|
|
this.typeaheadTimer = this.soma.uix.timers.schedule(
|
|
|
|
|
this.typeaheadTimerKey,
|
|
|
|
|
this.opts.typeaheadTimeout.current,
|
|
|
|
|
() => {
|
|
|
|
|
this.typeaheadTimer = null;
|
|
|
|
|
this.typeaheadBuffer = '';
|
|
|
|
|
},
|
|
|
|
|
{ replace: true, meta: { component: 'grid-list', action: 'typeahead-reset' } }
|
|
|
|
|
);
|
|
|
|
|
const needle = this.typeahead.push(char);
|
|
|
|
|
if (needle === null) return;
|
|
|
|
|
|
|
|
|
|
const rows = this.getRows();
|
|
|
|
|
if (rows.length === 0) return;
|
|
|
|
|
const needle = this.typeaheadBuffer;
|
|
|
|
|
const start = needle.length === 1 ? fromIndex + 1 : fromIndex;
|
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
|
|
|
const idx = (start + i) % rows.length;
|
|
|
|
|
@ -395,17 +390,6 @@ export class GridListProvider {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private clearTypeaheadTimer() {
|
|
|
|
|
if (this.typeaheadTimer) {
|
|
|
|
|
this.typeaheadTimer.cancel();
|
|
|
|
|
this.typeaheadTimer = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private get typeaheadTimerKey() {
|
|
|
|
|
return `soma:grid-list:${this.opts.id.current}:typeahead`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleRowClick(value: string, e: SomaMouseEvent<HTMLElement>) {
|
|
|
|
|
if (this.isDisabled || this.isReadonly) return;
|
|
|
|
|
const mode =
|
|
|
|
|
|