@crisp-ui-kit/crisp 0.34.0 → 0.36.0

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 (59) hide show
  1. package/dist/bulkbar.css +3 -0
  2. package/dist/bulkbar.layered.css +3 -0
  3. package/dist/cjs/components/bulkbar/bulkbar.js +1 -1
  4. package/dist/cjs/components/bulkbar/bulkbar.recipe.js +6 -0
  5. package/dist/cjs/components/datatable/datatable.js +65 -5
  6. package/dist/cjs/components/datatable/datatable.recipe.js +2 -0
  7. package/dist/cjs/components/filldoc/filldoc.js +102 -0
  8. package/dist/cjs/components/filldoc/filldoc.recipe.js +211 -0
  9. package/dist/cjs/components/filldoc/index.js +10 -0
  10. package/dist/cjs/components/filldoc/serialize.js +39 -0
  11. package/dist/cjs/components/fillsheet/fillsheet.js +719 -0
  12. package/dist/cjs/components/fillsheet/fillsheet.recipe.js +325 -0
  13. package/dist/cjs/components/fillsheet/index.js +12 -0
  14. package/dist/cjs/components/fillsheet/serialize.js +82 -0
  15. package/dist/cjs/index.js +2 -0
  16. package/dist/cjs/product/recordpanel/recordpanel.js +4 -2
  17. package/dist/cjs/product/recordpanel/recordpanel.recipe.js +8 -0
  18. package/dist/cjs/product/sidebar/sidebar.js +16 -3
  19. package/dist/datatable.css +13 -5
  20. package/dist/datatable.layered.css +13 -5
  21. package/dist/esm/components/bulkbar/bulkbar.js +1 -1
  22. package/dist/esm/components/bulkbar/bulkbar.recipe.js +6 -0
  23. package/dist/esm/components/datatable/datatable.js +65 -5
  24. package/dist/esm/components/datatable/datatable.recipe.js +2 -0
  25. package/dist/esm/components/filldoc/filldoc.js +66 -0
  26. package/dist/esm/components/filldoc/filldoc.recipe.js +208 -0
  27. package/dist/esm/components/filldoc/index.js +3 -0
  28. package/dist/esm/components/filldoc/serialize.js +35 -0
  29. package/dist/esm/components/fillsheet/fillsheet.js +683 -0
  30. package/dist/esm/components/fillsheet/fillsheet.recipe.js +322 -0
  31. package/dist/esm/components/fillsheet/index.js +3 -0
  32. package/dist/esm/components/fillsheet/serialize.js +76 -0
  33. package/dist/esm/index.js +2 -0
  34. package/dist/esm/product/recordpanel/recordpanel.js +4 -2
  35. package/dist/esm/product/recordpanel/recordpanel.recipe.js +8 -0
  36. package/dist/esm/product/sidebar/sidebar.js +17 -4
  37. package/dist/filldoc.css +31 -0
  38. package/dist/filldoc.d.ts +7 -0
  39. package/dist/filldoc.layered.css +33 -0
  40. package/dist/fillsheet.css +29 -0
  41. package/dist/fillsheet.d.ts +7 -0
  42. package/dist/fillsheet.layered.css +31 -0
  43. package/dist/recordpanel.css +6 -0
  44. package/dist/recordpanel.layered.css +6 -0
  45. package/dist/styles.css +84 -5
  46. package/dist/styles.layered.css +84 -5
  47. package/dist/types/components/datatable/datatable.d.ts +7 -1
  48. package/dist/types/components/filldoc/filldoc.d.ts +79 -0
  49. package/dist/types/components/filldoc/filldoc.recipe.d.ts +201 -0
  50. package/dist/types/components/filldoc/index.d.ts +3 -0
  51. package/dist/types/components/filldoc/serialize.d.ts +5 -0
  52. package/dist/types/components/fillsheet/fillsheet.d.ts +165 -0
  53. package/dist/types/components/fillsheet/fillsheet.recipe.d.ts +202 -0
  54. package/dist/types/components/fillsheet/index.d.ts +3 -0
  55. package/dist/types/components/fillsheet/serialize.d.ts +45 -0
  56. package/dist/types/index.d.ts +2 -0
  57. package/dist/types/product/recordpanel/recordpanel.d.ts +11 -1
  58. package/dist/types/product/sidebar/sidebar.d.ts +56 -0
  59. package/package.json +11 -1
