@crisp-ui-kit/crisp 0.51.0 → 0.52.1
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/cjs/components/datatable/datatable.js +98 -24
- package/dist/cjs/core/caret-rect.js +73 -0
- package/dist/cjs/core/index.js +7 -1
- package/dist/cjs/core/mention.js +32 -0
- package/dist/cjs/core/use-mention-trigger.js +124 -0
- package/dist/cjs/product/recordpanel/recordpanel.js +13 -81
- package/dist/cjs/product/recordpanel/recordpanel.recipe.js +53 -44
- package/dist/cjs/product/recordstable/editable-field.js +11 -8
- package/dist/cjs/product/recordstable/recordstable.tag.js +8 -3
- package/dist/cjs/product/richtext/index.js +2 -1
- package/dist/cjs/product/richtext/richtext.js +440 -23
- package/dist/cjs/product/richtext/richtext.recipe.js +59 -9
- package/dist/esm/components/datatable/datatable.js +98 -24
- package/dist/esm/core/caret-rect.js +70 -0
- package/dist/esm/core/index.js +3 -0
- package/dist/esm/core/mention.js +29 -0
- package/dist/esm/core/use-mention-trigger.js +88 -0
- package/dist/esm/product/recordpanel/index.js +1 -1
- package/dist/esm/product/recordpanel/recordpanel.js +15 -83
- package/dist/esm/product/recordpanel/recordpanel.recipe.js +54 -45
- package/dist/esm/product/recordstable/editable-field.js +11 -8
- package/dist/esm/product/recordstable/recordstable.tag.js +8 -3
- package/dist/esm/product/richtext/index.js +1 -1
- package/dist/esm/product/richtext/richtext.js +439 -22
- package/dist/esm/product/richtext/richtext.recipe.js +60 -10
- package/dist/recordpanel.css +47 -41
- package/dist/recordpanel.layered.css +47 -41
- package/dist/richtext.css +31 -5
- package/dist/richtext.layered.css +31 -5
- package/dist/styles.css +79 -47
- package/dist/styles.layered.css +79 -47
- package/dist/types/components/datatable/datatable.d.ts +11 -10
- package/dist/types/core/caret-rect.d.ts +16 -0
- package/dist/types/core/index.d.ts +3 -0
- package/dist/types/core/mention.d.ts +27 -0
- package/dist/types/core/use-mention-trigger.d.ts +41 -0
- package/dist/types/product/recordpanel/index.d.ts +1 -1
- package/dist/types/product/recordpanel/recordpanel.d.ts +81 -27
- package/dist/types/product/recordstable/editable-field.d.ts +11 -8
- package/dist/types/product/recordstable/recordstable.tag.d.ts +8 -3
- package/dist/types/product/richtext/index.d.ts +1 -1
- package/dist/types/product/richtext/richtext.d.ts +168 -7
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
1
|
+
import { defineRecipe, resolveRecipe, rtlFlipCss } from "../../core/index.js";
|
|
2
2
|
import { contract as c, elevation as e } from "../../tokens/index.js";
|
|
3
|
-
import { recordTagCss } from "../recordstable/recordstable.tag.js";
|
|
4
3
|
// A right-side record peek panel — slides in over the main content to show one
|
|
5
|
-
// record's
|
|
6
|
-
// relative container (the app main area); slides in via
|
|
4
|
+
// record's Highlights and Activity, the way Attio's own peek does. Positioned
|
|
5
|
+
// absolutely inside a relative container (the app main area); slides in via
|
|
6
|
+
// @starting-style.
|
|
7
7
|
const config = defineRecipe({
|
|
8
8
|
name: "recordpanel",
|
|
9
9
|
base: `
|
|
@@ -18,53 +18,60 @@ const config = defineRecipe({
|
|
|
18
18
|
@starting-style { transform: translateX(calc(100% + ${c.space["2"]})); }
|
|
19
19
|
& * { box-sizing: border-box; }
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
/* #448: measured live on app.attio.com's peek -- a toolbar row above
|
|
22
|
+
the identity header: Close, then Previous/Next, on the left; "Open
|
|
23
|
+
record" on the right. 49px tall, 8px/10px padding, a hairline border
|
|
24
|
+
beneath (the same borderSubtle every other panel/row edge here
|
|
25
|
+
already uses). Close keeps the existing transparent ghost button;
|
|
26
|
+
Previous/Next/Open record are a distinct field-elevation style
|
|
27
|
+
(white fill, e.field's own ring+shadow, solid icon) -- measured as
|
|
28
|
+
genuinely different chrome from Close, not a reuse. */
|
|
29
|
+
& .crisp-recordpanel-toolbar { display: flex; align-items: center; height: 49px; flex-shrink: 0; padding: ${c.space["2"]} ${c.space["2.5"]}; border-bottom: 1px solid ${c.borderSubtle}; }
|
|
30
|
+
& .crisp-recordpanel-toolbar-spacer { flex: 1; }
|
|
31
|
+
& .crisp-recordpanel-pager { display: flex; align-items: center; gap: ${c.space["1.5"]}; margin-inline-start: ${c.space["3"]}; }
|
|
32
|
+
& .crisp-recordpanel-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: ${c.radius.md}; background: transparent; color: color-mix(in srgb, ${c.fgPrimary} 55%, transparent); font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
33
|
+
& .crisp-recordpanel-iconbtn:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
|
|
34
|
+
& .crisp-recordpanel-navbtn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: ${c.radius.md}; background: ${c.bgSurface}; box-shadow: ${e.field}; color: ${c.fgPrimary}; font-family: inherit; cursor: pointer; }
|
|
35
|
+
& .crisp-recordpanel-navbtn:hover:not(:disabled) { background: ${c.bgRaised}; }
|
|
36
|
+
& .crisp-recordpanel-navbtn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
37
|
+
|
|
38
|
+
& .crisp-recordpanel-header { display: flex; align-items: center; gap: ${c.space["2"]}; height: 52px; flex-shrink: 0; padding: 0 ${c.space["3"]}; }
|
|
22
39
|
& .crisp-recordpanel-fav { width: 22px; height: ${c.size.chip}; border-radius: ${c.radius.sm}; flex-shrink: 0; object-fit: cover; background: ${c.bgRaised}; }
|
|
23
40
|
& .crisp-recordpanel-title { flex: 1; min-width: 0; font-size: ${c.text.xl}; font-weight: ${c.weight.semibold}; letter-spacing: -0.2px; color: ${c.fgPrimary}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
24
|
-
& .crisp-recordpanel-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: ${c.radius.md}; background: transparent; color: ${c.fgMuted}; font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
25
|
-
& .crisp-recordpanel-iconbtn:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
|
|
26
41
|
|
|
27
42
|
& .crisp-recordpanel-body { flex: 1; min-height: 0; overflow-y: auto; padding: ${c.space["2"]} ${c.space["3"]}; }
|
|
28
|
-
& .crisp-recordpanel-sectionlabel { font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: ${c.fgMuted}; padding: ${c.space["2"]} ${c.space["1"]} ${c.space["1"]}; }
|
|
29
|
-
& .crisp-recordpanel-field { display: flex; align-items: flex-start; gap: ${c.space["2"]}; min-height: 32px; padding: ${c.space["1"]} ${c.space["1"]}; border-radius: ${c.radius.md}; }
|
|
30
|
-
& .crisp-recordpanel-field:hover { background: color-mix(in srgb, ${c.fgPrimary} 3%, transparent); }
|
|
31
|
-
& .crisp-recordpanel-field-label { display: flex; align-items: center; gap: ${c.space["1.5"]}; width: 132px; flex-shrink: 0; padding-top: 3px; color: ${c.fgMuted}; font-size: ${c.text.md}; }
|
|
32
|
-
& .crisp-recordpanel-field-label svg { flex-shrink: 0; }
|
|
33
|
-
& .crisp-recordpanel-field-value { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: ${c.space["1"]}; padding-top: 3px; font-size: ${c.text.lg}; color: ${c.fgPrimary}; }
|
|
34
|
-
& .crisp-recordpanel-field-value[data-editable] { cursor: pointer; }
|
|
35
|
-
/* the click-to-edit trigger is a real <button> (keyboard-operable),
|
|
36
|
-
reset back to plain inline text and made a flex-wrap container of its
|
|
37
|
-
own -- FieldValue can render several chips (tags), which still need
|
|
38
|
-
to wrap the way they did before this had a button around them at
|
|
39
|
-
all. It wraps only FieldValue, never the error slot beside it (see
|
|
40
|
-
the component's own comment) -- NOT \`display: contents\`, which
|
|
41
|
-
drops a button's accessible semantics in some engines. */
|
|
42
|
-
& .crisp-recordpanel-edit-trigger { display: inline-flex; flex-wrap: wrap; align-items: center; gap: ${c.space["1"]}; margin: 0; padding: 0; border: none; background: none; text-align: start; font: inherit; color: inherit; cursor: pointer; }
|
|
43
|
-
& .crisp-recordpanel-empty { color: ${c.fgMuted}; }
|
|
44
43
|
|
|
45
|
-
/* #
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
& .crisp-recordpanel-
|
|
54
|
-
/*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
& .crisp-recordpanel-
|
|
59
|
-
& .crisp-recordpanel-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
60
|
-
& .crisp-recordpanel-edit-pending { flex-shrink: 0; color: ${c.fgMuted}; }
|
|
61
|
-
& .crisp-recordpanel-edit-error { flex-basis: 100%; margin-top: ${c.space["0.5"]}; font-size: ${c.text.sm}; color: ${c.bgDanger}; }
|
|
62
|
-
& .crisp-recordpanel-edit-picker { display: flex; align-items: center; gap: ${c.space["1"]}; width: 100%; }
|
|
63
|
-
& .crisp-recordpanel-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
|
|
44
|
+
/* #448: measured live on app.attio.com's peek (hover a Companies row,
|
|
45
|
+
click "Open preview"). Section label reads identically for both
|
|
46
|
+
Highlights and Activity: 14px/500, the same 63%-of-fgPrimary mix
|
|
47
|
+
RecordPage's own field-label already uses (product/recordpage's
|
|
48
|
+
.crisp-recordpage-field-label) -- one more surface landing on the
|
|
49
|
+
same measured "muted section heading" colour. */
|
|
50
|
+
& .crisp-recordpanel-section { padding: ${c.space["2"]} ${c.space["1"]} 0; }
|
|
51
|
+
& .crisp-recordpanel-section-head { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["2"]}; height: 20px; margin-bottom: ${c.space["2"]}; }
|
|
52
|
+
& .crisp-recordpanel-section-label { font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); }
|
|
53
|
+
/* "View all" -- measured 10px/400, a lighter mix than the section label
|
|
54
|
+
(55%, not 63%) -- its own distinct, honestly-measured value, not a
|
|
55
|
+
reuse of a nearby one that happened to be close. */
|
|
56
|
+
& .crisp-recordpanel-section-action { border: none; background: none; padding: 0; font-family: inherit; font-size: 10px; font-weight: ${c.weight.regular}; color: color-mix(in srgb, ${c.fgPrimary} 55%, transparent); cursor: pointer; }
|
|
57
|
+
& .crisp-recordpanel-section-action:hover { color: ${c.fgPrimary}; }
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
/* Highlights: a 2-column grid of label+icon+value cards, no card
|
|
60
|
+
chrome at all (no border/background/radius/shadow -- confirmed on
|
|
61
|
+
the live DOM: every highlight's computed style is fully transparent).
|
|
62
|
+
8px row/column gap; two rows measured "233x86" inside 473px content
|
|
63
|
+
width is exactly (473 - 8) / 2. */
|
|
64
|
+
& .crisp-recordpanel-highlights { display: grid; grid-template-columns: 1fr 1fr; gap: ${c.space["2"]}; }
|
|
65
|
+
& .crisp-recordpanel-highlight { display: flex; flex-direction: column; gap: ${c.space["1.5"]}; min-width: 0; }
|
|
66
|
+
& .crisp-recordpanel-highlight-label { display: flex; align-items: center; gap: ${c.space["1"]}; font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); }
|
|
67
|
+
& .crisp-recordpanel-highlight-label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
68
|
+
& .crisp-recordpanel-highlight-label svg { flex-shrink: 0; color: ${c.fgMuted}; }
|
|
69
|
+
& .crisp-recordpanel-highlight-value { font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; overflow: hidden; text-overflow: ellipsis; }
|
|
70
|
+
|
|
71
|
+
/* Activity is a consumer-supplied slot (crisp has no activity-feed
|
|
72
|
+
model of its own to render) -- this only wraps it with the measured
|
|
73
|
+
section heading above. */
|
|
74
|
+
& .crisp-recordpanel-activity { font-size: ${c.text.lg}; }
|
|
68
75
|
|
|
69
76
|
/* #329: an optional slot for record actions (e.g. Edit). Reuses the
|
|
70
77
|
header's own measured values rather than inventing new ones -- the
|
|
@@ -73,6 +80,8 @@ const config = defineRecipe({
|
|
|
73
80
|
edge), and the header/body gap (space.2) for its own vertical padding
|
|
74
81
|
and for spacing between whatever it's given. */
|
|
75
82
|
& .crisp-recordpanel-footer { flex-shrink: 0; display: flex; align-items: center; gap: ${c.space["2"]}; padding: ${c.space["2"]} ${c.space["3"]}; border-top: 1px solid ${c.borderSubtle}; }
|
|
83
|
+
|
|
84
|
+
${rtlFlipCss}
|
|
76
85
|
`,
|
|
77
86
|
variants: {},
|
|
78
87
|
defaultVariants: {},
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
/**
|
|
3
|
-
* The async-commit state machine behind `
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* The async-commit state machine behind `RecordPage`'s editable Details
|
|
4
|
+
* column (#431) — the same shape `DataTable`'s own `commitEdit` uses
|
|
5
|
+
* (#385/#430): a sync `onEdit` (returns `undefined`) commits and closes
|
|
6
6
|
* immediately; a Promise-returning `onEdit` keeps the field open and pending
|
|
7
7
|
* while it settles, then either closes it (resolve) or closes it AND
|
|
8
8
|
* reverts while showing the rejection's message (reject). The revert needs
|
|
9
|
-
* no extra code here either:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* no extra code here either: `RecordPage` never mutates `record` itself, so
|
|
10
|
+
* once `editingKey` clears, the field simply renders `record[key]` again —
|
|
11
|
+
* whatever it still says.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* `RecordPanel` (the peek panel) used to share this hook too, before #448
|
|
14
|
+
* found that Attio's own peek has no editable field at all to attach it to
|
|
15
|
+
* — only `RecordPage`'s Details column does. Kept as its own module rather
|
|
16
|
+
* than folded back into `recordpage.tsx`: a second consumer turning up
|
|
17
|
+
* again is a smaller change than re-extracting it would be.
|
|
15
18
|
*/
|
|
16
19
|
export function useEditableField(onEdit) {
|
|
17
20
|
const [editingKey, setEditingKey] = React.useState(null);
|
|
@@ -4,9 +4,14 @@ import { contract as c } from "../../tokens/index.js";
|
|
|
4
4
|
* `border-radius: ${c.radius.chip}`, 14px/20px at weight 500, and a 1px inset ring in the
|
|
5
5
|
* tone's colour.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* to be
|
|
9
|
-
*
|
|
7
|
+
* Its own module (not pasted into `recordstable.recipe.ts`) because it used
|
|
8
|
+
* to be shared with `RecordPanel`'s own Details field list too, and the two
|
|
9
|
+
* copies had already started to drift before that sharing existed — adding
|
|
10
|
+
* the measured inset ring meant patching the same rules twice. `RecordPanel`
|
|
11
|
+
* no longer renders a Details list (#448, Attio's own peek doesn't have
|
|
12
|
+
* one), so `RecordsTable` is this module's only consumer now; kept as its
|
|
13
|
+
* own file rather than folded back in, since a second consumer showing up
|
|
14
|
+
* again is exactly how the drift happened the first time.
|
|
10
15
|
*/
|
|
11
16
|
export const recordTagCss = `& .crisp-recordstable-tag { display: inline-flex; align-items: center; height: ${c.size.chip}; padding: 0 ${c.space["1"]}; border-radius: ${c.radius.chip}; font-size: ${c.text.lg}; line-height: 20px; font-weight: ${c.weight.medium}; white-space: nowrap; background: color-mix(in srgb, ${c.fgPrimary} 6%, transparent); color: color-mix(in srgb, ${c.fgPrimary} 82%, transparent); box-shadow: inset 0 0 0 1px ${c.borderSubtle}; }
|
|
12
17
|
& .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, ${c.bgBrandSolid} 14%, transparent); color: color-mix(in srgb, ${c.bgBrandSolid} 82%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgBrandSolid} 30%, transparent); }
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { RichTextEditor, } from "./richtext.js";
|
|
1
|
+
export { RICHTEXT_COLOR_TONES, RichTextEditor, } from "./richtext.js";
|
|
2
2
|
export { richtextRecipe } from "./richtext.recipe.js";
|