Skip to content

Commit

Permalink
Theme options for edit hover indicators (#926)
Browse files Browse the repository at this point in the history
* Better and themeable edit hover indicators

* fix build

* Rename
  • Loading branch information
ivoelbert authored Mar 18, 2024
1 parent d01f452 commit bdffd4c
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 45 deletions.
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 11 additions & 2 deletions packages/core/src/cells/number-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from "react";
import { drawTextCell, prepTextCell } from "../internal/data-grid/render/data-grid-lib.js";
import { GridCellKind, type NumberCell } from "../internal/data-grid/data-grid-types.js";
import type { InternalCellRenderer } from "./cell-types.js";
import { drawEditHoverIndicator } from "../internal/data-grid/render/draw-edit-hover-indicator.js";

const NumberOverlayEditor = React.lazy(
async () => await import("../internal/data-grid-overlay-editor/private/number-overlay-editor.js")
Expand All @@ -11,11 +12,19 @@ const NumberOverlayEditor = React.lazy(
export const numberCellRenderer: InternalCellRenderer<NumberCell> = {
getAccessibilityString: c => c.data?.toString() ?? "",
kind: GridCellKind.Number,
needsHover: false,
needsHover: cell => cell.hoverEffect === true,
needsHoverPosition: false,
useLabel: true,
drawPrep: prepTextCell,
draw: a => drawTextCell(a, a.cell.displayData, a.cell.contentAlign),
draw: a => {
const { hoverAmount, cell, ctx, theme, rect, overrideCursor } = a;
const { hoverEffect, displayData, hoverEffectTheme } = cell;

if (hoverEffect === true && hoverAmount > 0) {
drawEditHoverIndicator(ctx, theme, hoverEffectTheme, displayData, rect, hoverAmount, overrideCursor);
}
drawTextCell(a, a.cell.displayData, a.cell.contentAlign);
},
measure: (ctx, cell, theme) => ctx.measureText(cell.displayData).width + theme.cellHorizontalPadding * 2,
onDelete: c => ({
...c,
Expand Down
37 changes: 4 additions & 33 deletions packages/core/src/cells/text-cell.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
/* eslint-disable react/display-name */
import * as React from "react";
import { GrowingEntry } from "../internal/growing-entry/growing-entry.js";
import {
drawTextCell,
measureTextCached,
prepTextCell,
roundedRect,
} from "../internal/data-grid/render/data-grid-lib.js";
import { drawTextCell, prepTextCell } from "../internal/data-grid/render/data-grid-lib.js";
import { GridCellKind, type TextCell } from "../internal/data-grid/data-grid-types.js";
import type { InternalCellRenderer } from "./cell-types.js";
import { withAlpha } from "../internal/data-grid/color-parser.js";
import { drawEditHoverIndicator } from "../internal/data-grid/render/draw-edit-hover-indicator.js";

export const textCellRenderer: InternalCellRenderer<TextCell> = {
getAccessibilityString: c => c.data?.toString() ?? "",
Expand All @@ -20,33 +15,9 @@ export const textCellRenderer: InternalCellRenderer<TextCell> = {
useLabel: true,
draw: a => {
const { cell, hoverAmount, hyperWrapping, ctx, rect, theme, overrideCursor } = a;
const { displayData, contentAlign, hoverEffect, allowWrapping } = cell;
const { displayData, contentAlign, hoverEffect, allowWrapping, hoverEffectTheme } = cell;
if (hoverEffect === true && hoverAmount > 0) {
ctx.textBaseline = "alphabetic";
const padX = theme.cellHorizontalPadding;
const padY = theme.cellVerticalPadding;
const m = measureTextCached(displayData, ctx, theme.baseFontFull, "alphabetic");
const maxH = rect.height - padY;
const h = Math.min(maxH, m.actualBoundingBoxAscent * 2.5);
ctx.beginPath();
roundedRect(
ctx,
rect.x + padX / 2,
rect.y + (rect.height - h) / 2 + 1,
m.width + padX * 3,
h - 1,
theme.roundingRadius ?? 4
);
ctx.globalAlpha = hoverAmount;
ctx.fillStyle = withAlpha(theme.textDark, 0.1);
ctx.fill();

// restore
ctx.globalAlpha = 1;
ctx.fillStyle = theme.textDark;
ctx.textBaseline = "middle";

overrideCursor?.("text");
drawEditHoverIndicator(ctx, theme, hoverEffectTheme, displayData, rect, hoverAmount, overrideCursor);
}
drawTextCell(a, displayData, contentAlign, allowWrapping, hyperWrapping);
},
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/data-editor/stories/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,15 @@ function getColumnsForCellTypes(): GridColumnWithMockingInfo[] {
data: name,
displayData: name,
allowOverlay: true,
hoverEffect: true,
themeOverride: {
cellVerticalPadding: 8,
cellHorizontalPadding: 8,
},
hoverEffectTheme: {
bgColor: "#f4f4f4",
fullSize: true,
},
};
},
},
Expand All @@ -633,6 +642,15 @@ function getColumnsForCellTypes(): GridColumnWithMockingInfo[] {
data: age,
displayData: `${age}`,
allowOverlay: true,
hoverEffect: true,
themeOverride: {
cellVerticalPadding: 8,
cellHorizontalPadding: 8,
},
hoverEffectTheme: {
bgColor: "#f4f4f4",
fullSize: true,
},
};
},
},
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/docs/examples/all-cell-kinds.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const AllCellKinds: React.VFC = () => {
columns={cols}
onCellEdited={setCellValue}
onPaste={true}
// rowHeight={55}
rowHeight={44}
onColumnResize={onColumnResize}
highlightRegions={[
{
Expand All @@ -52,6 +52,9 @@ export const AllCellKinds: React.VFC = () => {
},
},
]}
cellActivationBehavior="single-click"
editorBloom={[-4, -4]}
drawFocusRing={false}
rows={1000}
/>
);
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/internal/data-grid/data-grid-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,11 @@ export interface ProtectedCell extends BaseGridCell {
readonly kind: GridCellKind.Protected;
}

export interface HoverEffectTheme {
bgColor: string;
fullSize: boolean;
}

/** @category Cells */
export interface TextCell extends BaseGridCell {
readonly kind: GridCellKind.Text;
Expand All @@ -338,6 +343,7 @@ export interface TextCell extends BaseGridCell {
readonly readonly?: boolean;
readonly allowWrapping?: boolean;
readonly hoverEffect?: boolean;
readonly hoverEffectTheme?: HoverEffectTheme;
}

/** @category Cells */
Expand All @@ -350,6 +356,8 @@ export interface NumberCell extends BaseGridCell {
readonly allowNegative?: boolean;
readonly thousandSeparator?: boolean | string;
readonly decimalSeparator?: string;
readonly hoverEffect?: boolean;
readonly hoverEffectTheme?: HoverEffectTheme;
}

/** @category Cells */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { FullTheme } from "../../../common/styles.js";
import type { Rectangle, HoverEffectTheme } from "../../../index.js";
import { roundedRect, measureTextCached } from "./data-grid-lib.js";
import { withAlpha } from "../color-parser.js";

export function drawEditHoverIndicator(
ctx: CanvasRenderingContext2D,
theme: FullTheme,
effectTheme: HoverEffectTheme | undefined,
displayData: string,
rect: Rectangle,
hoverAmount: number,
overrideCursor: ((cursor: React.CSSProperties["cursor"] | undefined) => void) | undefined
) {
ctx.textBaseline = "alphabetic";

const effectRect = getHoverEffectRect(ctx, rect, displayData, theme, effectTheme?.fullSize ?? false);

ctx.beginPath();
roundedRect(ctx, effectRect.x, effectRect.y, effectRect.width, effectRect.height, theme.roundingRadius ?? 4);
ctx.globalAlpha = hoverAmount;
ctx.fillStyle = effectTheme?.bgColor ?? withAlpha(theme.textDark, 0.1);
ctx.fill();

// restore
ctx.globalAlpha = 1;
ctx.fillStyle = theme.textDark;
ctx.textBaseline = "middle";

overrideCursor?.("text");
}

function getHoverEffectRect(
ctx: CanvasRenderingContext2D,
cellRect: Rectangle,
displayData: string,
theme: FullTheme,
fullSize: boolean
): Rectangle {
const padX = theme.cellHorizontalPadding;
const padY = theme.cellVerticalPadding;

if (fullSize) {
return {
x: cellRect.x + padX / 2,
y: cellRect.y + padY / 2 + 1,
width: cellRect.width - padX,
height: cellRect.height - padY - 1,
};
}

const m = measureTextCached(displayData, ctx, theme.baseFontFull, "alphabetic");
const maxH = cellRect.height - padY;
const h = Math.min(maxH, m.actualBoundingBoxAscent * 2.5);
return {
x: cellRect.x + padX / 2,
y: cellRect.y + (cellRect.height - h) / 2 + 1,
width: m.width + padX * 3,
height: h - 1,
};
}

0 comments on commit bdffd4c

Please sign in to comment.