Skip to content
Merged
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
17 changes: 13 additions & 4 deletions v2/pink-sb/src/lib/Keyboard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@

<script lang="ts">
export let key: string;
export let size: 's' | 'm' = 'm';
export let autoWidth: boolean = false;
</script>

<kbd class:autoWidth>{key}</kbd>
<kbd class:m={size === 'm'} class:s={size === 's'} class:autoWidth>{key}</kbd>

<style lang="scss">
kbd {
display: flex;
width: var(--icon-size-l);
height: var(--icon-size-l);
padding: var(--space-1);
flex-direction: column;
justify-content: center;
Expand All @@ -30,9 +29,19 @@

border-radius: 6px;
background: var(--overlay-on-neutral);

&.s {
width: var(--icon-size-m);
height: var(--icon-size-m);
}

&.m {
width: var(--icon-size-l);
height: var(--icon-size-l);
}
}
.autoWidth {
width: fit-content;
width: fit-content !important;
padding-inline: var(--space-3);
}
</style>
30 changes: 26 additions & 4 deletions v2/pink-sb/src/lib/spreadsheet/Cell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
createEventDispatcher,
type ComponentType
} from 'svelte';
import { getRowContext } from './context.js';

export let root: RootProp;
export let value: string | undefined = undefined;
Expand All @@ -34,6 +35,7 @@
let wasDraggable = false;
let originalValue = value;
let rowIndex: number = -1;
let rowId: string | undefined = undefined;

/* edit slot close() triggers blur which calls commitChange, this prevents that */
let isClosingFloatingEditor = false;
Expand Down Expand Up @@ -151,12 +153,17 @@
return;
}

