refactor(editor): enable the noUncheckedIndexedAccess rule for the data-view package (#9351)

close: BS-2230
This commit is contained in:
zzj3720
2024-12-26 14:00:11 +00:00
parent 040f427e9e
commit 188cabc7d7
40 changed files with 258 additions and 123 deletions

View File

@@ -56,7 +56,9 @@ export class Overflow extends SignalWatcher(WithDisposable(ShadowlessElement)) {
let width = 0; let width = 0;
for (let i = 0; i < this.items.length; i++) { for (let i = 0; i < this.items.length; i++) {
const itemWidth = this.items[i].getBoundingClientRect().width; const item = this.items[i];
if (!item) continue;
const itemWidth = item.getBoundingClientRect().width;
// Try to calculate the width occupied by rendering n+1 items; // Try to calculate the width occupied by rendering n+1 items;
// if it exceeds the limit, render n items(in i++ round). // if it exceeds the limit, render n items(in i++ round).
const totalWidth = const totalWidth =

View File

@@ -74,12 +74,13 @@ const selectTagColorPoll = selectOptionColors.map(color => color.color);
function tagColorHelper() { function tagColorHelper() {
let colors = [...selectTagColorPoll]; let colors = [...selectTagColorPoll];
return () => { return (): string => {
if (colors.length === 0) { if (colors.length === 0) {
colors = [...selectTagColorPoll]; colors = [...selectTagColorPoll];
} }
const index = Math.floor(Math.random() * colors.length); const index = Math.floor(Math.random() * colors.length);
const color = colors.splice(index, 1)[0]; const color = colors.splice(index, 1)[0];
if (!color) return '';
return color; return color;
}; };
} }

View File

@@ -243,7 +243,10 @@ export class MultiTagSelect extends SignalWatcher(
event.stopPropagation(); event.stopPropagation();
const inputValue = this.text.value.trim(); const inputValue = this.text.value.trim();
if (event.key === 'Backspace' && inputValue === '') { if (event.key === 'Backspace' && inputValue === '') {
this.tagManager.deleteTag(this.value.value[this.value.value.length - 1]); const lastId = this.value.value[this.value.value.length - 1];
if (lastId) {
this.tagManager.deleteTag(lastId);
}
} else if (event.key === 'Enter' && !event.isComposing) { } else if (event.key === 'Enter' && !event.isComposing) {
this.selectedTag$.value?.select(); this.selectedTag$.value?.select();
} else if (event.key === 'ArrowUp') { } else if (event.key === 'ArrowUp') {

View File

@@ -87,6 +87,9 @@ export class DataViewRenderer extends SignalWatcher(
return; return;
} }
const view = this.viewMap$.value[currentViewId]; const view = this.viewMap$.value[currentViewId];
if (!view) {
return;
}
return { return {
view: view, view: view,
selection$: computed(() => { selection$: computed(() => {

View File

@@ -25,7 +25,7 @@ export const evalFilter = (
for (let i = 0; i < expectArgLen; i++) { for (let i = 0; i < expectArgLen; i++) {
const argValue = evalValue(filter.args[i]); const argValue = evalValue(filter.args[i]);
const argType = func.args[i]; const argType = func.args[i];
if (argValue == null) { if (argValue == null || argType == null) {
return true; return true;
} }
if (!argType.valueValidate(argValue)) { if (!argType.valueValidate(argValue)) {

View File

@@ -32,7 +32,7 @@ export type FilterConfig<
) => boolean; ) => boolean;
defaultValue?: (args: { defaultValue?: (args: {
[K in keyof Args]: ValueTypeOf<ReplaceVar<Args[K], Vars>>; [K in keyof Args]: ValueTypeOf<ReplaceVar<Args[K], Vars>>;
}) => ValueTypeOf<ReplaceVar<Self, Vars>>; }) => ValueTypeOf<ReplaceVar<Self, Vars>> | undefined;
}; };
type FindVar< type FindVar<
Vars extends TypeVarDefinitionInstance[], Vars extends TypeVarDefinitionInstance[],

View File

@@ -23,7 +23,13 @@ export const multiTagFilter = [
} }
return value.some(v => self.includes(v)); return value.some(v => self.includes(v));
}, },
defaultValue: args => [args[0][0]], defaultValue: args => {
const value = args[0][0];
if (value != null) {
return [value];
}
return;
},
}), }),
createFilter({ createFilter({
name: 'doesNotContainOneOf', name: 'doesNotContainOneOf',

View File

@@ -28,6 +28,7 @@ export function generateDefaultValues(
for (const [propertyId, conditions] of propertyConditions) { for (const [propertyId, conditions] of propertyConditions) {
if (conditions.length === 1) { if (conditions.length === 1) {
const condition = conditions[0]; const condition = conditions[0];
if (!condition) continue;
const filterConfig = filterMatcher.getFilterByName(condition.function); const filterConfig = filterMatcher.getFilterByName(condition.function);
if (filterConfig?.defaultValue) { if (filterConfig?.defaultValue) {
const argValues = condition.args.map(arg => arg.value); const argValues = condition.args.map(arg => arg.value);

View File

@@ -27,9 +27,16 @@ export const firstFilterByRef = (
}; };
}; };
export const firstFilter = (vars: Variable[]): SingleFilter => { export const firstFilter = (vars: Variable[]): SingleFilter => {
const variable = vars[0];
if (!variable) {
throw new BlockSuiteError(
ErrorCode.DatabaseBlockError,
`can't find any variable`
);
}
const ref: VariableRef = { const ref: VariableRef = {
type: 'ref', type: 'ref',
name: vars[0].id, name: variable.id,
}; };
const filter = firstFilterName(vars, ref); const filter = firstFilterName(vars, ref);
if (!filter) { if (!filter) {

View File

@@ -78,8 +78,8 @@ export class GroupSetting extends SignalWatcher(
const activeId = evt.active.id; const activeId = evt.active.id;
const groups = this.groups$.value; const groups = this.groups$.value;
if (over && over.id !== activeId && groups) { if (over && over.id !== activeId && groups) {
const activeIndex = groups.findIndex(data => data.key === activeId); const activeIndex = groups.findIndex(data => data?.key === activeId);
const overIndex = groups.findIndex(data => data.key === over.id); const overIndex = groups.findIndex(data => data?.key === over.id);
this.groupTrait.moveGroupTo( this.groupTrait.moveGroupTo(
activeId, activeId,
@@ -104,7 +104,11 @@ export class GroupSetting extends SignalWatcher(
}, },
], ],
items: computed(() => { items: computed(() => {
return this.groupTrait.groupsDataList$.value?.map(v => v.key) ?? []; return (
this.groupTrait.groupsDataList$.value?.map(
v => v?.key ?? 'default key'
) ?? []
);
}), }),
strategy: verticalListSortingStrategy, strategy: verticalListSortingStrategy,
}); });
@@ -136,8 +140,9 @@ export class GroupSetting extends SignalWatcher(
> >
${repeat( ${repeat(
groups, groups,
group => group.key, group => group?.key ?? 'default key',
group => { group => {
if (!group) return;
const props: GroupRenderProps = { const props: GroupRenderProps = {
value: group.value, value: group.value,
data: group.property.data$.value, data: group.property.data$.value,

View File

@@ -110,9 +110,12 @@ export class GroupTrait {
} }
const sortedGroup = this.ops.sortGroup(Object.keys(groupMap)); const sortedGroup = this.ops.sortGroup(Object.keys(groupMap));
sortedGroup.forEach(key => { sortedGroup.forEach(key => {
if (!groupMap[key]) return;
groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows); groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows);
}); });
return (this.preDataList = sortedGroup.map(key => groupMap[key])); return (this.preDataList = sortedGroup
.map(key => groupMap[key])
.filter((v): v is GroupData => v != null));
}); });
groupsDataList$ = computed(() => { groupsDataList$ = computed(() => {
@@ -166,7 +169,7 @@ export class GroupTrait {
} }
const addTo = this.config$.value?.addToGroup ?? (value => value); const addTo = this.config$.value?.addToGroup ?? (value => value);
const newValue = addTo( const newValue = addTo(
groupMap[key].value, groupMap[key]?.value,
this.view.cellJsonValueGet(rowId, propertyId) this.view.cellJsonValueGet(rowId, propertyId)
); );
this.view.cellValueSet(rowId, propertyId, newValue); this.view.cellValueSet(rowId, propertyId, newValue);
@@ -240,10 +243,10 @@ export class GroupTrait {
); );
} }
const addTo = this.config$.value?.addToGroup ?? (value => value); const addTo = this.config$.value?.addToGroup ?? (value => value);
newValue = addTo(groupMap[toGroupKey].value, newValue); newValue = addTo(groupMap[toGroupKey]?.value, newValue);
this.view.cellValueSet(rowId, propertyId, newValue); this.view.cellValueSet(rowId, propertyId, newValue);
} }
const rows = groupMap[toGroupKey].rows.filter(id => id !== rowId); const rows = groupMap[toGroupKey]?.rows.filter(id => id !== rowId) ?? [];
const index = insertPositionToIndex(position, rows, id => id); const index = insertPositionToIndex(position, rows, id => id);
rows.splice(index, 0, rowId); rows.splice(index, 0, rowId);
this.changeCardSort(toGroupKey, rows); this.changeCardSort(toGroupKey, rows);
@@ -275,7 +278,7 @@ export class GroupTrait {
} }
const remove = this.config$.value?.removeFromGroup ?? (() => undefined); const remove = this.config$.value?.removeFromGroup ?? (() => undefined);
const newValue = remove( const newValue = remove(
groupMap[key].value, groupMap[key]?.value,
this.view.cellJsonValueGet(rowId, propertyId) this.view.cellJsonValueGet(rowId, propertyId)
); );
this.view.cellValueSet(rowId, propertyId, newValue); this.view.cellValueSet(rowId, propertyId, newValue);

View File

@@ -29,7 +29,7 @@ export class TypeVarReferenceInstance<Name extends string = string>
constructor(readonly varName: Name) {} constructor(readonly varName: Name) {}
subst(ctx: TypeVarContext): void | TypeInstance { subst(ctx: TypeVarContext): void | TypeInstance {
return ctx[this.varName].type; return ctx[this.varName]?.type;
} }
unify(_ctx: TypeVarContext, _type: TypeInstance, _unify: Unify): boolean { unify(_ctx: TypeVarContext, _type: TypeInstance, _unify: Unify): boolean {

View File

@@ -34,7 +34,12 @@ const compareList = <T>(
) => { ) => {
let i = 0; let i = 0;
while (i < listA.length && i < listB.length) { while (i < listA.length && i < listB.length) {
const result = compare(listA[i], listB[i]); const a = listA[i];
const b = listB[i];
if (a == null || b == null) {
continue;
}
const result = compare(a, b);
if (result !== 0) { if (result !== 0) {
return result; return result;
} }

View File

@@ -41,12 +41,15 @@ export const numberStatsFunctions: StatisticsConfig[] = [
dataType: t.number.instance(), dataType: t.number.instance(),
impl: data => { impl: data => {
const arr = withoutNull(data).sort((a, b) => a - b); const arr = withoutNull(data).sort((a, b) => a - b);
let result = 0; let result: number | undefined = undefined;
if (arr.length % 2 === 1) { if (arr.length % 2 === 1) {
result = arr[(arr.length - 1) / 2]; result = arr[(arr.length - 1) / 2];
} else { } else {
const index = arr.length / 2; const index = arr.length / 2;
result = (arr[index] + arr[index - 1]) / 2; const a = arr[index];
const b = arr[index - 1];
if (a == null || b == null) return 'None';
result = (a + b) / 2;
} }
return result?.toString() ?? 'None'; return result?.toString() ?? 'None';
}, },

View File

@@ -77,14 +77,18 @@ export function hasViewportRelativeCoordinates(
const getEventCoordinates = (event: Event) => { const getEventCoordinates = (event: Event) => {
if (event instanceof TouchEvent) { if (event instanceof TouchEvent) {
if (event.touches && event.touches.length) { if (event.touches && event.touches.length) {
const { clientX: x, clientY: y } = event.touches[0]; const touch = event.touches[0];
if (!touch) return;
const { clientX: x, clientY: y } = touch;
return { return {
x, x,
y, y,
}; };
} else if (event.changedTouches && event.changedTouches.length) { } else if (event.changedTouches && event.changedTouches.length) {
const { clientX: x, clientY: y } = event.changedTouches[0]; const touch = event.changedTouches[0];
if (!touch) return;
const { clientX: x, clientY: y } = touch;
return { return {
x, x,

View File

@@ -68,7 +68,10 @@ export class SortContext extends DndContext {
overIndex: list.findIndex(v => v.id === this.overId$.value), overIndex: list.findIndex(v => v.id === this.overId$.value),
}); });
transforms.forEach((transform, i) => { transforms.forEach((transform, i) => {
const node = list[i].node; const node = list[i]?.node;
if (!node) {
return;
}
if (transform != null) { if (transform != null) {
node.style.transform = `translate3d(${Math.round(transform.x)}px,${Math.round(transform.y)}px,0) node.style.transform = `translate3d(${Math.round(transform.x)}px,${Math.round(transform.y)}px,0)
scaleX(${transform.scaleX}) scaleY(${transform.scaleY})`; scaleX(${transform.scaleX}) scaleY(${transform.scaleY})`;

View File

@@ -12,6 +12,7 @@ export const horizontalListSortingStrategy: SortingStrategy = ({
overIndex, overIndex,
}) => { }) => {
const activeNodeRect = rects[activeIndex] ?? fallbackActiveRect; const activeNodeRect = rects[activeIndex] ?? fallbackActiveRect;
if (!activeNodeRect) return [];
const strategy = (index: number) => { const strategy = (index: number) => {
const itemGap = getItemGap(rects, index, activeIndex); const itemGap = getItemGap(rects, index, activeIndex);
@@ -73,12 +74,22 @@ function getItemGap(
} }
if (activeIndex < index) { if (activeIndex < index) {
return previousRect if (previousRect) {
? currentRect.left - (previousRect.left + previousRect.width) return currentRect.left - (previousRect.left + previousRect.width);
: nextRect.left - (currentRect.left + currentRect.width); }
if (nextRect) {
return nextRect.left - (currentRect.left + currentRect.width);
}
return 0;
} }
return nextRect if (nextRect) {
? nextRect.left - (currentRect.left + currentRect.width) return nextRect.left - (currentRect.left + currentRect.width);
: currentRect.left - (previousRect.left + previousRect.width); }
if (previousRect) {
return currentRect.left - (previousRect.left + previousRect.width);
}
return 0;
} }

View File

@@ -12,7 +12,7 @@ export const verticalListSortingStrategy: SortingStrategy = ({
overIndex, overIndex,
}) => { }) => {
const activeNodeRect = rects[activeIndex] ?? fallbackActiveRect; const activeNodeRect = rects[activeIndex] ?? fallbackActiveRect;
if (!activeNodeRect) return [];
const strategy = (index: number) => { const strategy = (index: number) => {
if (index === activeIndex) { if (index === activeIndex) {
const overIndexRect = rects[overIndex]; const overIndexRect = rects[overIndex];

View File

@@ -3,11 +3,11 @@
*/ */
export function arrayMove<T>(array: T[], from: number, to: number): T[] { export function arrayMove<T>(array: T[], from: number, to: number): T[] {
const newArray = array.slice(); const newArray = array.slice();
newArray.splice( const value = newArray.splice(from, 1)[0];
to < 0 ? newArray.length + to : to, if (value == null) {
0, return newArray;
newArray.splice(from, 1)[0] }
); newArray.splice(to < 0 ? newArray.length + to : to, 0, value);
return newArray; return newArray;
} }

View File

@@ -76,9 +76,9 @@ export interface SingleView {
rowGet(rowId: string): Row; rowGet(rowId: string): Row;
rowPrevGet(rowId: string): string; rowPrevGet(rowId: string): string | undefined;
rowNextGet(rowId: string): string; rowNextGet(rowId: string): string | undefined;
readonly propertyMetas: PropertyMetaConfig[]; readonly propertyMetas: PropertyMetaConfig[];
@@ -116,7 +116,7 @@ export interface SingleView {
propertyIndexGet(propertyId: string): number; propertyIndexGet(propertyId: string): number;
propertyIdGetByIndex(index: number): string; propertyIdGetByIndex(index: number): string | undefined;
propertyReadonlyGet(propertyId: string): boolean; propertyReadonlyGet(propertyId: string): boolean;
@@ -387,7 +387,7 @@ export abstract class SingleViewBase<
return this.dataSource.propertyMetaGet(type).renderer.icon; return this.dataSource.propertyMetaGet(type).renderer.icon;
} }
propertyIdGetByIndex(index: number): string { propertyIdGetByIndex(index: number): string | undefined {
return this.propertyIds$.value[index]; return this.propertyIds$.value[index];
} }
@@ -410,9 +410,10 @@ export abstract class SingleViewBase<
} }
propertyNextGet(propertyId: string): Property | undefined { propertyNextGet(propertyId: string): Property | undefined {
return this.propertyGet( const index = this.propertyIndexGet(propertyId);
this.propertyIdGetByIndex(this.propertyIndexGet(propertyId) + 1) const nextId = this.propertyIdGetByIndex(index + 1);
); if (!nextId) return;
return this.propertyGet(nextId);
} }
propertyParseValueFromString(propertyId: string, cellData: string) { propertyParseValueFromString(propertyId: string, cellData: string) {
@@ -430,9 +431,10 @@ export abstract class SingleViewBase<
} }
propertyPreGet(propertyId: string): Property | undefined { propertyPreGet(propertyId: string): Property | undefined {
return this.propertyGet( const index = this.propertyIndexGet(propertyId);
this.propertyIdGetByIndex(this.propertyIndexGet(propertyId) - 1) const prevId = this.propertyIdGetByIndex(index - 1);
); if (!prevId) return;
return this.propertyGet(prevId);
} }
propertyReadonlyGet(propertyId: string): boolean { propertyReadonlyGet(propertyId: string): boolean {
@@ -463,9 +465,9 @@ export abstract class SingleViewBase<
this.dataSource.rowMove(rowId, position); this.dataSource.rowMove(rowId, position);
} }
abstract rowNextGet(rowId: string): string; abstract rowNextGet(rowId: string): string | undefined;
abstract rowPrevGet(rowId: string): string; abstract rowPrevGet(rowId: string): string | undefined;
protected rowsMapping(rows: string[]): string[] { protected rowsMapping(rows: string[]): string[] {
return this.searchRowsMapping(rows, this.searchString.value); return this.searchRowsMapping(rows, this.searchString.value);

View File

@@ -15,8 +15,8 @@ export interface ViewManager {
dataSource: DataSource; dataSource: DataSource;
readonly$: ReadonlySignal<boolean>; readonly$: ReadonlySignal<boolean>;
currentViewId$: ReadonlySignal<string>; currentViewId$: ReadonlySignal<string | undefined>;
currentView$: ReadonlySignal<SingleView>; currentView$: ReadonlySignal<SingleView | undefined>;
setCurrentView(id: string): void; setCurrentView(id: string): void;
@@ -49,7 +49,9 @@ export class ViewManagerBase implements ViewManager {
}); });
currentView$ = computed(() => { currentView$ = computed(() => {
return this.viewGet(this.currentViewId$.value); const id = this.currentViewId$.value;
if (!id) return;
return this.viewGet(id);
}); });
readonly$ = computed(() => { readonly$ = computed(() => {
@@ -66,7 +68,7 @@ export class ViewManagerBase implements ViewManager {
this.dataSource.viewDataMoveTo(id, position); this.dataSource.viewDataMoveTo(id, position);
} }
setCurrentView(id: string): void { setCurrentView(id: string | undefined): void {
this._currentViewId$.value = id; this._currentViewId$.value = id;
} }

View File

@@ -37,6 +37,9 @@ export const selectPropertyModelConfig = selectPropertyType.modelConfig<
.split(',') .split(',')
.map(v => v.trim()) .map(v => v.trim())
.filter(v => v)[0]; .filter(v => v)[0];
if (!name) {
return { value: null, data: data };
}
let value: string | undefined; let value: string | undefined;
const option = optionMap[name]; const option = optionMap[name];

View File

@@ -53,6 +53,12 @@ export const kanbanViewModel = kanbanViewType.createModel<KanbanViewData>({
return 1; return 1;
}; };
const columnId = allowList.sort((a, b) => getWeight(b) - getWeight(a))[0]; const columnId = allowList.sort((a, b) => getWeight(b) - getWeight(a))[0];
if (!columnId) {
throw new BlockSuiteError(
ErrorCode.DatabaseBlockError,
'no groupable column found'
);
}
const type = viewManager.dataSource.propertyTypeGet(columnId); const type = viewManager.dataSource.propertyTypeGet(columnId);
const meta = type && viewManager.dataSource.propertyMetaGet(type); const meta = type && viewManager.dataSource.propertyMetaGet(type);
const data = viewManager.dataSource.propertyDataGet(columnId); const data = viewManager.dataSource.propertyDataGet(columnId);

View File

@@ -285,6 +285,9 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
} }
const columns = [...view.columns]; const columns = [...view.columns];
const [column] = columns.splice(columnIndex, 1); const [column] = columns.splice(columnIndex, 1);
if (!column) {
return {};
}
const index = insertPositionToIndex(toAfterOfColumn, columns); const index = insertPositionToIndex(toAfterOfColumn, columns);
columns.splice(index, 0, column); columns.splice(index, 0, column);
return { return {
@@ -297,12 +300,12 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
this.dataSource.rowMove(rowId, position); this.dataSource.rowMove(rowId, position);
} }
override rowNextGet(rowId: string): string { override rowNextGet(rowId: string): string | undefined {
const index = this.rows$.value.indexOf(rowId); const index = this.rows$.value.indexOf(rowId);
return this.rows$.value[index + 1]; return this.rows$.value[index + 1];
} }
override rowPrevGet(rowId: string): string { override rowPrevGet(rowId: string): string | undefined {
const index = this.rows$.value.indexOf(rowId); const index = this.rows$.value.indexOf(rowId);
return this.rows$.value[index - 1]; return this.rows$.value[index - 1];
} }

View File

@@ -183,7 +183,7 @@ export class KanbanSelectionController implements ReactiveController {
if (selection.selectionType === 'card') { if (selection.selectionType === 'card') {
const card = getSelectedCards(this.host, selection)[0]; const card = getSelectedCards(this.host, selection)[0];
const cell = card?.querySelector('affine-data-view-kanban-cell'); const cell = card?.querySelector('affine-data-view-kanban-cell');
if (cell) { if (card && cell) {
this.selection = { this.selection = {
groupKey: card.groupKey, groupKey: card.groupKey,
cardId: card.cardId, cardId: card.cardId,
@@ -209,11 +209,9 @@ export class KanbanSelectionController implements ReactiveController {
const index = kanbanCells.findIndex( const index = kanbanCells.findIndex(
cell => cell.column.id === selection.columnId cell => cell.column.id === selection.columnId
); );
const { cell, cardId, groupKey } = this.getNextFocusCell( const result = this.getNextFocusCell(selection, index, position);
selection, if (!result) return;
index, const { cell, cardId, groupKey } = result;
position
);
if (cell instanceof KanbanCell) { if (cell instanceof KanbanCell) {
this.selection = { this.selection = {
...selection, ...selection,
@@ -234,7 +232,9 @@ export class KanbanSelectionController implements ReactiveController {
const index = cardElements.findIndex( const index = cardElements.findIndex(
card => card.cardId === selection.cards[0].cardId card => card.cardId === selection.cards[0].cardId
); );
const { card, cards } = this.getNextFocusCard(selection, index, position); const result = this.getNextFocusCard(selection, index, position);
if (!result) return;
const { card, cards } = result;
if (card instanceof KanbanCard) { if (card instanceof KanbanCard) {
const newCards = cards ?? selection.cards; const newCards = cards ?? selection.cards;
this.selection = atLeastOne(newCards) this.selection = atLeastOne(newCards)
@@ -286,10 +286,12 @@ export class KanbanSelectionController implements ReactiveController {
selection: KanbanCardSelection, selection: KanbanCardSelection,
index: number, index: number,
nextPosition: 'up' | 'down' | 'left' | 'right' nextPosition: 'up' | 'down' | 'left' | 'right'
): { ):
card: KanbanCard; | {
cards: KanbanCardSelectionCard[]; card: KanbanCard;
} { cards: KanbanCardSelectionCard[];
}
| undefined {
const group = this.host.querySelector( const group = this.host.querySelector(
`affine-data-view-kanban-group[data-key="${selection.cards[0].groupKey}"]` `affine-data-view-kanban-group[data-key="${selection.cards[0].groupKey}"]`
); );
@@ -301,7 +303,7 @@ export class KanbanSelectionController implements ReactiveController {
const nextIndex = index - 1; const nextIndex = index - 1;
const nextCardIndex = nextIndex < 0 ? kanbanCards.length - 1 : nextIndex; const nextCardIndex = nextIndex < 0 ? kanbanCards.length - 1 : nextIndex;
const card = kanbanCards[nextCardIndex]; const card = kanbanCards[nextCardIndex];
if (!card) return;
return { return {
card, card,
cards: [ cards: [
@@ -317,7 +319,7 @@ export class KanbanSelectionController implements ReactiveController {
const nextIndex = index + 1; const nextIndex = index + 1;
const nextCardIndex = nextIndex > kanbanCards.length - 1 ? 0 : nextIndex; const nextCardIndex = nextIndex > kanbanCards.length - 1 ? 0 : nextIndex;
const card = kanbanCards[nextCardIndex]; const card = kanbanCards[nextCardIndex];
if (!card) return;
return { return {
card, card,
cards: [ cards: [
@@ -360,11 +362,13 @@ export class KanbanSelectionController implements ReactiveController {
selection: KanbanCellSelection, selection: KanbanCellSelection,
index: number, index: number,
nextPosition: 'up' | 'down' | 'left' | 'right' nextPosition: 'up' | 'down' | 'left' | 'right'
): { ):
cell: KanbanCell; | {
cardId?: string; cell: KanbanCell;
groupKey?: string; cardId?: string;
} { groupKey?: string;
}
| undefined {
const kanbanCells = getCardCellsBySelection(this.host, selection); const kanbanCells = getCardCellsBySelection(this.host, selection);
const group = this.host.querySelector( const group = this.host.querySelector(
`affine-data-view-kanban-group[data-key="${selection.groupKey}"]` `affine-data-view-kanban-group[data-key="${selection.groupKey}"]`
@@ -384,11 +388,14 @@ export class KanbanSelectionController implements ReactiveController {
cardIndex => (cardIndex === 0 ? cards.length - 1 : cardIndex - 1) cardIndex => (cardIndex === 0 ? cards.length - 1 : cardIndex - 1)
); );
} else { } else {
const cell = kanbanCells[kanbanCells.length - 1];
if (!cell) return;
return { return {
cell: kanbanCells[kanbanCells.length - 1], cell,
}; };
} }
} }
if (!kanbanCells[nextIndex]) return;
return { return {
cell: kanbanCells[nextIndex], cell: kanbanCells[nextIndex],
}; };
@@ -405,11 +412,14 @@ export class KanbanSelectionController implements ReactiveController {
cardIndex => (cardIndex === cards.length - 1 ? 0 : cardIndex + 1) cardIndex => (cardIndex === cards.length - 1 ? 0 : cardIndex + 1)
); );
} else { } else {
const cell = kanbanCells[0];
if (!cell) return;
return { return {
cell: kanbanCells[0], cell,
}; };
} }
} }
if (!kanbanCells[nextIndex]) return;
return { return {
cell: kanbanCells[nextIndex], cell: kanbanCells[nextIndex],
}; };
@@ -569,19 +579,19 @@ function getNextGroupFocusElement(
groups: KanbanGroup[], groups: KanbanGroup[],
selection: KanbanCellSelection, selection: KanbanCellSelection,
getNextGroupIndex: (groupIndex: number) => number getNextGroupIndex: (groupIndex: number) => number
): NextFocusCell; ): NextFocusCell | undefined;
function getNextGroupFocusElement( function getNextGroupFocusElement(
viewElement: Element, viewElement: Element,
groups: KanbanGroup[], groups: KanbanGroup[],
selection: KanbanCardSelection, selection: KanbanCardSelection,
getNextGroupIndex: (groupIndex: number) => number getNextGroupIndex: (groupIndex: number) => number
): NextFocusCard; ): NextFocusCard | undefined;
function getNextGroupFocusElement( function getNextGroupFocusElement(
viewElement: Element, viewElement: Element,
groups: KanbanGroup[], groups: KanbanGroup[],
selection: KanbanCellSelection | KanbanCardSelection, selection: KanbanCellSelection | KanbanCardSelection,
getNextGroupIndex: (groupIndex: number) => number getNextGroupIndex: (groupIndex: number) => number
): NextFocusCell | NextFocusCard { ): NextFocusCell | NextFocusCard | undefined {
const groupIndex = groups.findIndex(group => { const groupIndex = groups.findIndex(group => {
if (selection.selectionType === 'cell') { if (selection.selectionType === 'cell') {
return group.group.key === selection.groupKey; return group.group.key === selection.groupKey;
@@ -591,10 +601,11 @@ function getNextGroupFocusElement(
let nextGroupIndex = getNextGroupIndex(groupIndex); let nextGroupIndex = getNextGroupIndex(groupIndex);
let nextGroup = groups[nextGroupIndex]; let nextGroup = groups[nextGroupIndex];
while (nextGroup.group.rows.length === 0) { while (nextGroup?.group.rows.length === 0) {
nextGroupIndex = getNextGroupIndex(nextGroupIndex); nextGroupIndex = getNextGroupIndex(nextGroupIndex);
nextGroup = groups[nextGroupIndex]; nextGroup = groups[nextGroupIndex];
} }
if (!nextGroup) return;
const element = const element =
selection.selectionType === 'cell' selection.selectionType === 'cell'
@@ -621,6 +632,7 @@ function getNextGroupFocusElement(
}); });
const nextCard = nextCards[cardPos.index]; const nextCard = nextCards[cardPos.index];
if (!nextCard) return;
if (selection.selectionType === 'card') { if (selection.selectionType === 'card') {
return { return {
card: nextCard, card: nextCard,
@@ -652,6 +664,7 @@ function getNextGroupFocusElement(
}); });
const nextCell = cells[cellPos.index]; const nextCell = cells[cellPos.index];
if (!nextCell) return;
return { return {
cell: nextCell, cell: nextCell,
cardId: nextCard.cardId, cardId: nextCard.cardId,
@@ -664,17 +677,21 @@ function getNextCardFocusCell(
cards: KanbanCard[], cards: KanbanCard[],
selection: KanbanCellSelection, selection: KanbanCellSelection,
getNextCardIndex: (cardIndex: number) => number getNextCardIndex: (cardIndex: number) => number
): { ):
cell: KanbanCell; | {
cardId: string; cell: KanbanCell;
} { cardId: string;
}
| undefined {
const cardIndex = cards.findIndex(card => card.cardId === selection.cardId); const cardIndex = cards.findIndex(card => card.cardId === selection.cardId);
const nextCardIndex = getNextCardIndex(cardIndex); const nextCardIndex = getNextCardIndex(cardIndex);
const nextCard = cards[nextCardIndex]; const nextCard = cards[nextCardIndex];
if (!nextCard) return;
const nextCells = Array.from( const nextCells = Array.from(
nextCard.querySelectorAll('affine-data-view-kanban-cell') nextCard.querySelectorAll('affine-data-view-kanban-cell')
); );
const nextCellIndex = nextPosition === 'up' ? nextCells.length - 1 : 0; const nextCellIndex = nextPosition === 'up' ? nextCells.length - 1 : 0;
if (!nextCells[nextCellIndex]) return;
return { return {
cell: nextCells[nextCellIndex], cell: nextCells[nextCellIndex],
cardId: nextCard.cardId, cardId: nextCard.cardId,

View File

@@ -101,13 +101,15 @@ export class KanbanGroup extends SignalWatcher(
requestAnimationFrame(() => { requestAnimationFrame(() => {
const kanban = this.closest('affine-data-view-kanban'); const kanban = this.closest('affine-data-view-kanban');
if (kanban) { if (kanban) {
const columnId =
this.view.mainProperties$.value.titleColumn ||
this.view.propertyIds$.value[0];
if (!columnId) return;
kanban.selectionController.selection = { kanban.selectionController.selection = {
selectionType: 'cell', selectionType: 'cell',
groupKey: this.group.key, groupKey: this.group.key,
cardId: id, cardId: id,
columnId: columnId,
this.view.mainProperties$.value.titleColumn ||
this.view.propertyIds$.value[0],
isEditing: true, isEditing: true,
}; };
} }
@@ -119,13 +121,15 @@ export class KanbanGroup extends SignalWatcher(
requestAnimationFrame(() => { requestAnimationFrame(() => {
const kanban = this.closest('affine-data-view-kanban'); const kanban = this.closest('affine-data-view-kanban');
if (kanban) { if (kanban) {
const columnId =
this.view.mainProperties$.value.titleColumn ||
this.view.propertyIds$.value[0];
if (!columnId) return;
kanban.selectionController.selection = { kanban.selectionController.selection = {
selectionType: 'cell', selectionType: 'cell',
groupKey: this.group.key, groupKey: this.group.key,
cardId: id, cardId: id,
columnId: columnId,
this.view.mainProperties$.value.titleColumn ||
this.view.propertyIds$.value[0],
isEditing: true, isEditing: true,
}; };
} }

View File

@@ -166,8 +166,8 @@ export class DataViewKanban extends DataViewBase<
const activeId = evt.active.id; const activeId = evt.active.id;
const groups = this.groupManager.groupsDataList$.value; const groups = this.groupManager.groupsDataList$.value;
if (over && over.id !== activeId && groups) { if (over && over.id !== activeId && groups) {
const activeIndex = groups.findIndex(data => data.key === activeId); const activeIndex = groups.findIndex(data => data?.key === activeId);
const overIndex = groups.findIndex(data => data.key === over.id); const overIndex = groups.findIndex(data => data?.key === over.id);
this.groupManager.moveGroupTo( this.groupManager.moveGroupTo(
activeId, activeId,
@@ -192,7 +192,11 @@ export class DataViewKanban extends DataViewBase<
}, },
], ],
items: computed(() => { items: computed(() => {
return this.groupManager.groupsDataList$.value?.map(v => v.key) ?? []; return (
this.groupManager.groupsDataList$.value?.map(
v => v?.key ?? 'default key'
) ?? []
);
}), }),
strategy: horizontalListSortingStrategy, strategy: horizontalListSortingStrategy,
}); });
@@ -268,8 +272,9 @@ export class DataViewKanban extends DataViewBase<
> >
${repeat( ${repeat(
groups, groups,
group => group.key, group => group?.key ?? 'default key',
group => { group => {
if (!group) return;
return html` <affine-data-view-kanban-group return html` <affine-data-view-kanban-group
${sortable(group.key)} ${sortable(group.key)}
data-key="${group.key}" data-key="${group.key}"

View File

@@ -197,7 +197,7 @@ function getSelectedArea(
const { rowsSelection, columnsSelection, groupKey } = selection; const { rowsSelection, columnsSelection, groupKey } = selection;
const data: SelectedArea = []; const data: SelectedArea = [];
const rows = groupKey const rows = groupKey
? view.groupTrait.groupDataMap$.value?.[groupKey].rows ? view.groupTrait.groupDataMap$.value?.[groupKey]?.rows
: view.rows$.value; : view.rows$.value;
const columns = view.propertyIds$.value; const columns = view.propertyIds$.value;
if (!rows) { if (!rows) {
@@ -208,8 +208,14 @@ function getSelectedArea(
cells: [], cells: [],
}; };
const rowId = rows[i]; const rowId = rows[i];
if (rowId == null) {
continue;
}
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) { for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
const columnId = columns[j]; const columnId = columns[j];
if (columnId == null) {
continue;
}
const cell = view.cellGet(rowId, columnId); const cell = view.cellGet(rowId, columnId);
row.cells.push(cell); row.cells.push(cell);
} }
@@ -237,7 +243,7 @@ function getTargetRangeFromSelection(
}, },
column: { column: {
start: focus.columnIndex, start: focus.columnIndex,
length: data[0].length, length: data[0]?.length ?? 0,
}, },
} }
: { : {
@@ -258,7 +264,7 @@ function pasteToCells(
selection: TableAreaSelection selection: TableAreaSelection
) { ) {
const srcRowLength = rows.length; const srcRowLength = rows.length;
const srcColumnLength = rows[0].length; const srcColumnLength = rows[0]?.length ?? 0;
const targetRange = getTargetRangeFromSelection(selection, rows); const targetRange = getTargetRangeFromSelection(selection, rows);
for (let i = 0; i < targetRange.row.length; i++) { for (let i = 0; i < targetRange.row.length; i++) {
for (let j = 0; j < targetRange.column.length; j++) { for (let j = 0; j < targetRange.column.length; j++) {
@@ -267,7 +273,7 @@ function pasteToCells(
const srcRowIndex = i % srcRowLength; const srcRowIndex = i % srcRowLength;
const srcColumnIndex = j % srcColumnLength; const srcColumnIndex = j % srcColumnLength;
const dataString = rows[srcRowIndex][srcColumnIndex]; const dataString = rows[srcRowIndex]?.[srcColumnIndex];
const targetContainer = table.selectionController.getCellContainer( const targetContainer = table.selectionController.getCellContainer(
selection.groupKey, selection.groupKey,
@@ -278,7 +284,7 @@ function pasteToCells(
const columnId = targetContainer?.dataset.columnId; const columnId = targetContainer?.dataset.columnId;
if (rowId && columnId) { if (rowId && columnId) {
targetContainer?.column.valueSetFromString(rowId, dataString); targetContainer?.column.valueSetFromString(rowId, dataString ?? '');
} }
} }
} }

View File

@@ -333,7 +333,8 @@ export class TableHotkeysController implements ReactiveController {
this.selectionController.selection = TableRowSelection.create({ this.selectionController.selection = TableRowSelection.create({
rows: rows:
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap( this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
group => group.rows.map(id => ({ groupKey: group.key, id })) group =>
group?.rows.map(id => ({ groupKey: group.key, id })) ?? []
) ?? ) ??
this.host.props.view.rows$.value.map(id => ({ this.host.props.view.rows$.value.map(id => ({
groupKey: undefined, groupKey: undefined,

View File

@@ -234,7 +234,7 @@ export class TableSelectionController implements ReactiveController {
} }
const rows = const rows =
groupKey != null groupKey != null
? this.view.groupTrait.groupDataMap$.value?.[groupKey].rows ? this.view.groupTrait.groupDataMap$.value?.[groupKey]?.rows
: this.view.rows$.value; : this.view.rows$.value;
requestAnimationFrame(() => { requestAnimationFrame(() => {
const index = this.host.props.view.properties$.value.findIndex( const index = this.host.props.view.properties$.value.findIndex(
@@ -285,7 +285,8 @@ export class TableSelectionController implements ReactiveController {
length: selection.rowsSelection.end - selection.rowsSelection.start + 1, length: selection.rowsSelection.end - selection.rowsSelection.start + 1,
}) })
.map((_, index) => index + selection.rowsSelection.start) .map((_, index) => index + selection.rowsSelection.start)
.map(row => rows[row]?.rowId); .map(row => rows[row]?.rowId)
.filter((id): id is string => id != null);
return ids.map(id => ({ id, groupKey: selection.groupKey })); return ids.map(id => ({ id, groupKey: selection.groupKey }));
} }
@@ -314,6 +315,7 @@ export class TableSelectionController implements ReactiveController {
}; };
for (let i = 0; i < rowOffsets.length; i++) { for (let i = 0; i < rowOffsets.length; i++) {
const offset = rowOffsets[i]; const offset = rowOffsets[i];
if (offset == null) continue;
if (offset < startY) { if (offset < startY) {
row.start = i; row.start = i;
} }
@@ -323,6 +325,7 @@ export class TableSelectionController implements ReactiveController {
} }
for (let i = 0; i < columnOffsets.length; i++) { for (let i = 0; i < columnOffsets.length; i++) {
const offset = columnOffsets[i]; const offset = columnOffsets[i];
if (offset == null) continue;
if (offset < startX) { if (offset < startX) {
column.start = i; column.start = i;
} }
@@ -544,20 +547,21 @@ export class TableSelectionController implements ReactiveController {
if (!TableRowSelection.is(this.selection)) return; if (!TableRowSelection.is(this.selection)) return;
const rows = this.selection.rows; const rows = this.selection.rows;
const lastRow = rows[rows.length - 1]; const lastRow = rows[rows.length - 1];
if (!lastRow) return;
const lastRowIndex = const lastRowIndex =
( (
this.getGroup(lastRow.groupKey)?.querySelector( this.getGroup(lastRow?.groupKey)?.querySelector(
`data-view-table-row[data-row-id='${lastRow.id}']` `data-view-table-row[data-row-id='${lastRow?.id}']`
) as TableRow | null ) as TableRow | null
)?.rowIndex ?? 0; )?.rowIndex ?? 0;
const getRowByIndex = (index: number) => { const getRowByIndex = (index: number) => {
const tableRow = this.rows(lastRow.groupKey)?.item(index); const tableRow = this.rows(lastRow?.groupKey)?.item(index);
if (!tableRow) { if (!tableRow) {
return; return;
} }
return { return {
id: tableRow.rowId, id: tableRow.rowId,
groupKey: lastRow.groupKey, groupKey: lastRow?.groupKey,
}; };
}; };
const prevRow = getRowByIndex(lastRowIndex - 1); const prevRow = getRowByIndex(lastRowIndex - 1);
@@ -621,10 +625,15 @@ export class TableSelectionController implements ReactiveController {
add.forEach(row => rows.add(key(row))); add.forEach(row => rows.add(key(row)));
const result = [...rows] const result = [...rows]
.map(r => r.split('.')) .map(r => r.split('.'))
.map(([id, groupKey]) => ({ .flatMap(([id, groupKey]) => {
id, if (id == null) return [];
groupKey: groupKey ? groupKey : undefined, return [
})); {
id,
groupKey: groupKey ? groupKey : undefined,
},
];
});
this.selection = TableRowSelection.create({ this.selection = TableRowSelection.create({
rows: result, rows: result,
}); });

View File

@@ -79,6 +79,7 @@ export const popRowMenu = (
return; return;
} }
const row = selection.rows[0]; const row = selection.rows[0];
if (!row) return;
popFilterableSimpleMenu(ele, [ popFilterableSimpleMenu(ele, [
menu.action({ menu.action({
name: 'Expand Row', name: 'Expand Row',

View File

@@ -94,12 +94,15 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
if (!groups[func.group]) { if (!groups[func.group]) {
groups[func.group] = {}; groups[func.group] = {};
} }
const oldFunc = groups[func.group][func.type]; const oldFunc = groups[func.group]?.[func.type];
if (!oldFunc || typeSystem.unify(func.dataType, oldFunc.dataType)) { if (!oldFunc || typeSystem.unify(func.dataType, oldFunc.dataType)) {
if (!func.impl) { if (!func.impl) {
delete groups[func.group][func.type]; delete groups[func.group]?.[func.type];
} else { } else {
groups[func.group][func.type] = func; const group = groups[func.group];
if (group) {
group[func.type] = func;
}
} }
} }
}); });

View File

@@ -306,6 +306,7 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
} }
const columns = [...this.computedColumns$.value]; const columns = [...this.computedColumns$.value];
const [column] = columns.splice(columnIndex, 1); const [column] = columns.splice(columnIndex, 1);
if (!column) return {};
const index = insertPositionToIndex(toAfterOfColumn, columns); const index = insertPositionToIndex(toAfterOfColumn, columns);
columns.splice(index, 0, column); columns.splice(index, 0, column);
return { return {
@@ -356,12 +357,12 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
this.groupTrait.moveCardTo(rowId, fromGroup, toGroup, position); this.groupTrait.moveCardTo(rowId, fromGroup, toGroup, position);
} }
override rowNextGet(rowId: string): string { override rowNextGet(rowId: string): string | undefined {
const index = this.rows$.value.indexOf(rowId); const index = this.rows$.value.indexOf(rowId);
return this.rows$.value[index + 1]; return this.rows$.value[index + 1];
} }
override rowPrevGet(rowId: string): string { override rowPrevGet(rowId: string): string | undefined {
const index = this.rows$.value.indexOf(rowId); const index = this.rows$.value.indexOf(rowId);
return this.rows$.value[index - 1]; return this.rows$.value[index - 1];
} }

View File

@@ -233,9 +233,12 @@ export class FilterConditionView extends SignalWatcher(ShadowlessElement) {
if (!type || !argValues || !data) { if (!type || !argValues || !data) {
return; return;
} }
const argDataList = argValues.map((v, i) => const argDataList = argValues.map((v, i) => {
v ? { value: v, type: type.args[i + 1] } : undefined if (v == null) return undefined;
); const argType = type.args[i + 1];
if (!argType) return undefined;
return { value: v, type: argType };
});
const valueString = data.shortString?.(...argDataList) ?? ''; const valueString = data.shortString?.(...argDataList) ?? '';
if (valueString) { if (valueString) {
return `${name}${valueString}`; return `${name}${valueString}`;

View File

@@ -262,6 +262,9 @@ export class FilterGroupView extends SignalWatcher(ShadowlessElement) {
private _clickConditionOps(target: HTMLElement, i: number) { private _clickConditionOps(target: HTMLElement, i: number) {
const filter = this.filterGroup.value.conditions[i]; const filter = this.filterGroup.value.conditions[i];
if (!filter) {
return;
}
popFilterableSimpleMenu(popupTargetFromElement(target), [ popFilterableSimpleMenu(popupTargetFromElement(target), [
menu.group({ menu.group({
items: [ items: [

View File

@@ -202,6 +202,9 @@ export class FilterRootView extends SignalWatcher(ShadowlessElement) {
private _clickConditionOps(target: HTMLElement, i: number) { private _clickConditionOps(target: HTMLElement, i: number) {
const filter = this.filterGroup.value.conditions[i]; const filter = this.filterGroup.value.conditions[i];
if (!filter) {
return;
}
popFilterableSimpleMenu(popupTargetFromElement(target), [ popFilterableSimpleMenu(popupTargetFromElement(target), [
menu.action({ menu.action({
name: filter.type === 'filter' ? 'Turn into group' : 'Wrap in group', name: filter.type === 'filter' ? 'Turn into group' : 'Wrap in group',

View File

@@ -259,7 +259,7 @@ export const popViewOptions = (
return; return;
} }
const isSelected = const isSelected =
meta.type === view.manager.currentView$.value.type; meta.type === view.manager.currentView$.value?.type;
const iconStyle = styleMap({ const iconStyle = styleMap({
fontSize: '24px', fontSize: '24px',
color: isSelected color: isSelected
@@ -285,10 +285,11 @@ export const popViewOptions = (
</div> </div>
`, `,
select: () => { select: () => {
view.manager.viewChangeType( const id = view.manager.currentViewId$.value;
view.manager.currentViewId$.value, if (!id) {
meta.type return;
); }
view.manager.viewChangeType(id, meta.type);
dataViewInstance.clearSelection(); dataViewInstance.clearSelection();
}, },
class: {}, class: {},

View File

@@ -148,7 +148,11 @@ export class DataViewHeaderViews extends WidgetBase {
} }
const views = this.viewManager.views$.value; const views = this.viewManager.views$.value;
const index = views.findIndex(v => v === id); const index = views.findIndex(v => v === id);
const view = this.viewManager.viewGet(views[index]); const viewId = views[index];
if (!viewId) {
return;
}
const view = this.viewManager.viewGet(viewId);
if (!view) { if (!view) {
return; return;
} }

View File

@@ -3,7 +3,8 @@
"compilerOptions": { "compilerOptions": {
"rootDir": "./src/", "rootDir": "./src/",
"outDir": "./dist/", "outDir": "./dist/",
"noEmit": false "noEmit": false,
"noUncheckedIndexedAccess": true
}, },
"include": ["./src"], "include": ["./src"],
"references": [ "references": [

View File

@@ -19,7 +19,7 @@ export class UaHelper {
console.error('Cannot get chrome version'); console.error('Cannot get chrome version');
return 0; return 0;
} }
return parseInt(raw[2], 10); return parseInt(raw[2] ?? '', 10);
}; };
constructor(private readonly navigator: Navigator) { constructor(private readonly navigator: Navigator) {