@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.
- package/dist/bulkbar.css +3 -0
- package/dist/bulkbar.layered.css +3 -0
- package/dist/cjs/components/bulkbar/bulkbar.js +1 -1
- package/dist/cjs/components/bulkbar/bulkbar.recipe.js +6 -0
- package/dist/cjs/components/datatable/datatable.js +65 -5
- package/dist/cjs/components/datatable/datatable.recipe.js +2 -0
- package/dist/cjs/components/filldoc/filldoc.js +102 -0
- package/dist/cjs/components/filldoc/filldoc.recipe.js +211 -0
- package/dist/cjs/components/filldoc/index.js +10 -0
- package/dist/cjs/components/filldoc/serialize.js +39 -0
- package/dist/cjs/components/fillsheet/fillsheet.js +719 -0
- package/dist/cjs/components/fillsheet/fillsheet.recipe.js +325 -0
- package/dist/cjs/components/fillsheet/index.js +12 -0
- package/dist/cjs/components/fillsheet/serialize.js +82 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/product/recordpanel/recordpanel.js +4 -2
- package/dist/cjs/product/recordpanel/recordpanel.recipe.js +8 -0
- package/dist/cjs/product/sidebar/sidebar.js +16 -3
- package/dist/datatable.css +13 -5
- package/dist/datatable.layered.css +13 -5
- package/dist/esm/components/bulkbar/bulkbar.js +1 -1
- package/dist/esm/components/bulkbar/bulkbar.recipe.js +6 -0
- package/dist/esm/components/datatable/datatable.js +65 -5
- package/dist/esm/components/datatable/datatable.recipe.js +2 -0
- package/dist/esm/components/filldoc/filldoc.js +66 -0
- package/dist/esm/components/filldoc/filldoc.recipe.js +208 -0
- package/dist/esm/components/filldoc/index.js +3 -0
- package/dist/esm/components/filldoc/serialize.js +35 -0
- package/dist/esm/components/fillsheet/fillsheet.js +683 -0
- package/dist/esm/components/fillsheet/fillsheet.recipe.js +322 -0
- package/dist/esm/components/fillsheet/index.js +3 -0
- package/dist/esm/components/fillsheet/serialize.js +76 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/product/recordpanel/recordpanel.js +4 -2
- package/dist/esm/product/recordpanel/recordpanel.recipe.js +8 -0
- package/dist/esm/product/sidebar/sidebar.js +17 -4
- package/dist/filldoc.css +31 -0
- package/dist/filldoc.d.ts +7 -0
- package/dist/filldoc.layered.css +33 -0
- package/dist/fillsheet.css +29 -0
- package/dist/fillsheet.d.ts +7 -0
- package/dist/fillsheet.layered.css +31 -0
- package/dist/recordpanel.css +6 -0
- package/dist/recordpanel.layered.css +6 -0
- package/dist/styles.css +84 -5
- package/dist/styles.layered.css +84 -5
- package/dist/types/components/datatable/datatable.d.ts +7 -1
- package/dist/types/components/filldoc/filldoc.d.ts +79 -0
- package/dist/types/components/filldoc/filldoc.recipe.d.ts +201 -0
- package/dist/types/components/filldoc/index.d.ts +3 -0
- package/dist/types/components/filldoc/serialize.d.ts +5 -0
- package/dist/types/components/fillsheet/fillsheet.d.ts +165 -0
- package/dist/types/components/fillsheet/fillsheet.recipe.d.ts +202 -0
- package/dist/types/components/fillsheet/index.d.ts +3 -0
- package/dist/types/components/fillsheet/serialize.d.ts +45 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/product/recordpanel/recordpanel.d.ts +11 -1
- package/dist/types/product/sidebar/sidebar.d.ts +56 -0
- 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,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;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
}
|