@crisp-ui-kit/crisp 0.53.4 → 0.53.5
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/product/richtext/richtext.js +3 -3
- package/dist/cjs/product/settingssection/settingssection.recipe.js +11 -2
- package/dist/esm/product/richtext/richtext.js +3 -3
- package/dist/esm/product/settingssection/settingssection.recipe.js +11 -2
- package/dist/settingssection.css +2 -2
- package/dist/settingssection.layered.css +2 -2
- package/dist/styles.css +2 -2
- package/dist/styles.layered.css +2 -2
- package/dist/types/product/richtext/richtext.d.ts +20 -0
- package/package.json +1 -1
|
@@ -149,7 +149,7 @@ const exec = (cmd, value) => document.execCommand(cmd, false, value);
|
|
|
149
149
|
* reach the identical menu, sitting at the end of the body's last line) —
|
|
150
150
|
* only the two entry points #426 asked for.
|
|
151
151
|
*/
|
|
152
|
-
exports.RichTextEditor = React.forwardRef(function RichTextEditor({ defaultTitle = "", defaultHtml = "", titlePlaceholder = "Untitled", placeholder = "Start writing, or press a toolbar button…", onChange, className, titleLabel = "Title", bodyLabel = "Note body", linkPromptLabel = "Link URL", imagePromptLabel = "Image URL", textColorLabel = "Text", highlightColorLabel = "Highlight", clearColorLabel = "Default", blockMenuLabel = "Body", blockMenuSearchPlaceholder = "Search items…", blockMenuEmptyLabel = "No matching blocks", mentionTrigger = "@", renderMentionPicker, readOnly = false, ...rest }, ref) {
|
|
152
|
+
exports.RichTextEditor = React.forwardRef(function RichTextEditor({ defaultTitle = "", defaultHtml = "", titlePlaceholder = "Untitled", placeholder = "Start writing, or press a toolbar button…", onChange, className, titleLabel = "Title", bodyLabel = "Note body", linkPromptLabel = "Link URL", imagePromptLabel = "Image URL", textColorLabel = "Text", highlightColorLabel = "Highlight", clearColorLabel = "Default", blockMenuLabel = "Body", blockMenuSearchPlaceholder = "Search items…", blockMenuEmptyLabel = "No matching blocks", mentionTrigger = "@", renderMentionPicker, readOnly = false, showTitle = true, ...rest }, ref) {
|
|
153
153
|
const titleRef = React.useRef(null);
|
|
154
154
|
const bodyRef = React.useRef(null);
|
|
155
155
|
const [bar, setBar] = React.useState(null);
|
|
@@ -550,12 +550,12 @@ exports.RichTextEditor = React.forwardRef(function RichTextEditor({ defaultTitle
|
|
|
550
550
|
ol: (0, jsx_runtime_1.jsx)(lucide_react_1.ListOrdered, { ...menuIc }),
|
|
551
551
|
image: (0, jsx_runtime_1.jsx)(lucide_react_1.Image, { ...menuIc }),
|
|
552
552
|
};
|
|
553
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, richtext_recipe_1.richtextRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsx)("div", { ref: titleRef, className: "crisp-rte-title", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-label": titleLabel, "aria-readonly": readOnly || undefined, "data-placeholder": titlePlaceholder, onInput: readOnly
|
|
553
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, richtext_recipe_1.richtextRecipe)({ className }), ...rest, children: [showTitle && ((0, jsx_runtime_1.jsx)("div", { ref: titleRef, className: "crisp-rte-title", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-label": titleLabel, "aria-readonly": readOnly || undefined, "data-placeholder": titlePlaceholder, onInput: readOnly
|
|
554
554
|
? undefined
|
|
555
555
|
: () => {
|
|
556
556
|
syncEmpty(titleRef.current);
|
|
557
557
|
emit();
|
|
558
|
-
} }), (0, jsx_runtime_1.jsx)("div", { ref: bodyRef, className: "crisp-rte-body", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-multiline": "true", "aria-label": bodyLabel, "aria-readonly": readOnly || undefined, "data-placeholder": placeholder, onInput: readOnly
|
|
558
|
+
} })), (0, jsx_runtime_1.jsx)("div", { ref: bodyRef, className: "crisp-rte-body", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-multiline": "true", "aria-label": bodyLabel, "aria-readonly": readOnly || undefined, "data-placeholder": placeholder, onInput: readOnly
|
|
559
559
|
? undefined
|
|
560
560
|
: () => {
|
|
561
561
|
syncEmpty(bodyRef.current);
|
|
@@ -6,13 +6,22 @@ const tokens_1 = require("../../tokens");
|
|
|
6
6
|
// A settings section — measured from Settings pages: a 16px/600 title
|
|
7
7
|
// with an optional subtitle, then a stack of rows (form fields, toggles…). Rows
|
|
8
8
|
// gap evenly; an optional trailing action sits on the title row.
|
|
9
|
+
//
|
|
10
|
+
// Title line-height/letter-spacing and subtitle font-size/line-height
|
|
11
|
+
// re-measured live at app.attio.com/<workspace>/settings/account against a
|
|
12
|
+
// subsection heading ("Time preferences") — crisp#522. The subtitle's own
|
|
13
|
+
// colour (measured `rgba(0,0,0,0.63)`, a black-alpha value) is left as
|
|
14
|
+
// `fgMuted` pending a dark-mode measurement: a raw alpha-black has no
|
|
15
|
+
// existing token match and would need its own light/dark pair to stay
|
|
16
|
+
// legible in dark mode (Lesson: theme-constant vs theme-flipping colours
|
|
17
|
+
// must each be independently verified).
|
|
9
18
|
const config = (0, core_1.defineRecipe)({
|
|
10
19
|
name: "settingssection",
|
|
11
20
|
base: `display: flex; flex-direction: column; box-sizing: border-box; font-family: ${tokens_1.contract.font.sans};
|
|
12
21
|
& .crisp-settingssection-head { display: flex; align-items: flex-start; gap: ${tokens_1.contract.space["2"]}; }
|
|
13
22
|
& .crisp-settingssection-heading { flex: 1; min-width: 0; }
|
|
14
|
-
& .crisp-settingssection-title { font-size: ${tokens_1.contract.text.xl}; line-height:
|
|
15
|
-
& .crisp-settingssection-sub { margin-top: ${tokens_1.contract.space["0.5"]}; font-size: ${tokens_1.contract.text.
|
|
23
|
+
& .crisp-settingssection-title { font-size: ${tokens_1.contract.text.xl}; line-height: 20px; font-weight: ${tokens_1.contract.weight.semibold}; letter-spacing: -0.16px; color: ${tokens_1.contract.fgPrimary}; }
|
|
24
|
+
& .crisp-settingssection-sub { margin-top: ${tokens_1.contract.space["0.5"]}; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; color: ${tokens_1.contract.fgMuted}; }
|
|
16
25
|
& .crisp-settingssection-action { flex-shrink: 0; }
|
|
17
26
|
& .crisp-settingssection-body { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["4"]}; margin-top: ${tokens_1.contract.space["3"]}; }`,
|
|
18
27
|
variants: {},
|
|
@@ -113,7 +113,7 @@ const exec = (cmd, value) => document.execCommand(cmd, false, value);
|
|
|
113
113
|
* reach the identical menu, sitting at the end of the body's last line) —
|
|
114
114
|
* only the two entry points #426 asked for.
|
|
115
115
|
*/
|
|
116
|
-
export const RichTextEditor = React.forwardRef(function RichTextEditor({ defaultTitle = "", defaultHtml = "", titlePlaceholder = "Untitled", placeholder = "Start writing, or press a toolbar button…", onChange, className, titleLabel = "Title", bodyLabel = "Note body", linkPromptLabel = "Link URL", imagePromptLabel = "Image URL", textColorLabel = "Text", highlightColorLabel = "Highlight", clearColorLabel = "Default", blockMenuLabel = "Body", blockMenuSearchPlaceholder = "Search items…", blockMenuEmptyLabel = "No matching blocks", mentionTrigger = "@", renderMentionPicker, readOnly = false, ...rest }, ref) {
|
|
116
|
+
export const RichTextEditor = React.forwardRef(function RichTextEditor({ defaultTitle = "", defaultHtml = "", titlePlaceholder = "Untitled", placeholder = "Start writing, or press a toolbar button…", onChange, className, titleLabel = "Title", bodyLabel = "Note body", linkPromptLabel = "Link URL", imagePromptLabel = "Image URL", textColorLabel = "Text", highlightColorLabel = "Highlight", clearColorLabel = "Default", blockMenuLabel = "Body", blockMenuSearchPlaceholder = "Search items…", blockMenuEmptyLabel = "No matching blocks", mentionTrigger = "@", renderMentionPicker, readOnly = false, showTitle = true, ...rest }, ref) {
|
|
117
117
|
const titleRef = React.useRef(null);
|
|
118
118
|
const bodyRef = React.useRef(null);
|
|
119
119
|
const [bar, setBar] = React.useState(null);
|
|
@@ -514,12 +514,12 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
|
|
|
514
514
|
ol: _jsx(ListOrdered, { ...menuIc }),
|
|
515
515
|
image: _jsx(ImageIcon, { ...menuIc }),
|
|
516
516
|
};
|
|
517
|
-
return (_jsxs("div", { className: richtextRecipe({ className }), ...rest, children: [_jsx("div", { ref: titleRef, className: "crisp-rte-title", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-label": titleLabel, "aria-readonly": readOnly || undefined, "data-placeholder": titlePlaceholder, onInput: readOnly
|
|
517
|
+
return (_jsxs("div", { className: richtextRecipe({ className }), ...rest, children: [showTitle && (_jsx("div", { ref: titleRef, className: "crisp-rte-title", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-label": titleLabel, "aria-readonly": readOnly || undefined, "data-placeholder": titlePlaceholder, onInput: readOnly
|
|
518
518
|
? undefined
|
|
519
519
|
: () => {
|
|
520
520
|
syncEmpty(titleRef.current);
|
|
521
521
|
emit();
|
|
522
|
-
} }), _jsx("div", { ref: bodyRef, className: "crisp-rte-body", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-multiline": "true", "aria-label": bodyLabel, "aria-readonly": readOnly || undefined, "data-placeholder": placeholder, onInput: readOnly
|
|
522
|
+
} })), _jsx("div", { ref: bodyRef, className: "crisp-rte-body", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-multiline": "true", "aria-label": bodyLabel, "aria-readonly": readOnly || undefined, "data-placeholder": placeholder, onInput: readOnly
|
|
523
523
|
? undefined
|
|
524
524
|
: () => {
|
|
525
525
|
syncEmpty(bodyRef.current);
|
|
@@ -3,13 +3,22 @@ import { contract as c } from "../../tokens/index.js";
|
|
|
3
3
|
// A settings section — measured from Settings pages: a 16px/600 title
|
|
4
4
|
// with an optional subtitle, then a stack of rows (form fields, toggles…). Rows
|
|
5
5
|
// gap evenly; an optional trailing action sits on the title row.
|
|
6
|
+
//
|
|
7
|
+
// Title line-height/letter-spacing and subtitle font-size/line-height
|
|
8
|
+
// re-measured live at app.attio.com/<workspace>/settings/account against a
|
|
9
|
+
// subsection heading ("Time preferences") — crisp#522. The subtitle's own
|
|
10
|
+
// colour (measured `rgba(0,0,0,0.63)`, a black-alpha value) is left as
|
|
11
|
+
// `fgMuted` pending a dark-mode measurement: a raw alpha-black has no
|
|
12
|
+
// existing token match and would need its own light/dark pair to stay
|
|
13
|
+
// legible in dark mode (Lesson: theme-constant vs theme-flipping colours
|
|
14
|
+
// must each be independently verified).
|
|
6
15
|
const config = defineRecipe({
|
|
7
16
|
name: "settingssection",
|
|
8
17
|
base: `display: flex; flex-direction: column; box-sizing: border-box; font-family: ${c.font.sans};
|
|
9
18
|
& .crisp-settingssection-head { display: flex; align-items: flex-start; gap: ${c.space["2"]}; }
|
|
10
19
|
& .crisp-settingssection-heading { flex: 1; min-width: 0; }
|
|
11
|
-
& .crisp-settingssection-title { font-size: ${c.text.xl}; line-height:
|
|
12
|
-
& .crisp-settingssection-sub { margin-top: ${c.space["0.5"]}; font-size: ${c.text.
|
|
20
|
+
& .crisp-settingssection-title { font-size: ${c.text.xl}; line-height: 20px; font-weight: ${c.weight.semibold}; letter-spacing: -0.16px; color: ${c.fgPrimary}; }
|
|
21
|
+
& .crisp-settingssection-sub { margin-top: ${c.space["0.5"]}; font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgMuted}; }
|
|
13
22
|
& .crisp-settingssection-action { flex-shrink: 0; }
|
|
14
23
|
& .crisp-settingssection-body { display: flex; flex-direction: column; gap: ${c.space["4"]}; margin-top: ${c.space["3"]}; }`,
|
|
15
24
|
variants: {},
|
package/dist/settingssection.css
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans);
|
|
3
3
|
& .crisp-settingssection-head { display: flex; align-items: flex-start; gap: var(--crisp-space-2); }
|
|
4
4
|
& .crisp-settingssection-heading { flex: 1; min-width: 0; }
|
|
5
|
-
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height:
|
|
6
|
-
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-
|
|
5
|
+
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height: 20px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.16px; color: var(--crisp-fg-primary); }
|
|
6
|
+
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-muted); }
|
|
7
7
|
& .crisp-settingssection-action { flex-shrink: 0; }
|
|
8
8
|
& .crisp-settingssection-body { display: flex; flex-direction: column; gap: var(--crisp-space-4); margin-top: var(--crisp-space-3); }
|
|
9
9
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans);
|
|
4
4
|
& .crisp-settingssection-head { display: flex; align-items: flex-start; gap: var(--crisp-space-2); }
|
|
5
5
|
& .crisp-settingssection-heading { flex: 1; min-width: 0; }
|
|
6
|
-
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height:
|
|
7
|
-
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-
|
|
6
|
+
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height: 20px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.16px; color: var(--crisp-fg-primary); }
|
|
7
|
+
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-muted); }
|
|
8
8
|
& .crisp-settingssection-action { flex-shrink: 0; }
|
|
9
9
|
& .crisp-settingssection-body { display: flex; flex-direction: column; gap: var(--crisp-space-4); margin-top: var(--crisp-space-3); }
|
|
10
10
|
}
|
package/dist/styles.css
CHANGED
|
@@ -2674,8 +2674,8 @@
|
|
|
2674
2674
|
display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans);
|
|
2675
2675
|
& .crisp-settingssection-head { display: flex; align-items: flex-start; gap: var(--crisp-space-2); }
|
|
2676
2676
|
& .crisp-settingssection-heading { flex: 1; min-width: 0; }
|
|
2677
|
-
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height:
|
|
2678
|
-
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-
|
|
2677
|
+
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height: 20px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.16px; color: var(--crisp-fg-primary); }
|
|
2678
|
+
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-muted); }
|
|
2679
2679
|
& .crisp-settingssection-action { flex-shrink: 0; }
|
|
2680
2680
|
& .crisp-settingssection-body { display: flex; flex-direction: column; gap: var(--crisp-space-4); margin-top: var(--crisp-space-3); }
|
|
2681
2681
|
}
|
package/dist/styles.layered.css
CHANGED
|
@@ -2675,8 +2675,8 @@
|
|
|
2675
2675
|
display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans);
|
|
2676
2676
|
& .crisp-settingssection-head { display: flex; align-items: flex-start; gap: var(--crisp-space-2); }
|
|
2677
2677
|
& .crisp-settingssection-heading { flex: 1; min-width: 0; }
|
|
2678
|
-
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height:
|
|
2679
|
-
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-
|
|
2678
|
+
& .crisp-settingssection-title { font-size: var(--crisp-text-xl); line-height: 20px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.16px; color: var(--crisp-fg-primary); }
|
|
2679
|
+
& .crisp-settingssection-sub { margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-muted); }
|
|
2680
2680
|
& .crisp-settingssection-action { flex-shrink: 0; }
|
|
2681
2681
|
& .crisp-settingssection-body { display: flex; flex-direction: column; gap: var(--crisp-space-4); margin-top: var(--crisp-space-3); }
|
|
2682
2682
|
}
|
|
@@ -93,6 +93,16 @@ export type RichTextEditorProps = Omit<React.ComponentPropsWithoutRef<"div">, "c
|
|
|
93
93
|
* headings/lists/marks all survive into it. No toolbar renders in this
|
|
94
94
|
* mode. */
|
|
95
95
|
readOnly?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Renders the title field at all (default `true`, matching Attio's own
|
|
98
|
+
* preview — see `readOnly` above). `false` omits it from the DOM
|
|
99
|
+
* entirely, not just visually: for a consumer whose own layout already
|
|
100
|
+
* shows a note's title elsewhere (a list row's own heading, a record's
|
|
101
|
+
* collapsed-note summary), rendering it a second time here would
|
|
102
|
+
* duplicate it rather than match anything Attio does. `onChange`'s
|
|
103
|
+
* `title` is always `""` with this off; `defaultTitle` is ignored.
|
|
104
|
+
*/
|
|
105
|
+
showTitle?: boolean;
|
|
96
106
|
className?: string;
|
|
97
107
|
};
|
|
98
108
|
/**
|
|
@@ -187,5 +197,15 @@ export declare const RichTextEditor: React.ForwardRefExoticComponent<Omit<Omit<R
|
|
|
187
197
|
* headings/lists/marks all survive into it. No toolbar renders in this
|
|
188
198
|
* mode. */
|
|
189
199
|
readOnly?: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Renders the title field at all (default `true`, matching Attio's own
|
|
202
|
+
* preview — see `readOnly` above). `false` omits it from the DOM
|
|
203
|
+
* entirely, not just visually: for a consumer whose own layout already
|
|
204
|
+
* shows a note's title elsewhere (a list row's own heading, a record's
|
|
205
|
+
* collapsed-note summary), rendering it a second time here would
|
|
206
|
+
* duplicate it rather than match anything Attio does. `onChange`'s
|
|
207
|
+
* `title` is always `""` with this off; `defaultTitle` is ignored.
|
|
208
|
+
*/
|
|
209
|
+
showTitle?: boolean;
|
|
190
210
|
className?: string;
|
|
191
211
|
} & React.RefAttributes<RichTextEditorHandle>>;
|