@synopackageland/ui 0.20.2 → 0.20.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSES/ux-foundation.md +83 -0
  3. package/LICENSES/ux-grid-baserow.md +35 -0
  4. package/LICENSES/ux-grid-teable.md +48 -0
  5. package/README.md +3 -0
  6. package/dist/cli.js +0 -0
  7. package/dist/components/app/app-layout/AppLayout.vue.d.ts +31 -4
  8. package/dist/components/app/app-root/AppRoot.vue.d.ts +9 -0
  9. package/dist/components/app/aside-panel/AsidePanel.vue.d.ts +69 -0
  10. package/dist/components/app/aside-panel/asidePreference.d.ts +28 -0
  11. package/dist/components/composite/adaptive-panel/AdaptivePanel.vue.d.ts +61 -0
  12. package/dist/components/composite/connection-topology/ConnectionTopology.vue.d.ts +1 -1
  13. package/dist/components/composite/data-grid/DataGrid.vue.d.ts +3 -2
  14. package/dist/components/composite/data-grid/viewState.d.ts +2 -2
  15. package/dist/components/composite/file-picker/FilePicker.vue.d.ts +1 -1
  16. package/dist/components/composite/mention-list/MentionList.vue.d.ts +2 -2
  17. package/dist/components/composite/overflow-toolbar/OverflowToolbar.vue.d.ts +2 -2
  18. package/dist/components/composite/record-panel/RecordPanel.vue.d.ts +77 -0
  19. package/dist/components/composite/record-panel/recordPanelWidth.d.ts +54 -0
  20. package/dist/components/composite/summary-card/SummaryCard.vue.d.ts +1 -1
  21. package/dist/components/composite/tag-editor/TagEditor.vue.d.ts +2 -2
  22. package/dist/components/composite/tree-view/TreeView.vue.d.ts +1 -1
  23. package/dist/components/composite/wizard/Wizard.vue.d.ts +2 -2
  24. package/dist/components/composite/wizard/WizardDialog.vue.d.ts +2 -2
  25. package/dist/components/containers/card/Card.vue.d.ts +1 -1
  26. package/dist/components/containers/card/CardField.vue.d.ts +26 -0
  27. package/dist/components/containers/card-grid/CardCover.vue.d.ts +16 -0
  28. package/dist/components/containers/card-grid/CardGrid.vue.d.ts +1 -1
  29. package/dist/components/containers/card-grid/VirtualCardGrid.vue.d.ts +71 -0
  30. package/dist/components/containers/expandable-item/ExpandableItem.vue.d.ts +1 -1
  31. package/dist/components/containers/fieldset/Fieldset.vue.d.ts +1 -1
  32. package/dist/components/containers/list-detail/ListDetail.vue.d.ts +1 -1
  33. package/dist/components/containers/split-pane/SplitPane.vue.d.ts +1 -1
  34. package/dist/components/feedback/notification/Notification.vue.d.ts +1 -1
  35. package/dist/components/feedback/notification/NotificationProvider.vue.d.ts +1 -1
  36. package/dist/components/primitives/breadcrumb/Breadcrumb.vue.d.ts +2 -2
  37. package/dist/components/primitives/context-menu/ContextMenu.vue.d.ts +3 -0
  38. package/dist/components/primitives/context-menu/ContextMenuItems.vue.d.ts +4 -5
  39. package/dist/components/primitives/dialog/Dialog.vue.d.ts +2 -2
  40. package/dist/components/primitives/dropdown-menu/DropdownMenu.vue.d.ts +4 -0
  41. package/dist/components/primitives/dropdown-menu/DropdownMenuItems.vue.d.ts +6 -3
  42. package/dist/components/primitives/popover/Popover.vue.d.ts +10 -2
  43. package/dist/components/primitives/popover/floating.d.ts +27 -0
  44. package/dist/components/primitives/popover/overlay.d.ts +36 -0
  45. package/dist/components/primitives/segment-control/SegmentControl.vue.d.ts +4 -1
  46. package/dist/components/primitives/segment-control/context.d.ts +3 -0
  47. package/dist/components/primitives/select/Select.vue.d.ts +4 -0
  48. package/dist/components/primitives/select/internal/ComboboxField.vue.d.ts +1 -0
  49. package/dist/components/primitives/select/internal/types.d.ts +2 -0
  50. package/dist/components/primitives/skeleton/SectionSkeleton.vue.d.ts +25 -4
  51. package/dist/components/primitives/skeleton/Skeleton.vue.d.ts +3 -0
  52. package/dist/components/primitives/tag/Tag.vue.d.ts +2 -2
  53. package/dist/components/primitives/toast/ToastProvider.vue.d.ts +0 -2
  54. package/dist/components/primitives/toast/toast.d.ts +21 -1
  55. package/dist/components/primitives/tooltip/Tooltip.vue.d.ts +8 -3
  56. package/dist/components/primitives/tooltip/tooltipStyle.d.ts +11 -1
  57. package/dist/composables/backDismiss.d.ts +19 -0
  58. package/dist/composables/phone.d.ts +8 -0
  59. package/dist/composables/softKeyboard.d.ts +26 -4
  60. package/dist/composables/visualViewport.d.ts +31 -0
  61. package/dist/dnd/autoScroll.d.ts +57 -0
  62. package/dist/dnd/dragKit.d.ts +111 -0
  63. package/dist/dnd/index.d.ts +4 -0
  64. package/dist/dnd/layout.d.ts +57 -0
  65. package/dist/dnd/vue.d.ts +16 -0
  66. package/dist/feedback/StatusSlot.vue.d.ts +24 -0
  67. package/dist/feedback/showAfter.d.ts +15 -0
  68. package/dist/feedback/stackMotion.d.ts +13 -0
  69. package/dist/feedback/timing.d.ts +23 -0
  70. package/dist/grid/cells/draw.d.ts +57 -0
  71. package/dist/grid/cells/images.d.ts +5 -0
  72. package/dist/grid/cells/index.d.ts +7 -0
  73. package/dist/grid/cells/number.d.ts +5 -0
  74. package/dist/grid/cells/registry.d.ts +56 -0
  75. package/dist/grid/cells/renderers.d.ts +8 -0
  76. package/dist/grid/cells/status.d.ts +6 -0
  77. package/dist/grid/cells/types.d.ts +200 -0
  78. package/dist/grid/cells/use.d.ts +7 -0
  79. package/dist/grid/clipboard/copy.d.ts +17 -0
  80. package/dist/grid/clipboard/index.d.ts +4 -0
  81. package/dist/grid/clipboard/proposal.d.ts +35 -0
  82. package/dist/grid/clipboard/tsv.d.ts +14 -0
  83. package/dist/grid/clipboard/write.d.ts +21 -0
  84. package/dist/grid/contracts.d.ts +200 -0
  85. package/dist/grid/edit/cell-status.d.ts +33 -0
  86. package/dist/grid/edit/edit-session.d.ts +97 -0
  87. package/dist/grid/edit/use-grid-edit.d.ts +48 -0
  88. package/dist/grid/focus/editor-style.d.ts +67 -0
  89. package/dist/grid/host/GridHost.vue.d.ts +137 -0
  90. package/dist/grid/host/controller.d.ts +219 -0
  91. package/dist/grid/host/dom.d.ts +10 -0
  92. package/dist/grid/index.d.ts +8 -0
  93. package/dist/grid/range/block.d.ts +30 -0
  94. package/dist/grid/range/fill.d.ts +27 -0
  95. package/dist/grid/range/index.d.ts +2 -0
  96. package/dist/grid/theme.d.ts +61 -0
  97. package/dist/grid-core/auto-scroll.d.ts +67 -0
  98. package/dist/grid-core/column-resize.d.ts +43 -0
  99. package/dist/grid-core/config.d.ts +39 -0
  100. package/dist/grid-core/coordinate.d.ts +64 -0
  101. package/dist/grid-core/drag.d.ts +46 -0
  102. package/dist/grid-core/freeze.d.ts +11 -0
  103. package/dist/grid-core/index.d.ts +17 -0
  104. package/dist/grid-core/keyboard.d.ts +34 -0
  105. package/dist/grid-core/keys.d.ts +38 -0
  106. package/dist/grid-core/lru.d.ts +10 -0
  107. package/dist/grid-core/range.d.ts +28 -0
  108. package/dist/grid-core/region.d.ts +73 -0
  109. package/dist/grid-core/renderer/base.d.ts +92 -0
  110. package/dist/grid-core/renderer/layout.d.ts +129 -0
  111. package/dist/grid-core/selection-controller.d.ts +62 -0
  112. package/dist/grid-core/selection.d.ts +35 -0
  113. package/dist/grid-core/theme.d.ts +81 -0
  114. package/dist/grid-core/types.d.ts +178 -0
  115. package/dist/grid.js +6173 -0
  116. package/dist/i18n/text.d.ts +6 -0
  117. package/dist/index.d.ts +32 -2
  118. package/dist/index.js +17662 -13637
  119. package/dist/interaction/editSession.d.ts +49 -0
  120. package/dist/interaction/focus.d.ts +28 -0
  121. package/dist/interaction/gesture.d.ts +49 -0
  122. package/dist/interaction/ime.d.ts +55 -0
  123. package/dist/interaction/index.d.ts +6 -0
  124. package/dist/interaction/outside.d.ts +40 -0
  125. package/dist/interaction/vue.d.ts +21 -0
  126. package/dist/json-ai-prompt.md +1 -1
  127. package/dist/json-preview/preview.js +8529 -7812
  128. package/dist/json.js +10 -10
  129. package/dist/lib/adaptiveLayout.d.ts +32 -2
  130. package/dist/lib/deviceViewport.d.ts +2 -1
  131. package/dist/lib/motion.d.ts +12 -0
  132. package/dist/reka-runtime.json +1 -1
  133. package/dist/theme.css +1 -1
  134. package/dist/themes/dsm7-embedded-theme.css +1 -1
  135. package/dist/themes/dsm7-embedded.css +1 -1
  136. package/dist/themes/dsm7.css +1 -1
  137. package/dist/virtual/cardHeight.d.ts +39 -0
  138. package/dist/virtual/galleryColumns.d.ts +11 -0
  139. package/dist/virtual/layoutMotion.d.ts +30 -0
  140. package/docs/adaptive-layout.md +137 -16
  141. package/docs/callout.md +22 -1
  142. package/docs/list-detail.md +54 -0
  143. package/docs/mobile-native.md +214 -0
  144. package/docs/release-checks.md +23 -0
  145. package/docs/theme-hooks.md +28 -7
  146. package/docs/ux-foundation.md +133 -0
  147. package/docs/ux-grid.md +217 -0
  148. package/package.json +94 -3
