@neo4j-ndl/react 4.22.0 → 4.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/_common/form-control/FormFieldLabel.js +52 -0
- package/lib/cjs/_common/form-control/FormFieldLabel.js.map +1 -0
- package/lib/cjs/_common/form-control/FormFieldMessage.js +72 -0
- package/lib/cjs/_common/form-control/FormFieldMessage.js.map +1 -0
- package/lib/cjs/_common/form-control/form-control-types.js +23 -0
- package/lib/cjs/_common/form-control/form-control-types.js.map +1 -0
- package/lib/cjs/_common/form-control/index.js +39 -0
- package/lib/cjs/_common/form-control/index.js.map +1 -0
- package/lib/cjs/_common/form-control/use-form-field.js +76 -0
- package/lib/cjs/_common/form-control/use-form-field.js.map +1 -0
- package/lib/cjs/_common/form-control/use-inline-field-label.js +76 -0
- package/lib/cjs/_common/form-control/use-inline-field-label.js.map +1 -0
- package/lib/cjs/checkbox/Checkbox.js +9 -21
- package/lib/cjs/checkbox/Checkbox.js.map +1 -1
- package/lib/cjs/combobox/Combobox.js +61 -45
- package/lib/cjs/combobox/Combobox.js.map +1 -1
- package/lib/cjs/combobox/combobox-types.js.map +1 -1
- package/lib/cjs/date-picker/DatePicker.js.map +1 -1
- package/lib/cjs/radio/Radio.js +12 -22
- package/lib/cjs/radio/Radio.js.map +1 -1
- package/lib/cjs/select/Select.js +14 -5
- package/lib/cjs/select/Select.js.map +1 -1
- package/lib/cjs/select/types.js.map +1 -1
- package/lib/cjs/switch/Switch.js +9 -21
- package/lib/cjs/switch/Switch.js.map +1 -1
- package/lib/cjs/text-area/TextArea.js +8 -21
- package/lib/cjs/text-area/TextArea.js.map +1 -1
- package/lib/cjs/text-input/TextInput.js +15 -38
- package/lib/cjs/text-input/TextInput.js.map +1 -1
- package/lib/cjs/text-input/stories/text-input-sizes.story.js +1 -1
- package/lib/cjs/text-input/stories/text-input-sizes.story.js.map +1 -1
- package/lib/cjs/time-picker/TimePicker.js +13 -7
- package/lib/cjs/time-picker/TimePicker.js.map +1 -1
- package/lib/cjs/timezone-picker/TimeZonePicker.js +13 -7
- package/lib/cjs/timezone-picker/TimeZonePicker.js.map +1 -1
- package/lib/esm/_common/form-control/FormFieldLabel.js +45 -0
- package/lib/esm/_common/form-control/FormFieldLabel.js.map +1 -0
- package/lib/esm/_common/form-control/FormFieldMessage.js +65 -0
- package/lib/esm/_common/form-control/FormFieldMessage.js.map +1 -0
- package/lib/esm/_common/form-control/form-control-types.js +22 -0
- package/lib/esm/_common/form-control/form-control-types.js.map +1 -0
- package/lib/esm/_common/form-control/index.js +32 -0
- package/lib/esm/_common/form-control/index.js.map +1 -0
- package/lib/esm/_common/form-control/use-form-field.js +72 -0
- package/lib/esm/_common/form-control/use-form-field.js.map +1 -0
- package/lib/esm/_common/form-control/use-inline-field-label.js +69 -0
- package/lib/esm/_common/form-control/use-inline-field-label.js.map +1 -0
- package/lib/esm/checkbox/Checkbox.js +10 -22
- package/lib/esm/checkbox/Checkbox.js.map +1 -1
- package/lib/esm/combobox/Combobox.js +62 -46
- package/lib/esm/combobox/Combobox.js.map +1 -1
- package/lib/esm/combobox/combobox-types.js.map +1 -1
- package/lib/esm/date-picker/DatePicker.js.map +1 -1
- package/lib/esm/radio/Radio.js +12 -22
- package/lib/esm/radio/Radio.js.map +1 -1
- package/lib/esm/select/Select.js +14 -5
- package/lib/esm/select/Select.js.map +1 -1
- package/lib/esm/select/types.js.map +1 -1
- package/lib/esm/switch/Switch.js +10 -22
- package/lib/esm/switch/Switch.js.map +1 -1
- package/lib/esm/text-area/TextArea.js +9 -22
- package/lib/esm/text-area/TextArea.js.map +1 -1
- package/lib/esm/text-input/TextInput.js +15 -38
- package/lib/esm/text-input/TextInput.js.map +1 -1
- package/lib/esm/text-input/stories/text-input-sizes.story.js +1 -1
- package/lib/esm/text-input/stories/text-input-sizes.story.js.map +1 -1
- package/lib/esm/time-picker/TimePicker.js +13 -7
- package/lib/esm/time-picker/TimePicker.js.map +1 -1
- package/lib/esm/timezone-picker/TimeZonePicker.js +13 -7
- package/lib/esm/timezone-picker/TimeZonePicker.js.map +1 -1
- package/lib/types/_common/form-control/FormFieldLabel.d.ts +58 -0
- package/lib/types/_common/form-control/FormFieldLabel.d.ts.map +1 -0
- package/lib/types/_common/form-control/FormFieldMessage.d.ts +46 -0
- package/lib/types/_common/form-control/FormFieldMessage.d.ts.map +1 -0
- package/lib/types/_common/form-control/form-control-types.d.ts +92 -0
- package/lib/types/_common/form-control/form-control-types.d.ts.map +1 -0
- package/lib/types/_common/form-control/index.d.ts +33 -0
- package/lib/types/_common/form-control/index.d.ts.map +1 -0
- package/lib/types/_common/form-control/use-form-field.d.ts +53 -0
- package/lib/types/_common/form-control/use-form-field.d.ts.map +1 -0
- package/lib/types/_common/form-control/use-inline-field-label.d.ts +69 -0
- package/lib/types/_common/form-control/use-inline-field-label.d.ts.map +1 -0
- package/lib/types/checkbox/Checkbox.d.ts.map +1 -1
- package/lib/types/combobox/Combobox.d.ts.map +1 -1
- package/lib/types/combobox/combobox-types.d.ts +1 -1
- package/lib/types/combobox/combobox-types.d.ts.map +1 -1
- package/lib/types/radio/Radio.d.ts.map +1 -1
- package/lib/types/select/Select.d.ts.map +1 -1
- package/lib/types/select/types.d.ts.map +1 -1
- package/lib/types/switch/Switch.d.ts.map +1 -1
- package/lib/types/text-area/TextArea.d.ts.map +1 -1
- package/lib/types/text-input/TextInput.d.ts.map +1 -1
- package/lib/types/text-input/stories/text-input-sizes.story.d.ts.map +1 -1
- package/lib/types/time-picker/TimePicker.d.ts +3 -1
- package/lib/types/time-picker/TimePicker.d.ts.map +1 -1
- package/lib/types/timezone-picker/TimeZonePicker.d.ts +4 -1
- package/lib/types/timezone-picker/TimeZonePicker.d.ts.map +1 -1
- package/package.json +2 -2
- package/skills/ndl-react/SKILL.md +1 -1
- package/skills/ndl-react/components/color-picker.md +32 -32
- package/skills/ndl-react/components/combobox.md +2 -2
- package/skills/ndl-react/components/dropzone.md +5 -5
- package/skills/ndl-react/components/next/tree-view.md +87 -1
- package/skills/ndl-react/components/text-input.md +6 -0
- package/skills/ndl-react/components/time-picker.md +1 -0
- package/skills/ndl-react/components/timezone-picker.md +1 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) "Neo4j"
|
|
5
|
+
* Neo4j Sweden AB [http://neo4j.com]
|
|
6
|
+
*
|
|
7
|
+
* This file is part of Neo4j.
|
|
8
|
+
*
|
|
9
|
+
* Neo4j is free software: you can redistribute it and/or modify
|
|
10
|
+
* it under the terms of the GNU General Public License as published by
|
|
11
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
* (at your option) any later version.
|
|
13
|
+
*
|
|
14
|
+
* This program is distributed in the hope that it will be useful,
|
|
15
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
* GNU General Public License for more details.
|
|
18
|
+
*
|
|
19
|
+
* You should have received a copy of the GNU General Public License
|
|
20
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
*/
|
|
22
|
+
import classNames from 'classnames';
|
|
23
|
+
import { useEffect, useMemo } from 'react';
|
|
24
|
+
import { needleWarningMessage } from '../utils';
|
|
25
|
+
/**
|
|
26
|
+
* The inline label of a selection control (Checkbox, Radio, Switch): the
|
|
27
|
+
* control and its label text sit side by side inside one wrapping `<label>`.
|
|
28
|
+
* A string label is wrapped in a titled span so a truncated label stays
|
|
29
|
+
* readable through its tooltip; a custom node is rendered as given, which is
|
|
30
|
+
* also why it needs `ariaLabel` to name the control. Warns when neither a
|
|
31
|
+
* label nor an aria label is provided.
|
|
32
|
+
*
|
|
33
|
+
* Returns the classes of the wrapping label along with its content, so that
|
|
34
|
+
* an adopting component never names a shared class itself. Typography and
|
|
35
|
+
* color sit on that wrapper rather than on the span here, so that a custom
|
|
36
|
+
* node inherits them too.
|
|
37
|
+
*
|
|
38
|
+
* TODO v5: wrap a custom node as well, once the DOM of a custom label can
|
|
39
|
+
* change. This would own all label styling, and the difference between the
|
|
40
|
+
* full-strength inline label and the weaker label above a control could then
|
|
41
|
+
* become a prop here instead of living in two stylesheet classes.
|
|
42
|
+
*/
|
|
43
|
+
export const useInlineFieldLabel = ({ componentName, label, ariaLabel, isDisabled = false, isFluid = false, isTruncated = false, }) => {
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (Boolean(label) === false && Boolean(ariaLabel) === false) {
|
|
46
|
+
needleWarningMessage(`${componentName} needs a valid label or ariaLabel: https://dequeuniversity.com/rules/axe/4.2/label?application=axeAPI`);
|
|
47
|
+
}
|
|
48
|
+
}, [ariaLabel, componentName, label]);
|
|
49
|
+
const labelContent = useMemo(() => {
|
|
50
|
+
if (Boolean(label) === false) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (typeof label === 'string') {
|
|
54
|
+
return (_jsx("span", { title: label, className: classNames('ndl-form-inline-label-text', {
|
|
55
|
+
'ndl-disabled': isDisabled,
|
|
56
|
+
'ndl-truncated': isTruncated,
|
|
57
|
+
}), children: label }));
|
|
58
|
+
}
|
|
59
|
+
return label;
|
|
60
|
+
}, [label, isDisabled, isTruncated]);
|
|
61
|
+
return {
|
|
62
|
+
fieldClassName: classNames('ndl-form-inline-field', {
|
|
63
|
+
'ndl-disabled': isDisabled,
|
|
64
|
+
'ndl-fluid': isFluid,
|
|
65
|
+
}),
|
|
66
|
+
labelContent,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=use-inline-field-label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-inline-field-label.js","sourceRoot":"","sources":["../../../../src/_common/form-control/use-inline-field-label.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAmChD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,EAClC,aAAa,EACb,KAAK,EACL,SAAS,EACT,UAAU,GAAG,KAAK,EAClB,OAAO,GAAG,KAAK,EACf,WAAW,GAAG,KAAK,GACQ,EAA6B,EAAE;IAC1D,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,KAAK,EAAE,CAAC;YAC7D,oBAAoB,CAClB,GAAG,aAAa,uGAAuG,CACxH,CAAC;QACJ,CAAC;IACH,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC;IAEtC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,CACL,eACE,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,UAAU,CAAC,4BAA4B,EAAE;oBAClD,cAAc,EAAE,UAAU;oBAC1B,eAAe,EAAE,WAAW;iBAC7B,CAAC,YAED,KAAK,GACD,CACR,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IAErC,OAAO;QACL,cAAc,EAAE,UAAU,CAAC,uBAAuB,EAAE;YAClD,cAAc,EAAE,UAAU;YAC1B,WAAW,EAAE,OAAO;SACrB,CAAC;QACF,YAAY;KACb,CAAC;AACJ,CAAC,CAAC","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport classNames from 'classnames';\nimport { useEffect, useMemo } from 'react';\n\nimport { needleWarningMessage } from '../utils';\n\ntype UseInlineFieldLabelOptions = {\n /** Component name used in the missing-label console warning. */\n componentName: string;\n /** The label rendered next to the control, inside its wrapping `<label>`. */\n label: React.ReactNode;\n // TODO v5: only needed while naming a selection control is split across\n // `label` and `ariaLabel`. Whenever the label alone names the control, as it\n // does on Combobox, the warning below has nothing left to check.\n /** The aria label naming the control when the label is not a string. */\n ariaLabel: string | undefined;\n /** Dims the label and the control it belongs to. */\n isDisabled?: boolean;\n /** Lets the field take the full available width of its container. */\n isFluid?: boolean;\n /**\n * Truncates an overflowing string label to one line with an ellipsis (the\n * `title` set on the span keeps the full text reachable). Radio behavior\n * today; a candidate to unify across all selection controls in v5.\n */\n isTruncated?: boolean;\n};\n\ntype UseInlineFieldLabelResult = {\n /** The label, rendered next to the control. */\n labelContent: React.ReactNode;\n /**\n * Classes for the `<label>` that wraps the control and the label, which the\n * adopting component renders. Owned here so that the shared classes stay an\n * implementation detail of this module.\n */\n fieldClassName: string;\n};\n\n/**\n * The inline label of a selection control (Checkbox, Radio, Switch): the\n * control and its label text sit side by side inside one wrapping `<label>`.\n * A string label is wrapped in a titled span so a truncated label stays\n * readable through its tooltip; a custom node is rendered as given, which is\n * also why it needs `ariaLabel` to name the control. Warns when neither a\n * label nor an aria label is provided.\n *\n * Returns the classes of the wrapping label along with its content, so that\n * an adopting component never names a shared class itself. Typography and\n * color sit on that wrapper rather than on the span here, so that a custom\n * node inherits them too.\n *\n * TODO v5: wrap a custom node as well, once the DOM of a custom label can\n * change. This would own all label styling, and the difference between the\n * full-strength inline label and the weaker label above a control could then\n * become a prop here instead of living in two stylesheet classes.\n */\nexport const useInlineFieldLabel = ({\n componentName,\n label,\n ariaLabel,\n isDisabled = false,\n isFluid = false,\n isTruncated = false,\n}: UseInlineFieldLabelOptions): UseInlineFieldLabelResult => {\n useEffect(() => {\n if (Boolean(label) === false && Boolean(ariaLabel) === false) {\n needleWarningMessage(\n `${componentName} needs a valid label or ariaLabel: https://dequeuniversity.com/rules/axe/4.2/label?application=axeAPI`,\n );\n }\n }, [ariaLabel, componentName, label]);\n\n const labelContent = useMemo(() => {\n if (Boolean(label) === false) {\n return null;\n }\n\n if (typeof label === 'string') {\n return (\n <span\n title={label}\n className={classNames('ndl-form-inline-label-text', {\n 'ndl-disabled': isDisabled,\n 'ndl-truncated': isTruncated,\n })}\n >\n {label}\n </span>\n );\n }\n\n return label;\n }, [label, isDisabled, isTruncated]);\n\n return {\n fieldClassName: classNames('ndl-form-inline-field', {\n 'ndl-disabled': isDisabled,\n 'ndl-fluid': isFluid,\n }),\n labelContent,\n };\n};\n"]}
|
|
@@ -32,9 +32,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
32
32
|
*/
|
|
33
33
|
import { useMergeRefs } from '@floating-ui/react';
|
|
34
34
|
import classNames from 'classnames';
|
|
35
|
-
import { useEffect,
|
|
35
|
+
import { useEffect, useRef } from 'react';
|
|
36
|
+
import { useInlineFieldLabel } from '../_common/form-control';
|
|
36
37
|
import { useSemiControlledInput } from '../_common/use-semi-controlled-input';
|
|
37
|
-
import { needleWarningMessage } from '../_common/utils';
|
|
38
38
|
export const Checkbox = (_a) => {
|
|
39
39
|
var _b;
|
|
40
40
|
var { isChecked, isDefaultChecked, isIndeterminate = false, isFluid = false, isDisabled = false, hasLeadingLabel, ariaLabel, onChange, onClick, value, label, className, style, htmlAttributes, ref } = _a, restProps = __rest(_a, ["isChecked", "isDefaultChecked", "isIndeterminate", "isFluid", "isDisabled", "hasLeadingLabel", "ariaLabel", "onChange", "onClick", "value", "label", "className", "style", "htmlAttributes", "ref"]);
|
|
@@ -46,10 +46,6 @@ export const Checkbox = (_a) => {
|
|
|
46
46
|
onChange,
|
|
47
47
|
state: (_b = isChecked !== null && isChecked !== void 0 ? isChecked : isDefaultChecked) !== null && _b !== void 0 ? _b : false,
|
|
48
48
|
});
|
|
49
|
-
const labelWrappingClasses = classNames('ndl-checkbox-label', {
|
|
50
|
-
'ndl-disabled': isDisabled,
|
|
51
|
-
'ndl-fluid': isFluid,
|
|
52
|
-
});
|
|
53
49
|
const checkboxClasses = classNames('ndl-checkbox', className, {
|
|
54
50
|
'ndl-disabled': isDisabled,
|
|
55
51
|
});
|
|
@@ -58,21 +54,13 @@ export const Checkbox = (_a) => {
|
|
|
58
54
|
internalRef.current.indeterminate = isIndeterminate;
|
|
59
55
|
}
|
|
60
56
|
}, [isIndeterminate]);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return label;
|
|
70
|
-
}, [label]);
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (Boolean(label) === false && Boolean(ariaLabel) === false) {
|
|
73
|
-
needleWarningMessage('Checkbox needs a valid label or ariaLabel: https://dequeuniversity.com/rules/axe/4.2/label?application=axeAPI');
|
|
74
|
-
}
|
|
75
|
-
}, [ariaLabel, label]);
|
|
76
|
-
return (_jsxs("label", { className: labelWrappingClasses, children: [hasLeadingLabel === true && labelContent, _jsx("input", Object.assign({ ref: mergedRefs, type: "checkbox", "aria-label": ariaLabel, disabled: isDisabled, checked: isCheckedState, onClick: onClick, onChange: internalOnChange, value: value, style: style, className: checkboxClasses }, restProps, htmlAttributes)), Boolean(hasLeadingLabel) === false && labelContent] }));
|
|
57
|
+
const { labelContent, fieldClassName } = useInlineFieldLabel({
|
|
58
|
+
ariaLabel,
|
|
59
|
+
componentName: 'Checkbox',
|
|
60
|
+
isDisabled,
|
|
61
|
+
isFluid,
|
|
62
|
+
label,
|
|
63
|
+
});
|
|
64
|
+
return (_jsxs("label", { className: fieldClassName, children: [hasLeadingLabel === true && labelContent, _jsx("input", Object.assign({ ref: mergedRefs, type: "checkbox", "aria-label": ariaLabel, disabled: isDisabled, checked: isCheckedState, onClick: onClick, onChange: internalOnChange, value: value, style: style, className: checkboxClasses }, restProps, htmlAttributes)), Boolean(hasLeadingLabel) === false && labelContent] }));
|
|
77
65
|
};
|
|
78
66
|
//# sourceMappingURL=Checkbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../../src/checkbox/Checkbox.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../../src/checkbox/Checkbox.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAuC9E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAiBa,EAAE,EAAE;;QAjBjB,EACvB,SAAS,EACT,gBAAgB,EAChB,eAAe,GAAG,KAAK,EACvB,OAAO,GAAG,KAAK,EACf,UAAU,GAAG,KAAK,EAClB,eAAe,EACf,SAAS,EACT,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,SAAS,EACT,KAAK,EACL,cAAc,EACd,GAAG,OAEiC,EADjC,SAAS,cAhBW,qMAiBxB,CADa;IAEZ,MAAM,WAAW,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;IAEpD,MAAM,CAAC,cAAc,EAAE,gBAAgB,CAAC,GAAG,sBAAsB,CAAC;QAChE,SAAS,EAAE,UAAU;QACrB,YAAY,EAAE,SAAS,KAAK,SAAS;QACrC,QAAQ;QACR,KAAK,EAAE,MAAA,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,gBAAgB,mCAAI,KAAK;KAC9C,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,UAAU,CAAC,cAAc,EAAE,SAAS,EAAE;QAC5D,cAAc,EAAE,UAAU;KAC3B,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACxB,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC;QACtD,CAAC;IACH,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,mBAAmB,CAAC;QAC3D,SAAS;QACT,aAAa,EAAE,UAAU;QACzB,UAAU;QACV,OAAO;QACP,KAAK;KACN,CAAC,CAAC;IAEH,OAAO,CACL,iBAAO,SAAS,EAAE,cAAc,aAC7B,eAAe,KAAK,IAAI,IAAI,YAAY,EACzC,8BACE,GAAG,EAAE,UAAU,EACf,IAAI,EAAC,UAAU,gBACH,SAAS,EACrB,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,eAAe,IACtB,SAAS,EACT,cAAc,EAClB,EACD,OAAO,CAAC,eAAe,CAAC,KAAK,KAAK,IAAI,YAAY,IAC7C,CACT,CAAC;AACJ,CAAC,CAAC","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport { useMergeRefs } from '@floating-ui/react';\nimport classNames from 'classnames';\nimport { useEffect, useRef } from 'react';\n\nimport { useInlineFieldLabel } from '../_common/form-control';\nimport { type CommonProps, type HtmlAttributes } from '../_common/types';\nimport { useSemiControlledInput } from '../_common/use-semi-controlled-input';\n\ntype CheckboxProps = {\n /** Whether the checkbox is checked */\n isChecked?: boolean;\n /** Checks the input by default in uncontrolled mode */\n isDefaultChecked?: boolean; // isDefaultChecked sounds weird to me?\n /** Whether the checkbox is indeterminate */\n isIndeterminate?: boolean;\n /** Whether the checkbox is disabled */\n isDisabled?: boolean;\n /** Whether the checkbox & label should take the full available width of its container */\n isFluid?: boolean;\n /** The value of the checkbox */\n value?: HtmlAttributes<'input'>['value']; // should this be required?\n /** Whether the label should be displayed before the checkbox */\n hasLeadingLabel?: boolean;\n /** The callback function triggered when the checkbox value changes */\n onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;\n /** The callback function triggered when the checkbox is clicked */\n onClick?: (event: React.MouseEvent<HTMLInputElement>) => void;\n} & (\n | {\n /** The aria label of the checkbox. Required if the label is not a string. */\n ariaLabel: string;\n // TODO v5: inconsistent naming. Naming the checkbox takes a union of\n // `label` and `ariaLabel`, so the accessible name comes from a different\n // prop depending on what the label renders. Combobox instead requires the\n // label and clips it with `hasVisuallyHiddenLabel`, and TextInput reaches\n // for htmlAttributes['aria-label'].\n /** The label of the checkbox. If not a string, supply an aria label for screen reader support. */\n label?: undefined;\n }\n | {\n ariaLabel?: string;\n label: React.ReactNode;\n }\n);\n\nexport const Checkbox = ({\n isChecked,\n isDefaultChecked,\n isIndeterminate = false,\n isFluid = false,\n isDisabled = false,\n hasLeadingLabel,\n ariaLabel,\n onChange,\n onClick,\n value,\n label,\n className,\n style,\n htmlAttributes,\n ref,\n ...restProps\n}: CommonProps<'input', CheckboxProps>) => {\n const internalRef = useRef<HTMLInputElement>(null);\n const mergedRefs = useMergeRefs([internalRef, ref]);\n\n const [isCheckedState, internalOnChange] = useSemiControlledInput({\n inputType: 'checkbox',\n isControlled: isChecked !== undefined,\n onChange,\n state: isChecked ?? isDefaultChecked ?? false,\n });\n\n const checkboxClasses = classNames('ndl-checkbox', className, {\n 'ndl-disabled': isDisabled,\n });\n\n useEffect(() => {\n if (internalRef.current) {\n internalRef.current.indeterminate = isIndeterminate;\n }\n }, [isIndeterminate]);\n\n const { labelContent, fieldClassName } = useInlineFieldLabel({\n ariaLabel,\n componentName: 'Checkbox',\n isDisabled,\n isFluid,\n label,\n });\n\n return (\n <label className={fieldClassName}>\n {hasLeadingLabel === true && labelContent}\n <input\n ref={mergedRefs}\n type=\"checkbox\"\n aria-label={ariaLabel}\n disabled={isDisabled}\n checked={isCheckedState}\n onClick={onClick}\n onChange={internalOnChange}\n value={value}\n style={style}\n className={checkboxClasses}\n {...restProps}\n {...htmlAttributes}\n />\n {Boolean(hasLeadingLabel) === false && labelContent}\n </label>\n );\n};\n"]}
|
|
@@ -34,13 +34,14 @@ import { FloatingFocusManager, FloatingPortal, useMergeRefs, } from '@floating-u
|
|
|
34
34
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
35
35
|
import classNames from 'classnames';
|
|
36
36
|
import { Fragment, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
37
|
+
import { FormFieldLabel, FormFieldMessage, useFormField, } from '../_common/form-control';
|
|
37
38
|
import { useSemicontrolledState } from '../_common/use-semi-controlled-state';
|
|
38
39
|
import { needleWarningMessage } from '../_common/utils';
|
|
39
40
|
import { Checkbox } from '../checkbox';
|
|
40
41
|
import { ConditionalWrap } from '../conditional-wrap';
|
|
41
42
|
import { DismissibleTag } from '../dismissible-tag';
|
|
42
43
|
import { Divider } from '../divider';
|
|
43
|
-
import { CheckIconOutline, ChevronDownIconOutline,
|
|
44
|
+
import { CheckIconOutline, ChevronDownIconOutline, MagnifyingGlassIconOutline, PlusIconOutline, XMarkIconOutline, } from '../icons/generated/heroIcons';
|
|
44
45
|
import { LoadingSpinner } from '../loading-spinner';
|
|
45
46
|
import { ReadOnlyTag } from '../read-only-tag';
|
|
46
47
|
import { useNeedleTheme } from '../theme';
|
|
@@ -102,9 +103,6 @@ export const Combobox = ({ label, name, formId, autoComplete, triggerRef, hasVis
|
|
|
102
103
|
const listboxId = `${baseId}-listbox`;
|
|
103
104
|
const panelId = `${baseId}-panel`;
|
|
104
105
|
const triggerId = `${baseId}-trigger`;
|
|
105
|
-
const labelId = `${baseId}-label`;
|
|
106
|
-
const helpTextId = `${baseId}-help-text`;
|
|
107
|
-
const errorTextId = `${baseId}-error-text`;
|
|
108
106
|
const initialValue = getInitialValue(value, defaultValue, isMulti);
|
|
109
107
|
const [selectedValue, setSelectedValue, resetSelectedValue] = useSemicontrolledState({
|
|
110
108
|
isControlled: value !== undefined,
|
|
@@ -462,13 +460,22 @@ export const Combobox = ({ label, name, formId, autoComplete, triggerRef, hasVis
|
|
|
462
460
|
? actionRows.map((row) => getOptionId(row)).join(' ')
|
|
463
461
|
: undefined;
|
|
464
462
|
const isDialogPanel = isSearchable;
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
463
|
+
// The shared form field resolves the ids and which message is displayed -
|
|
464
|
+
// an error always replaces the help text. A native validation failure
|
|
465
|
+
// reported by the hidden form control acts as the error while no errorText
|
|
466
|
+
// is set.
|
|
467
|
+
const { field: formField, controlProps: fieldControlProps } = useFormField({
|
|
468
|
+
baseId,
|
|
469
|
+
controlId: triggerId,
|
|
470
|
+
errorText: errorText !== null && errorText !== void 0 ? errorText : nativeValidationMessage,
|
|
471
|
+
hasRequiredIndicator,
|
|
472
|
+
hasVisuallyHiddenLabel,
|
|
473
|
+
helpText,
|
|
474
|
+
isRequired,
|
|
475
|
+
label,
|
|
476
|
+
});
|
|
477
|
+
const hasError = formField.hasError;
|
|
478
|
+
const labelId = formField.labelId;
|
|
472
479
|
const triggerAria = {
|
|
473
480
|
'aria-activedescendant': isPanelOpen && !isSearchable ? activeOptionId : undefined,
|
|
474
481
|
'aria-controls': getTriggerControlsId({
|
|
@@ -478,11 +485,11 @@ export const Combobox = ({ label, name, formId, autoComplete, triggerRef, hasVis
|
|
|
478
485
|
listboxId,
|
|
479
486
|
panelId,
|
|
480
487
|
}),
|
|
481
|
-
'aria-describedby':
|
|
488
|
+
'aria-describedby': fieldControlProps['aria-describedby'],
|
|
482
489
|
'aria-disabled': isDisabled ? true : undefined,
|
|
483
490
|
'aria-expanded': isPanelOpen,
|
|
484
491
|
'aria-haspopup': isDialogPanel ? 'dialog' : 'listbox',
|
|
485
|
-
'aria-invalid':
|
|
492
|
+
'aria-invalid': fieldControlProps['aria-invalid'],
|
|
486
493
|
'aria-required': isRequired ? true : undefined,
|
|
487
494
|
// The label points at the trigger through `htmlFor`, so clicking it moves
|
|
488
495
|
// focus to the trigger.
|
|
@@ -502,13 +509,13 @@ export const Combobox = ({ label, name, formId, autoComplete, triggerRef, hasVis
|
|
|
502
509
|
// is referenced, keeping "Optional" out of the accessible name.
|
|
503
510
|
'aria-labelledby': labelId, ref: builtInTriggerRef });
|
|
504
511
|
const field = {
|
|
505
|
-
errorText:
|
|
506
|
-
errorTextId,
|
|
512
|
+
errorText: formField.errorText,
|
|
513
|
+
errorTextId: formField.errorTextId,
|
|
507
514
|
hasError,
|
|
508
515
|
hasRequiredIndicator,
|
|
509
516
|
hasVisuallyHiddenLabel,
|
|
510
517
|
helpText,
|
|
511
|
-
helpTextId,
|
|
518
|
+
helpTextId: formField.helpTextId,
|
|
512
519
|
isRequired,
|
|
513
520
|
label,
|
|
514
521
|
labelId,
|
|
@@ -595,6 +602,42 @@ const getSelectedOptions = (value) => {
|
|
|
595
602
|
}
|
|
596
603
|
return value === null ? [] : [value];
|
|
597
604
|
};
|
|
605
|
+
/**
|
|
606
|
+
* The public `ComboboxFieldState` keeps its own shape - a custom trigger
|
|
607
|
+
* renders from it - so the built-in field parts map it onto the shared form
|
|
608
|
+
* field state (`_common/form-control`) instead of taking it directly.
|
|
609
|
+
*/
|
|
610
|
+
const toFormFieldState = (field) => {
|
|
611
|
+
const hasHelpText = Boolean(field.helpText) && !field.hasError;
|
|
612
|
+
return {
|
|
613
|
+
controlId: field.triggerId,
|
|
614
|
+
errorText: field.errorText,
|
|
615
|
+
errorTextId: field.errorTextId,
|
|
616
|
+
hasError: field.hasError,
|
|
617
|
+
hasRequiredIndicator: field.hasRequiredIndicator,
|
|
618
|
+
hasVisuallyHiddenLabel: field.hasVisuallyHiddenLabel,
|
|
619
|
+
helpText: field.helpText,
|
|
620
|
+
helpTextId: field.helpTextId,
|
|
621
|
+
isRequired: field.isRequired,
|
|
622
|
+
label: field.label,
|
|
623
|
+
labelId: field.labelId,
|
|
624
|
+
message: field.hasError
|
|
625
|
+
? field.errorText
|
|
626
|
+
: hasHelpText
|
|
627
|
+
? field.helpText
|
|
628
|
+
: undefined,
|
|
629
|
+
messageId: field.hasError
|
|
630
|
+
? field.errorTextId
|
|
631
|
+
: hasHelpText
|
|
632
|
+
? field.helpTextId
|
|
633
|
+
: undefined,
|
|
634
|
+
messageSeverity: field.hasError
|
|
635
|
+
? 'error'
|
|
636
|
+
: hasHelpText
|
|
637
|
+
? 'help'
|
|
638
|
+
: undefined,
|
|
639
|
+
};
|
|
640
|
+
};
|
|
598
641
|
/**
|
|
599
642
|
* A native select mirrors the custom control for submission, autofill,
|
|
600
643
|
* constraint validation, and reset. It is hidden from assistive technology
|
|
@@ -691,6 +734,7 @@ const Control = ({ triggerProps, state, field, placeholder, isClearable, isClean
|
|
|
691
734
|
const { isOpen, isMulti, isDisabled, selectedOptions, clear, remove } = state;
|
|
692
735
|
const { MultiValue, SingleValue } = components;
|
|
693
736
|
const { ref: triggerRef } = triggerProps, triggerAria = __rest(triggerProps, ["ref"]);
|
|
737
|
+
const sharedField = toFormFieldState(field);
|
|
694
738
|
const hasValue = selectedOptions.length > 0;
|
|
695
739
|
const singleValueOption = isMulti ? undefined : selectedOptions[0];
|
|
696
740
|
// Selected values are shown as tags, so the trigger renders nothing and is
|
|
@@ -800,7 +844,7 @@ const Control = ({ triggerProps, state, field, placeholder, isClearable, isClean
|
|
|
800
844
|
return (
|
|
801
845
|
// A fragment, so the label, the control, and the message are rows of the
|
|
802
846
|
// combobox itself. A custom trigger replaces all three.
|
|
803
|
-
_jsxs(_Fragment, { children: [_jsx(
|
|
847
|
+
_jsxs(_Fragment, { children: [_jsx(FormFieldLabel, { field: sharedField, size: size, onLabelClick: handleLabelClick }), _jsxs("div", Object.assign({ ref: referenceRef, className: classNames('ndl-combobox-control', {
|
|
804
848
|
'ndl-clean': isClean,
|
|
805
849
|
'ndl-disabled': isDisabled,
|
|
806
850
|
'ndl-has-error': field.hasError,
|
|
@@ -846,35 +890,7 @@ const Control = ({ triggerProps, state, field, placeholder, isClearable, isClean
|
|
|
846
890
|
// Select.
|
|
847
891
|
_jsxs("div", { className: "ndl-combobox-clear", children: [_jsx("button", { type: "button", className: "ndl-combobox-clear-button", "aria-label": "Clear selection", onKeyDown: handleAuxiliaryActionKeyDown, onClick: handleClear, children: _jsx(XMarkIconOutline, {}) }), _jsx("div", { className: "ndl-combobox-divider" })] })), _jsx("div", { className: classNames('ndl-combobox-dropdown-indicator', {
|
|
848
892
|
'ndl-open': isOpen,
|
|
849
|
-
}), children: _jsx(ChevronDownIconOutline, { "aria-hidden": "true", className: "ndl-combobox-icon" }) })] })] })), _jsx(
|
|
850
|
-
};
|
|
851
|
-
/**
|
|
852
|
-
* The trigger is a button with `role="combobox"`, which takes its accessible
|
|
853
|
-
* name from the author rather than from its content, so the label is referenced
|
|
854
|
-
* through `aria-labelledby` instead of wrapping the control in a `<label>`.
|
|
855
|
-
* The label is still a `<label>` associated through `htmlFor`, and clicking it
|
|
856
|
-
* moves focus to the trigger, as with a native select. It deliberately does
|
|
857
|
-
* not open the panel, which is also what a native select label leaves alone.
|
|
858
|
-
*/
|
|
859
|
-
const FieldLabel = ({ field, size, onLabelClick }) => {
|
|
860
|
-
const { label, labelId, triggerId, hasVisuallyHiddenLabel, hasRequiredIndicator, isRequired, } = field;
|
|
861
|
-
const variant = size === 'large' ? 'body-large' : 'body-medium';
|
|
862
|
-
return (_jsxs("div", { className: classNames('ndl-combobox-label-row', {
|
|
863
|
-
'ndl-visually-hidden': hasVisuallyHiddenLabel,
|
|
864
|
-
}), children: [_jsx(Typography, { as: "label", variant: variant, className: "ndl-combobox-label", htmlAttributes: {
|
|
865
|
-
htmlFor: triggerId,
|
|
866
|
-
id: labelId,
|
|
867
|
-
onClick: onLabelClick,
|
|
868
|
-
}, children: label }), hasRequiredIndicator && (_jsx(Typography, { variant: variant, className: "ndl-combobox-label-indicator", children: isRequired ? 'Required' : 'Optional' }))] }));
|
|
869
|
-
};
|
|
870
|
-
/**
|
|
871
|
-
* Rendered whether or not there is a message, so the live region is in the
|
|
872
|
-
* document before the text arrives and a validation error is announced.
|
|
873
|
-
*/
|
|
874
|
-
const FieldMessage = ({ field, size }) => {
|
|
875
|
-
const { errorText, hasError, helpText, helpTextId, errorTextId } = field;
|
|
876
|
-
const variant = size === 'large' ? 'body-medium' : 'body-small';
|
|
877
|
-
return (_jsxs("div", { className: "ndl-combobox-message", "aria-live": "polite", children: [hasError && (_jsxs(_Fragment, { children: [_jsx("div", { className: "ndl-error-icon", children: _jsx(ExclamationCircleIconSolid, {}) }), _jsx(Typography, { variant: variant, className: "ndl-error-text", htmlAttributes: { id: errorTextId }, children: errorText })] })), !hasError && Boolean(helpText) && (_jsx(Typography, { variant: variant, htmlAttributes: { id: helpTextId }, children: helpText }))] }));
|
|
893
|
+
}), children: _jsx(ChevronDownIconOutline, { "aria-hidden": "true", className: "ndl-combobox-icon" }) })] })] })), _jsx(FormFieldMessage, { field: sharedField, size: size })] }));
|
|
878
894
|
};
|
|
879
895
|
const Panel = (_a) => {
|
|
880
896
|
var { children, ref, id, ariaLabel, isDialog, className, style } = _a, restProps = __rest(_a, ["children", "ref", "id", "ariaLabel", "isDialog", "className", "style"]);
|