Add selectibleMinCount property and fix required for Selects, add a custom validation rule component.
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful

This commit is contained in:
Madeorsk 2024-09-25 15:36:16 +02:00
parent dd91d8dcfc
commit a1b0cc816c
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
6 changed files with 60 additions and 1 deletions

View file

@ -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>

View file

@ -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";

View file

@ -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": {

View 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={() => {}} />
);
}

View file

@ -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]) => (

View file

@ -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;
}