@@ -0,0 +1,33 @@
1
+ import { GridCellStatus as GridCellMark } from '../cells/types';
2
+ import { GridCellSaveStatus } from '../contracts';
3
+ /** Saves faster than this never show the saving mark (spec §3.4). */
4
+ export declare const SAVING_MARK_DELAY_MS = 300;
5
+ export interface GridSaveTicket {
6
+ /** The save landed: drop the saving mark. */
7
+ done(): void;
8
+ /** The save failed; the caller has rolled the value back. Shows the error mark with this (translated) reason. */
9
+ fail(message: string): void;
10
+ }
11
+ export interface GridCellStatusStore {
12
+ /** Status for a key, as the host's `cellStatus` returns it. */
13
+ get(key: string): GridCellSaveStatus;
14
+ /** A save started for this key. A newer save for the same key replaces the older one. */
15
+ track(key: string): GridSaveTicket;
16
+ fail(key: string, message: string): void;
17
+ clear(key: string): void;
18
+ dispose(): void;
19
+ }
20
+ export interface GridCellStatusStoreOptions {
21
+ /** Something visible changed: call the host's `invalidate()`. */
22
+ onChange: () => void;
23
+ delayMs?: number;
24
+ setTimer?: (run: () => void, ms: number) => unknown;
25
+ clearTimer?: (handle: unknown) => void;
26
+ }
27
+ /**
28
+ * Keys are the caller's (for example `${rowId}:${fieldId}`), so marks stay on
29
+ * their record when rows are sorted or inserted.
30
+ */
31
+ export declare function createCellStatusStore(options: GridCellStatusStoreOptions): GridCellStatusStore;
32
+ /** The mark the cell renderer draws for a save state (`useGridCells` `getStatus`). */
33
+ export declare function cellStatusMark(status: GridCellSaveStatus): GridCellMark | null;
@@ -0,0 +1,97 @@
1
+ import { EditCommitResult } from '../../interaction/editSession';
2
+ import { GridCell, GridCellCommitReason, GridEditMove, GridEditTrigger, GridEditorKind } from '../contracts';
3
+ /** `edit` changes the value; `view` shows a read-only cell in full (Enter on a formula, for example). */
4
+ export type GridEditMode = 'edit' | 'view';
5
+ export interface GridEditBegin {
6
+ cell: GridCell;
7
+ kind: Exclude<GridEditorKind, 'custom'>;
8
+ mode: GridEditMode;
9
+ trigger: GridEditTrigger;
10
+ /** The cell's editable text, from the loaded row (never fetched). */
11
+ value: string;
12
+ }
13
+ /** What the focus owner should show when an edit begins. */
14
+ export interface GridEditStart {
15
+ /** The text to put in the focus owner: '' when the typed key replaces the value. */
16
+ text: string;
17
+ /** Put the caret after the text (Enter, F2, double-click). */
18
+ caretAtEnd: boolean;
19
+ }
20
+ export type GridEditKeyInput = Pick<KeyboardEvent, 'key' | 'shiftKey' | 'altKey'> & {
21
+ commandKey: boolean;
22
+ /** An input method owns the key: composing, keyCode 229, or Safari's Enter right after `compositionend`. */
23
+ imeOwned: boolean;
24
+ };
25
+ /**
26
+ * - `pass`: leave the key to the focus owner (characters, caret keys, a newline)
27
+ * - `prevent`: the session used the key; cancel it and stop it there
28
+ */
29
+ export type GridEditKeyResult = 'pass' | 'prevent';
30
+ export interface GridEditState {
31
+ readonly active: boolean;
32
+ readonly cell: GridCell | null;
33
+ readonly mode: GridEditMode;
34
+ readonly kind: Exclude<GridEditorKind, 'custom'>;
35
+ readonly draft: string;
36
+ readonly composing: boolean;
37
+ readonly dirty: boolean;
38
+ /** Message from a rejected commit; the editor stays open with a danger ring. */
39
+ readonly error: string | null;
40
+ readonly version: number;
41
+ }
42
+ export interface GridEditCommitInput {
43
+ cell: GridCell;
44
+ value: string;
45
+ previous: string;
46
+ move: GridEditMove;
47
+ reason: GridCellCommitReason;
48
+ }
49
+ export interface GridEditSessionOptions {
50
+ /** Return a translated message to keep the editor open (an unparsable number, for example). */
51
+ validate?: (cell: GridCell, text: string) => string | null | undefined;
52
+ /** Write the value to the row model now, synchronously; saving is the caller's job. */
53
+ onCommit: (commit: GridEditCommitInput) => void;
54
+ /** The edit ended without a commit (Esc, or nothing changed). */
55
+ onEnd?: (cell: GridCell, committed: boolean) => void;
56
+ onChange?: (state: GridEditState) => void;
57
+ }
58
+ export declare class GridEditSession {
59
+ private readonly session;
60
+ private readonly options;
61
+ private mode;
62
+ private kind;
63
+ /** Where the selection goes once the pending commit lands (a commit can wait for composition to end). */
64
+ private pending;
65
+ private wrote;
66
+ readonly state: GridEditState;
67
+ constructor(options: GridEditSessionOptions);
68
+ /**
69
+ * Enters edit synchronously, in the same handler as the key, press or
70
+ * composition that asked for it. A second request for the cell already being
71
+ * edited (the IME keydown, then its `compositionstart`) changes nothing.
72
+ */
73
+ begin(request: GridEditBegin): GridEditStart | null;
74
+ /** The focus owner's text changed (typing, paste, IME). Ignored in view mode. */
75
+ input(text: string): void;
76
+ /** Composition start/end. Ending it runs a commit that was waiting for it (Enter or a press during composition). */
77
+ setComposing(composing: boolean): void;
78
+ /**
79
+ * The key table while editing (§3.4). Command keys that an IME owns pass to
80
+ * it untouched; a commit that is still waiting for composition keeps the
81
+ * selection where it is.
82
+ */
83
+ keyDown(event: GridEditKeyInput): GridEditKeyResult;
84
+ /**
85
+ * Ends the edit, writing the draft when it changed. `invalid` keeps the
86
+ * editor open; `composing` defers the commit to the end of composition.
87
+ * After the first commit the session is idle, so a second call (Enter, then
88
+ * the blur it causes) never writes twice.
89
+ */
90
+ commit(reason: GridCellCommitReason, move?: GridEditMove): EditCommitResult;
91
+ /** Esc: revert and leave edit, keeping the selection. False while composing (Esc belongs to the IME). */
92
+ cancel(): boolean;
93
+ /** Where the last commit sent the selection; read inside `onCommit`/`onEnd`. */
94
+ get lastMove(): GridEditMove;
95
+ /** What ended the last edit (`blur` after Esc or a deferred commit); read inside `onEnd`. */
96
+ get lastReason(): GridCellCommitReason;
97
+ }
@@ -0,0 +1,48 @@
1
+ import { ShallowRef } from 'vue';
2
+ import { GridCell, GridCellCommitReason, GridColumn, GridEditMove, GridEditTrigger } from '../contracts';
3
+ import { TextBox } from '../focus/editor-style';
4
+ import { GridEditCommitInput, GridEditMode, GridEditState } from './edit-session';
5
+ export interface UseGridEditOptions {
6
+ owner: () => HTMLTextAreaElement | null | undefined;
7
+ /** The grid's root: presses inside it are the grid's own. */
8
+ root: () => HTMLElement | null | undefined;
9
+ /** Built-in editing is on (the host has `getEditText`). */
10
+ enabled: () => boolean;
11
+ getEditText: (cell: GridCell) => string;
12
+ validate?: (cell: GridCell, column: GridColumn, text: string) => string | null | undefined;
13
+ columnAt: (index: number) => GridColumn | undefined;
14
+ rowHeight: () => number;
15
+ /** From `measureTextBox`, so the editor text sits where the canvas draws it. */
16
+ textBox: () => TextBox | null;
17
+ /** Long text shows a hint band only when the caller gives its (translated) text. */
18
+ hasHint: () => boolean;
19
+ /** The grid has a selection (presses outside then clear it). */
20
+ hasSelection: () => boolean;
21
+ onCommit: (commit: GridEditCommitInput & {
22
+ column: GridColumn;
23
+ }) => void;
24
+ onEnd: (cell: GridCell, column: GridColumn, committed: boolean) => void;
25
+ moveSelection: (move: Exclude<GridEditMove, 'none'>) => void;
26
+ clearSelection: () => void;
27
+ refocus: () => void;
28
+ }
29
+ export interface GridEditHandle {
30
+ /** One shallowRef bump per session change. */
31
+ state: Readonly<ShallowRef<GridEditState>>;
32
+ /** Wrapped lines the open editor needs (long text and the read-only view). */
33
+ lines: Readonly<ShallowRef<number>>;
34
+ readonly active: boolean;
35
+ /** True when this column edits in place (not `custom`). */
36
+ isInline: (column: GridColumn) => boolean;
37
+ /** Opens the editor for an edit or view request; false when the column is not inline. */
38
+ open: (cell: GridCell, column: GridColumn, trigger: GridEditTrigger, mode: GridEditMode) => boolean;
39
+ /** Keydown on the focus owner while open; true when the session used it. */
40
+ keyDown: (event: KeyboardEvent, commandKey: boolean) => boolean;
41
+ compositionStart: () => void;
42
+ compositionEnd: () => void;
43
+ input: () => void;
44
+ blur: () => void;
45
+ commit: (reason: GridCellCommitReason) => boolean;
46
+ cancel: () => void;
47
+ }
48
+ export declare function useGridEdit(options: UseGridEditOptions): GridEditHandle;
@@ -0,0 +1,67 @@
1
+ import { IGridTheme } from '../../grid-core/theme';
2
+ import { GridEditorKind } from '../contracts';
3
+ /** Ring width outside the cell box while editing. */
4
+ export declare const EDITOR_RING_PX = 2;
5
+ /** Inner radius; with the 2 px ring the outer corner is Teable's 6 px. */
6
+ export declare const EDITOR_RADIUS_PX = 4;
7
+ /** Line height of wrapped text in the long-text editor and the read-only view. */
8
+ export declare const WRAP_LINE_HEIGHT_PX = 20;
9
+ export declare const LONG_TEXT_MIN_LINES = 2;
10
+ export declare const WRAP_MAX_LINES = 5;
11
+ /** Bottom band for the "Shift + Enter for a new line" hint (spec: 16-22 px). */
12
+ export declare const HINT_BAND_PX = 16;
13
+ export type FocusOwnerTheme = Pick<IGridTheme, 'fontFamily' | 'fontSizeSM' | 'cellTextColor' | 'cellBg' | 'cellLineColorActived'>;
14
+ export interface FocusOwnerLook {
15
+ /** A built-in editor (or the read-only view) is open on this element. */
16
+ open: boolean;
17
+ kind: Exclude<GridEditorKind, 'custom'>;
18
+ view: boolean;
19
+ invalid: boolean;
20
+ /** Shows the hint band (long text with a caller-provided hint). */
21
+ hint: boolean;
22
+ rowHeight: number;
23
+ /** Wrapped lines the text needs, measured by the caller; ignored for single-line editors. */
24
+ lines: number;
25
+ theme: FocusOwnerTheme;
26
+ dangerColor: string;
27
+ /** The grid font's box and where the canvas draws its baseline, from `measureTextBox`; null when unknown. */
28
+ textBox?: TextBox | null;
29
+ }
30
+ type PaddingLook = Pick<FocusOwnerLook, 'kind' | 'view' | 'hint' | 'rowHeight' | 'textBox'>;
31
+ /** The grid font's box (CSS content area) and the canvas baseline, in CSS px. */
32
+ export interface TextBox {
33
+ ascent: number;
34
+ descent: number;
35
+ /** How far below the row's middle the canvas draws the alphabetic baseline. */
36
+ drop: number;
37
+ }
38
+ /**
39
+ * The grid draws text with `textBaseline = 'middle'` at the row centre, and
40
+ * engines put that baseline differently (13 px Verdana: Chromium 3.73 px
41
+ * below the middle, WebKit 5 px), so ask the canvas rather than assume.
42
+ */
43
+ export declare function measureTextBox(doc: Document, fontSize: number, fontFamily: string): TextBox | null;
44
+ /**
45
+ * Padding and line height that put the first line's baseline where the canvas
46
+ * draws it. Engines floor the half-leading, so a fractional line height cannot
47
+ * land a fractional baseline: the line box is the font's own box (no
48
+ * leading), at whole pixels, placed by the top padding. Wrapped text keeps
49
+ * its 20 px lines and moves the first one the same way. Without metrics
50
+ * (no canvas), the text centres on the row.
51
+ */
52
+ export declare function editorPadding(look: PaddingLook): {
53
+ top: number;
54
+ bottom: number;
55
+ lineHeight: number;
56
+ };
57
+ /** Editor height: the row for single-line editors; 2-5 lines (1-5 in the read-only view) for wrapped text, never less than the row. */
58
+ export declare function editorHeight(look: PaddingLook & Pick<FocusOwnerLook, 'lines'>): number;
59
+ /** Lines the content needs, from the textarea's scrollHeight measured at zero height. */
60
+ export declare function measuredLines(scrollHeight: number, look: PaddingLook): number;
61
+ /** Inline style of the focus owner textarea. */
62
+ export declare function focusOwnerStyle(look: FocusOwnerLook): Record<string, string>;
63
+ /** The hint band's text, inside the editor's bottom padding. */
64
+ export declare function hintStyle(look: Pick<FocusOwnerLook, 'theme'>): Record<string, string>;
65
+ /** The error reason under the cell: an overlay, never in flow. */
66
+ export declare function messageStyle(look: Pick<FocusOwnerLook, 'theme'>): Record<string, string>;
67
+ export {};
@@ -0,0 +1,137 @@
1
+ import { IGridTheme } from '../../grid-core/theme';
2
+ import { GridCopyEvent, GridPasteProposal } from '../clipboard';
3
+ import { GridCellBlock } from '../range/block';
4
+ import { GridFillRequest } from '../range/fill';
5
+ import { GridCell, GridCellCommit, GridCellSaveStatus, GridEditEnd, GridCellRenderer, GridCellTextGetter, GridColumn, GridContextMenuEvent, GridDraggable, GridEditRequest, GridHostExpose, GridIconDrawArgs, GridRect, GridReorderEvent, GridRowControl, GridSelectable, GridSelectionState, GridVisibleRange } from '../contracts';
6
+ type __VLS_Props = {
7
+ columns: GridColumn[];
8
+ rowCount: number;
9
+ /** Text of each cell: drawn by the default renderer and read to assistive tech. */
10
+ getCellText: GridCellTextGetter;
11
+ /** Accessible name of the grid, already translated. */
12
+ label: string;
13
+ /** Accessible name of a row, already translated. Default: the row number. */
14
+ getRowLabel?: (rowIndex: number) => string;
15
+ /** False draws the row as a static placeholder bar until its data arrives. */
16
+ isRowLoaded?: (rowIndex: number) => boolean;
17
+ /** Custom cell painter per column (vendor extension for field renderers). */
18
+ cellRenderer?: (column: GridColumn, columnIndex: number) => GridCellRenderer | undefined;
19
+ drawIcon?: (ctx: CanvasRenderingContext2D, icon: GridIconDrawArgs) => void;
20
+ /** First load: skeleton rows at the real row height, after `loadingDelay`. */
21
+ loading?: boolean;
22
+ loadingDelay?: number;
23
+ readonly?: boolean;
24
+ /** A caller's own editor (a `custom` column) is open over the active cell: keys belong to it, not to the grid. */
25
+ editing?: boolean;
26
+ /**
27
+ * The editable text of a cell, from the loaded row. Given, the grid edits
28
+ * `text`, `long-text` and `number` columns in place on its focus owner and
29
+ * emits `cell-commit`; read-only cells open a read-only view on Enter.
30
+ */
31
+ getEditText?: (cell: GridCell) => string;
32
+ /** Return a translated message to keep the editor open with a danger ring (an unparsable number, for example). */
33
+ validateEdit?: (cell: GridCell, column: GridColumn, text: string) => string | null | undefined;
34
+ /** Save state per cell (see `createCellStatusStore`): an error's message shows under the active cell; call `invalidate()` when it changes. The cell renderer draws the marks. */
35
+ cellStatus?: (cell: GridCell) => GridCellSaveStatus;
36
+ /** Hint at the bottom of the long-text editor, already translated (for example “Shift + Enter for a new line”). */
37
+ editHint?: string;
38
+ /** Row and header height in px. Default: the `--ui-grid-row-height` / `--ui-grid-header-height` tokens. */
39
+ rowHeight?: number;
40
+ headerHeight?: number;
41
+ freezeColumnCount?: number;
42
+ rowControls?: GridRowControl[];
43
+ selectable?: GridSelectable;
44
+ draggable?: GridDraggable;
45
+ columnResizable?: boolean;
46
+ /** Shows the fill handle on a cell range; a drag emits `fill-request`. */
47
+ fillable?: boolean;
48
+ /** Text a copy puts on the clipboard for a cell. Default: `getCellText`. */
49
+ getCopyText?: GridCellTextGetter;
50
+ /**
51
+ * Text of a block that has rows not loaded yet (`isRowLoaded` false), row by
52
+ * row. Without it, copying such a block fails with `ok: false`.
53
+ */
54
+ resolveCopy?: (block: GridCellBlock) => Promise<string[][]>;
55
+ themeOverrides?: Partial<IGridTheme>;
56
+ /** Row tinted as open: the record shown in the caller's sheet or panel, so the user keeps their place. */
57
+ highlightedRow?: number | null;
58
+ };
59
+ type __VLS_Slots = {
60
+ /** Shown over the body when there are no rows and nothing is loading. Default: “No data”. */
61
+ empty?: () => unknown;
62
+ /** Layer above the focus owner for editors and popovers (task 26). */
63
+ overlay?: (scope: {
64
+ activeCell: GridCell | null;
65
+ getCellRect: (cell: GridCell) => GridRect | null;
66
+ }) => unknown;
67
+ };
68
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, GridHostExpose, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
69
+ "context-menu": (event: GridContextMenuEvent) => any;
70
+ "selection-change": (state: GridSelectionState) => any;
71
+ "edit-request": (request: GridEditRequest) => any;
72
+ "cell-commit": (commit: GridCellCommit) => any;
73
+ "edit-end": (end: GridEditEnd) => any;
74
+ "clear-request": (selection: GridSelectionState) => any;
75
+ "row-expand": (rowIndex: number, columnIndex?: number | undefined) => any;
76
+ "header-menu": (columnIndex: number, rect: GridRect) => any;
77
+ "column-resize": (columnIndex: number, width: number) => any;
78
+ "column-resize-end": (columnIndex: number, width: number) => any;
79
+ "column-width-save": (columnIndex: number, width: number) => any;
80
+ "fill-request": (request: GridFillRequest) => any;
81
+ "clipboard-copy": (event: GridCopyEvent) => any;
82
+ "paste-request": (proposal: GridPasteProposal) => any;
83
+ "column-reorder": (event: GridReorderEvent) => any;
84
+ "row-reorder": (event: GridReorderEvent) => any;
85
+ "visible-range-change": (range: GridVisibleRange) => any;
86
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
87
+ "onContext-menu"?: ((event: GridContextMenuEvent) => any) | undefined;
88
+ "onSelection-change"?: ((state: GridSelectionState) => any) | undefined;
89
+ "onEdit-request"?: ((request: GridEditRequest) => any) | undefined;
90
+ "onCell-commit"?: ((commit: GridCellCommit) => any) | undefined;
91
+ "onEdit-end"?: ((end: GridEditEnd) => any) | undefined;
92
+ "onClear-request"?: ((selection: GridSelectionState) => any) | undefined;
93
+ "onRow-expand"?: ((rowIndex: number, columnIndex?: number | undefined) => any) | undefined;
94
+ "onHeader-menu"?: ((columnIndex: number, rect: GridRect) => any) | undefined;
95
+ "onColumn-resize"?: ((columnIndex: number, width: number) => any) | undefined;
96
+ "onColumn-resize-end"?: ((columnIndex: number, width: number) => any) | undefined;
97
+ "onColumn-width-save"?: ((columnIndex: number, width: number) => any) | undefined;
98
+ "onFill-request"?: ((request: GridFillRequest) => any) | undefined;
99
+ "onClipboard-copy"?: ((event: GridCopyEvent) => any) | undefined;
100
+ "onPaste-request"?: ((proposal: GridPasteProposal) => any) | undefined;
101
+ "onColumn-reorder"?: ((event: GridReorderEvent) => any) | undefined;
102
+ "onRow-reorder"?: ((event: GridReorderEvent) => any) | undefined;
103
+ "onVisible-range-change"?: ((range: GridVisibleRange) => any) | undefined;
104
+ }>, {
105
+ loading: boolean;
106
+ readonly: boolean;
107
+ rowHeight: number;
108
+ draggable: GridDraggable;
109
+ selectable: GridSelectable;
110
+ editing: boolean;
111
+ freezeColumnCount: number;
112
+ rowControls: GridRowControl[];
113
+ drawIcon: (ctx: CanvasRenderingContext2D, icon: GridIconDrawArgs) => void;
114
+ headerHeight: number;
115
+ columnResizable: boolean;
116
+ fillable: boolean;
117
+ getRowLabel: (rowIndex: number) => string;
118
+ isRowLoaded: (rowIndex: number) => boolean;
119
+ cellRenderer: (column: GridColumn, columnIndex: number) => GridCellRenderer | undefined;
120
+ loadingDelay: number;
121
+ getEditText: (cell: GridCell) => string;
122
+ validateEdit: (cell: GridCell, column: GridColumn, text: string) => string | null | undefined;
123
+ cellStatus: (cell: GridCell) => GridCellSaveStatus;
124
+ editHint: string;
125
+ getCopyText: GridCellTextGetter;
126
+ resolveCopy: (block: GridCellBlock) => Promise<string[][]>;
127
+ themeOverrides: Partial<IGridTheme>;
128
+ highlightedRow: number | null;
129
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
130
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
131
+ declare const _default: typeof __VLS_export;
132
+ export default _default;
133
+ type __VLS_WithSlots<T, S> = T & {
134
+ new (): {
135
+ $slots: S;
136
+ };
137
+ };
@@ -0,0 +1,219 @@
1
+ import { AutoScroller } from '../../grid-core/auto-scroll';
2
+ import { ColumnResizeController } from '../../grid-core/column-resize';
3
+ import { CoordinateManager } from '../../grid-core/coordinate';
4
+ import { DragController } from '../../grid-core/drag';
5
+ import { IGridRenderProps, IVisibleRegion } from '../../grid-core/renderer/layout';
6
+ import { SelectionController } from '../../grid-core/selection-controller';
7
+ import { IGridTheme } from '../../grid-core/theme';
8
+ import { IColumnResizeState, IDragState, IGridColumn, IMouseState, IPosition, IRowControlItem, IScrollState } from '../../grid-core/types';
9
+ import { GridCell, GridColumn, GridDraggable, GridEditTrigger, GridRange, GridRect, GridRowControl, GridSelectable, GridSelectionState, GridVisibleRange } from '../contracts';
10
+ import { GridFillRequest } from '../range/fill';
11
+ export interface GridHostConfig {
12
+ columns: readonly GridColumn[];
13
+ /** Data rows to lay out (skeleton rows while the first page loads). */
14
+ rowCount: number;
15
+ rowHeight: number;
16
+ headerHeight: number;
17
+ freezeColumnCount: number;
18
+ rowControls: readonly GridRowControl[];
19
+ selectable: GridSelectable;
20
+ draggable: GridDraggable;
21
+ columnResizable: boolean;
22
+ /** No edit or clear requests. */
23
+ readonly: boolean;
24
+ /** Shows the fill handle on a cell range (ignored when read-only). */
25
+ fillable?: boolean;
26
+ /**
27
+ * Touch UI (a coarse primary pointer): no fill handle and no column resize
28
+ * handle, as in Teable's touch layer. Touch gestures go through `tap` and
29
+ * `longPress`, never through the pointer methods. At a grid width of
30
+ * `PHONE_MAX_WIDTH` or less it is the phone layout (see `isPhone`).
31
+ */
32
+ touch?: boolean;
33
+ }
34
+ export interface GridHostHooks {
35
+ /** Something visible changed; draw on the next frame. */
36
+ render: () => void;
37
+ selectionChange: (state: GridSelectionState) => void;
38
+ /** Scroll the host's scroller to this position now (auto-scroll, keyboard). */
39
+ scrollTo: (left: number, top: number) => void;
40
+ editRequest: (cell: GridCell, column: GridColumn, trigger: GridEditTrigger, replace: boolean) => void;
41
+ /** Enter, F2 or double-click on a read-only cell: show its value in full (the edit layer's read-only view). */
42
+ viewRequest?: (cell: GridCell, column: GridColumn, trigger: GridEditTrigger) => void;
43
+ clearRequest: (selection: GridSelectionState) => void;
44
+ /** Open the row's record. `columnIndex`: the tapped field, on a phone (scroll the record to it). */
45
+ rowExpand: (rowIndex: number, columnIndex?: number) => void;
46
+ headerMenu: (columnIndex: number, rect: GridRect) => void;
47
+ columnResize: (columnIndex: number, width: number, done: boolean) => void;
48
+ reorder: (kind: 'columns' | 'rows', ranges: readonly GridRange[], dropIndex: number) => void;
49
+ contextMenu: (selection: GridSelectionState, point: IPosition) => void;
50
+ visibleRangeChange: (range: GridVisibleRange) => void;
51
+ /** A fill-handle drag ended past the source rows; the selection already covers source and target. */
52
+ fill?: (request: GridFillRequest) => void;
53
+ }
54
+ export interface GridPointerInput extends IPosition {
55
+ shiftKey: boolean;
56
+ /** The platform command key: Cmd on macOS, Ctrl elsewhere. */
57
+ commandKey: boolean;
58
+ }
59
+ export type GridKeyInput = Pick<KeyboardEvent, 'key' | 'keyCode' | 'shiftKey' | 'altKey' | 'metaKey' | 'ctrlKey' | 'isComposing'> & {
60
+ /** The platform command key: Cmd on macOS, Ctrl elsewhere. */
61
+ commandKey: boolean;
62
+ };
63
+ /** `prevent`: the host cancels the key. `pass`: the key goes on to the focus owner. `ignore`: not the grid's key. */
64
+ export type GridKeyResult = 'prevent' | 'pass' | 'ignore';
65
+ /** Width of the row header: the row number, plus 24 px per row control beyond the first. */
66
+ export declare const rowHeaderWidth: (controls: readonly GridRowControl[]) => number;
67
+ /** Grid widths up to this are a phone on touch: a tap opens the record (Teable's `(max-width: 640px)`). */
68
+ export declare const PHONE_MAX_WIDTH = 640;
69
+ /** The phone's row header: row numbers only, as Teable's touch grid. */
70
+ export declare const PHONE_ROW_HEADER_WIDTH = 50;
71
+ export declare class GridHostController {
72
+ coord: CoordinateManager;
73
+ scroll: IScrollState;
74
+ mouseState: IMouseState;
75
+ readonly selection: SelectionController;
76
+ readonly resize: ColumnResizeController;
77
+ readonly drag: DragController;
78
+ readonly autoScroller: AutoScroller;
79
+ /** The grid-core column list; a new array whenever columns or widths change. */
80
+ gridColumns: IGridColumn[];
81
+ rowControls: IRowControlItem[];
82
+ dragState: IDragState;
83
+ resizeState: IColumnResizeState;
84
+ /** Bumped on every change that needs the cached cell layer redrawn. */
85
+ contentVersion: number;
86
+ theme: Pick<IGridTheme, 'iconSizeXS' | 'iconSizeMD'>;
87
+ private config;
88
+ private width;
89
+ private height;
90
+ /** Live widths from a resize in progress or not yet echoed back through `columns`. */
91
+ private widthOverrides;
92
+ private visible;
93
+ private down;
94
+ private lastPoint;
95
+ private lastModifiers;
96
+ private lastSelection;
97
+ /** The source block while the fill handle is dragged. */
98
+ private fillSource;
99
+ private readonly hooks;
100
+ constructor(config: GridHostConfig, hooks: GridHostHooks);
101
+ /**
102
+ * Touch on a grid no wider than 640 px. There a tap opens the record (the
103
+ * cell and its editor cannot fit beside the keyboard), the row header shows
104
+ * numbers only and no column is frozen: Teable's phone grid.
105
+ */
106
+ get isPhone(): boolean;
107
+ private visibleRowControls;
108
+ private gutterWidth;
109
+ private createCoordinate;
110
+ private applyColumns;
111
+ /**
112
+ * Frozen columns that fit: the scrolling part keeps at least 240 px, so on a
113
+ * phone a wide first column stops freezing (Teable's getMaxFreezeColumnCount).
114
+ */
115
+ private freezeCount;
116
+ configure(next: GridHostConfig): void;
117
+ /** Drops a selection that points past the data after rows or columns went away. */
118
+ private clampSelection;
119
+ get settings(): Readonly<GridHostConfig>;
120
+ setViewport(width: number, height: number): void;
121
+ get viewport(): {
122
+ width: number;
123
+ height: number;
124
+ };
125
+ /** Scrollable content size for the scroller's spacer. */
126
+ get contentSize(): {
127
+ width: number;
128
+ height: number;
129
+ };
130
+ /** The scroller moved (native scroll or our own `scrollTo`). */
131
+ setScroll(left: number, top: number): void;
132
+ private scrollTo;
133
+ private updateVisible;
134
+ get visibleRegion(): IVisibleRegion;
135
+ /** Marks cell content stale (data, columns or theme changed). */
136
+ invalidate(): void;
137
+ getSelection(): GridSelectionState;
138
+ setSelection(next: Omit<GridSelectionState, 'activeCell'> & {
139
+ activeCell?: GridCell | null;
140
+ }): void;
141
+ private selectCell;
142
+ getCellRect(cell: GridCell): GridRect | null;
143
+ /** Minimal scroll that shows the cell with Teable's 16 px buffer; instant. */
144
+ scrollToCell(cell: GridCell): void;
145
+ private regionContext;
146
+ private get isFillEnabled();
147
+ /** No resize handle on touch: a 5 px edge is no finger target (Teable has none on touch either). */
148
+ private get isResizeEnabled();
149
+ /** True while the fill handle is dragged. */
150
+ get isFilling(): boolean;
151
+ /**
152
+ * The data cell under a drag, with the pointer clamped to the body: past an
153
+ * edge, over a header or over blank space it is the nearest visible cell, so
154
+ * the head never leaves the data while auto-scroll runs.
155
+ */
156
+ private cellUnderDrag;
157
+ /** The row under a fill drag (vertical only). */
158
+ private fillTargetRow;
159
+ activeCellBound(): {
160
+ columnIndex: number;
161
+ rowIndex: number;
162
+ width: number;
163
+ height: number;
164
+ } | null;
165
+ hitTest(point: IPosition): IMouseState;
166
+ /** Returns true when the press belongs to the grid (the host then claims focus). */
167
+ pointerDown(input: GridPointerInput): boolean;
168
+ /** Hover and gestures. `fromAutoScroll` re-derives the head after an auto-scroll tick. */
169
+ pointerMove(input: GridPointerInput, fromAutoScroll?: boolean): void;
170
+ pointerUp(input: GridPointerInput): void;
171
+ private endFill;
172
+ /** Pointer left the host without a press. */
173
+ pointerLeave(): void;
174
+ /** Lost pointer capture or Esc during a gesture: nothing moves, the width comes back. */
175
+ cancelGesture(): boolean;
176
+ get isGestureActive(): boolean;
177
+ doubleClick(point: IPosition): void;
178
+ contextMenu(point: IPosition): boolean;
179
+ /** True when `cell` is the active cell and the whole selection. */
180
+ private isSoleActiveCell;
181
+ /**
182
+ * A finger tap (no move past the slop, released before the long press).
183
+ * The tapped row or header keeps a pressed tint until `clearTapFeedback`.
184
+ *
185
+ * - A cell on a phone (`isPhone`): selects it and opens the record
186
+ * (`rowExpand` with the column), as Teable's touch grid.
187
+ * - A cell on a wider touch screen: the first tap selects it, a second tap
188
+ * on the selected cell asks for an edit (`tap` trigger).
189
+ * - A header with a menu opens the menu, as in Teable's touch layer (the
190
+ * 16 px chevron is no finger target); without one it selects the column.
191
+ * - The row header is one target: on a phone it opens the record; wider, it
192
+ * expands the row with the `expand` control, else toggles the checkbox.
193
+ *
194
+ * There is no range, fill or drag on touch.
195
+ */
196
+ tap(input: IPosition): void;
197
+ /** Ends the pressed tint of the last tap (the host calls it about 400 ms later). */
198
+ clearTapFeedback(): void;
199
+ /** A finger held still for 250 ms: the context menu, as a right click (keeps a selection it lands in). */
200
+ longPress(input: IPosition): boolean;
201
+ private requestEdit;
202
+ /**
203
+ * After an edit of `cell` commits: Enter moves down, Tab right, Shift+Tab
204
+ * left (Teable's enterCommitSelection / tabSelection), from the edited cell.
205
+ */
206
+ moveAfterEdit(cell: GridCell, move: 'down' | 'right' | 'left'): void;
207
+ /** An input method started composing on the selected cell. */
208
+ compositionStart(): void;
209
+ /**
210
+ * Key on the focus owner while no editor is open (spec §3.1–3.2). Printable
211
+ * and IME keys pass through so the first character lands in the focus owner.
212
+ */
213
+ keyDown(event: GridKeyInput): GridKeyResult;
214
+ /** Everything `drawGrid` needs except the theme and the cell/icon painters. */
215
+ frame(): Omit<IGridRenderProps, 'theme' | 'drawCell' | 'drawIcon' | 'height'>;
216
+ /** Cursor for the current hover state; set on the same move (spec §3.12). */
217
+ get cursor(): string;
218
+ dispose(): void;
219
+ }
@@ -0,0 +1,10 @@
1
+ /** macOS and iPadOS use Cmd as the command key; everything else uses Ctrl. */
2
+ export declare const isMacPlatform: () => boolean;
3
+ export interface GridDensity {
4
+ rowHeight: number;
5
+ headerHeight: number;
6
+ }
7
+ /** Row and header height from the density tokens in effect on `el` (task 08 owns their values). */
8
+ export declare const readGridDensity: (el: Element, fallback?: GridDensity) => GridDensity;
9
+ /** Skeleton rows for the first load: as many whole rows as fit under the header (spec §3.12). */
10
+ export declare const skeletonRowCount: (viewportHeight: number, headerHeight: number, rowHeight: number) => number;
@@ -0,0 +1,8 @@
1
+ export { default as GridHost } from './host/GridHost.vue';
2
+ export type * from './contracts';
3
+ export type { IGridTheme as GridTheme } from '../grid-core/theme';
4
+ export * from './cells';
5
+ export { createGridCellTheme, readGridCellTheme, gridCellMetrics, defaultGridCellTheme, GRID_CELL_TOKENS, GRID_CHIP_TOKENS, type GridCellTheme, type GridChipColors } from './theme';
6
+ export { cellStatusMark, createCellStatusStore, SAVING_MARK_DELAY_MS, type GridCellStatusStore, type GridCellStatusStoreOptions, type GridSaveTicket } from './edit/cell-status';
7
+ export { cellRange, selectionBlock, selectionStart, blockCellCount, planFill, filledRange, fillSeries, type GridCellRange, type GridCellBlock, type GridBounds, type GridFillRequest, type GridFillDirection, type GridFillValue } from './range';
8
+ export { toTsv, parseTsv, toHtmlTable, buildPasteProposal, writeClipboardAsync, writeClipboardEvent, type GridPasteProposal, type GridPasteContext, type GridCopyEvent, type GridClipboardData } from './clipboard';
@@ -0,0 +1,30 @@
1
+ import { GridCell, GridSelectionState } from '../contracts';
2
+ /** A rectangle of cells, inclusive on both ends. */
3
+ export interface GridCellRange {
4
+ startColumn: number;
5
+ endColumn: number;
6
+ startRow: number;
7
+ endRow: number;
8
+ }
9
+ /**
10
+ * The cells a selection covers, as ordered row and column indexes. A cell
11
+ * selection is one rectangle; a row selection covers every column of its
12
+ * rows; a column selection covers every row of its columns. Several runs
13
+ * (Cmd+click on headers) are joined in index order, as Teable copies them.
14
+ */
15
+ export interface GridCellBlock {
16
+ rows: readonly number[];
17
+ columns: readonly number[];
18
+ }
19
+ export interface GridBounds {
20
+ rowCount: number;
21
+ columnCount: number;
22
+ }
23
+ /** The rectangle of a cell selection, clamped to the data; null for other kinds. */
24
+ export declare const cellRange: (state: Pick<GridSelectionState, "kind" | "ranges">, bounds: GridBounds) => GridCellRange | null;
25
+ /** The cells a selection covers; null when nothing is selected or there is no data. */
26
+ export declare const selectionBlock: (state: Pick<GridSelectionState, "kind" | "ranges">, bounds: GridBounds) => GridCellBlock | null;
27
+ /** The top-left cell of a selection: where a paste starts. */
28
+ export declare const selectionStart: (state: GridSelectionState, bounds: GridBounds) => GridCell | null;
29
+ /** Number of cells in a block. */
30
+ export declare const blockCellCount: (block: GridCellBlock) => number;