if (e.key === 'Enter' && isEditable && !isAction && !isSelect) {
// Only check modifiers if expand shortcut is configured
const hasModifier = root.expandKbdShortcut
? e.metaKey || e.ctrlKey || e.shiftKey || e.altKey
: false;

if (e.key === 'Enter' && !hasModifier && isEditable && !isAction && !isSelect) {
originalValue = value;
root.setEditing(id);
e.preventDefault();
return;
} else if (e.key === 'Enter' && isAction) {
} else if (e.key === 'Enter' && !hasModifier && isAction) {
const actionElement = cellEl.firstElementChild as HTMLElement;
if (actionElement && typeof actionElement.click === 'function') {
actionElement.click();
Expand Down Expand Up @@ -194,14 +201,27 @@

$: if (
hasKeyboardNavigation &&
hasContext('row') &&
typeof cellEl !== 'undefined' &&
!isEmptyCell
) {
rowIndex = getContext<number>('row');
const rowContext = getRowContext();
rowId = rowContext?.id ?? undefined;
rowIndex = rowContext?.index ?? -1;
root.registerForNavigation(cellEl, rowIndex, columnIndex);
}

function handleCellFocus() {
if (rowId && rowIndex > 0 && !isEditing) {
root.setFocusedRow(rowId, rowIndex - 1);
}
}

function handleCellBlur() {
if (!isEditing) {
root.setFocusedRow(null, null);
}
}

$: if (isEditing) {
tick().then(() => {
const selects = cellEl?.querySelector('button.input') as HTMLDivElement;
Expand Down Expand Up @@ -290,6 +310,8 @@
}}
on:drop={root.endDrag}
on:keydown={handleCellKeydown}
on:focus={handleCellFocus}
on:blur={handleCellBlur}
on:mouseenter={() => {
if (isHeader && options?.draggable) {
isHeaderBeingHovered = true;
Expand Down
47 changes: 45 additions & 2 deletions v2/pink-sb/src/lib/spreadsheet/Root.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
export let emptyCells: false | number = false;
export let selection: true | 'hidden' | 'disabled' = true;
export let borderRadius: 'xs' | 's' | 'm' | undefined = undefined;
export let expandKbdShortcut: string | undefined = undefined;

export let bottomActionTooltip:
| {
Expand Down Expand Up @@ -57,6 +58,7 @@

let currentlyHoveredColumn: string | null = null;
let currentlyEditingCellId: string | null = null;
let currentFocusedRow: { rowId: string; rowIndex: number } | null = null;
let cellGridRegistry: (HTMLElement | undefined)[][] = [];

let dragManager: DragManager;
Expand Down Expand Up @@ -296,6 +298,14 @@
currentlyEditingCellId = cell;
}

function setFocusedRow(rowId: string | null, rowIndex: number | null) {
if (rowId !== null && rowIndex !== null) {
currentFocusedRow = { rowId, rowIndex };
} else {
currentFocusedRow = null;
}
}

function startDrag(columnId: string, event?: DragEvent) {
draggingColumn = columnId;
dragManager.startDrag(columnId, event);
Expand Down Expand Up @@ -524,6 +534,31 @@
(document.activeElement as HTMLElement | null)?.blur();
}

function handleExpandKbdShortcut(event: KeyboardEvent) {
if (!expandKbdShortcut || !currentFocusedRow || currentlyEditingCellId) return;

const parts = expandKbdShortcut.split('+').map((p) => p.trim().toLowerCase());
const expectedKey = parts[parts.length - 1];
const expectedModifiers = parts.slice(0, -1).sort().join('+');

// build current pressed combination
const pressedModifiers: string[] = [];
if (event.metaKey || event.ctrlKey) pressedModifiers.push('cmd');
if (event.shiftKey) pressedModifiers.push('shift');
if (event.altKey) pressedModifiers.push('alt');
const actualModifiers = pressedModifiers.sort().join('+');

// check if key and modifiers match
if (event.key.toLowerCase() !== expectedKey) return;
if (expectedModifiers !== actualModifiers) return;

event.preventDefault();
dispatch('expandKbdShortcut', {
rowId: currentFocusedRow.rowId,
rowIndex: currentFocusedRow.rowIndex
});
}

$: emptyRowsCount = typeof emptyCells === 'number' ? emptyCells : 0;

$: someRowsSelected =
Expand Down Expand Up @@ -562,7 +597,10 @@
unregisterForNavigation,
moveFocus,
setColumnHeaderHovered,
currentlyHoveredColumnHeader: currentlyHoveredColumn
currentlyHoveredColumnHeader: currentlyHoveredColumn,
expandKbdShortcut,
currentFocusedRow,
setFocusedRow
} as RootProp;

const virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({
Expand Down Expand Up @@ -621,7 +659,12 @@
}
</script>

<svelte:window on:keydown={clearNavFocusOnEscape} />
<svelte:window
on:keydown={(e) => {
clearNavFocusOnEscape(e);
handleExpandKbdShortcut(e);
}}
/>

<div
class="root"
Expand Down
16 changes: 16 additions & 0 deletions v2/pink-sb/src/lib/spreadsheet/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getContext, hasContext, setContext } from 'svelte';

type RowContext = {
id?: string;
index?: number;
}

const ROW_CONTEXT_KEY = Symbol("row");

export function setRowContext(rowInfo: RowContext) {
setContext(ROW_CONTEXT_KEY, rowInfo);
}

export function getRowContext(): RowContext | null {
return hasContext(ROW_CONTEXT_KEY) ? getContext(ROW_CONTEXT_KEY) : null;
}
3 changes: 3 additions & 0 deletions v2/pink-sb/src/lib/spreadsheet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export type RootProp = Readonly<{
moveFocus: (row: number, col: number, direction: string) => void;
currentlyHoveredColumnHeader: string;
setColumnHeaderHovered: (col: string | null | undefined) => void;
expandKbdShortcut?: string | undefined;
currentFocusedRow: { rowId: string; rowIndex: number } | null;
setFocusedRow: (rowId: string | null, rowIndex: number | null) => void;
}>;

export type Alignment =
Expand Down
3 changes: 2 additions & 1 deletion v2/pink-sb/src/lib/spreadsheet/row/Base.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { onMount, setContext } from 'svelte';
import type { RowBaseProps } from './index.js';
import Checkbox from '$lib/selector/Checkbox.svelte';
import { setRowContext } from '../context.js';

type $$Props = RowBaseProps &
Partial<{
Expand Down Expand Up @@ -66,7 +67,7 @@

if (root.keyboardNavigation && !isEmptyRow) {
const rowIndex = isHeader ? 0 : (index ?? 0) + 1;
setContext('row', rowIndex);
setRowContext({ id, index: rowIndex });
}

$: fontSizeStyle = (() => {
Expand Down
20 changes: 11 additions & 9 deletions v2/pink-sb/src/stories/Keyboard.stories.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
title: 'Components/Keyboard',
component: Keyboard,
args: {
key: 'A'
size: 'm'
}
};
</script>
Expand All @@ -18,15 +18,17 @@

<Template let:args>
<Stack>
<Keyboard {...args} />
<Keyboard key={SpecialCharacter.Command} />
<Keyboard key={SpecialCharacter.Option} />
<Keyboard key={SpecialCharacter.Up} />
<Keyboard key={SpecialCharacter.Right} />
<Keyboard key={SpecialCharacter.Down} />
<Keyboard key={SpecialCharacter.Left} />
<Keyboard key={'Enter'} autoWidth={true} />
<Keyboard {...args} key="A" />
<Keyboard key={SpecialCharacter.Command} {...args} />
<Keyboard key={SpecialCharacter.Option} {...args} />
<Keyboard key={SpecialCharacter.Up} {...args} />
<Keyboard key={SpecialCharacter.Right} {...args} />
<Keyboard key={SpecialCharacter.Down} {...args} />
<Keyboard key={SpecialCharacter.Left} {...args} />
<Keyboard key={'Enter'} autoWidth {...args} />
</Stack>
</Template>

<Story name="Default" />

<Story name="Small size" args={{ size: 's' }} />
Loading
Loading