mirror of
https://github.com/docmost/docmost.git
synced 2026-08-28 17:27:06 +08:00
feat(base): seed editor value on type-to-edit for free-text cells
This commit is contained in:
@@ -15,9 +15,18 @@ type CellEmailProps = {
|
|||||||
const toDraft = (value: unknown) => (typeof value === "string" ? value : "");
|
const toDraft = (value: unknown) => (typeof value === "string" ? value : "");
|
||||||
const parse = (draft: string) => draft || null;
|
const parse = (draft: string) => draft || null;
|
||||||
|
|
||||||
export function CellEmail({ value, isEditing, onCommit, onCancel }: CellEmailProps) {
|
export function CellEmail({ value, property, rowId, isEditing, onCommit, onCancel }: CellEmailProps) {
|
||||||
const { draft, setDraft, inputRef, handleKeyDown, handleBlur } =
|
const { draft, setDraft, inputRef, handleKeyDown, handleBlur } =
|
||||||
useEditableTextCell({ value, isEditing, onCommit, onCancel, toDraft, parse });
|
useEditableTextCell({
|
||||||
|
value,
|
||||||
|
isEditing,
|
||||||
|
onCommit,
|
||||||
|
onCancel,
|
||||||
|
toDraft,
|
||||||
|
parse,
|
||||||
|
rowId,
|
||||||
|
propertyId: property.id,
|
||||||
|
});
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export function parseNumberDraft(draft: string): number | null {
|
|||||||
export function CellNumber({
|
export function CellNumber({
|
||||||
value,
|
value,
|
||||||
property,
|
property,
|
||||||
|
rowId,
|
||||||
isEditing,
|
isEditing,
|
||||||
onCommit,
|
onCommit,
|
||||||
onCancel,
|
onCancel,
|
||||||
@@ -105,6 +106,8 @@ export function CellNumber({
|
|||||||
onCancel,
|
onCancel,
|
||||||
toDraft,
|
toDraft,
|
||||||
parse: parseNumberDraft,
|
parse: parseNumberDraft,
|
||||||
|
rowId,
|
||||||
|
propertyId: property.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
|
|||||||
@@ -16,9 +16,18 @@ type CellTextProps = {
|
|||||||
const toDraft = (value: unknown) => (typeof value === "string" ? value : "");
|
const toDraft = (value: unknown) => (typeof value === "string" ? value : "");
|
||||||
const parse = (draft: string) => draft;
|
const parse = (draft: string) => draft;
|
||||||
|
|
||||||
export function CellText({ value, isEditing, onCommit, onCancel }: CellTextProps) {
|
export function CellText({ value, property, rowId, isEditing, onCommit, onCancel }: CellTextProps) {
|
||||||
const { draft, setDraft, inputRef, handleKeyDown, handleBlur } =
|
const { draft, setDraft, inputRef, handleKeyDown, handleBlur } =
|
||||||
useEditableTextCell({ value, isEditing, onCommit, onCancel, toDraft, parse });
|
useEditableTextCell({
|
||||||
|
value,
|
||||||
|
isEditing,
|
||||||
|
onCommit,
|
||||||
|
onCancel,
|
||||||
|
toDraft,
|
||||||
|
parse,
|
||||||
|
rowId,
|
||||||
|
propertyId: property.id,
|
||||||
|
});
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,9 +16,18 @@ type CellUrlProps = {
|
|||||||
const toDraft = (value: unknown) => (typeof value === "string" ? value : "");
|
const toDraft = (value: unknown) => (typeof value === "string" ? value : "");
|
||||||
const parse = (draft: string) => draft || null;
|
const parse = (draft: string) => draft || null;
|
||||||
|
|
||||||
export function CellUrl({ value, isEditing, onCommit, onCancel }: CellUrlProps) {
|
export function CellUrl({ value, property, rowId, isEditing, onCommit, onCancel }: CellUrlProps) {
|
||||||
const { draft, setDraft, inputRef, handleKeyDown, handleBlur } =
|
const { draft, setDraft, inputRef, handleKeyDown, handleBlur } =
|
||||||
useEditableTextCell({ value, isEditing, onCommit, onCancel, toDraft, parse });
|
useEditableTextCell({
|
||||||
|
value,
|
||||||
|
isEditing,
|
||||||
|
onCommit,
|
||||||
|
onCancel,
|
||||||
|
toDraft,
|
||||||
|
parse,
|
||||||
|
rowId,
|
||||||
|
propertyId: property.id,
|
||||||
|
});
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { useStore, type PrimitiveAtom } from "jotai";
|
||||||
|
import { pendingTypeInsertAtom, type PendingTypeInsert } from "@/ee/base/atoms/base-atoms";
|
||||||
|
|
||||||
export type UseEditableTextCellParams = {
|
export type UseEditableTextCellParams = {
|
||||||
value: unknown;
|
value: unknown;
|
||||||
@@ -9,6 +11,8 @@ export type UseEditableTextCellParams = {
|
|||||||
toDraft: (value: unknown) => string;
|
toDraft: (value: unknown) => string;
|
||||||
/** draft string -> the value passed to onCommit */
|
/** draft string -> the value passed to onCommit */
|
||||||
parse: (draft: string) => unknown;
|
parse: (draft: string) => unknown;
|
||||||
|
rowId?: string;
|
||||||
|
propertyId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EditableTextCell = {
|
export type EditableTextCell = {
|
||||||
@@ -26,6 +30,8 @@ export function useEditableTextCell({
|
|||||||
onCancel,
|
onCancel,
|
||||||
toDraft,
|
toDraft,
|
||||||
parse,
|
parse,
|
||||||
|
rowId,
|
||||||
|
propertyId,
|
||||||
}: UseEditableTextCellParams): EditableTextCell {
|
}: UseEditableTextCellParams): EditableTextCell {
|
||||||
const [draft, setDraft] = useState(() => toDraft(value));
|
const [draft, setDraft] = useState(() => toDraft(value));
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -33,18 +39,37 @@ export function useEditableTextCell({
|
|||||||
const wasEditingRef = useRef(false);
|
const wasEditingRef = useRef(false);
|
||||||
const toDraftRef = useRef(toDraft);
|
const toDraftRef = useRef(toDraft);
|
||||||
toDraftRef.current = toDraft;
|
toDraftRef.current = toDraft;
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEditing && !wasEditingRef.current) {
|
if (isEditing && !wasEditingRef.current) {
|
||||||
committedRef.current = false;
|
committedRef.current = false;
|
||||||
setDraft(toDraftRef.current(value));
|
const pending = store.get(pendingTypeInsertAtom);
|
||||||
requestAnimationFrame(() => {
|
const seeded =
|
||||||
inputRef.current?.focus();
|
pending != null &&
|
||||||
inputRef.current?.select();
|
pending.rowId === rowId &&
|
||||||
});
|
pending.propertyId === propertyId;
|
||||||
|
if (seeded) {
|
||||||
|
setDraft(pending.char);
|
||||||
|
store.set(pendingTypeInsertAtom as PrimitiveAtom<PendingTypeInsert>, null);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const el = inputRef.current;
|
||||||
|
if (el) {
|
||||||
|
el.focus();
|
||||||
|
const len = el.value.length;
|
||||||
|
el.setSelectionRange(len, len);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setDraft(toDraftRef.current(value));
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
inputRef.current?.select();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wasEditingRef.current = isEditing;
|
wasEditingRef.current = isEditing;
|
||||||
}, [isEditing, value]);
|
}, [isEditing, value, rowId, propertyId, store]);
|
||||||
|
|
||||||
const commitOnce = useCallback(
|
const commitOnce = useCallback(
|
||||||
(val: unknown) => {
|
(val: unknown) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user