@crisp-ui-kit/crisp 0.26.1 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/multiselect/multiselect.js +3 -2
- package/dist/cjs/components/radiogroup/radiogroup.js +2 -2
- package/dist/cjs/components/select/select.js +2 -2
- package/dist/cjs/components/switch/switch.js +9 -9
- package/dist/esm/components/multiselect/multiselect.js +3 -2
- package/dist/esm/components/radiogroup/radiogroup.js +2 -2
- package/dist/esm/components/select/select.js +2 -2
- package/dist/esm/components/switch/switch.js +10 -10
- package/dist/types/components/multiselect/multiselect.d.ts +8 -1
- package/dist/types/components/radiogroup/radiogroup.d.ts +6 -0
- package/dist/types/components/select/select.d.ts +8 -1
- package/dist/types/components/switch/switch.d.ts +18 -0
- package/package.json +1 -1
|
@@ -65,7 +65,7 @@ function Chip({ opt }) {
|
|
|
65
65
|
* popover with a search field over rows of "[chip] … [checkbox]" that toggle on
|
|
66
66
|
* click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
|
|
67
67
|
*/
|
|
68
|
-
function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
68
|
+
function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, name, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
69
69
|
// Virtual focus: DOM focus stays on the trigger (or the search field) and the
|
|
70
70
|
// active option is named by `aria-activedescendant`. `data-highlighted` is a
|
|
71
71
|
// styling hook only -- a screen reader cannot see it.
|
|
@@ -154,7 +154,8 @@ function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, p
|
|
|
154
154
|
document.addEventListener("pointerdown", onDown);
|
|
155
155
|
return () => document.removeEventListener("pointerdown", onDown);
|
|
156
156
|
}, [open]);
|
|
157
|
-
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, multiselect_recipe_1.multiselectRecipe)({ className }), ...rest, children: [
|
|
157
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, multiselect_recipe_1.multiselectRecipe)({ className }), ...rest, children: [name &&
|
|
158
|
+
value.map((v) => (0, jsx_runtime_1.jsx)("input", { type: "hidden", name: name, value: v }, v)), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "multiselect-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: (e) => {
|
|
158
159
|
if (open && !searchable)
|
|
159
160
|
onNavKeys(e);
|
|
160
161
|
else if (!open && (e.key === "ArrowDown" || e.key === "Enter")) {
|
|
@@ -57,7 +57,7 @@ function RadioGroup({ value, defaultValue, onValueChange, variant = "default", n
|
|
|
57
57
|
onValueChange?.(v);
|
|
58
58
|
}, [value, onValueChange]);
|
|
59
59
|
const { onKeyDown, groupRef } = (0, radiogroup_keyboard_1.useRadioGroupKeyboard)({ select });
|
|
60
|
-
return ((0, jsx_runtime_1.jsx)(RadioContext.Provider, { value: { value: selected, select, name: groupName, variant }, children: (0, jsx_runtime_1.
|
|
60
|
+
return ((0, jsx_runtime_1.jsx)(RadioContext.Provider, { value: { value: selected, select, name: groupName, variant }, children: (0, jsx_runtime_1.jsxs)("div", { ref: groupRef, role: "radiogroup", "aria-label": ariaLabel, "data-variant": variant, className: (0, radiogroup_recipe_1.radiogroupRecipe)({ className }), ...rest, onKeyDown: onKeyDown, children: [children, name && selected && ((0, jsx_runtime_1.jsx)("input", { type: "hidden", name: name, value: selected }))] }) }));
|
|
61
61
|
}
|
|
62
62
|
RadioGroup.displayName = "RadioGroup";
|
|
63
63
|
function Radio({ value, label, children, disabled }) {
|
|
@@ -65,7 +65,7 @@ function Radio({ value, label, children, disabled }) {
|
|
|
65
65
|
if (!ctx)
|
|
66
66
|
throw new Error("Radio must be used within a RadioGroup");
|
|
67
67
|
const checked = ctx.value === value;
|
|
68
|
-
return ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "radio", "aria-checked": checked,
|
|
68
|
+
return ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "radio", "aria-checked": checked, disabled: disabled, "data-value": value, "data-checked": checked ? "" : undefined,
|
|
69
69
|
// roving tabindex: the selected (or first, when none) radio is tabbable
|
|
70
70
|
tabIndex: checked ? 0 : -1, className: "radio", onClick: () => ctx.select(value), children: ctx.variant === "card" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "radio-preview", children: children }), (0, jsx_runtime_1.jsx)("span", { className: "radio-cardfoot", children: (0, jsx_runtime_1.jsx)("span", { className: "radio-label", children: label }) })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "radio-dot" }), (0, jsx_runtime_1.jsx)("span", { className: "radio-label", children: label ?? children })] })) }));
|
|
71
71
|
}
|
|
@@ -52,7 +52,7 @@ function optionText(o) {
|
|
|
52
52
|
* "No results" empty state). Full keyboard model — ↑/↓ move, Enter/Space pick,
|
|
53
53
|
* Esc close, Home/End jump, opens upward when tight. Controlled or uncontrolled.
|
|
54
54
|
*/
|
|
55
|
-
function Select({ options, value, defaultValue, onValueChange, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
55
|
+
function Select({ options, value, defaultValue, onValueChange, name, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
56
56
|
// Virtual focus: DOM focus stays on the trigger (or the search field), and the
|
|
57
57
|
// active option is named by `aria-activedescendant`. That is the only thing a
|
|
58
58
|
// screen reader has to go on -- `data-highlighted` is invisible to it -- so the
|
|
@@ -150,7 +150,7 @@ function Select({ options, value, defaultValue, onValueChange, placeholder = "Se
|
|
|
150
150
|
document.addEventListener("pointerdown", onDown);
|
|
151
151
|
return () => document.removeEventListener("pointerdown", onDown);
|
|
152
152
|
}, [open]);
|
|
153
|
-
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, select_recipe_1.selectRecipe)({ variant, width, className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsxs)("div", { id: listboxId, className: "select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
153
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, select_recipe_1.selectRecipe)({ variant, width, className }), ...rest, children: [name && selected && (0, jsx_runtime_1.jsx)("input", { type: "hidden", name: name, value: selected }), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsxs)("div", { id: listboxId, className: "select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
154
154
|
setQuery(e.target.value);
|
|
155
155
|
setHighlight(0);
|
|
156
156
|
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "select-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
@@ -43,16 +43,16 @@ const switch_recipe_1 = require("./switch.recipe");
|
|
|
43
43
|
* label association and keyboard (Space/Enter) come for free. Controlled via
|
|
44
44
|
* `checked`/`onCheckedChange`, or uncontrolled from `defaultChecked`.
|
|
45
45
|
*/
|
|
46
|
-
exports.Switch = React.forwardRef(({ checked, defaultChecked = false, onCheckedChange, disabled, className, onClick, ...props }, ref) => {
|
|
46
|
+
exports.Switch = React.forwardRef(({ checked, defaultChecked = false, onCheckedChange, disabled, className, onClick, name, value = "on", ...props }, ref) => {
|
|
47
47
|
const [internal, setInternal] = React.useState(defaultChecked);
|
|
48
48
|
const on = checked ?? internal;
|
|
49
|
-
return ((0, jsx_runtime_1.jsx)("button", { ref: ref, type: "button", role: "switch", "aria-checked": on, disabled: disabled, "data-checked": on ? "" : undefined, className: (0, switch_recipe_1.switchRecipe)({ className }), onClick: (e) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { ref: ref, type: "button", role: "switch", "aria-checked": on, disabled: disabled, "data-checked": on ? "" : undefined, className: (0, switch_recipe_1.switchRecipe)({ className }), onClick: (e) => {
|
|
50
|
+
onClick?.(e);
|
|
51
|
+
if (e.defaultPrevented)
|
|
52
|
+
return;
|
|
53
|
+
if (checked === undefined)
|
|
54
|
+
setInternal((v) => !v);
|
|
55
|
+
onCheckedChange?.(!on);
|
|
56
|
+
}, ...props, children: (0, jsx_runtime_1.jsx)("span", { className: "switch-thumb" }) }), name && on && (0, jsx_runtime_1.jsx)("input", { type: "hidden", name: name, value: value })] }));
|
|
57
57
|
});
|
|
58
58
|
exports.Switch.displayName = "Switch";
|
|
@@ -29,7 +29,7 @@ function Chip({ opt }) {
|
|
|
29
29
|
* popover with a search field over rows of "[chip] … [checkbox]" that toggle on
|
|
30
30
|
* click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
|
|
31
31
|
*/
|
|
32
|
-
export function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
32
|
+
export function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, name, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
33
33
|
// Virtual focus: DOM focus stays on the trigger (or the search field) and the
|
|
34
34
|
// active option is named by `aria-activedescendant`. `data-highlighted` is a
|
|
35
35
|
// styling hook only -- a screen reader cannot see it.
|
|
@@ -118,7 +118,8 @@ export function MultiSelect({ options, value: valueProp, defaultValue, onValueCh
|
|
|
118
118
|
document.addEventListener("pointerdown", onDown);
|
|
119
119
|
return () => document.removeEventListener("pointerdown", onDown);
|
|
120
120
|
}, [open]);
|
|
121
|
-
return (_jsxs("div", { ref: rootRef, className: multiselectRecipe({ className }), ...rest, children: [
|
|
121
|
+
return (_jsxs("div", { ref: rootRef, className: multiselectRecipe({ className }), ...rest, children: [name &&
|
|
122
|
+
value.map((v) => _jsx("input", { type: "hidden", name: name, value: v }, v)), _jsxs("button", { ref: triggerRef, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "multiselect-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: (e) => {
|
|
122
123
|
if (open && !searchable)
|
|
123
124
|
onNavKeys(e);
|
|
124
125
|
else if (!open && (e.key === "ArrowDown" || e.key === "Enter")) {
|
|
@@ -20,7 +20,7 @@ export function RadioGroup({ value, defaultValue, onValueChange, variant = "defa
|
|
|
20
20
|
onValueChange?.(v);
|
|
21
21
|
}, [value, onValueChange]);
|
|
22
22
|
const { onKeyDown, groupRef } = useRadioGroupKeyboard({ select });
|
|
23
|
-
return (_jsx(RadioContext.Provider, { value: { value: selected, select, name: groupName, variant }, children:
|
|
23
|
+
return (_jsx(RadioContext.Provider, { value: { value: selected, select, name: groupName, variant }, children: _jsxs("div", { ref: groupRef, role: "radiogroup", "aria-label": ariaLabel, "data-variant": variant, className: radiogroupRecipe({ className }), ...rest, onKeyDown: onKeyDown, children: [children, name && selected && (_jsx("input", { type: "hidden", name: name, value: selected }))] }) }));
|
|
24
24
|
}
|
|
25
25
|
RadioGroup.displayName = "RadioGroup";
|
|
26
26
|
export function Radio({ value, label, children, disabled }) {
|
|
@@ -28,7 +28,7 @@ export function Radio({ value, label, children, disabled }) {
|
|
|
28
28
|
if (!ctx)
|
|
29
29
|
throw new Error("Radio must be used within a RadioGroup");
|
|
30
30
|
const checked = ctx.value === value;
|
|
31
|
-
return (_jsx("button", { type: "button", role: "radio", "aria-checked": checked,
|
|
31
|
+
return (_jsx("button", { type: "button", role: "radio", "aria-checked": checked, disabled: disabled, "data-value": value, "data-checked": checked ? "" : undefined,
|
|
32
32
|
// roving tabindex: the selected (or first, when none) radio is tabbable
|
|
33
33
|
tabIndex: checked ? 0 : -1, className: "radio", onClick: () => ctx.select(value), children: ctx.variant === "card" ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "radio-preview", children: children }), _jsx("span", { className: "radio-cardfoot", children: _jsx("span", { className: "radio-label", children: label }) })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "radio-dot" }), _jsx("span", { className: "radio-label", children: label ?? children })] })) }));
|
|
34
34
|
}
|
|
@@ -16,7 +16,7 @@ function optionText(o) {
|
|
|
16
16
|
* "No results" empty state). Full keyboard model — ↑/↓ move, Enter/Space pick,
|
|
17
17
|
* Esc close, Home/End jump, opens upward when tight. Controlled or uncontrolled.
|
|
18
18
|
*/
|
|
19
|
-
export function Select({ options, value, defaultValue, onValueChange, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
19
|
+
export function Select({ options, value, defaultValue, onValueChange, name, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
20
20
|
// Virtual focus: DOM focus stays on the trigger (or the search field), and the
|
|
21
21
|
// active option is named by `aria-activedescendant`. That is the only thing a
|
|
22
22
|
// screen reader has to go on -- `data-highlighted` is invisible to it -- so the
|
|
@@ -114,7 +114,7 @@ export function Select({ options, value, defaultValue, onValueChange, placeholde
|
|
|
114
114
|
document.addEventListener("pointerdown", onDown);
|
|
115
115
|
return () => document.removeEventListener("pointerdown", onDown);
|
|
116
116
|
}, [open]);
|
|
117
|
-
return (_jsxs("div", { ref: rootRef, className: selectRecipe({ variant, width, className }), ...rest, children: [_jsxs("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [_jsx("span", { className: "select-value", children: selectedOption ? (_jsxs(_Fragment, { children: [selectedOption.color && (_jsx("span", { className: "select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : (_jsx("span", { className: "select-placeholder", children: placeholder })) }), _jsx(ChevronDown, { className: "select-chevron", size: 16, strokeWidth: 1.75 })] }), open && (_jsxs("div", { id: listboxId, className: "select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && (_jsxs("div", { className: "select-search", children: [_jsx(Search, { size: 15, strokeWidth: 1.75 }), _jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
117
|
+
return (_jsxs("div", { ref: rootRef, className: selectRecipe({ variant, width, className }), ...rest, children: [name && selected && _jsx("input", { type: "hidden", name: name, value: selected }), _jsxs("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [_jsx("span", { className: "select-value", children: selectedOption ? (_jsxs(_Fragment, { children: [selectedOption.color && (_jsx("span", { className: "select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : (_jsx("span", { className: "select-placeholder", children: placeholder })) }), _jsx(ChevronDown, { className: "select-chevron", size: 16, strokeWidth: 1.75 })] }), open && (_jsxs("div", { id: listboxId, className: "select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && (_jsxs("div", { className: "select-search", children: [_jsx(Search, { size: 15, strokeWidth: 1.75 }), _jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
118
118
|
setQuery(e.target.value);
|
|
119
119
|
setHighlight(0);
|
|
120
120
|
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? (_jsx("div", { className: "select-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { switchRecipe } from "./switch.recipe.js";
|
|
4
4
|
/**
|
|
@@ -7,16 +7,16 @@ import { switchRecipe } from "./switch.recipe.js";
|
|
|
7
7
|
* label association and keyboard (Space/Enter) come for free. Controlled via
|
|
8
8
|
* `checked`/`onCheckedChange`, or uncontrolled from `defaultChecked`.
|
|
9
9
|
*/
|
|
10
|
-
export const Switch = React.forwardRef(({ checked, defaultChecked = false, onCheckedChange, disabled, className, onClick, ...props }, ref) => {
|
|
10
|
+
export const Switch = React.forwardRef(({ checked, defaultChecked = false, onCheckedChange, disabled, className, onClick, name, value = "on", ...props }, ref) => {
|
|
11
11
|
const [internal, setInternal] = React.useState(defaultChecked);
|
|
12
12
|
const on = checked ?? internal;
|
|
13
|
-
return (_jsx("button", { ref: ref, type: "button", role: "switch", "aria-checked": on, disabled: disabled, "data-checked": on ? "" : undefined, className: switchRecipe({ className }), onClick: (e) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { ref: ref, type: "button", role: "switch", "aria-checked": on, disabled: disabled, "data-checked": on ? "" : undefined, className: switchRecipe({ className }), onClick: (e) => {
|
|
14
|
+
onClick?.(e);
|
|
15
|
+
if (e.defaultPrevented)
|
|
16
|
+
return;
|
|
17
|
+
if (checked === undefined)
|
|
18
|
+
setInternal((v) => !v);
|
|
19
|
+
onCheckedChange?.(!on);
|
|
20
|
+
}, ...props, children: _jsx("span", { className: "switch-thumb" }) }), name && on && _jsx("input", { type: "hidden", name: name, value: value })] }));
|
|
21
21
|
});
|
|
22
22
|
Switch.displayName = "Switch";
|
|
@@ -11,6 +11,13 @@ export type MultiSelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "valu
|
|
|
11
11
|
value?: string[];
|
|
12
12
|
/** Initially selected values, when uncontrolled. */
|
|
13
13
|
defaultValue?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Submits with the form under this name. crisp's MultiSelect is a
|
|
16
|
+
* a button and a listbox, not a native control, so the value reaches a `<form>` only through a
|
|
17
|
+
* hidden input — without one, `name` type-checks, renders, and contributes
|
|
18
|
+
* nothing to FormData (#243).
|
|
19
|
+
*/
|
|
20
|
+
name?: string;
|
|
14
21
|
/** Fires with the next value on every toggle, controlled or not. */
|
|
15
22
|
onValueChange?: (value: string[]) => void;
|
|
16
23
|
placeholder?: string;
|
|
@@ -29,7 +36,7 @@ export type MultiSelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "valu
|
|
|
29
36
|
* popover with a search field over rows of "[chip] … [checkbox]" that toggle on
|
|
30
37
|
* click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
|
|
31
38
|
*/
|
|
32
|
-
export declare function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, emptyLabel, disabled, className, "aria-label": ariaLabel, ...rest }: MultiSelectProps): React.JSX.Element;
|
|
39
|
+
export declare function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, name, placeholder, searchable, searchPlaceholder, emptyLabel, disabled, className, "aria-label": ariaLabel, ...rest }: MultiSelectProps): React.JSX.Element;
|
|
33
40
|
export declare namespace MultiSelect {
|
|
34
41
|
var displayName: string;
|
|
35
42
|
}
|
|
@@ -6,6 +6,12 @@ export type RadioGroupProps = Omit<React.ComponentPropsWithoutRef<"div">, "value
|
|
|
6
6
|
onValueChange?: (value: string) => void;
|
|
7
7
|
/** `default` = rows (dot + label); `card` = bordered preview cards. */
|
|
8
8
|
variant?: "default" | "card";
|
|
9
|
+
/**
|
|
10
|
+
* Submits with the form under this name. It used to land on each radio
|
|
11
|
+
* `<button>`, where a name means nothing unless the button submits the form:
|
|
12
|
+
* the prop type-checked, rendered, and contributed nothing to FormData
|
|
13
|
+
* (#243). The value now rides on a hidden input.
|
|
14
|
+
*/
|
|
9
15
|
name?: string;
|
|
10
16
|
className?: string;
|
|
11
17
|
"aria-label"?: string;
|
|
@@ -14,6 +14,13 @@ export type SelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "value" |
|
|
|
14
14
|
value?: string;
|
|
15
15
|
defaultValue?: string;
|
|
16
16
|
onValueChange?: (value: string) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Submits with the form under this name. crisp's Select is a
|
|
19
|
+
* a button and a listbox, not a native control, so the value reaches a `<form>` only through a
|
|
20
|
+
* hidden input — without one, `name` type-checks, renders, and contributes
|
|
21
|
+
* nothing to FormData (#243).
|
|
22
|
+
*/
|
|
23
|
+
name?: string;
|
|
17
24
|
placeholder?: string;
|
|
18
25
|
/** `ghost` is borderless until hover — for inline selects (e.g. sort rows). */
|
|
19
26
|
variant?: "default" | "ghost";
|
|
@@ -36,7 +43,7 @@ export type SelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "value" |
|
|
|
36
43
|
* "No results" empty state). Full keyboard model — ↑/↓ move, Enter/Space pick,
|
|
37
44
|
* Esc close, Home/End jump, opens upward when tight. Controlled or uncontrolled.
|
|
38
45
|
*/
|
|
39
|
-
export declare function Select({ options, value, defaultValue, onValueChange, placeholder, variant, width, searchable, searchPlaceholder, emptyLabel, disabled, className, "aria-label": ariaLabel, id, ...rest }: SelectProps): React.JSX.Element;
|
|
46
|
+
export declare function Select({ options, value, defaultValue, onValueChange, name, placeholder, variant, width, searchable, searchPlaceholder, emptyLabel, disabled, className, "aria-label": ariaLabel, id, ...rest }: SelectProps): React.JSX.Element;
|
|
40
47
|
export declare namespace Select {
|
|
41
48
|
var displayName: string;
|
|
42
49
|
}
|
|
@@ -4,6 +4,15 @@ export type SwitchProps = Omit<React.ComponentPropsWithoutRef<"button">, "onChan
|
|
|
4
4
|
checked?: boolean;
|
|
5
5
|
defaultChecked?: boolean;
|
|
6
6
|
onCheckedChange?: (checked: boolean) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Submits with the form under this name. crisp's Switch is a
|
|
9
|
+
* `role="switch"` button`, not a native control, so the value reaches a `<form>` only
|
|
10
|
+
* through a hidden input — without one, `name` type-checks, renders, and
|
|
11
|
+
* contributes nothing to FormData (#243).
|
|
12
|
+
*/
|
|
13
|
+
name?: string;
|
|
14
|
+
/** The value submitted when it is on (default "on", as a checkbox does). */
|
|
15
|
+
value?: string;
|
|
7
16
|
};
|
|
8
17
|
/**
|
|
9
18
|
* An on/off toggle — the settings switch, measured from Attio. Renders a
|
|
@@ -16,4 +25,13 @@ export declare const Switch: React.ForwardRefExoticComponent<Omit<Omit<React.Det
|
|
|
16
25
|
checked?: boolean;
|
|
17
26
|
defaultChecked?: boolean;
|
|
18
27
|
onCheckedChange?: (checked: boolean) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Submits with the form under this name. crisp's Switch is a
|
|
30
|
+
* `role="switch"` button`, not a native control, so the value reaches a `<form>` only
|
|
31
|
+
* through a hidden input — without one, `name` type-checks, renders, and
|
|
32
|
+
* contributes nothing to FormData (#243).
|
|
33
|
+
*/
|
|
34
|
+
name?: string;
|
|
35
|
+
/** The value submitted when it is on (default "on", as a checkbox does). */
|
|
36
|
+
value?: string;
|
|
19
37
|
} & React.RefAttributes<HTMLButtonElement>>;
|