@@ -0,0 +1,201 @@
1
+ declare const spec: {
2
+ readonly name: "filldoc";
3
+ readonly base: {
4
+ readonly display: "flex";
5
+ readonly flexDirection: "column";
6
+ readonly gap: `var(--${string})`;
7
+ readonly boxSizing: "border-box";
8
+ readonly width: "210mm";
9
+ readonly minHeight: "297mm";
10
+ readonly padding: "20mm";
11
+ readonly margin: "0 auto";
12
+ readonly background: `var(--${string})`;
13
+ readonly fontFamily: `var(--${string})`;
14
+ readonly fontSize: `var(--${string})`;
15
+ readonly lineHeight: "22px";
16
+ readonly color: `var(--${string})`;
17
+ readonly borderRadius: "0";
18
+ readonly boxShadow: `inset 0 0 0 1px var(--${string}), 0 0 0 1px var(--${string}), 0 4px 8px -4px var(--${string}), 0 4px 12px -2px var(--${string})`;
19
+ };
20
+ readonly parts: [{
21
+ readonly selector: "&:focus, &:focus-visible";
22
+ readonly decls: {
23
+ readonly outline: "none";
24
+ };
25
+ }, {
26
+ readonly slot: "block";
27
+ readonly selector: "& .crisp-filldoc-block";
28
+ readonly decls: {
29
+ readonly position: "relative";
30
+ };
31
+ }, {
32
+ readonly slot: "heading";
33
+ readonly selector: "& .crisp-filldoc-heading";
34
+ readonly decls: {
35
+ readonly margin: "0";
36
+ readonly fontSize: `var(--${string})`;
37
+ readonly fontWeight: `var(--${string})`;
38
+ readonly lineHeight: "26px";
39
+ readonly color: `var(--${string})`;
40
+ readonly whiteSpace: "pre-wrap";
41
+ readonly overflowWrap: "break-word";
42
+ };
43
+ }, {
44
+ readonly slot: "paragraph";
45
+ readonly selector: "& .crisp-filldoc-text";
46
+ readonly decls: {
47
+ readonly margin: "0";
48
+ readonly fontSize: `var(--${string})`;
49
+ readonly lineHeight: "22px";
50
+ readonly color: `var(--${string})`;
51
+ readonly whiteSpace: "pre-wrap";
52
+ readonly overflowWrap: "break-word";
53
+ };
54
+ }, {
55
+ readonly slot: "writer";
56
+ readonly selector: "& .crisp-filldoc-writer";
57
+ readonly decls: {
58
+ readonly display: "block";
59
+ readonly width: "100%";
60
+ readonly margin: "0";
61
+ readonly padding: "0";
62
+ readonly border: "none";
63
+ readonly outline: "none";
64
+ readonly background: "transparent";
65
+ readonly fontFamily: "inherit";
66
+ readonly fontSize: "inherit";
67
+ readonly fontWeight: "inherit";
68
+ readonly lineHeight: "inherit";
69
+ readonly color: "inherit";
70
+ readonly resize: "none";
71
+ readonly overflow: "hidden";
72
+ readonly userSelect: "text";
73
+ readonly WebkitUserSelect: "text";
74
+ };
75
+ }, {
76
+ readonly slot: "inserter";
77
+ readonly selector: "& .crisp-filldoc-insert";
78
+ readonly decls: {
79
+ readonly position: "absolute";
80
+ readonly insetInlineStart: "0";
81
+ readonly insetInlineEnd: "0";
82
+ readonly insetBlockEnd: `calc(var(--${string}) / -2)`;
83
+ readonly height: `var(--${string})`;
84
+ readonly display: "flex";
85
+ readonly alignItems: "center";
86
+ readonly gap: `var(--${string})`;
87
+ readonly opacity: "0";
88
+ readonly transition: "opacity .1s";
89
+ readonly cursor: "pointer";
90
+ };
91
+ }, {
92
+ readonly selector: "& .crisp-filldoc-block:hover .crisp-filldoc-insert, & .crisp-filldoc-insert:focus-within";
93
+ readonly decls: {
94
+ readonly opacity: "1";
95
+ };
96
+ }, {
97
+ readonly selector: "& .crisp-filldoc-insert-rule";
98
+ readonly decls: {
99
+ readonly flex: "1";
100
+ readonly height: "1px";
101
+ readonly background: `var(--${string})`;
102
+ };
103
+ }, {
104
+ readonly slot: "insertButton";
105
+ readonly selector: "& .crisp-filldoc-insert button";
106
+ readonly decls: {
107
+ readonly display: "inline-flex";
108
+ readonly alignItems: "center";
109
+ readonly height: "20px";
110
+ readonly padding: `0 var(--${string})`;
111
+ readonly border: "none";
112
+ readonly borderRadius: `var(--${string})`;
113
+ readonly background: `var(--${string})`;
114
+ readonly fontFamily: "inherit";
115
+ readonly fontSize: `var(--${string})`;
116
+ readonly color: `var(--${string})`;
117
+ readonly cursor: "pointer";
118
+ };
119
+ }, {
120
+ readonly selector: "& .crisp-filldoc-insert button:hover";
121
+ readonly decls: {
122
+ background: `var(--${string})`;
123
+ color: `var(--${string})`;
124
+ };
125
+ }, {
126
+ readonly slot: "blockTools";
127
+ readonly selector: "& .crisp-filldoc-tools";
128
+ readonly decls: {
129
+ readonly position: "absolute";
130
+ readonly insetInlineEnd: "100%";
131
+ readonly insetBlockStart: "0";
132
+ readonly paddingInlineEnd: `var(--${string})`;
133
+ readonly display: "flex";
134
+ readonly gap: `var(--${string})`;
135
+ readonly opacity: "0";
136
+ readonly transition: "opacity .1s";
137
+ };
138
+ }, {
139
+ readonly selector: "& .crisp-filldoc-block:hover .crisp-filldoc-tools, & .crisp-filldoc-tools:focus-within";
140
+ readonly decls: {
141
+ readonly opacity: "1";
142
+ };
143
+ }, {
144
+ readonly selector: "& .crisp-filldoc-tools button";
145
+ readonly decls: {
146
+ readonly width: "20px";
147
+ readonly height: "20px";
148
+ readonly display: "inline-flex";
149
+ readonly alignItems: "center";
150
+ readonly justifyContent: "center";
151
+ readonly padding: "0";
152
+ readonly border: "none";
153
+ readonly borderRadius: `var(--${string})`;
154
+ readonly background: "transparent";
155
+ readonly fontFamily: "inherit";
156
+ readonly fontSize: `var(--${string})`;
157
+ readonly color: `var(--${string})`;
158
+ readonly cursor: "pointer";
159
+ };
160
+ }, {
161
+ readonly selector: "& .crisp-filldoc-tools button:hover";
162
+ readonly decls: {
163
+ background: `var(--${string})`;
164
+ color: `var(--${string})`;
165
+ };
166
+ }];
167
+ readonly variants: {
168
+ readonly page: {
169
+ readonly a4: {};
170
+ readonly auto: {
171
+ readonly width: "100%";
172
+ readonly minHeight: "0";
173
+ readonly maxWidth: "210mm";
174
+ };
175
+ };
176
+ };
177
+ readonly defaultVariants: {
178
+ readonly page: "a4";
179
+ };
180
+ };
181
+ declare const config: import("../..").RecipeConfig<{
182
+ readonly page: {
183
+ readonly a4: string;
184
+ readonly auto: string;
185
+ };
186
+ }>;
187
+ export declare const filldocRecipe: import("../..").RecipeFn<{
188
+ readonly page: {
189
+ readonly a4: string;
190
+ readonly auto: string;
191
+ };
192
+ }> & {
193
+ config: import("../..").RecipeConfig<{
194
+ readonly page: {
195
+ readonly a4: string;
196
+ readonly auto: string;
197
+ };
198
+ }>;
199
+ };
200
+ export { spec };
201
+ export default config;
@@ -0,0 +1,3 @@
1
+ export { FillDoc, type FillDocBlock, type FillDocProps } from "./filldoc";
2
+ export { filldocRecipe } from "./filldoc.recipe";
3
+ export { docToHTML, docToMarkdown } from "./serialize";
@@ -0,0 +1,5 @@
1
+ import type { FillDocBlock } from "./filldoc";
2
+ /** An HTML document body: what Word, Google Docs and Pages paste as a document. */
3
+ export declare function docToHTML(blocks: FillDocBlock[]): string;
4
+ /** The same document as Markdown. */
5
+ export declare function docToMarkdown(blocks: FillDocBlock[]): string;
@@ -0,0 +1,165 @@
1
+ import * as React from "react";
2
+ import { type SelectOption } from "../select";
3
+ /** A cell address. Row and column are both zero-based. */
4
+ export type FillSheetCell = {
5
+ r: number;
6
+ c: number;
7
+ };
8
+ export type FillSheetColumn = {
9
+ /**
10
+ * The column's name in an EXPORT — the header row of a copied table, a CSV
11
+ * or a Markdown table. It is never rendered: the sheet has no header row,
12
+ * deliberately, and this exists so a document made from it still does.
13
+ */
14
+ label?: string;
15
+ /**
16
+ * Turns the column into a select: clicking a cell opens this list instead of
17
+ * a text caret, and the cell stores the chosen option's **value**.
18
+ *
19
+ * The closed cell still renders as plain text — no chevron, no control, no
20
+ * hint that it is a select until you are in it. That is the whole point of
21
+ * the component and it is not worth trading for an affordance.
22
+ *
23
+ * A function of the row when only SOME rows are a select. A grid where a
24
+ * column means one field wants the list; a key/value form, where the rows
25
+ * carry the meaning and the columns are just "label" and "value", wants the
26
+ * function — and that second shape is the one the fms transaction detail
27
+ * has, so a column-only option would not have served the thing this was
28
+ * built for.
29
+ */
30
+ options?: SelectOption[] | ((row: number) => SelectOption[] | undefined);
31
+ /**
32
+ * How the stored value is DISPLAYED when the cell is closed. A select column
33
+ * shows its option's label through this by default; give it explicitly for
34
+ * anything else (a date, a currency). It never changes what is stored — fill,
35
+ * clear and tiling all keep working on the raw string.
36
+ */
37
+ format?: (raw: string) => string;
38
+ /**
39
+ * This column holds prose, so **Enter puts in a line break** and
40
+ * **⌘/Ctrl+Enter commits** — the Slack/Linear/GitHub convention, which is
41
+ * the one people's hands already have for a box you write paragraphs in.
42
+ *
43
+ * Everywhere else Enter commits and moves down, because that is what makes
44
+ * a form quick to fill. The column is the right place to decide this: it
45
+ * knows whether it holds a note or a quantity.
46
+ */
47
+ multiline?: boolean;
48
+ /** Starting width in px. Falls back to `columnWidth`. */
49
+ width?: number;
50
+ /** `end` right-aligns and turns on tabular figures — use it for numbers. */
51
+ align?: "start" | "end";
52
+ /** This column cannot be typed into or filled. */
53
+ readOnly?: boolean;
54
+ };
55
+ export type FillSheetProps = {
56
+ /** Column count, or one entry per column to label/size/align them. */
57
+ columns: number | FillSheetColumn[];
58
+ /** Row count. Ignored when `value`/`defaultValue` is longer. */
59
+ rows?: number;
60
+ /** Controlled cell text, row-major. Pair with `onValueChange`. */
61
+ value?: string[][];
62
+ /** Starting cell text when uncontrolled. */
63
+ defaultValue?: string[][];
64
+ /** Fires on every commit: typing, filling, and clearing. */
65
+ onValueChange?: (next: string[][]) => void;
66
+ /** Nothing can be edited or filled. */
67
+ readOnly?: boolean;
68
+ /** Width for columns that do not set their own. */
69
+ columnWidth?: number;
70
+ /** The MINIMUM height of a row. Rows grow past it when their text wraps. */
71
+ rowHeight?: number;
72
+ /**
73
+ * Who is holding it.
74
+ *
75
+ * `edit` is the person who MAKES the form: columns and rows can be dragged
76
+ * to size. `fill` is the person who fills it in — the structure is locked
77
+ * and only the blanks are theirs. Both can type and drag-fill; the only
78
+ * difference is whether the shape of the form is yours to change.
79
+ */
80
+ mode?: "edit" | "fill";
81
+ /**
82
+ * The sheet's accessible name. A `role="grid"` with no name is announced as
83
+ * an anonymous grid, which tells a screen-reader user nothing about which
84
+ * of several sheets on a page they have landed in.
85
+ *
86
+ * There is deliberately **no English default**: a generic fallback would
87
+ * read "Sheet" out loud in the middle of a Mongolian form. The name is
88
+ * content, so it comes from the consumer or it is not there.
89
+ */
90
+ label?: string;
91
+ /** `panel` is a framed panel; `paper` lifts it; `bare` drops the frame. */
92
+ surface?: "panel" | "paper" | "bare";
93
+ className?: string;
94
+ style?: React.CSSProperties;
95
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "defaultValue" | "children">;
96
+ /**
97
+ * A sheet of cells you can select, type into, resize and drag-fill.
98
+ *
99
+ * It comes in two modes, because two different people hold a form: in `edit`
100
+ * you build it, dragging columns and rows to size; in `fill` you only fill it
101
+ * in, and the structure is locked. Typing and drag-filling work in both --
102
+ * what `fill` withholds is the shape, not the content.
103
+ *
104
+ * Tab off the last cell appends a row, which is the gesture a word processor
105
+ * uses and the reason its tables are quick to fill: your hands are already on
106
+ * the keyboard, and the row you want is the one after the one you just
107
+ * finished. It grows in both modes -- a row is content, not structure.
108
+ *
109
+ * One click puts you in a cell, in both modes. A word processor places the
110
+ * caret where you clicked; there is no select-then-click-again step.
111
+ *
112
+ * Deliberately NOT built on Table or DataTable. Those take rows of records and
113
+ * own the layout; this owns neither. A cell here is addressed by (row, column)
114
+ * and carries text, its column can be any width and its row any height, and
115
+ * the grid is CSS `grid` rather than `<table>` precisely so those two axes are
116
+ * independent.
117
+ *
118
+ * It is also deliberately bare: cells and hairlines, nothing else. No A/B/C
119
+ * lettering, no numbered row gutter, no header row, and no prop that turns
120
+ * any of them on. Those are what make a grid read as spreadsheet SOFTWARE,
121
+ * and a boxed-in cell is the loudest of them -- so a cell carries one rule
122
+ * underneath and nothing else. A consumer that wants headings puts them
123
+ * above the sheet, where they are content rather than chrome.
124
+ */
125
+ export declare const FillSheet: React.ForwardRefExoticComponent<{
126
+ /** Column count, or one entry per column to label/size/align them. */
127
+ columns: number | FillSheetColumn[];
128
+ /** Row count. Ignored when `value`/`defaultValue` is longer. */
129
+ rows?: number;
130
+ /** Controlled cell text, row-major. Pair with `onValueChange`. */
131
+ value?: string[][];
132
+ /** Starting cell text when uncontrolled. */
133
+ defaultValue?: string[][];
134
+ /** Fires on every commit: typing, filling, and clearing. */
135
+ onValueChange?: (next: string[][]) => void;
136
+ /** Nothing can be edited or filled. */
137
+ readOnly?: boolean;
138
+ /** Width for columns that do not set their own. */
139
+ columnWidth?: number;
140
+ /** The MINIMUM height of a row. Rows grow past it when their text wraps. */
141
+ rowHeight?: number;
142
+ /**
143
+ * Who is holding it.
144
+ *
145
+ * `edit` is the person who MAKES the form: columns and rows can be dragged
146
+ * to size. `fill` is the person who fills it in — the structure is locked
147
+ * and only the blanks are theirs. Both can type and drag-fill; the only
148
+ * difference is whether the shape of the form is yours to change.
149
+ */
150
+ mode?: "edit" | "fill";
151
+ /**
152
+ * The sheet's accessible name. A `role="grid"` with no name is announced as
153
+ * an anonymous grid, which tells a screen-reader user nothing about which
154
+ * of several sheets on a page they have landed in.
155
+ *
156
+ * There is deliberately **no English default**: a generic fallback would
157
+ * read "Sheet" out loud in the middle of a Mongolian form. The name is
158
+ * content, so it comes from the consumer or it is not there.
159
+ */
160
+ label?: string;
161
+ /** `panel` is a framed panel; `paper` lifts it; `bare` drops the frame. */
162
+ surface?: "panel" | "paper" | "bare";
163
+ className?: string;
164
+ style?: React.CSSProperties;
165
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "defaultValue" | "onChange"> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,202 @@
1
+ declare const spec: {
2
+ readonly name: "fillsheet";
3
+ readonly base: {
4
+ readonly display: "grid";
5
+ readonly gridTemplateColumns: "var(--crisp-fillsheet-cols)";
6
+ readonly gridTemplateRows: "var(--crisp-fillsheet-rows)";
7
+ readonly position: "relative";
8
+ readonly overflow: "auto";
9
+ readonly maxWidth: "100%";
10
+ readonly boxSizing: "border-box";
11
+ readonly fontFamily: `var(--${string})`;
12
+ readonly fontSize: `var(--${string})`;
13
+ readonly color: `var(--${string})`;
14
+ readonly background: `var(--${string})`;
15
+ readonly borderRadius: "0";
16
+ readonly border: `1px solid var(--${string})`;
17
+ readonly userSelect: "none";
18
+ readonly WebkitUserSelect: "none";
19
+ };
20
+ readonly parts: [{
21
+ readonly selector: "&:focus, &:focus-visible";
22
+ readonly decls: {
23
+ readonly outline: "none";
24
+ };
25
+ }, {
26
+ readonly slot: "cell";
27
+ readonly selector: "& .crisp-fillsheet-cell";
28
+ readonly decls: {
29
+ readonly position: "relative";
30
+ readonly display: "flex";
31
+ readonly alignItems: "flex-start";
32
+ readonly minWidth: "0";
33
+ readonly paddingInline: `var(--${string})`;
34
+ readonly paddingBlock: `var(--${string})`;
35
+ readonly borderInlineEnd: `1px solid var(--${string})`;
36
+ readonly borderBlockEnd: `1px solid var(--${string})`;
37
+ readonly background: `var(--${string})`;
38
+ readonly whiteSpace: "pre-wrap";
39
+ readonly overflowWrap: "break-word";
40
+ readonly lineHeight: "20px";
41
+ readonly cursor: "cell";
42
+ };
43
+ }, {
44
+ readonly selector: "& .crisp-fillsheet-cell[aria-readonly]";
45
+ readonly decls: {
46
+ background: `var(--${string})`;
47
+ color: `var(--${string})`;
48
+ };
49
+ }, {
50
+ readonly selector: "& .crisp-fillsheet-cell:last-child";
51
+ readonly decls: {
52
+ readonly borderInlineEnd: "none";
53
+ };
54
+ }, {
55
+ readonly selector: "& > div:last-child .crisp-fillsheet-cell";
56
+ readonly decls: {
57
+ readonly borderBlockEnd: "none";
58
+ };
59
+ }, {
60
+ readonly selector: "& .crisp-fillsheet-cell[data-align=\"end\"]";
61
+ readonly decls: {
62
+ readonly justifyContent: "flex-end";
63
+ readonly fontVariantNumeric: "tabular-nums";
64
+ };
65
+ }, {
66
+ readonly selector: "&[data-multi] .crisp-fillsheet-cell[data-range]";
67
+ readonly decls: {
68
+ background: string;
69
+ };
70
+ }, {
71
+ readonly selector: "&[data-keynav] .crisp-fillsheet-cell[data-selected]";
72
+ readonly decls: {
73
+ readonly background: string;
74
+ readonly boxShadow: `inset 0 -2px 0 var(--${string})`;
75
+ };
76
+ }, {
77
+ readonly selector: "& .crisp-fillsheet-cell[data-editing]";
78
+ readonly decls: {
79
+ background: `var(--${string})`;
80
+ };
81
+ }, {
82
+ readonly slot: "columnResize";
83
+ readonly selector: "& .crisp-fillsheet-colresize";
84
+ readonly decls: {
85
+ readonly position: "absolute";
86
+ readonly insetBlockStart: "0";
87
+ readonly insetBlockEnd: "0";
88
+ readonly insetInlineEnd: "0";
89
+ readonly zIndex: "3";
90
+ readonly width: "5px";
91
+ readonly cursor: "col-resize";
92
+ readonly touchAction: "none";
93
+ };
94
+ }, {
95
+ readonly slot: "rowResize";
96
+ readonly selector: "& .crisp-fillsheet-rowresize";
97
+ readonly decls: {
98
+ readonly position: "absolute";
99
+ readonly insetInlineStart: "0";
100
+ readonly insetInlineEnd: "0";
101
+ readonly insetBlockEnd: "0";
102
+ readonly zIndex: "3";
103
+ readonly height: "5px";
104
+ readonly cursor: "row-resize";
105
+ readonly touchAction: "none";
106
+ };
107
+ }, {
108
+ readonly selector: "& .crisp-fillsheet-colresize:hover";
109
+ readonly decls: {
110
+ readonly background: `linear-gradient(to right, transparent 0, transparent calc(50% - 1px), var(--${string}) calc(50% - 1px), var(--${string}) calc(50% + 1px), transparent calc(50% + 1px))`;
111
+ };
112
+ }, {
113
+ readonly selector: "& .crisp-fillsheet-rowresize:hover";
114
+ readonly decls: {
115
+ readonly background: `linear-gradient(to bottom, transparent 0, transparent calc(50% - 1px), var(--${string}) calc(50% - 1px), var(--${string}) calc(50% + 1px), transparent calc(50% + 1px))`;
116
+ };
117
+ }, {
118
+ readonly slot: "editor";
119
+ readonly selector: "& .crisp-fillsheet-editor";
120
+ readonly decls: {
121
+ readonly display: "block";
122
+ readonly flexGrow: "1";
123
+ readonly width: "100%";
124
+ readonly minWidth: "0";
125
+ readonly margin: "0";
126
+ readonly padding: "0";
127
+ readonly border: "none";
128
+ readonly outline: "none";
129
+ readonly background: "transparent";
130
+ readonly fontFamily: "inherit";
131
+ readonly fontSize: "inherit";
132
+ readonly lineHeight: "20px";
133
+ readonly color: "inherit";
134
+ readonly resize: "none";
135
+ readonly userSelect: "text";
136
+ readonly WebkitUserSelect: "text";
137
+ readonly overflow: "hidden";
138
+ };
139
+ }, {
140
+ readonly slot: "selectCell";
141
+ readonly selector: "& .crisp-fillsheet-select .crisp-select-trigger";
142
+ readonly decls: {
143
+ readonly width: "100%";
144
+ readonly height: "100%";
145
+ readonly padding: "0";
146
+ readonly border: "none";
147
+ readonly background: "transparent";
148
+ readonly boxShadow: "none";
149
+ readonly font: "inherit";
150
+ readonly color: "inherit";
151
+ };
152
+ }, {
153
+ readonly selector: "& .crisp-fillsheet-select .crisp-select-trigger:hover, & .crisp-fillsheet-select .crisp-select-trigger[data-open]";
154
+ readonly decls: {
155
+ readonly background: "transparent";
156
+ readonly boxShadow: "none";
157
+ };
158
+ }, {
159
+ readonly selector: "& .crisp-fillsheet-select .crisp-select-chevron";
160
+ readonly decls: {
161
+ readonly display: "none";
162
+ };
163
+ }];
164
+ readonly variants: {
165
+ readonly surface: {
166
+ readonly panel: {};
167
+ readonly paper: {
168
+ boxShadow: `inset 0 0 0 1px var(--${string}), 0 0 0 1px var(--${string}), 0 4px 8px -4px var(--${string}), 0 4px 12px -2px var(--${string})`;
169
+ };
170
+ readonly bare: {
171
+ readonly border: "none";
172
+ };
173
+ };
174
+ };
175
+ readonly defaultVariants: {
176
+ readonly surface: "panel";
177
+ };
178
+ };
179
+ declare const config: import("../..").RecipeConfig<{
180
+ readonly surface: {
181
+ readonly panel: string;
182
+ readonly paper: string;
183
+ readonly bare: string;
184
+ };
185
+ }>;
186
+ export declare const fillsheetRecipe: import("../..").RecipeFn<{
187
+ readonly surface: {
188
+ readonly panel: string;
189
+ readonly paper: string;
190
+ readonly bare: string;
191
+ };
192
+ }> & {
193
+ config: import("../..").RecipeConfig<{
194
+ readonly surface: {
195
+ readonly panel: string;
196
+ readonly paper: string;
197
+ readonly bare: string;
198
+ };
199
+ }>;
200
+ };
201
+ export { spec };
202
+ export default config;
@@ -0,0 +1,3 @@
1
+ export { FillSheet, type FillSheetCell, type FillSheetColumn, type FillSheetProps, } from "./fillsheet";
2
+ export { fillsheetRecipe } from "./fillsheet.recipe";
3
+ export { type FillSheetExportColumn, type FillSheetExportOptions, toCSV, toHTML, toMarkdown, toTSV, } from "./serialize";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Getting a sheet OUT, in the formats documents actually read.
3
+ *
4
+ * These are plain functions over `string[][]` — no React, no DOM — so they run
5
+ * in a server route building an export just as well as in a click handler.
6
+ * That is the whole reason the grid is `string[][]`: the value IS the
7
+ * document, and nothing here has to know what a cell meant.
8
+ */
9
+ /** A column's heading in an export. Nothing is emitted when it has none. */
10
+ export type FillSheetExportColumn = {
11
+ label?: string;
12
+ };
13
+ export type FillSheetExportOptions = {
14
+ /** Column headings. A row of them is emitted only if at least one exists. */
15
+ columns?: FillSheetExportColumn[];
16
+ /** Included as a caption (HTML) or a heading line (Markdown). */
17
+ title?: string;
18
+ };
19
+ /**
20
+ * Tab-separated, the clipboard's lingua franca for a grid: Excel, Sheets,
21
+ * Numbers and Word's table paste all read it as CELLS.
22
+ */
23
+ export declare function toTSV(value: string[][]): string;
24
+ /** RFC 4180: quote a field that contains a comma, a quote or a newline. */
25
+ export declare function toCSV(value: string[][], o?: FillSheetExportOptions): string;
26
+ /**
27
+ * A real `<table>`.
28
+ *
29
+ * This is the one that makes "paste into a document" work: Word, Google Docs,
30
+ * Pages and every rich-text editor read `text/html` from the clipboard and
31
+ * build a formatted table from it. Given only tab-separated text they paste a
32
+ * paragraph of tabs instead.
33
+ *
34
+ * Newlines inside a cell become `<br>`, so a wrapped note survives as a
35
+ * wrapped note rather than collapsing into one line.
36
+ */
37
+ export declare function toHTML(value: string[][], o?: FillSheetExportOptions): string;
38
+ /**
39
+ * A GitHub-flavoured Markdown table.
40
+ *
41
+ * A pipe inside a cell is escaped and a newline becomes `<br>`, because a
42
+ * Markdown table row is a single line — an unescaped one silently splits the
43
+ * row into columns that were never there.
44
+ */
45
+ export declare function toMarkdown(value: string[][], o?: FillSheetExportOptions): string;
@@ -20,6 +20,8 @@ export * from "./components/donutchart";
20
20
  export * from "./components/drawer";
21
21
  export * from "./components/emptystate";
22
22
  export * from "./components/fileupload";
23
+ export * from "./components/filldoc";
24
+ export * from "./components/fillsheet";
23
25
  export * from "./components/formfield";
24
26
  export * from "./components/input";
25
27
  export * from "./components/kbd";
@@ -4,10 +4,20 @@ export type RecordPanelProps = Omit<React.ComponentPropsWithoutRef<"div">, "chil
4
4
  /** The record to show; render nothing when null. */
5
5
  /** Label for the close control. */
6
6
  closeLabel?: string;
7
+ /**
8
+ * The panel's heading, and so its accessible name (via `aria-labelledby`,
9
+ * not a stringified `aria-label` — it also drives the visible title, which
10
+ * can be any node). Defaults to the entity column's name, same as before
11
+ * this prop existed; pass it to title the panel by something else, e.g.
12
+ * "{Collection} record".
13
+ */
14
+ title?: React.ReactNode;
7
15
  /** Heading above the field list. */
8
16
  detailsLabel?: React.ReactNode;
9
17
  /** Shown in place of a field that has no value. */
10
18
  emptyLabel?: React.ReactNode;
19
+ /** Rendered below the field list, e.g. an Edit button. Omit for no footer. */
20
+ footer?: React.ReactNode;
11
21
  record: Row | null;
12
22
  columns: Column[];
13
23
  onClose: () => void;
@@ -19,7 +29,7 @@ export type RecordPanelProps = Omit<React.ComponentPropsWithoutRef<"div">, "chil
19
29
  * relative` container (e.g. the app main area); it slides in from the right and
20
30
  * overlays the content.
21
31
  */
22
- export declare function RecordPanel({ record, columns, onClose, className, closeLabel, detailsLabel, emptyLabel, ...rest }: RecordPanelProps): React.JSX.Element | null;
32
+ export declare function RecordPanel({ record, columns, onClose, className, closeLabel, title, detailsLabel, emptyLabel, footer, ...rest }: RecordPanelProps): React.JSX.Element | null;
23
33
  export declare namespace RecordPanel {
24
34
  var displayName: string;
25
35
  }