Add selectibleMinCount property and fix required for Selects.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
dd91d8dcfc
commit
fa9a40c03a
6 changed files with 60 additions and 1 deletions
|
@ -56,6 +56,7 @@ export function DemoApp()
|
||||||
const [datetime, setDatetime] = useState(null);
|
const [datetime, setDatetime] = useState(null);
|
||||||
|
|
||||||
const [selected, setSelected] = useState(null);
|
const [selected, setSelected] = useState(null);
|
||||||
|
const [anotherSelected, setAnotherSelected] = useState(null);
|
||||||
|
|
||||||
const [page, setPage] = useState(11);
|
const [page, setPage] = useState(11);
|
||||||
|
|
||||||
|
@ -176,6 +177,18 @@ export function DemoApp()
|
||||||
}} value={selected} onChange={setSelected} selectibleMaxCount={3} placeholder={"Simple test"}>
|
}} value={selected} onChange={setSelected} selectibleMaxCount={3} placeholder={"Simple test"}>
|
||||||
Simple select test
|
Simple select test
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
|
<Select value={anotherSelected} onChange={setAnotherSelected} options={{
|
||||||
|
"test": "test",
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
}} required={true}>
|
||||||
|
At least one selected element
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<Buttons placement={"center"}>
|
||||||
|
<button>Validation test</button>
|
||||||
|
</Buttons>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2>HTML</h2>
|
<h2>HTML</h2>
|
||||||
|
|
1
index.ts
1
index.ts
|
@ -15,6 +15,7 @@ export * from "./src/Components/Dates/Datepicker";
|
||||||
export * from "./src/Components/Floating/Float";
|
export * from "./src/Components/Floating/Float";
|
||||||
export * from "./src/Components/Floating/Tooltip";
|
export * from "./src/Components/Floating/Tooltip";
|
||||||
export * from "./src/Components/Forms/Checkbox";
|
export * from "./src/Components/Forms/Checkbox";
|
||||||
|
export * from "./src/Components/Forms/CustomValidationRule";
|
||||||
export * from "./src/Components/Forms/DatepickerInput";
|
export * from "./src/Components/Forms/DatepickerInput";
|
||||||
export * from "./src/Components/Forms/PasswordInput";
|
export * from "./src/Components/Forms/PasswordInput";
|
||||||
export * from "./src/Components/Forms/Radio";
|
export * from "./src/Components/Forms/Radio";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.4.2",
|
"version": "1.5.0",
|
||||||
"name": "@kernelui/core",
|
"name": "@kernelui/core",
|
||||||
"description": "Kernel UI Core.",
|
"description": "Kernel UI Core.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
24
src/Components/Forms/CustomValidationRule.tsx
Normal file
24
src/Components/Forms/CustomValidationRule.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React, {useEffect, useRef} from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom validation rule in a form.
|
||||||
|
*/
|
||||||
|
export function CustomValidationRule({valid, errorMessage}: {
|
||||||
|
valid: boolean;
|
||||||
|
errorMessage: string;
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// HTML virtual input ref.
|
||||||
|
const ref = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
// When the validation is invalid, set a custom error message, set the custom error message.
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current.setCustomValidity(valid ? "" : errorMessage);
|
||||||
|
}, [ref, valid, errorMessage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input ref={ref}
|
||||||
|
className={"virtual"} type={"text"} required={true}
|
||||||
|
value={valid ? "true" : ""} onChange={() => {}} />
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import {Suggestible} from "./Suggestible";
|
||||||
import {OptionsSuggestions, useSuggestionsNavigation} from "./OptionsSuggestions";
|
import {OptionsSuggestions, useSuggestionsNavigation} from "./OptionsSuggestions";
|
||||||
import {classes, Modify, normalizeString} from "../../Utils";
|
import {classes, Modify, normalizeString} from "../../Utils";
|
||||||
import {CaretDown, Check, X} from "@phosphor-icons/react";
|
import {CaretDown, Check, X} from "@phosphor-icons/react";
|
||||||
|
import {CustomValidationRule} from "../Forms/CustomValidationRule";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic select component properties.
|
* Generic select component properties.
|
||||||
|
@ -37,6 +38,12 @@ export type SelectProperties<OptionKey extends keyof any, Option> = React.PropsW
|
||||||
*/
|
*/
|
||||||
match?: (search: string, option: Option) => boolean;
|
match?: (search: string, option: Option) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Min count of options to allow to select.
|
||||||
|
* 0 by default, 1 when required is true.
|
||||||
|
*/
|
||||||
|
selectibleMinCount?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Max count of options to allow to select.
|
* Max count of options to allow to select.
|
||||||
* 1 by default when multiple is false, infinity when multiple is true.
|
* 1 by default when multiple is false, infinity when multiple is true.
|
||||||
|
@ -74,6 +81,7 @@ export function Select<OptionKey extends keyof any, Option>(
|
||||||
className,
|
className,
|
||||||
value, onChange,
|
value, onChange,
|
||||||
options, renderOption, match,
|
options, renderOption, match,
|
||||||
|
required, selectibleMinCount,
|
||||||
selectibleMaxCount, multiple,
|
selectibleMaxCount, multiple,
|
||||||
blurOnSelect, blurWhenMaxCountSelected,
|
blurOnSelect, blurWhenMaxCountSelected,
|
||||||
// Properties to pass down.
|
// Properties to pass down.
|
||||||
|
@ -90,6 +98,10 @@ export function Select<OptionKey extends keyof any, Option>(
|
||||||
// If `multiple` is set and `selectibleMaxCount` is not, allow an infinite count of options to select.
|
// If `multiple` is set and `selectibleMaxCount` is not, allow an infinite count of options to select.
|
||||||
selectibleMaxCount = selectibleMaxCount ?? ((multiple === undefined ? false : multiple) ? Infinity : 1);
|
selectibleMaxCount = selectibleMaxCount ?? ((multiple === undefined ? false : multiple) ? Infinity : 1);
|
||||||
|
|
||||||
|
// By default, allow to select no option.
|
||||||
|
// If `required` is set, `selectibleMinCount` will be set as 1 by default.
|
||||||
|
selectibleMinCount = selectibleMinCount ?? (required ? 1 : 0);
|
||||||
|
|
||||||
// true by default.
|
// true by default.
|
||||||
blurOnSelect = blurOnSelect === undefined ? false : blurOnSelect;
|
blurOnSelect = blurOnSelect === undefined ? false : blurOnSelect;
|
||||||
blurWhenMaxCountSelected = blurWhenMaxCountSelected === undefined ? true : blurWhenMaxCountSelected;
|
blurWhenMaxCountSelected = blurWhenMaxCountSelected === undefined ? true : blurWhenMaxCountSelected;
|
||||||
|
@ -213,6 +225,9 @@ export function Select<OptionKey extends keyof any, Option>(
|
||||||
<a className={"button"} tabIndex={-1}><CaretDown /></a>
|
<a className={"button"} tabIndex={-1}><CaretDown /></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CustomValidationRule valid={selectedOptions.length >= selectibleMinCount}
|
||||||
|
errorMessage={`At least ${selectibleMinCount} option${selectibleMinCount > 1 ? "s are" : " is"} required.`} />
|
||||||
|
|
||||||
<ul className={"selected"}>
|
<ul className={"selected"}>
|
||||||
{ // Showing each selected value.
|
{ // Showing each selected value.
|
||||||
selectedOptions.map(([optionKey, option]) => (
|
selectedOptions.map(([optionKey, option]) => (
|
||||||
|
|
|
@ -27,3 +27,9 @@ p
|
||||||
margin: 0.75em auto;
|
margin: 0.75em auto;
|
||||||
line-height: 1.6em;
|
line-height: 1.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.virtual
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue