@crisp-ui-kit/crisp 0.29.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,14 +37,71 @@ exports.FormField = FormField;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const React = __importStar(require("react"));
39
39
  const formfield_recipe_1 = require("./formfield.recipe");
40
+ /**
41
+ * Tell the control what the field knows about it.
42
+ *
43
+ * FormField renders the label, the description, the error and the required
44
+ * marker; the control is an opaque child, so the three facts that belong ON
45
+ * the control have to be put there by id — the same id the label already
46
+ * points at with `htmlFor`.
47
+ *
48
+ * Until now none of them were. The message got an id and nobody referenced it,
49
+ * `aria-invalid` was never set, and the required asterisk was `aria-hidden`
50
+ * decoration with no `aria-required` behind it: a screen-reader user heard the
51
+ * label and nothing else (#251).
52
+ *
53
+ * Only what this put there is taken back, so a consumer's own
54
+ * `aria-describedby` survives both ways.
55
+ */
56
+ function useFieldWiring({ htmlFor, messageId, invalid, required, }) {
57
+ React.useEffect(() => {
58
+ if (!htmlFor)
59
+ return;
60
+ const el = document.getElementById(htmlFor);
61
+ if (!el)
62
+ return;
63
+ const previous = el.getAttribute("aria-describedby");
64
+ if (messageId) {
65
+ const ids = previous ? previous.split(/\s+/) : [];
66
+ if (!ids.includes(messageId))
67
+ el.setAttribute("aria-describedby", [...ids, messageId].join(" "));
68
+ }
69
+ if (invalid)
70
+ el.setAttribute("aria-invalid", "true");
71
+ if (required)
72
+ el.setAttribute("aria-required", "true");
73
+ return () => {
74
+ if (messageId) {
75
+ const now = (el.getAttribute("aria-describedby") ?? "")
76
+ .split(/\s+/)
77
+ .filter((id) => id && id !== messageId);
78
+ if (now.length)
79
+ el.setAttribute("aria-describedby", now.join(" "));
80
+ else
81
+ el.removeAttribute("aria-describedby");
82
+ }
83
+ if (invalid)
84
+ el.removeAttribute("aria-invalid");
85
+ if (required)
86
+ el.removeAttribute("aria-required");
87
+ };
88
+ }, [htmlFor, messageId, invalid, required]);
89
+ }
40
90
  /**
41
91
  * A labelled form field — measured from Attio's Settings forms. A muted label
42
92
  * above any control, with an optional description or error line below. When
43
- * `error` is set, it replaces the description and gets `aria-describedby` wiring.
93
+ * `error` is set it replaces the description; the control is given
94
+ * `aria-describedby`, plus `aria-invalid` and `aria-required` as they apply.
44
95
  */
45
96
  function FormField({ label, htmlFor, required, optional, description, error, children, className, ...rest }) {
46
97
  const descId = React.useId();
47
98
  const hasMsg = error != null || description != null;
99
+ useFieldWiring({
100
+ htmlFor,
101
+ messageId: hasMsg ? descId : undefined,
102
+ invalid: error != null,
103
+ required: !!required,
104
+ });
48
105
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, formfield_recipe_1.formfieldRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("label", { className: "formfield-label", htmlFor: htmlFor, children: [label, required && ((0, jsx_runtime_1.jsx)("span", { className: "formfield-required", "aria-hidden": "true", children: "*" })), optional && !required && ((0, jsx_runtime_1.jsx)("span", { className: "formfield-optional", children: optional === true ? "(optional)" : optional }))] }), children, error != null ? ((0, jsx_runtime_1.jsx)("span", { className: "formfield-error", id: hasMsg ? descId : undefined, children: error })) : description != null ? ((0, jsx_runtime_1.jsx)("span", { className: "formfield-desc", id: hasMsg ? descId : undefined, children: description })) : null] }));
49
106
  }
50
107
  FormField.displayName = "FormField";
@@ -1,22 +1,105 @@
1
1
  "use strict";
2
2
  "use client";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
37
  exports.Slider = Slider;
5
38
  const jsx_runtime_1 = require("react/jsx-runtime");
6
39
  const react_slider_1 = require("@seed-design/react-slider");
40
+ const React = __importStar(require("react"));
7
41
  const slider_recipe_1 = require("./slider.recipe");
8
- /** Reads the thumb count from the engine rather than re-deriving it from props. */
9
- function Thumbs() {
42
+ const defaultThumbLabel = (index, count) => count === 2 ? (index === 0 ? "minimum" : "maximum") : String(index + 1);
43
+ /**
44
+ * The name for each thumb.
45
+ *
46
+ * The thumbs are what carry `role="slider"`, and they are rendered by the
47
+ * engine, so a name put on the crisp root — an `aria-label` prop, or a
48
+ * FormField's `<label for>` — reached nothing at all (#248). The root is a
49
+ * plain `<div>`: it has no role to name.
50
+ *
51
+ * Both sources are threaded down. Text (`aria-label`) is composed directly;
52
+ * an id reference (`aria-labelledby`, or the label found for the root's own
53
+ * id) is concatenated with a hidden span per thumb, because that is how ARIA
54
+ * builds a name out of parts without inventing a second copy of the label.
55
+ */
56
+ function Thumbs({ ariaLabel, labelledBy, thumbLabel, idBase, }) {
10
57
  const { values } = (0, react_slider_1.useSliderContext)();
11
- return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: values.map((_, i) => ((0, jsx_runtime_1.jsx)(react_slider_1.SliderThumb
12
- // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional — the index IS the identity the engine addresses them by
13
- , { thumbIndex: i, className: "slider-thumb" }, i))) }));
58
+ const count = values.length;
59
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: values.map((_, i) => {
60
+ const suffix = count > 1 ? thumbLabel(i, count) : undefined;
61
+ const suffixId = `${idBase}-thumb-${i}`;
62
+ return ((0, jsx_runtime_1.jsxs)(React.Fragment
63
+ // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional — the index IS the identity the engine addresses them by
64
+ , { children: [suffix && labelledBy && !ariaLabel && ((0, jsx_runtime_1.jsx)("span", { id: suffixId, className: "slider-thumblabel", children: suffix })), (0, jsx_runtime_1.jsx)(react_slider_1.SliderThumb, { thumbIndex: i, className: "slider-thumb", "aria-label": ariaLabel
65
+ ? suffix
66
+ ? `${ariaLabel} ${suffix}`
67
+ : ariaLabel
68
+ : undefined, "aria-labelledby": !ariaLabel && labelledBy
69
+ ? suffix
70
+ ? `${labelledBy} ${suffixId}`
71
+ : labelledBy
72
+ : undefined })] }, i));
73
+ }) }));
14
74
  }
15
75
  /**
16
76
  * A range input, driven by seed-design's headless engine so the keyboard model,
17
77
  * the pointer maths and multi-thumb constraints are theirs by construction.
18
78
  * Pass one value for a simple slider, two for a range.
19
79
  */
20
- function Slider({ className, ...props }) {
21
- return ((0, jsx_runtime_1.jsx)(react_slider_1.SliderRoot, { ...props, className: (0, slider_recipe_1.sliderRecipe)({ className }), children: (0, jsx_runtime_1.jsxs)("span", { className: "slider-control", children: [(0, jsx_runtime_1.jsx)("span", { className: "slider-track", children: (0, jsx_runtime_1.jsx)(react_slider_1.SliderRange, { className: "slider-range" }) }), (0, jsx_runtime_1.jsx)(Thumbs, {})] }) }));
80
+ function Slider({ className, thumbLabel = defaultThumbLabel, ...props }) {
81
+ const rootRef = React.useRef(null);
82
+ const generatedId = React.useId();
83
+ const idBase = props.id ?? generatedId;
84
+ const ariaLabel = props["aria-label"];
85
+ const labelledByProp = props["aria-labelledby"];
86
+ // A `<label for>` on the root cannot name a div, and the thumbs are what
87
+ // need the name, so the label is resolved from the DOM and threaded down.
88
+ // State rather than a mutation: the thumbs take props, so the name belongs
89
+ // in the render rather than being written over it afterwards.
90
+ const [foundLabelId, setFoundLabelId] = React.useState();
91
+ React.useEffect(() => {
92
+ if (ariaLabel || labelledByProp)
93
+ return;
94
+ const el = rootRef.current;
95
+ if (!el?.id)
96
+ return;
97
+ const label = el.ownerDocument.querySelector(`label[for="${CSS.escape(el.id)}"]`);
98
+ if (!label)
99
+ return;
100
+ if (!label.id)
101
+ label.id = `${el.id}-label`;
102
+ setFoundLabelId(label.id);
103
+ }, [ariaLabel, labelledByProp]);
104
+ return ((0, jsx_runtime_1.jsx)(react_slider_1.SliderRoot, { ref: rootRef, ...props, className: (0, slider_recipe_1.sliderRecipe)({ className }), children: (0, jsx_runtime_1.jsxs)("span", { className: "slider-control", children: [(0, jsx_runtime_1.jsx)("span", { className: "slider-track", children: (0, jsx_runtime_1.jsx)(react_slider_1.SliderRange, { className: "slider-range" }) }), (0, jsx_runtime_1.jsx)(Thumbs, { ariaLabel: ariaLabel, labelledBy: labelledByProp ?? foundLabelId, thumbLabel: thumbLabel, idBase: idBase })] }) }));
22
105
  }
@@ -27,6 +27,12 @@ const config = (0, core_1.defineRecipe)({
27
27
  width: 100%; min-width: 0; gap: ${tokens_1.contract.space["0.5"]};
28
28
  font-family: ${tokens_1.contract.font.sans}; user-select: none; touch-action: none;
29
29
 
30
+ /* the position words ("minimum"/"maximum") that ARIA concatenates onto a
31
+ thumb's name — read, never drawn */
32
+ & .slider-thumblabel {
33
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
34
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
35
+ }
30
36
  & .slider-control { position: relative; height: 26px; display: flex; align-items: center; }
31
37
 
32
38
  & .slider-track {
@@ -1,14 +1,71 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { formfieldRecipe } from "./formfield.recipe.js";
4
+ /**
5
+ * Tell the control what the field knows about it.
6
+ *
7
+ * FormField renders the label, the description, the error and the required
8
+ * marker; the control is an opaque child, so the three facts that belong ON
9
+ * the control have to be put there by id — the same id the label already
10
+ * points at with `htmlFor`.
11
+ *
12
+ * Until now none of them were. The message got an id and nobody referenced it,
13
+ * `aria-invalid` was never set, and the required asterisk was `aria-hidden`
14
+ * decoration with no `aria-required` behind it: a screen-reader user heard the
15
+ * label and nothing else (#251).
16
+ *
17
+ * Only what this put there is taken back, so a consumer's own
18
+ * `aria-describedby` survives both ways.
19
+ */
20
+ function useFieldWiring({ htmlFor, messageId, invalid, required, }) {
21
+ React.useEffect(() => {
22
+ if (!htmlFor)
23
+ return;
24
+ const el = document.getElementById(htmlFor);
25
+ if (!el)
26
+ return;
27
+ const previous = el.getAttribute("aria-describedby");
28
+ if (messageId) {
29
+ const ids = previous ? previous.split(/\s+/) : [];
30
+ if (!ids.includes(messageId))
31
+ el.setAttribute("aria-describedby", [...ids, messageId].join(" "));
32
+ }
33
+ if (invalid)
34
+ el.setAttribute("aria-invalid", "true");
35
+ if (required)
36
+ el.setAttribute("aria-required", "true");
37
+ return () => {
38
+ if (messageId) {
39
+ const now = (el.getAttribute("aria-describedby") ?? "")
40
+ .split(/\s+/)
41
+ .filter((id) => id && id !== messageId);
42
+ if (now.length)
43
+ el.setAttribute("aria-describedby", now.join(" "));
44
+ else
45
+ el.removeAttribute("aria-describedby");
46
+ }
47
+ if (invalid)
48
+ el.removeAttribute("aria-invalid");
49
+ if (required)
50
+ el.removeAttribute("aria-required");
51
+ };
52
+ }, [htmlFor, messageId, invalid, required]);
53
+ }
4
54
  /**
5
55
  * A labelled form field — measured from Attio's Settings forms. A muted label
6
56
  * above any control, with an optional description or error line below. When
7
- * `error` is set, it replaces the description and gets `aria-describedby` wiring.
57
+ * `error` is set it replaces the description; the control is given
58
+ * `aria-describedby`, plus `aria-invalid` and `aria-required` as they apply.
8
59
  */
9
60
  export function FormField({ label, htmlFor, required, optional, description, error, children, className, ...rest }) {
10
61
  const descId = React.useId();
11
62
  const hasMsg = error != null || description != null;
63
+ useFieldWiring({
64
+ htmlFor,
65
+ messageId: hasMsg ? descId : undefined,
66
+ invalid: error != null,
67
+ required: !!required,
68
+ });
12
69
  return (_jsxs("div", { className: formfieldRecipe({ className }), ...rest, children: [_jsxs("label", { className: "formfield-label", htmlFor: htmlFor, children: [label, required && (_jsx("span", { className: "formfield-required", "aria-hidden": "true", children: "*" })), optional && !required && (_jsx("span", { className: "formfield-optional", children: optional === true ? "(optional)" : optional }))] }), children, error != null ? (_jsx("span", { className: "formfield-error", id: hasMsg ? descId : undefined, children: error })) : description != null ? (_jsx("span", { className: "formfield-desc", id: hasMsg ? descId : undefined, children: description })) : null] }));
13
70
  }
14
71
  FormField.displayName = "FormField";
@@ -1,19 +1,69 @@
1
1
  "use client";
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { SliderRange, SliderRoot, SliderThumb, useSliderContext, } from "@seed-design/react-slider";
4
+ import * as React from "react";
4
5
  import { sliderRecipe } from "./slider.recipe.js";
5
- /** Reads the thumb count from the engine rather than re-deriving it from props. */
6
- function Thumbs() {
6
+ const defaultThumbLabel = (index, count) => count === 2 ? (index === 0 ? "minimum" : "maximum") : String(index + 1);
7
+ /**
8
+ * The name for each thumb.
9
+ *
10
+ * The thumbs are what carry `role="slider"`, and they are rendered by the
11
+ * engine, so a name put on the crisp root — an `aria-label` prop, or a
12
+ * FormField's `<label for>` — reached nothing at all (#248). The root is a
13
+ * plain `<div>`: it has no role to name.
14
+ *
15
+ * Both sources are threaded down. Text (`aria-label`) is composed directly;
16
+ * an id reference (`aria-labelledby`, or the label found for the root's own
17
+ * id) is concatenated with a hidden span per thumb, because that is how ARIA
18
+ * builds a name out of parts without inventing a second copy of the label.
19
+ */
20
+ function Thumbs({ ariaLabel, labelledBy, thumbLabel, idBase, }) {
7
21
  const { values } = useSliderContext();
8
- return (_jsx(_Fragment, { children: values.map((_, i) => (_jsx(SliderThumb
9
- // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional — the index IS the identity the engine addresses them by
10
- , { thumbIndex: i, className: "slider-thumb" }, i))) }));
22
+ const count = values.length;
23
+ return (_jsx(_Fragment, { children: values.map((_, i) => {
24
+ const suffix = count > 1 ? thumbLabel(i, count) : undefined;
25
+ const suffixId = `${idBase}-thumb-${i}`;
26
+ return (_jsxs(React.Fragment
27
+ // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional — the index IS the identity the engine addresses them by
28
+ , { children: [suffix && labelledBy && !ariaLabel && (_jsx("span", { id: suffixId, className: "slider-thumblabel", children: suffix })), _jsx(SliderThumb, { thumbIndex: i, className: "slider-thumb", "aria-label": ariaLabel
29
+ ? suffix
30
+ ? `${ariaLabel} ${suffix}`
31
+ : ariaLabel
32
+ : undefined, "aria-labelledby": !ariaLabel && labelledBy
33
+ ? suffix
34
+ ? `${labelledBy} ${suffixId}`
35
+ : labelledBy
36
+ : undefined })] }, i));
37
+ }) }));
11
38
  }
12
39
  /**
13
40
  * A range input, driven by seed-design's headless engine so the keyboard model,
14
41
  * the pointer maths and multi-thumb constraints are theirs by construction.
15
42
  * Pass one value for a simple slider, two for a range.
16
43
  */
17
- export function Slider({ className, ...props }) {
18
- return (_jsx(SliderRoot, { ...props, className: sliderRecipe({ className }), children: _jsxs("span", { className: "slider-control", children: [_jsx("span", { className: "slider-track", children: _jsx(SliderRange, { className: "slider-range" }) }), _jsx(Thumbs, {})] }) }));
44
+ export function Slider({ className, thumbLabel = defaultThumbLabel, ...props }) {
45
+ const rootRef = React.useRef(null);
46
+ const generatedId = React.useId();
47
+ const idBase = props.id ?? generatedId;
48
+ const ariaLabel = props["aria-label"];
49
+ const labelledByProp = props["aria-labelledby"];
50
+ // A `<label for>` on the root cannot name a div, and the thumbs are what
51
+ // need the name, so the label is resolved from the DOM and threaded down.
52
+ // State rather than a mutation: the thumbs take props, so the name belongs
53
+ // in the render rather than being written over it afterwards.
54
+ const [foundLabelId, setFoundLabelId] = React.useState();
55
+ React.useEffect(() => {
56
+ if (ariaLabel || labelledByProp)
57
+ return;
58
+ const el = rootRef.current;
59
+ if (!el?.id)
60
+ return;
61
+ const label = el.ownerDocument.querySelector(`label[for="${CSS.escape(el.id)}"]`);
62
+ if (!label)
63
+ return;
64
+ if (!label.id)
65
+ label.id = `${el.id}-label`;
66
+ setFoundLabelId(label.id);
67
+ }, [ariaLabel, labelledByProp]);
68
+ return (_jsx(SliderRoot, { ref: rootRef, ...props, className: sliderRecipe({ className }), children: _jsxs("span", { className: "slider-control", children: [_jsx("span", { className: "slider-track", children: _jsx(SliderRange, { className: "slider-range" }) }), _jsx(Thumbs, { ariaLabel: ariaLabel, labelledBy: labelledByProp ?? foundLabelId, thumbLabel: thumbLabel, idBase: idBase })] }) }));
19
69
  }
@@ -24,6 +24,12 @@ const config = defineRecipe({
24
24
  width: 100%; min-width: 0; gap: ${c.space["0.5"]};
25
25
  font-family: ${c.font.sans}; user-select: none; touch-action: none;
26
26
 
27
+ /* the position words ("minimum"/"maximum") that ARIA concatenates onto a
28
+ thumb's name — read, never drawn */
29
+ & .slider-thumblabel {
30
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
31
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
32
+ }
27
33
  & .slider-control { position: relative; height: 26px; display: flex; align-items: center; }
28
34
 
29
35
  & .slider-track {
package/dist/slider.css CHANGED
@@ -4,6 +4,12 @@
4
4
  width: 100%; min-width: 0; gap: var(--space-0-5);
5
5
  font-family: var(--font-sans); user-select: none; touch-action: none;
6
6
 
7
+ /* the position words ("minimum"/"maximum") that ARIA concatenates onto a
8
+ thumb's name — read, never drawn */
9
+ & .slider-thumblabel {
10
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
11
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
12
+ }
7
13
  & .slider-control { position: relative; height: 26px; display: flex; align-items: center; }
8
14
 
9
15
  & .slider-track {
package/dist/styles.css CHANGED
@@ -1454,6 +1454,12 @@
1454
1454
  width: 100%; min-width: 0; gap: var(--space-0-5);
1455
1455
  font-family: var(--font-sans); user-select: none; touch-action: none;
1456
1456
 
1457
+ /* the position words ("minimum"/"maximum") that ARIA concatenates onto a
1458
+ thumb's name — read, never drawn */
1459
+ & .slider-thumblabel {
1460
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
1461
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
1462
+ }
1457
1463
  & .slider-control { position: relative; height: 26px; display: flex; align-items: center; }
1458
1464
 
1459
1465
  & .slider-track {
@@ -21,7 +21,8 @@ export type FormFieldProps = React.ComponentPropsWithoutRef<"div"> & {
21
21
  /**
22
22
  * A labelled form field — measured from Attio's Settings forms. A muted label
23
23
  * above any control, with an optional description or error line below. When
24
- * `error` is set, it replaces the description and gets `aria-describedby` wiring.
24
+ * `error` is set it replaces the description; the control is given
25
+ * `aria-describedby`, plus `aria-invalid` and `aria-required` as they apply.
25
26
  */
26
27
  export declare function FormField({ label, htmlFor, required, optional, description, error, children, className, ...rest }: FormFieldProps): React.JSX.Element;
27
28
  export declare namespace FormField {
@@ -1,8 +1,19 @@
1
1
  import { type SliderRootProps } from "@seed-design/react-slider";
2
- export type SliderProps = SliderRootProps;
2
+ import * as React from "react";
3
+ export type SliderProps = SliderRootProps & {
4
+ /**
5
+ * What to call each thumb when there is more than one, appended to the
6
+ * slider's own name: "Price minimum" / "Price maximum".
7
+ *
8
+ * A formatter, not two strings, because the number of thumbs is open-ended
9
+ * and the words differ by language. With a single thumb it is never called —
10
+ * the thumb simply takes the slider's name.
11
+ */
12
+ thumbLabel?: (index: number, count: number) => string;
13
+ };
3
14
  /**
4
15
  * A range input, driven by seed-design's headless engine so the keyboard model,
5
16
  * the pointer maths and multi-thumb constraints are theirs by construction.
6
17
  * Pass one value for a simple slider, two for a range.
7
18
  */
8
- export declare function Slider({ className, ...props }: SliderProps): import("react").JSX.Element;
19
+ export declare function Slider({ className, thumbLabel, ...props }: SliderProps): React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.29.0",
3
+ "version": "0.30.1",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",