feat: implement barebone zola templates
This commit is contained in:
parent
9c20f5ed2e
commit
f99a9ae2ac
198 changed files with 2434 additions and 227991 deletions
|
|
@ -1,142 +0,0 @@
|
|||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import styles from './SelectInput.module.css';
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onChange: (value: string | string[] | null) => void;
|
||||
options: Option[];
|
||||
isMultiple?: boolean;
|
||||
value?: string | string[];
|
||||
}
|
||||
|
||||
export default function SelectInput({
|
||||
options,
|
||||
isMultiple = false,
|
||||
onChange,
|
||||
value = [],
|
||||
}: Props): JSX.Element {
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [filteredOptions, setFilteredOptions] = useState<Props['options']>([]);
|
||||
const [isOptionsOpen, setIsOptionsOpen] = useState<boolean>(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredOptions(options);
|
||||
}, [options]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selected.length === 0) {
|
||||
onChange(null);
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(isMultiple ? selected : selected[0]);
|
||||
}, [selected]);
|
||||
|
||||
const handleFocusInput = (): void => {
|
||||
setIsOptionsOpen(true);
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
const handleAddElement = (item: any): void => {
|
||||
setSelected((prev) => {
|
||||
if (isMultiple) {
|
||||
return [...prev, item];
|
||||
}
|
||||
return [item];
|
||||
});
|
||||
setIsOptionsOpen(false);
|
||||
|
||||
if (inputRef.current === null) return;
|
||||
|
||||
inputRef.current.value = '';
|
||||
};
|
||||
|
||||
const handleRemoveElement = (idx: number): void => {
|
||||
setIsOptionsOpen(false);
|
||||
setSelected((prev) => {
|
||||
prev.splice(idx, 1);
|
||||
|
||||
return [...prev];
|
||||
});
|
||||
};
|
||||
|
||||
const handleLooseFocus = (e: React.FocusEvent<HTMLDivElement>): void => {
|
||||
if (!e.currentTarget.contains(e.relatedTarget)) {
|
||||
// Not triggered when swapping focus between children
|
||||
setIsOptionsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFilterOptions = ({
|
||||
target,
|
||||
}: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
if (target.value === '') {
|
||||
setFilteredOptions(options);
|
||||
return;
|
||||
}
|
||||
|
||||
const newOptions = options.filter(
|
||||
(item) =>
|
||||
item.label.toLowerCase().search(target.value.toLowerCase()) !== -1,
|
||||
);
|
||||
setFilteredOptions(newOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper} onBlur={handleLooseFocus}>
|
||||
<div
|
||||
className={styles.input}
|
||||
onClick={() => {
|
||||
handleFocusInput();
|
||||
}}
|
||||
>
|
||||
{selected.map((item, idx) => (
|
||||
<div className={styles.selectedItem + ' hstack'} key={idx}>
|
||||
<div>{item}</div>
|
||||
<div
|
||||
className={styles.deleteItem}
|
||||
onClick={() => {
|
||||
handleRemoveElement(idx);
|
||||
}}
|
||||
>
|
||||
{'X'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={styles.realInput}
|
||||
type="text"
|
||||
onChange={handleFilterOptions}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
onChange(null);
|
||||
setSelected([]);
|
||||
}}
|
||||
>
|
||||
X
|
||||
</button>
|
||||
<div className={styles.optionList} hidden={!isOptionsOpen}>
|
||||
{filteredOptions.map((item, idx) => (
|
||||
<button
|
||||
className={styles.optionItem}
|
||||
key={idx}
|
||||
disabled={selected.includes(item.value)}
|
||||
onClick={() => {
|
||||
handleAddElement(item.value);
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue