@crisp-ui-kit/crisp 0.33.4 → 0.33.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/checkbox.css +1 -1
- package/dist/checkbox.layered.css +1 -1
- package/dist/cjs/components/avatargroup/avatargroup.recipe.js +21 -8
- package/dist/cjs/components/barchart/barchart.js +6 -1
- package/dist/cjs/components/checkbox/checkbox.recipe.js +50 -17
- package/dist/cjs/components/datepicker/datepicker.js +7 -1
- package/dist/cjs/components/kbd/kbd.recipe.js +22 -9
- package/dist/cjs/components/linechart/linechart.js +6 -1
- package/dist/cjs/components/skeleton/skeleton.recipe.js +14 -8
- package/dist/cjs/components/table/table.js +6 -2
- package/dist/cjs/components/textarea/textarea.js +9 -2
- package/dist/cjs/components/textarea/textarea.recipe.js +14 -5
- package/dist/cjs/components/tooltip/tooltip.recipe.js +27 -6
- package/dist/command.css +1 -1
- package/dist/command.layered.css +1 -1
- package/dist/esm/components/avatargroup/avatargroup.recipe.js +21 -8
- package/dist/esm/components/barchart/barchart.js +6 -1
- package/dist/esm/components/checkbox/checkbox.recipe.js +50 -17
- package/dist/esm/components/datepicker/datepicker.js +7 -1
- package/dist/esm/components/kbd/kbd.recipe.js +22 -9
- package/dist/esm/components/linechart/linechart.js +6 -1
- package/dist/esm/components/skeleton/skeleton.recipe.js +14 -8
- package/dist/esm/components/table/table.js +6 -2
- package/dist/esm/components/textarea/textarea.js +9 -2
- package/dist/esm/components/textarea/textarea.recipe.js +14 -5
- package/dist/esm/components/tooltip/tooltip.recipe.js +27 -6
- package/dist/navitem.css +1 -1
- package/dist/navitem.layered.css +1 -1
- package/dist/sidebar.css +1 -1
- package/dist/sidebar.layered.css +1 -1
- package/dist/styles.css +6 -6
- package/dist/styles.layered.css +6 -6
- package/dist/textarea.css +2 -2
- package/dist/textarea.layered.css +2 -2
- package/dist/types/components/avatargroup/avatargroup.recipe.d.ts +25 -2
- package/dist/types/components/checkbox/checkbox.recipe.d.ts +51 -3
- package/dist/types/components/kbd/kbd.recipe.d.ts +24 -3
- package/dist/types/components/skeleton/skeleton.recipe.d.ts +19 -2
- package/dist/types/components/tooltip/tooltip.recipe.d.ts +30 -2
- package/package.json +2 -1
package/dist/checkbox.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.crisp-checkbox {
|
|
2
|
-
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm)
|
|
2
|
+
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s;
|
|
3
3
|
&:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
|
|
4
4
|
&:checked, &:indeterminate { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; }
|
|
5
5
|
&:checked { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-checkbox {
|
|
3
|
-
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm)
|
|
3
|
+
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s;
|
|
4
4
|
&:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
|
|
5
5
|
&:checked, &:indeterminate { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; }
|
|
6
6
|
&:checked { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.avatargroupRecipe = void 0;
|
|
3
|
+
exports.spec = exports.avatargroupRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
6
|
// A row of overlapping avatars — each ringed in the surface colour so it reads
|
|
7
7
|
// against its neighbour, with a "+N" overflow chip.
|
|
8
|
-
const
|
|
8
|
+
const spec = (0, core_1.defineSpec)({
|
|
9
9
|
name: "avatargroup",
|
|
10
|
-
base:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
base: { display: "inline-flex", alignItems: "center" },
|
|
11
|
+
parts: [
|
|
12
|
+
{
|
|
13
|
+
slot: "member",
|
|
14
|
+
selector: "& > *",
|
|
15
|
+
decls: {
|
|
16
|
+
marginInlineStart: `-${tokens_1.contract.space["2"]}`,
|
|
17
|
+
boxShadow: `0 0 0 2px ${tokens_1.contract.bgSurface}`,
|
|
18
|
+
position: "relative",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
slot: "member",
|
|
23
|
+
selector: "& > *:first-child",
|
|
24
|
+
decls: { marginInlineStart: 0 },
|
|
25
|
+
},
|
|
26
|
+
],
|
|
16
27
|
});
|
|
28
|
+
exports.spec = spec;
|
|
29
|
+
const config = (0, core_1.specToRecipe)(spec);
|
|
17
30
|
exports.avatargroupRecipe = (0, core_1.resolveRecipe)(config);
|
|
18
31
|
exports.default = config;
|
|
@@ -34,9 +34,14 @@ function BarChart({ data, height = 240, max, ticks = 5, color, format = (n) => n
|
|
|
34
34
|
: `max(34px, ${longestTickChars}ch + ${tokens_1.contract.space["4"]})`;
|
|
35
35
|
const summary = ariaLabel ??
|
|
36
36
|
`Bar chart: ${data.map((d) => `${d.label} ${format(d.value)}`).join(", ")}`;
|
|
37
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, barchart_recipe_1.barchartRecipe)({ className }), ...rest,
|
|
37
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, barchart_recipe_1.barchartRecipe)({ className }), ...rest,
|
|
38
|
+
// merged, not assigned: written plainly this sits AFTER `{...rest}` and
|
|
39
|
+
// overwrites whatever style the consumer passed, so `style={{ maxWidth }}`
|
|
40
|
+
// on the chart did nothing at all. The consumer's wins on collision.
|
|
41
|
+
style: {
|
|
38
42
|
height,
|
|
39
43
|
"--crisp-barchart-yaxis-width": yAxisWidthValue,
|
|
44
|
+
...rest.style,
|
|
40
45
|
}, role: "img", "aria-label": summary, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-barchart-body", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-barchart-yaxis", children: tickValues.map((t) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-barchart-ytick", style: { bottom: `${(t / top) * 100}%` }, children: format(t) }, t))) }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-barchart-plot", children: [tickValues.map((t) => ((0, jsx_runtime_1.jsx)("div", { className: "crisp-barchart-grid", style: { bottom: `${(t / top) * 100}%` } }, t))), data.map((d) => ((0, jsx_runtime_1.jsx)("div", { className: "crisp-barchart-col", children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-barchart-bar", "data-empty": d.value <= 0 ? "" : undefined, style: {
|
|
41
46
|
height: `${Math.max(0, (d.value / top) * 100)}%`,
|
|
42
47
|
background: color,
|
|
@@ -1,28 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkboxRecipe = void 0;
|
|
3
|
+
exports.spec = exports.checkboxRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
6
|
// A 16px square checkbox measured from a record grid: r6, a subtle 1px
|
|
7
7
|
// inset ring when empty, brand fill + white check when on. The check is a
|
|
8
8
|
// data-URI SVG background so it renders reliably (input pseudo-content doesn't).
|
|
9
9
|
const CHECK = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E\")";
|
|
10
|
-
const
|
|
10
|
+
const spec = (0, core_1.defineSpec)({
|
|
11
11
|
name: "checkbox",
|
|
12
|
-
base:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
base: {
|
|
13
|
+
appearance: "none",
|
|
14
|
+
WebkitAppearance: "none",
|
|
15
|
+
boxSizing: "border-box",
|
|
16
|
+
flexShrink: 0,
|
|
17
|
+
margin: 0,
|
|
18
|
+
width: "16px",
|
|
19
|
+
height: "16px",
|
|
20
|
+
border: "none",
|
|
21
|
+
borderRadius: tokens_1.contract.radius.sm,
|
|
22
|
+
background: tokens_1.contract.bgSurface,
|
|
23
|
+
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.fgPrimary} 9%, transparent)`,
|
|
24
|
+
cursor: "pointer",
|
|
25
|
+
transition: "background-color .12s, box-shadow .12s",
|
|
26
|
+
},
|
|
27
|
+
parts: [
|
|
28
|
+
{
|
|
29
|
+
selector: "&:hover",
|
|
30
|
+
decls: {
|
|
31
|
+
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.fgPrimary} 30%, transparent)`,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
selector: "&:checked, &:indeterminate",
|
|
36
|
+
decls: {
|
|
37
|
+
background: `${tokens_1.contract.bgBrandSolid} center / 10px no-repeat`,
|
|
38
|
+
boxShadow: "none",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{ selector: "&:checked", decls: { backgroundImage: CHECK } },
|
|
42
|
+
{
|
|
43
|
+
selector: "&:indeterminate",
|
|
44
|
+
decls: {
|
|
45
|
+
backgroundImage: `linear-gradient(${tokens_1.contract.fgOnBrand}, ${tokens_1.contract.fgOnBrand})`,
|
|
46
|
+
backgroundSize: "8px 2px",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
selector: "&:focus-visible",
|
|
51
|
+
decls: {
|
|
52
|
+
outline: `2px solid ${tokens_1.contract.bgBrandSolid}`,
|
|
53
|
+
outlineOffset: "1px",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
],
|
|
27
57
|
});
|
|
58
|
+
exports.spec = spec;
|
|
59
|
+
const config = (0, core_1.specToRecipe)(spec);
|
|
60
|
+
exports.checkboxRecipe = Object.assign((0, core_1.resolveRecipe)(config), { config });
|
|
28
61
|
exports.default = config;
|
|
@@ -126,7 +126,13 @@ const DatePickerImplementation = React.forwardRef((props, forwardedRef) => {
|
|
|
126
126
|
}, ...(0, dom_utils_1.mergeProps)(api.rootProps, rootProps), "aria-label": rootAriaLabel, "aria-labelledby": ariaLabelledby, className: (0, datepicker_recipe_1.datepickerRecipe)({
|
|
127
127
|
visibleRange: api.visibleRange,
|
|
128
128
|
className,
|
|
129
|
-
}),
|
|
129
|
+
}),
|
|
130
|
+
// merged: this sits after the spread above, so assigning it plainly
|
|
131
|
+
// dropped any style the consumer passed. Theirs wins on collision.
|
|
132
|
+
style: {
|
|
133
|
+
...styleOf({ height, minHeight, maxHeight }),
|
|
134
|
+
...rootProps?.style,
|
|
135
|
+
}, children: [api.visibleRange === "continuous" ? ((0, jsx_runtime_1.jsxs)("div", { ref: api.refs.continuousScroll, ...api.continuousScrollProps, "data-date-picker-continuous-scroll": "", className: "crisp-datepicker-continuous-scroll", children: [(0, jsx_runtime_1.jsx)(WeekdayRow, { api: api, semantic: false }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-datepicker-continuous-content", children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": "true", className: "datepicker-continuous-spacer", style: { height: api.virtual.topHeight } }), api.months.map((month) => ((0, jsx_runtime_1.jsx)(MonthView, { api: api, month: month, renderDateCellContent: renderDateCellContent, renderDateCellSupplement: renderDateCellSupplement, showWeekdays: false, showMonthLabel: true, monthRef: api.refs.continuousMonth(month.key) }, month.key))), (0, jsx_runtime_1.jsx)("div", { "aria-hidden": "true", className: "datepicker-continuous-spacer", style: { height: api.virtual.bottomHeight } })] })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [api.visibleRange !== "twoMonths" && (0, jsx_runtime_1.jsx)(Header, { api: api }), (0, jsx_runtime_1.jsx)("div", { "data-wheel-open": api.isWheelOpen && api.visibleRange !== "twoMonths"
|
|
130
136
|
? ""
|
|
131
137
|
: undefined, "aria-hidden": api.isWheelOpen && api.visibleRange !== "twoMonths"
|
|
132
138
|
? true
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.kbdRecipe = void 0;
|
|
3
|
+
exports.spec = exports.kbdRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
|
-
const
|
|
6
|
+
const spec = (0, core_1.defineSpec)({
|
|
7
7
|
name: "kbd",
|
|
8
|
-
base:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
base: {
|
|
9
|
+
boxSizing: "border-box",
|
|
10
|
+
display: "inline-flex",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
justifyContent: "center",
|
|
13
|
+
gap: tokens_1.contract.space["0.5"],
|
|
14
|
+
minWidth: "20px",
|
|
15
|
+
height: "20px",
|
|
16
|
+
paddingInline: tokens_1.contract.space["1"],
|
|
17
|
+
borderRadius: tokens_1.contract.radius.sm,
|
|
18
|
+
fontFamily: tokens_1.contract.font.sans,
|
|
19
|
+
fontSize: tokens_1.contract.text.xs,
|
|
20
|
+
fontWeight: tokens_1.contract.weight.regular,
|
|
21
|
+
color: `color-mix(in srgb, ${tokens_1.contract.fgPrimary} 50%, transparent)`,
|
|
22
|
+
background: "transparent",
|
|
23
|
+
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent)`,
|
|
24
|
+
},
|
|
15
25
|
});
|
|
26
|
+
exports.spec = spec;
|
|
27
|
+
const config = (0, core_1.specToRecipe)(spec);
|
|
28
|
+
exports.kbdRecipe = Object.assign((0, core_1.resolveRecipe)(config), { config });
|
|
16
29
|
exports.default = config;
|
|
@@ -44,9 +44,14 @@ function LineChart({ data, height = 240, max, ticks = 5, color, area = true, dot
|
|
|
44
44
|
: "";
|
|
45
45
|
const summary = ariaLabel ??
|
|
46
46
|
`Line chart: ${data.map((d) => `${d.label} ${format(d.value)}`).join(", ")}`;
|
|
47
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, linechart_recipe_1.linechartRecipe)({ className }), ...rest,
|
|
47
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, linechart_recipe_1.linechartRecipe)({ className }), ...rest,
|
|
48
|
+
// merged, not assigned: written plainly this sits AFTER `{...rest}` and
|
|
49
|
+
// overwrites whatever style the consumer passed, so `style={{ maxWidth }}`
|
|
50
|
+
// on the chart did nothing at all. The consumer's wins on collision.
|
|
51
|
+
style: {
|
|
48
52
|
height,
|
|
49
53
|
"--crisp-linechart-yaxis-width": yAxisWidthValue,
|
|
54
|
+
...rest.style,
|
|
50
55
|
}, role: "img", "aria-label": summary, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-linechart-body", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-linechart-yaxis", children: tickValues.map((t) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-linechart-ytick", style: { bottom: `${(t / top) * 100}%` }, children: format(t) }, t))) }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-linechart-plot", children: [tickValues.map((t) => ((0, jsx_runtime_1.jsx)("div", { className: "crisp-linechart-grid", style: { bottom: `${(t / top) * 100}%` } }, t))), pts.length > 1 && ((0, jsx_runtime_1.jsxs)("svg", { className: "crisp-linechart-svg", viewBox: "0 0 100 100", preserveAspectRatio: "none", "aria-hidden": "true", children: [area && (0, jsx_runtime_1.jsx)("path", { className: "crisp-linechart-area", d: areaPath }), (0, jsx_runtime_1.jsx)("polyline", { className: "crisp-linechart-line", points: line, style: { stroke: color } })] })), dots &&
|
|
51
56
|
pts.map((p, i) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-linechart-dot-html", style: {
|
|
52
57
|
left: `${p.x}%`,
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.skeletonRecipe = void 0;
|
|
3
|
+
exports.spec = exports.skeletonRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
6
|
// A pulsing placeholder block for loading states.
|
|
7
|
-
const
|
|
7
|
+
const spec = (0, core_1.defineSpec)({
|
|
8
8
|
name: "skeleton",
|
|
9
|
-
globalCss:
|
|
10
|
-
base:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
globalCss: "@keyframes crisp-skeleton { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }",
|
|
10
|
+
base: {
|
|
11
|
+
display: "block",
|
|
12
|
+
background: `color-mix(in srgb, ${tokens_1.contract.fgPrimary} 8%, transparent)`,
|
|
13
|
+
borderRadius: tokens_1.contract.radius.md,
|
|
14
|
+
animation: "crisp-skeleton 1.4s ease-in-out infinite",
|
|
15
|
+
},
|
|
16
|
+
parts: [
|
|
17
|
+
{ selector: "&[data-circle]", decls: { borderRadius: tokens_1.contract.radius.full } },
|
|
18
|
+
],
|
|
15
19
|
});
|
|
20
|
+
exports.spec = spec;
|
|
21
|
+
const config = (0, core_1.specToRecipe)(spec);
|
|
16
22
|
exports.skeletonRecipe = (0, core_1.resolveRecipe)(config);
|
|
17
23
|
exports.default = config;
|
|
@@ -44,11 +44,15 @@ const table_recipe_1 = require("./table.recipe");
|
|
|
44
44
|
* `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`,
|
|
45
45
|
* `TableCell`, and `TableCaption`.
|
|
46
46
|
*/
|
|
47
|
-
exports.Table = React.forwardRef(({ interactive, surface, className, wrapperClassName, scrollProps, children,
|
|
47
|
+
exports.Table = React.forwardRef(({ interactive, surface, className, wrapperClassName, scrollProps, children,
|
|
48
|
+
// on the root for the same reason as Textarea: the wrapper is the box a
|
|
49
|
+
// consumer sees and the scroll container they want to size. Native
|
|
50
|
+
// table attributes still reach the <table> below.
|
|
51
|
+
style, ...props }, ref) => ((0, jsx_runtime_1.jsx)("div", { className: (0, table_recipe_1.tableRecipe)({
|
|
48
52
|
interactive,
|
|
49
53
|
surface,
|
|
50
54
|
className: wrapperClassName,
|
|
51
|
-
}), children: (0, jsx_runtime_1.jsx)("div", { ...scrollProps, className: `crisp-table-scroll${scrollProps?.className ? ` ${scrollProps.className}` : ""}`, children: (0, jsx_runtime_1.jsx)("table", { ref: ref, className: className, ...props, children: children }) }) })));
|
|
55
|
+
}), style: style, children: (0, jsx_runtime_1.jsx)("div", { ...scrollProps, className: `crisp-table-scroll${scrollProps?.className ? ` ${scrollProps.className}` : ""}`, children: (0, jsx_runtime_1.jsx)("table", { ref: ref, className: className, ...props, children: children }) }) })));
|
|
52
56
|
exports.Table.displayName = "Table";
|
|
53
57
|
exports.TableHeader = React.forwardRef((props, ref) => (0, jsx_runtime_1.jsx)("thead", { ref: ref, ...props }));
|
|
54
58
|
exports.TableHeader.displayName = "TableHeader";
|
|
@@ -42,12 +42,19 @@ const textarea_recipe_1 = require("./textarea.recipe");
|
|
|
42
42
|
* `<textarea>` prop; pass `showCount` with `maxLength` to render a live
|
|
43
43
|
* character counter (which turns red if the value exceeds the limit).
|
|
44
44
|
*/
|
|
45
|
-
exports.Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
45
|
+
exports.Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
46
|
+
// `style` is pulled out and put on the ROOT, the way Input does it.
|
|
47
|
+
// Everything else belongs on the <textarea> -- id, name, form
|
|
48
|
+
// association, aria -- but `style` sizes the visual box, and the box is
|
|
49
|
+
// the root: it carries the focus ring (an ::after on the wrap) and the
|
|
50
|
+
// character counter. Left on the field, `style={{ maxWidth: 480 }}` gave
|
|
51
|
+
// a 480px control inside a full-width ring with the counter stranded.
|
|
52
|
+
style, ...props }, ref) => {
|
|
46
53
|
const initial = String(value ?? defaultValue ?? "").length;
|
|
47
54
|
const [count, setCount] = React.useState(initial);
|
|
48
55
|
const len = value !== undefined ? String(value).length : count;
|
|
49
56
|
const counted = showCount && maxLength != null;
|
|
50
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, textarea_recipe_1.textareaRecipe)({ className: wrapperClassName }), "data-counted": counted ? "" : undefined, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-textarea-wrap", children: (0, jsx_runtime_1.jsx)("textarea", { ref: ref, className: `crisp-textarea-field${className ? ` ${className}` : ""}`, maxLength: maxLength, value: value, defaultValue: defaultValue, onChange: (e) => {
|
|
57
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, textarea_recipe_1.textareaRecipe)({ className: wrapperClassName }), style: style, "data-counted": counted ? "" : undefined, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-textarea-wrap", children: (0, jsx_runtime_1.jsx)("textarea", { ref: ref, className: `crisp-textarea-field${className ? ` ${className}` : ""}`, maxLength: maxLength, value: value, defaultValue: defaultValue, onChange: (e) => {
|
|
51
58
|
if (value === undefined)
|
|
52
59
|
setCount(e.target.value.length);
|
|
53
60
|
onChange?.(e);
|
|
@@ -3,9 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.textareaRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
|
-
// A multiline text field
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// A multiline text field: an inset 1px ring (radius 8), 14px text, and an
|
|
7
|
+
// optional character counter pinned bottom-right (12px, muted). Focus rings
|
|
8
|
+
// the field brand.
|
|
9
|
+
//
|
|
10
|
+
// The inset is seed-design's, not Attio's -- the second deliberate divergence
|
|
11
|
+
// after the focus treatment. seed's text-input sets its inline inset to
|
|
12
|
+
// `$dimension.x3_5`, which rootage/dimension.yaml gives as **14px**, against
|
|
13
|
+
// the 10px this had. seed ships no textarea, so only the horizontal inset is
|
|
14
|
+
// theirs to copy; the vertical is derived from it: 10px above and below a
|
|
15
|
+
// 20px line makes a single-line box 40px tall, which is exactly seed's
|
|
16
|
+
// `$dimension.x10` medium field height. The counter's own inset moves to 14px
|
|
17
|
+
// with it, so it lines up with the text rather than sitting 4px inside it.
|
|
9
18
|
const config = (0, core_1.defineRecipe)({
|
|
10
19
|
name: "textarea",
|
|
11
20
|
base: `
|
|
@@ -13,7 +22,7 @@ const config = (0, core_1.defineRecipe)({
|
|
|
13
22
|
|
|
14
23
|
& .crisp-textarea-field {
|
|
15
24
|
box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical;
|
|
16
|
-
margin: 0; padding: ${tokens_1.contract.space["
|
|
25
|
+
margin: 0; padding: ${tokens_1.contract.space["2.5"]} 14px; border: none; border-radius: ${tokens_1.contract.radius.md};
|
|
17
26
|
background: ${tokens_1.contract.bgSurface}; box-shadow: ${tokens_1.elevation.field};
|
|
18
27
|
font-family: inherit; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; color: ${tokens_1.contract.fgPrimary};
|
|
19
28
|
outline: none; transition: box-shadow .15s ease;
|
|
@@ -35,7 +44,7 @@ const config = (0, core_1.defineRecipe)({
|
|
|
35
44
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: ${tokens_1.contract.bgDanger}; }
|
|
36
45
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: ${tokens_1.contract.bgDanger}; }
|
|
37
46
|
&[data-counted] .crisp-textarea-field { padding-bottom: ${tokens_1.contract.space["5"]}; }
|
|
38
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end:
|
|
47
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: ${tokens_1.contract.space["2.5"]}; font-size: ${tokens_1.contract.text.sm}; color: ${tokens_1.contract.fgMuted}; pointer-events: none; }
|
|
39
48
|
& .crisp-textarea-count[data-over] { color: ${tokens_1.contract.bgDanger}; }
|
|
40
49
|
`,
|
|
41
50
|
variants: {},
|
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.tooltipRecipe = void 0;
|
|
3
|
+
exports.spec = exports.tooltipRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
6
|
// The floating bubble for a Tooltip — a dark hint chip. Applied to the
|
|
7
7
|
// portaled bubble itself (not a wrapper), so its styles survive the portal.
|
|
8
|
-
const
|
|
8
|
+
const spec = (0, core_1.defineSpec)({
|
|
9
9
|
name: "tooltip",
|
|
10
|
-
base:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
base: {
|
|
11
|
+
position: "fixed",
|
|
12
|
+
zIndex: 70,
|
|
13
|
+
boxSizing: "border-box",
|
|
14
|
+
pointerEvents: "none",
|
|
15
|
+
maxWidth: "260px",
|
|
16
|
+
padding: `${tokens_1.contract.space["1"]} ${tokens_1.contract.space["2"]}`,
|
|
17
|
+
borderRadius: tokens_1.contract.radius.sm,
|
|
18
|
+
background: "#20242c",
|
|
19
|
+
color: "#fff",
|
|
20
|
+
fontFamily: tokens_1.contract.font.sans,
|
|
21
|
+
fontSize: tokens_1.contract.text.sm,
|
|
22
|
+
fontWeight: tokens_1.contract.weight.medium,
|
|
23
|
+
lineHeight: "1.45",
|
|
24
|
+
boxShadow: "0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04)",
|
|
25
|
+
opacity: 1,
|
|
26
|
+
transition: "opacity 0.12s ease",
|
|
27
|
+
},
|
|
28
|
+
parts: [
|
|
29
|
+
// An at-rule whose body is declarations rather than rules, so a part
|
|
30
|
+
// expresses it directly: the entry state the overlay animates from.
|
|
31
|
+
{ selector: "@starting-style", decls: { opacity: 0 } },
|
|
32
|
+
],
|
|
14
33
|
});
|
|
34
|
+
exports.spec = spec;
|
|
35
|
+
const config = (0, core_1.specToRecipe)(spec);
|
|
15
36
|
exports.tooltipRecipe = (0, core_1.resolveRecipe)(config);
|
|
16
37
|
exports.default = config;
|
package/dist/command.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.crisp-command {
|
|
2
|
-
display: contents; font-family: var(--crisp-font-sans)
|
|
2
|
+
display: contents; font-family: var(--crisp-font-sans);
|
|
3
3
|
& .crisp-command-content, & .crisp-command-card { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
4
4
|
& .crisp-command-backdrop { position: fixed; inset: 0; z-index: 50; background: transparent; }
|
|
5
5
|
& .crisp-command-positioner { position: fixed; inset: 0; z-index: 51; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); }
|
package/dist/command.layered.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-command {
|
|
3
|
-
display: contents; font-family: var(--crisp-font-sans)
|
|
3
|
+
display: contents; font-family: var(--crisp-font-sans);
|
|
4
4
|
& .crisp-command-content, & .crisp-command-card { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
5
5
|
& .crisp-command-backdrop { position: fixed; inset: 0; z-index: 50; background: transparent; }
|
|
6
6
|
& .crisp-command-positioner { position: fixed; inset: 0; z-index: 51; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); }
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
|
|
2
2
|
import { contract as c } from "../../tokens/index.js";
|
|
3
3
|
// A row of overlapping avatars — each ringed in the surface colour so it reads
|
|
4
4
|
// against its neighbour, with a "+N" overflow chip.
|
|
5
|
-
const
|
|
5
|
+
const spec = defineSpec({
|
|
6
6
|
name: "avatargroup",
|
|
7
|
-
base:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
base: { display: "inline-flex", alignItems: "center" },
|
|
8
|
+
parts: [
|
|
9
|
+
{
|
|
10
|
+
slot: "member",
|
|
11
|
+
selector: "& > *",
|
|
12
|
+
decls: {
|
|
13
|
+
marginInlineStart: `-${c.space["2"]}`,
|
|
14
|
+
boxShadow: `0 0 0 2px ${c.bgSurface}`,
|
|
15
|
+
position: "relative",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
slot: "member",
|
|
20
|
+
selector: "& > *:first-child",
|
|
21
|
+
decls: { marginInlineStart: 0 },
|
|
22
|
+
},
|
|
23
|
+
],
|
|
13
24
|
});
|
|
25
|
+
const config = specToRecipe(spec);
|
|
14
26
|
export const avatargroupRecipe = resolveRecipe(config);
|
|
27
|
+
export { spec };
|
|
15
28
|
export default config;
|
|
@@ -31,9 +31,14 @@ export function BarChart({ data, height = 240, max, ticks = 5, color, format = (
|
|
|
31
31
|
: `max(34px, ${longestTickChars}ch + ${c.space["4"]})`;
|
|
32
32
|
const summary = ariaLabel ??
|
|
33
33
|
`Bar chart: ${data.map((d) => `${d.label} ${format(d.value)}`).join(", ")}`;
|
|
34
|
-
return (_jsxs("div", { className: barchartRecipe({ className }), ...rest,
|
|
34
|
+
return (_jsxs("div", { className: barchartRecipe({ className }), ...rest,
|
|
35
|
+
// merged, not assigned: written plainly this sits AFTER `{...rest}` and
|
|
36
|
+
// overwrites whatever style the consumer passed, so `style={{ maxWidth }}`
|
|
37
|
+
// on the chart did nothing at all. The consumer's wins on collision.
|
|
38
|
+
style: {
|
|
35
39
|
height,
|
|
36
40
|
"--crisp-barchart-yaxis-width": yAxisWidthValue,
|
|
41
|
+
...rest.style,
|
|
37
42
|
}, role: "img", "aria-label": summary, children: [_jsxs("div", { className: "crisp-barchart-body", children: [_jsx("div", { className: "crisp-barchart-yaxis", children: tickValues.map((t) => (_jsx("span", { className: "crisp-barchart-ytick", style: { bottom: `${(t / top) * 100}%` }, children: format(t) }, t))) }), _jsxs("div", { className: "crisp-barchart-plot", children: [tickValues.map((t) => (_jsx("div", { className: "crisp-barchart-grid", style: { bottom: `${(t / top) * 100}%` } }, t))), data.map((d) => (_jsx("div", { className: "crisp-barchart-col", children: _jsx("div", { className: "crisp-barchart-bar", "data-empty": d.value <= 0 ? "" : undefined, style: {
|
|
38
43
|
height: `${Math.max(0, (d.value / top) * 100)}%`,
|
|
39
44
|
background: color,
|
|
@@ -1,25 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
|
|
2
2
|
import { contract as c } from "../../tokens/index.js";
|
|
3
3
|
// A 16px square checkbox measured from a record grid: r6, a subtle 1px
|
|
4
4
|
// inset ring when empty, brand fill + white check when on. The check is a
|
|
5
5
|
// data-URI SVG background so it renders reliably (input pseudo-content doesn't).
|
|
6
6
|
const CHECK = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E\")";
|
|
7
|
-
const
|
|
7
|
+
const spec = defineSpec({
|
|
8
8
|
name: "checkbox",
|
|
9
|
-
base:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
base: {
|
|
10
|
+
appearance: "none",
|
|
11
|
+
WebkitAppearance: "none",
|
|
12
|
+
boxSizing: "border-box",
|
|
13
|
+
flexShrink: 0,
|
|
14
|
+
margin: 0,
|
|
15
|
+
width: "16px",
|
|
16
|
+
height: "16px",
|
|
17
|
+
border: "none",
|
|
18
|
+
borderRadius: c.radius.sm,
|
|
19
|
+
background: c.bgSurface,
|
|
20
|
+
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 9%, transparent)`,
|
|
21
|
+
cursor: "pointer",
|
|
22
|
+
transition: "background-color .12s, box-shadow .12s",
|
|
23
|
+
},
|
|
24
|
+
parts: [
|
|
25
|
+
{
|
|
26
|
+
selector: "&:hover",
|
|
27
|
+
decls: {
|
|
28
|
+
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 30%, transparent)`,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
selector: "&:checked, &:indeterminate",
|
|
33
|
+
decls: {
|
|
34
|
+
background: `${c.bgBrandSolid} center / 10px no-repeat`,
|
|
35
|
+
boxShadow: "none",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{ selector: "&:checked", decls: { backgroundImage: CHECK } },
|
|
39
|
+
{
|
|
40
|
+
selector: "&:indeterminate",
|
|
41
|
+
decls: {
|
|
42
|
+
backgroundImage: `linear-gradient(${c.fgOnBrand}, ${c.fgOnBrand})`,
|
|
43
|
+
backgroundSize: "8px 2px",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
selector: "&:focus-visible",
|
|
48
|
+
decls: {
|
|
49
|
+
outline: `2px solid ${c.bgBrandSolid}`,
|
|
50
|
+
outlineOffset: "1px",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
24
54
|
});
|
|
55
|
+
const config = specToRecipe(spec);
|
|
56
|
+
export const checkboxRecipe = Object.assign(resolveRecipe(config), { config });
|
|
57
|
+
export { spec };
|
|
25
58
|
export default config;
|
|
@@ -90,7 +90,13 @@ const DatePickerImplementation = React.forwardRef((props, forwardedRef) => {
|
|
|
90
90
|
}, ...mergeProps(api.rootProps, rootProps), "aria-label": rootAriaLabel, "aria-labelledby": ariaLabelledby, className: datepickerRecipe({
|
|
91
91
|
visibleRange: api.visibleRange,
|
|
92
92
|
className,
|
|
93
|
-
}),
|
|
93
|
+
}),
|
|
94
|
+
// merged: this sits after the spread above, so assigning it plainly
|
|
95
|
+
// dropped any style the consumer passed. Theirs wins on collision.
|
|
96
|
+
style: {
|
|
97
|
+
...styleOf({ height, minHeight, maxHeight }),
|
|
98
|
+
...rootProps?.style,
|
|
99
|
+
}, children: [api.visibleRange === "continuous" ? (_jsxs("div", { ref: api.refs.continuousScroll, ...api.continuousScrollProps, "data-date-picker-continuous-scroll": "", className: "crisp-datepicker-continuous-scroll", children: [_jsx(WeekdayRow, { api: api, semantic: false }), _jsxs("div", { className: "crisp-datepicker-continuous-content", children: [_jsx("div", { "aria-hidden": "true", className: "datepicker-continuous-spacer", style: { height: api.virtual.topHeight } }), api.months.map((month) => (_jsx(MonthView, { api: api, month: month, renderDateCellContent: renderDateCellContent, renderDateCellSupplement: renderDateCellSupplement, showWeekdays: false, showMonthLabel: true, monthRef: api.refs.continuousMonth(month.key) }, month.key))), _jsx("div", { "aria-hidden": "true", className: "datepicker-continuous-spacer", style: { height: api.virtual.bottomHeight } })] })] })) : (_jsxs(_Fragment, { children: [api.visibleRange !== "twoMonths" && _jsx(Header, { api: api }), _jsx("div", { "data-wheel-open": api.isWheelOpen && api.visibleRange !== "twoMonths"
|
|
94
100
|
? ""
|
|
95
101
|
: undefined, "aria-hidden": api.isWheelOpen && api.visibleRange !== "twoMonths"
|
|
96
102
|
? true
|
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
|
|
2
2
|
import { contract as c } from "../../tokens/index.js";
|
|
3
|
-
const
|
|
3
|
+
const spec = defineSpec({
|
|
4
4
|
name: "kbd",
|
|
5
|
-
base:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
base: {
|
|
6
|
+
boxSizing: "border-box",
|
|
7
|
+
display: "inline-flex",
|
|
8
|
+
alignItems: "center",
|
|
9
|
+
justifyContent: "center",
|
|
10
|
+
gap: c.space["0.5"],
|
|
11
|
+
minWidth: "20px",
|
|
12
|
+
height: "20px",
|
|
13
|
+
paddingInline: c.space["1"],
|
|
14
|
+
borderRadius: c.radius.sm,
|
|
15
|
+
fontFamily: c.font.sans,
|
|
16
|
+
fontSize: c.text.xs,
|
|
17
|
+
fontWeight: c.weight.regular,
|
|
18
|
+
color: `color-mix(in srgb, ${c.fgPrimary} 50%, transparent)`,
|
|
19
|
+
background: "transparent",
|
|
20
|
+
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 6%, transparent)`,
|
|
21
|
+
},
|
|
12
22
|
});
|
|
23
|
+
const config = specToRecipe(spec);
|
|
24
|
+
export const kbdRecipe = Object.assign(resolveRecipe(config), { config });
|
|
25
|
+
export { spec };
|
|
13
26
|
export default config;
|
|
@@ -41,9 +41,14 @@ export function LineChart({ data, height = 240, max, ticks = 5, color, area = tr
|
|
|
41
41
|
: "";
|
|
42
42
|
const summary = ariaLabel ??
|
|
43
43
|
`Line chart: ${data.map((d) => `${d.label} ${format(d.value)}`).join(", ")}`;
|
|
44
|
-
return (_jsxs("div", { className: linechartRecipe({ className }), ...rest,
|
|
44
|
+
return (_jsxs("div", { className: linechartRecipe({ className }), ...rest,
|
|
45
|
+
// merged, not assigned: written plainly this sits AFTER `{...rest}` and
|
|
46
|
+
// overwrites whatever style the consumer passed, so `style={{ maxWidth }}`
|
|
47
|
+
// on the chart did nothing at all. The consumer's wins on collision.
|
|
48
|
+
style: {
|
|
45
49
|
height,
|
|
46
50
|
"--crisp-linechart-yaxis-width": yAxisWidthValue,
|
|
51
|
+
...rest.style,
|
|
47
52
|
}, role: "img", "aria-label": summary, children: [_jsxs("div", { className: "crisp-linechart-body", children: [_jsx("div", { className: "crisp-linechart-yaxis", children: tickValues.map((t) => (_jsx("span", { className: "crisp-linechart-ytick", style: { bottom: `${(t / top) * 100}%` }, children: format(t) }, t))) }), _jsxs("div", { className: "crisp-linechart-plot", children: [tickValues.map((t) => (_jsx("div", { className: "crisp-linechart-grid", style: { bottom: `${(t / top) * 100}%` } }, t))), pts.length > 1 && (_jsxs("svg", { className: "crisp-linechart-svg", viewBox: "0 0 100 100", preserveAspectRatio: "none", "aria-hidden": "true", children: [area && _jsx("path", { className: "crisp-linechart-area", d: areaPath }), _jsx("polyline", { className: "crisp-linechart-line", points: line, style: { stroke: color } })] })), dots &&
|
|
48
53
|
pts.map((p, i) => (_jsx("span", { className: "crisp-linechart-dot-html", style: {
|
|
49
54
|
left: `${p.x}%`,
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
|
|
2
2
|
import { contract as c } from "../../tokens/index.js";
|
|
3
3
|
// A pulsing placeholder block for loading states.
|
|
4
|
-
const
|
|
4
|
+
const spec = defineSpec({
|
|
5
5
|
name: "skeleton",
|
|
6
|
-
globalCss:
|
|
7
|
-
base:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
globalCss: "@keyframes crisp-skeleton { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }",
|
|
7
|
+
base: {
|
|
8
|
+
display: "block",
|
|
9
|
+
background: `color-mix(in srgb, ${c.fgPrimary} 8%, transparent)`,
|
|
10
|
+
borderRadius: c.radius.md,
|
|
11
|
+
animation: "crisp-skeleton 1.4s ease-in-out infinite",
|
|
12
|
+
},
|
|
13
|
+
parts: [
|
|
14
|
+
{ selector: "&[data-circle]", decls: { borderRadius: c.radius.full } },
|
|
15
|
+
],
|
|
12
16
|
});
|
|
17
|
+
const config = specToRecipe(spec);
|
|
13
18
|
export const skeletonRecipe = resolveRecipe(config);
|
|
19
|
+
export { spec };
|
|
14
20
|
export default config;
|
|
@@ -8,11 +8,15 @@ import { tableRecipe } from "./table.recipe.js";
|
|
|
8
8
|
* `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`,
|
|
9
9
|
* `TableCell`, and `TableCaption`.
|
|
10
10
|
*/
|
|
11
|
-
export const Table = React.forwardRef(({ interactive, surface, className, wrapperClassName, scrollProps, children,
|
|
11
|
+
export const Table = React.forwardRef(({ interactive, surface, className, wrapperClassName, scrollProps, children,
|
|
12
|
+
// on the root for the same reason as Textarea: the wrapper is the box a
|
|
13
|
+
// consumer sees and the scroll container they want to size. Native
|
|
14
|
+
// table attributes still reach the <table> below.
|
|
15
|
+
style, ...props }, ref) => (_jsx("div", { className: tableRecipe({
|
|
12
16
|
interactive,
|
|
13
17
|
surface,
|
|
14
18
|
className: wrapperClassName,
|
|
15
|
-
}), children: _jsx("div", { ...scrollProps, className: `crisp-table-scroll${scrollProps?.className ? ` ${scrollProps.className}` : ""}`, children: _jsx("table", { ref: ref, className: className, ...props, children: children }) }) })));
|
|
19
|
+
}), style: style, children: _jsx("div", { ...scrollProps, className: `crisp-table-scroll${scrollProps?.className ? ` ${scrollProps.className}` : ""}`, children: _jsx("table", { ref: ref, className: className, ...props, children: children }) }) })));
|
|
16
20
|
Table.displayName = "Table";
|
|
17
21
|
export const TableHeader = React.forwardRef((props, ref) => _jsx("thead", { ref: ref, ...props }));
|
|
18
22
|
TableHeader.displayName = "TableHeader";
|
|
@@ -6,12 +6,19 @@ import { textareaRecipe } from "./textarea.recipe.js";
|
|
|
6
6
|
* `<textarea>` prop; pass `showCount` with `maxLength` to render a live
|
|
7
7
|
* character counter (which turns red if the value exceeds the limit).
|
|
8
8
|
*/
|
|
9
|
-
export const Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
9
|
+
export const Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
10
|
+
// `style` is pulled out and put on the ROOT, the way Input does it.
|
|
11
|
+
// Everything else belongs on the <textarea> -- id, name, form
|
|
12
|
+
// association, aria -- but `style` sizes the visual box, and the box is
|
|
13
|
+
// the root: it carries the focus ring (an ::after on the wrap) and the
|
|
14
|
+
// character counter. Left on the field, `style={{ maxWidth: 480 }}` gave
|
|
15
|
+
// a 480px control inside a full-width ring with the counter stranded.
|
|
16
|
+
style, ...props }, ref) => {
|
|
10
17
|
const initial = String(value ?? defaultValue ?? "").length;
|
|
11
18
|
const [count, setCount] = React.useState(initial);
|
|
12
19
|
const len = value !== undefined ? String(value).length : count;
|
|
13
20
|
const counted = showCount && maxLength != null;
|
|
14
|
-
return (_jsxs("div", { className: textareaRecipe({ className: wrapperClassName }), "data-counted": counted ? "" : undefined, children: [_jsx("span", { className: "crisp-textarea-wrap", children: _jsx("textarea", { ref: ref, className: `crisp-textarea-field${className ? ` ${className}` : ""}`, maxLength: maxLength, value: value, defaultValue: defaultValue, onChange: (e) => {
|
|
21
|
+
return (_jsxs("div", { className: textareaRecipe({ className: wrapperClassName }), style: style, "data-counted": counted ? "" : undefined, children: [_jsx("span", { className: "crisp-textarea-wrap", children: _jsx("textarea", { ref: ref, className: `crisp-textarea-field${className ? ` ${className}` : ""}`, maxLength: maxLength, value: value, defaultValue: defaultValue, onChange: (e) => {
|
|
15
22
|
if (value === undefined)
|
|
16
23
|
setCount(e.target.value.length);
|
|
17
24
|
onChange?.(e);
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
2
|
import { contract as c, elevation as e } from "../../tokens/index.js";
|
|
3
|
-
// A multiline text field
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// A multiline text field: an inset 1px ring (radius 8), 14px text, and an
|
|
4
|
+
// optional character counter pinned bottom-right (12px, muted). Focus rings
|
|
5
|
+
// the field brand.
|
|
6
|
+
//
|
|
7
|
+
// The inset is seed-design's, not Attio's -- the second deliberate divergence
|
|
8
|
+
// after the focus treatment. seed's text-input sets its inline inset to
|
|
9
|
+
// `$dimension.x3_5`, which rootage/dimension.yaml gives as **14px**, against
|
|
10
|
+
// the 10px this had. seed ships no textarea, so only the horizontal inset is
|
|
11
|
+
// theirs to copy; the vertical is derived from it: 10px above and below a
|
|
12
|
+
// 20px line makes a single-line box 40px tall, which is exactly seed's
|
|
13
|
+
// `$dimension.x10` medium field height. The counter's own inset moves to 14px
|
|
14
|
+
// with it, so it lines up with the text rather than sitting 4px inside it.
|
|
6
15
|
const config = defineRecipe({
|
|
7
16
|
name: "textarea",
|
|
8
17
|
base: `
|
|
@@ -10,7 +19,7 @@ const config = defineRecipe({
|
|
|
10
19
|
|
|
11
20
|
& .crisp-textarea-field {
|
|
12
21
|
box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical;
|
|
13
|
-
margin: 0; padding: ${c.space["
|
|
22
|
+
margin: 0; padding: ${c.space["2.5"]} 14px; border: none; border-radius: ${c.radius.md};
|
|
14
23
|
background: ${c.bgSurface}; box-shadow: ${e.field};
|
|
15
24
|
font-family: inherit; font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgPrimary};
|
|
16
25
|
outline: none; transition: box-shadow .15s ease;
|
|
@@ -32,7 +41,7 @@ const config = defineRecipe({
|
|
|
32
41
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: ${c.bgDanger}; }
|
|
33
42
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: ${c.bgDanger}; }
|
|
34
43
|
&[data-counted] .crisp-textarea-field { padding-bottom: ${c.space["5"]}; }
|
|
35
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end:
|
|
44
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: ${c.space["2.5"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; pointer-events: none; }
|
|
36
45
|
& .crisp-textarea-count[data-over] { color: ${c.bgDanger}; }
|
|
37
46
|
`,
|
|
38
47
|
variants: {},
|
|
@@ -1,13 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
|
|
2
2
|
import { contract as c } from "../../tokens/index.js";
|
|
3
3
|
// The floating bubble for a Tooltip — a dark hint chip. Applied to the
|
|
4
4
|
// portaled bubble itself (not a wrapper), so its styles survive the portal.
|
|
5
|
-
const
|
|
5
|
+
const spec = defineSpec({
|
|
6
6
|
name: "tooltip",
|
|
7
|
-
base:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
base: {
|
|
8
|
+
position: "fixed",
|
|
9
|
+
zIndex: 70,
|
|
10
|
+
boxSizing: "border-box",
|
|
11
|
+
pointerEvents: "none",
|
|
12
|
+
maxWidth: "260px",
|
|
13
|
+
padding: `${c.space["1"]} ${c.space["2"]}`,
|
|
14
|
+
borderRadius: c.radius.sm,
|
|
15
|
+
background: "#20242c",
|
|
16
|
+
color: "#fff",
|
|
17
|
+
fontFamily: c.font.sans,
|
|
18
|
+
fontSize: c.text.sm,
|
|
19
|
+
fontWeight: c.weight.medium,
|
|
20
|
+
lineHeight: "1.45",
|
|
21
|
+
boxShadow: "0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04)",
|
|
22
|
+
opacity: 1,
|
|
23
|
+
transition: "opacity 0.12s ease",
|
|
24
|
+
},
|
|
25
|
+
parts: [
|
|
26
|
+
// An at-rule whose body is declarations rather than rules, so a part
|
|
27
|
+
// expresses it directly: the entry state the overlay animates from.
|
|
28
|
+
{ selector: "@starting-style", decls: { opacity: 0 } },
|
|
29
|
+
],
|
|
11
30
|
});
|
|
31
|
+
const config = specToRecipe(spec);
|
|
12
32
|
export const tooltipRecipe = resolveRecipe(config);
|
|
33
|
+
export { spec };
|
|
13
34
|
export default config;
|
package/dist/navitem.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.crisp-navitem {
|
|
2
|
-
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5)
|
|
2
|
+
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding-inline: var(--crisp-space-2); padding-inline-end: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; line-height: 20px; color: var(--crisp-fg-primary); text-decoration: none; cursor: pointer; user-select: none; width: 100%; transition: background-color 0.16s;
|
|
3
3
|
& .crisp-navitem-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
4
4
|
& .crisp-navitem-trailing { margin-inline-start: auto; display: inline-flex; align-items: center; flex-shrink: 0; }
|
|
5
5
|
&:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); }
|
package/dist/navitem.layered.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-navitem {
|
|
3
|
-
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5)
|
|
3
|
+
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding-inline: var(--crisp-space-2); padding-inline-end: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; line-height: 20px; color: var(--crisp-fg-primary); text-decoration: none; cursor: pointer; user-select: none; width: 100%; transition: background-color 0.16s;
|
|
4
4
|
& .crisp-navitem-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5
5
|
& .crisp-navitem-trailing { margin-inline-start: auto; display: inline-flex; align-items: center; flex-shrink: 0; }
|
|
6
6
|
&:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); }
|
package/dist/sidebar.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.crisp-sidebar {
|
|
2
|
-
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar)
|
|
2
|
+
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar); border-inline-end: 1px solid var(--crisp-border-subtle); padding: var(--crisp-space-2); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-y: auto;
|
|
3
3
|
& .crisp-sidebar-section { display: flex; flex-direction: column; gap: var(--crisp-space-px); margin-top: var(--crisp-space-2); }
|
|
4
4
|
& .crisp-sidebar-section[data-collapsed] { display: none; }
|
|
5
5
|
& .crisp-sidebar-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); }
|
package/dist/sidebar.layered.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-sidebar {
|
|
3
|
-
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar)
|
|
3
|
+
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar); border-inline-end: 1px solid var(--crisp-border-subtle); padding: var(--crisp-space-2); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-y: auto;
|
|
4
4
|
& .crisp-sidebar-section { display: flex; flex-direction: column; gap: var(--crisp-space-px); margin-top: var(--crisp-space-2); }
|
|
5
5
|
& .crisp-sidebar-section[data-collapsed] { display: none; }
|
|
6
6
|
& .crisp-sidebar-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); }
|
package/dist/styles.css
CHANGED
|
@@ -483,7 +483,7 @@
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
.crisp-checkbox {
|
|
486
|
-
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm)
|
|
486
|
+
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s;
|
|
487
487
|
&:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
|
|
488
488
|
&:checked, &:indeterminate { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; }
|
|
489
489
|
&:checked { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
|
|
@@ -1531,7 +1531,7 @@
|
|
|
1531
1531
|
|
|
1532
1532
|
.crisp-textarea {
|
|
1533
1533
|
position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
|
|
1534
|
-
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-
|
|
1534
|
+
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-2-5) 14px; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
1535
1535
|
& .crisp-textarea-field::placeholder { color: var(--crisp-fg-muted); }
|
|
1536
1536
|
& .crisp-textarea-field:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1537
1537
|
/* seed-design's focus treatment, same as Input: a 2px ring on an ::after
|
|
@@ -1547,7 +1547,7 @@
|
|
|
1547
1547
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: var(--crisp-bg-danger); }
|
|
1548
1548
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: var(--crisp-bg-danger); }
|
|
1549
1549
|
&[data-counted] .crisp-textarea-field { padding-bottom: var(--crisp-space-5); }
|
|
1550
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end:
|
|
1550
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: var(--crisp-space-2-5); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); pointer-events: none; }
|
|
1551
1551
|
& .crisp-textarea-count[data-over] { color: var(--crisp-bg-danger); }
|
|
1552
1552
|
}
|
|
1553
1553
|
|
|
@@ -1717,7 +1717,7 @@
|
|
|
1717
1717
|
}
|
|
1718
1718
|
|
|
1719
1719
|
.crisp-command {
|
|
1720
|
-
display: contents; font-family: var(--crisp-font-sans)
|
|
1720
|
+
display: contents; font-family: var(--crisp-font-sans);
|
|
1721
1721
|
& .crisp-command-content, & .crisp-command-card { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
1722
1722
|
& .crisp-command-backdrop { position: fixed; inset: 0; z-index: 50; background: transparent; }
|
|
1723
1723
|
& .crisp-command-positioner { position: fixed; inset: 0; z-index: 51; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); }
|
|
@@ -1859,7 +1859,7 @@
|
|
|
1859
1859
|
}
|
|
1860
1860
|
|
|
1861
1861
|
.crisp-navitem {
|
|
1862
|
-
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5)
|
|
1862
|
+
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding-inline: var(--crisp-space-2); padding-inline-end: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; line-height: 20px; color: var(--crisp-fg-primary); text-decoration: none; cursor: pointer; user-select: none; width: 100%; transition: background-color 0.16s;
|
|
1863
1863
|
& .crisp-navitem-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1864
1864
|
& .crisp-navitem-trailing { margin-inline-start: auto; display: inline-flex; align-items: center; flex-shrink: 0; }
|
|
1865
1865
|
&:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); }
|
|
@@ -2380,7 +2380,7 @@
|
|
|
2380
2380
|
}
|
|
2381
2381
|
|
|
2382
2382
|
.crisp-sidebar {
|
|
2383
|
-
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar)
|
|
2383
|
+
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar); border-inline-end: 1px solid var(--crisp-border-subtle); padding: var(--crisp-space-2); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-y: auto;
|
|
2384
2384
|
& .crisp-sidebar-section { display: flex; flex-direction: column; gap: var(--crisp-space-px); margin-top: var(--crisp-space-2); }
|
|
2385
2385
|
& .crisp-sidebar-section[data-collapsed] { display: none; }
|
|
2386
2386
|
& .crisp-sidebar-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); }
|
package/dist/styles.layered.css
CHANGED
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
.crisp-checkbox {
|
|
487
|
-
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm)
|
|
487
|
+
appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s;
|
|
488
488
|
&:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
|
|
489
489
|
&:checked, &:indeterminate { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; }
|
|
490
490
|
&:checked { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
|
|
@@ -1532,7 +1532,7 @@
|
|
|
1532
1532
|
|
|
1533
1533
|
.crisp-textarea {
|
|
1534
1534
|
position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
|
|
1535
|
-
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-
|
|
1535
|
+
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-2-5) 14px; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
1536
1536
|
& .crisp-textarea-field::placeholder { color: var(--crisp-fg-muted); }
|
|
1537
1537
|
& .crisp-textarea-field:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1538
1538
|
/* seed-design's focus treatment, same as Input: a 2px ring on an ::after
|
|
@@ -1548,7 +1548,7 @@
|
|
|
1548
1548
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: var(--crisp-bg-danger); }
|
|
1549
1549
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: var(--crisp-bg-danger); }
|
|
1550
1550
|
&[data-counted] .crisp-textarea-field { padding-bottom: var(--crisp-space-5); }
|
|
1551
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end:
|
|
1551
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: var(--crisp-space-2-5); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); pointer-events: none; }
|
|
1552
1552
|
& .crisp-textarea-count[data-over] { color: var(--crisp-bg-danger); }
|
|
1553
1553
|
}
|
|
1554
1554
|
|
|
@@ -1718,7 +1718,7 @@
|
|
|
1718
1718
|
}
|
|
1719
1719
|
|
|
1720
1720
|
.crisp-command {
|
|
1721
|
-
display: contents; font-family: var(--crisp-font-sans)
|
|
1721
|
+
display: contents; font-family: var(--crisp-font-sans);
|
|
1722
1722
|
& .crisp-command-content, & .crisp-command-card { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
1723
1723
|
& .crisp-command-backdrop { position: fixed; inset: 0; z-index: 50; background: transparent; }
|
|
1724
1724
|
& .crisp-command-positioner { position: fixed; inset: 0; z-index: 51; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); }
|
|
@@ -1860,7 +1860,7 @@
|
|
|
1860
1860
|
}
|
|
1861
1861
|
|
|
1862
1862
|
.crisp-navitem {
|
|
1863
|
-
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5)
|
|
1863
|
+
box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding-inline: var(--crisp-space-2); padding-inline-end: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; line-height: 20px; color: var(--crisp-fg-primary); text-decoration: none; cursor: pointer; user-select: none; width: 100%; transition: background-color 0.16s;
|
|
1864
1864
|
& .crisp-navitem-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1865
1865
|
& .crisp-navitem-trailing { margin-inline-start: auto; display: inline-flex; align-items: center; flex-shrink: 0; }
|
|
1866
1866
|
&:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); }
|
|
@@ -2381,7 +2381,7 @@
|
|
|
2381
2381
|
}
|
|
2382
2382
|
|
|
2383
2383
|
.crisp-sidebar {
|
|
2384
|
-
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar)
|
|
2384
|
+
box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: var(--crisp-bg-sidebar); border-inline-end: 1px solid var(--crisp-border-subtle); padding: var(--crisp-space-2); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-y: auto;
|
|
2385
2385
|
& .crisp-sidebar-section { display: flex; flex-direction: column; gap: var(--crisp-space-px); margin-top: var(--crisp-space-2); }
|
|
2386
2386
|
& .crisp-sidebar-section[data-collapsed] { display: none; }
|
|
2387
2387
|
& .crisp-sidebar-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); }
|
package/dist/textarea.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.crisp-textarea {
|
|
2
2
|
position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
|
|
3
|
-
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-
|
|
3
|
+
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-2-5) 14px; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
4
4
|
& .crisp-textarea-field::placeholder { color: var(--crisp-fg-muted); }
|
|
5
5
|
& .crisp-textarea-field:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
6
6
|
/* seed-design's focus treatment, same as Input: a 2px ring on an ::after
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: var(--crisp-bg-danger); }
|
|
17
17
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: var(--crisp-bg-danger); }
|
|
18
18
|
&[data-counted] .crisp-textarea-field { padding-bottom: var(--crisp-space-5); }
|
|
19
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end:
|
|
19
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: var(--crisp-space-2-5); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); pointer-events: none; }
|
|
20
20
|
& .crisp-textarea-count[data-over] { color: var(--crisp-bg-danger); }
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-textarea {
|
|
3
3
|
position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
|
|
4
|
-
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-
|
|
4
|
+
& .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-2-5) 14px; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
5
5
|
& .crisp-textarea-field::placeholder { color: var(--crisp-fg-muted); }
|
|
6
6
|
& .crisp-textarea-field:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
7
7
|
/* seed-design's focus treatment, same as Input: a 2px ring on an ::after
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: var(--crisp-bg-danger); }
|
|
18
18
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: var(--crisp-bg-danger); }
|
|
19
19
|
&[data-counted] .crisp-textarea-field { padding-bottom: var(--crisp-space-5); }
|
|
20
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end:
|
|
20
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: var(--crisp-space-2-5); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); pointer-events: none; }
|
|
21
21
|
& .crisp-textarea-count[data-over] { color: var(--crisp-bg-danger); }
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
1
|
+
declare const spec: {
|
|
2
|
+
readonly name: "avatargroup";
|
|
3
|
+
readonly base: {
|
|
4
|
+
readonly display: "inline-flex";
|
|
5
|
+
readonly alignItems: "center";
|
|
6
|
+
};
|
|
7
|
+
readonly parts: [{
|
|
8
|
+
readonly slot: "member";
|
|
9
|
+
readonly selector: "& > *";
|
|
10
|
+
readonly decls: {
|
|
11
|
+
readonly marginInlineStart: `-var(--${string})`;
|
|
12
|
+
readonly boxShadow: `0 0 0 2px var(--${string})`;
|
|
13
|
+
readonly position: "relative";
|
|
14
|
+
};
|
|
15
|
+
}, {
|
|
16
|
+
readonly slot: "member";
|
|
17
|
+
readonly selector: "& > *:first-child";
|
|
18
|
+
readonly decls: {
|
|
19
|
+
readonly marginInlineStart: 0;
|
|
20
|
+
};
|
|
21
|
+
}];
|
|
22
|
+
};
|
|
23
|
+
declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
24
|
+
export declare const avatargroupRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>>;
|
|
25
|
+
export { spec };
|
|
3
26
|
export default config;
|
|
@@ -1,5 +1,53 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
declare const spec: {
|
|
2
|
+
readonly name: "checkbox";
|
|
3
|
+
readonly base: {
|
|
4
|
+
readonly appearance: "none";
|
|
5
|
+
readonly WebkitAppearance: "none";
|
|
6
|
+
readonly boxSizing: "border-box";
|
|
7
|
+
readonly flexShrink: 0;
|
|
8
|
+
readonly margin: 0;
|
|
9
|
+
readonly width: "16px";
|
|
10
|
+
readonly height: "16px";
|
|
11
|
+
readonly border: "none";
|
|
12
|
+
readonly borderRadius: `var(--${string})`;
|
|
13
|
+
readonly background: `var(--${string})`;
|
|
14
|
+
readonly boxShadow: `inset 0 0 0 1px color-mix(in srgb, var(--${string}) 9%, transparent)`;
|
|
15
|
+
readonly cursor: "pointer";
|
|
16
|
+
readonly transition: "background-color .12s, box-shadow .12s";
|
|
17
|
+
};
|
|
18
|
+
readonly parts: [{
|
|
19
|
+
readonly selector: "&:hover";
|
|
20
|
+
readonly decls: {
|
|
21
|
+
readonly boxShadow: `inset 0 0 0 1px color-mix(in srgb, var(--${string}) 30%, transparent)`;
|
|
22
|
+
};
|
|
23
|
+
}, {
|
|
24
|
+
readonly selector: "&:checked, &:indeterminate";
|
|
25
|
+
readonly decls: {
|
|
26
|
+
readonly background: `var(--${string}) center / 10px no-repeat`;
|
|
27
|
+
readonly boxShadow: "none";
|
|
28
|
+
};
|
|
29
|
+
}, {
|
|
30
|
+
readonly selector: "&:checked";
|
|
31
|
+
readonly decls: {
|
|
32
|
+
backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E\")";
|
|
33
|
+
};
|
|
34
|
+
}, {
|
|
35
|
+
readonly selector: "&:indeterminate";
|
|
36
|
+
readonly decls: {
|
|
37
|
+
readonly backgroundImage: `linear-gradient(var(--${string}), var(--${string}))`;
|
|
38
|
+
readonly backgroundSize: "8px 2px";
|
|
39
|
+
};
|
|
40
|
+
}, {
|
|
41
|
+
readonly selector: "&:focus-visible";
|
|
42
|
+
readonly decls: {
|
|
43
|
+
readonly outline: `2px solid var(--${string})`;
|
|
44
|
+
readonly outlineOffset: "1px";
|
|
45
|
+
};
|
|
46
|
+
}];
|
|
4
47
|
};
|
|
48
|
+
declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
49
|
+
export declare const checkboxRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>> & {
|
|
50
|
+
config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
51
|
+
};
|
|
52
|
+
export { spec };
|
|
5
53
|
export default config;
|
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
declare const spec: {
|
|
2
|
+
readonly name: "kbd";
|
|
3
|
+
readonly base: {
|
|
4
|
+
readonly boxSizing: "border-box";
|
|
5
|
+
readonly display: "inline-flex";
|
|
6
|
+
readonly alignItems: "center";
|
|
7
|
+
readonly justifyContent: "center";
|
|
8
|
+
readonly gap: `var(--${string})`;
|
|
9
|
+
readonly minWidth: "20px";
|
|
10
|
+
readonly height: "20px";
|
|
11
|
+
readonly paddingInline: `var(--${string})`;
|
|
12
|
+
readonly borderRadius: `var(--${string})`;
|
|
13
|
+
readonly fontFamily: `var(--${string})`;
|
|
14
|
+
readonly fontSize: `var(--${string})`;
|
|
15
|
+
readonly fontWeight: `var(--${string})`;
|
|
16
|
+
readonly color: `color-mix(in srgb, var(--${string}) 50%, transparent)`;
|
|
17
|
+
readonly background: "transparent";
|
|
18
|
+
readonly boxShadow: `inset 0 0 0 1px color-mix(in srgb, var(--${string}) 6%, transparent)`;
|
|
19
|
+
};
|
|
4
20
|
};
|
|
21
|
+
declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
22
|
+
export declare const kbdRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>> & {
|
|
23
|
+
config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
24
|
+
};
|
|
25
|
+
export { spec };
|
|
5
26
|
export default config;
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
1
|
+
declare const spec: {
|
|
2
|
+
readonly name: "skeleton";
|
|
3
|
+
readonly globalCss: "@keyframes crisp-skeleton { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }";
|
|
4
|
+
readonly base: {
|
|
5
|
+
readonly display: "block";
|
|
6
|
+
readonly background: `color-mix(in srgb, var(--${string}) 8%, transparent)`;
|
|
7
|
+
readonly borderRadius: `var(--${string})`;
|
|
8
|
+
readonly animation: "crisp-skeleton 1.4s ease-in-out infinite";
|
|
9
|
+
};
|
|
10
|
+
readonly parts: [{
|
|
11
|
+
readonly selector: "&[data-circle]";
|
|
12
|
+
readonly decls: {
|
|
13
|
+
borderRadius: `var(--${string})`;
|
|
14
|
+
};
|
|
15
|
+
}];
|
|
16
|
+
};
|
|
17
|
+
declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
18
|
+
export declare const skeletonRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>>;
|
|
19
|
+
export { spec };
|
|
3
20
|
export default config;
|
|
@@ -1,3 +1,31 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
1
|
+
declare const spec: {
|
|
2
|
+
readonly name: "tooltip";
|
|
3
|
+
readonly base: {
|
|
4
|
+
readonly position: "fixed";
|
|
5
|
+
readonly zIndex: 70;
|
|
6
|
+
readonly boxSizing: "border-box";
|
|
7
|
+
readonly pointerEvents: "none";
|
|
8
|
+
readonly maxWidth: "260px";
|
|
9
|
+
readonly padding: `var(--${string}) var(--${string})`;
|
|
10
|
+
readonly borderRadius: `var(--${string})`;
|
|
11
|
+
readonly background: "#20242c";
|
|
12
|
+
readonly color: "#fff";
|
|
13
|
+
readonly fontFamily: `var(--${string})`;
|
|
14
|
+
readonly fontSize: `var(--${string})`;
|
|
15
|
+
readonly fontWeight: `var(--${string})`;
|
|
16
|
+
readonly lineHeight: "1.45";
|
|
17
|
+
readonly boxShadow: "0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04)";
|
|
18
|
+
readonly opacity: 1;
|
|
19
|
+
readonly transition: "opacity 0.12s ease";
|
|
20
|
+
};
|
|
21
|
+
readonly parts: [{
|
|
22
|
+
readonly selector: "@starting-style";
|
|
23
|
+
readonly decls: {
|
|
24
|
+
readonly opacity: 0;
|
|
25
|
+
};
|
|
26
|
+
}];
|
|
27
|
+
};
|
|
28
|
+
declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
|
|
29
|
+
export declare const tooltipRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>>;
|
|
30
|
+
export { spec };
|
|
3
31
|
export default config;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crisp-ui-kit/crisp",
|
|
3
|
-
"version": "0.33.
|
|
3
|
+
"version": "0.33.5",
|
|
4
4
|
"description": "A lean React design system, measured pixel-for-pixel from Attio.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -413,6 +413,7 @@
|
|
|
413
413
|
"build:css": "bun run build.mjs",
|
|
414
414
|
"build:types": "tsc -p tsconfig.build.json",
|
|
415
415
|
"build": "bun run build:css && bun run build:tokens && bun run build:esm && bun run build:cjs && bun run build:types",
|
|
416
|
+
"audit:docs": "bun scripts/audit-docs.mjs",
|
|
416
417
|
"check:css": "bun scripts/check-css.mjs",
|
|
417
418
|
"check:dist": "bun scripts/check-dist.mjs",
|
|
418
419
|
"prepublishOnly": "bun run build && bun run check:dist && bun run check:css",
|