@bouku/core 0.4.2 → 0.4.3
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/assets/icons/trash.svg +6 -0
- package/dist/components/button/normal.d.ts +2 -2
- package/dist/components/button/normal.js +3 -2
- package/dist/components/container/flex.d.ts +5 -8
- package/dist/components/container/flex.js +0 -7
- package/dist/components/date/_.d.ts +1 -0
- package/dist/components/date/_.js +1 -0
- package/dist/components/date/date-picker.d.ts +38 -0
- package/dist/components/date/date-picker.js +52 -0
- package/dist/components/miscellaneous/checkbox/index.d.ts +3 -3
- package/dist/components/miscellaneous/checkbox/index.js +12 -18
- package/dist/components/miscellaneous/dropdown/multi-select.d.ts +4 -2
- package/dist/components/miscellaneous/dropdown/multi-select.js +4 -4
- package/dist/components/miscellaneous/dropdown/normal.d.ts +2 -1
- package/dist/components/miscellaneous/dropdown/normal.js +4 -4
- package/dist/components/miscellaneous/dropdown/select.d.ts +4 -2
- package/dist/components/miscellaneous/dropdown/select.js +3 -3
- package/dist/components/table/actions.js +1 -1
- package/dist/components/table/date-range.d.ts +8 -0
- package/dist/components/table/date-range.js +10 -0
- package/dist/components/table/filters.d.ts +10 -0
- package/dist/components/table/filters.js +32 -0
- package/dist/components/table/header.d.ts +5 -1
- package/dist/components/table/header.js +7 -4
- package/dist/components/table/index.d.ts +8 -2
- package/dist/components/table/index.js +44 -30
- package/dist/components/table/row.d.ts +3 -1
- package/dist/components/table/row.js +16 -7
- package/dist/components/table/search-bar.js +2 -1
- package/dist/components/table/state/filters.js +0 -1
- package/dist/components/table/state/sort.js +7 -3
- package/dist/components/table/utils/date.js +4 -3
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="1em" height="1em">
|
|
2
|
+
<path
|
|
3
|
+
d="M232.7 69.9C237.1 56.8 249.3 48 263.1 48L377 48C390.8 48 403 56.8 407.4 69.9L416 96L512 96C529.7 96 544 110.3 544 128C544 145.7 529.7 160 512 160L128 160C110.3 160 96 145.7 96 128C96 110.3 110.3 96 128 96L224 96L232.7 69.9zM128 208L512 208L512 512C512 547.3 483.3 576 448 576L192 576C156.7 576 128 547.3 128 512L128 208zM216 272C202.7 272 192 282.7 192 296L192 488C192 501.3 202.7 512 216 512C229.3 512 240 501.3 240 488L240 296C240 282.7 229.3 272 216 272zM320 272C306.7 272 296 282.7 296 296L296 488C296 501.3 306.7 512 320 512C333.3 512 344 501.3 344 488L344 296C344 282.7 333.3 272 320 272zM424 272C410.7 272 400 282.7 400 296L400 488C400 501.3 410.7 512 424 512C437.3 512 448 501.3 448 488L448 296C448 282.7 437.3 272 424 272z"
|
|
4
|
+
fill="currentColor"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Component } from "../../utils/types/element.js";
|
|
2
2
|
export type ButtonProps = Component & {
|
|
3
|
-
size?: "xs" | "sm" | "md" | "lg";
|
|
3
|
+
size?: "icon" | "xs" | "sm" | "md" | "lg";
|
|
4
4
|
variant?: "surface" | "accent" | "outline" | "ghost" | "error";
|
|
5
5
|
icon?: React.ReactNode;
|
|
6
6
|
action?: () => void;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { cn } from "../../utils/index.js";
|
|
5
|
-
import { tv } from "tailwind-variants";
|
|
6
4
|
import Spinner from "../../assets/icons/spinner.svg";
|
|
5
|
+
import { cn } from "../../utils/functions/style.js";
|
|
6
|
+
import { tv } from "tailwind-variants";
|
|
7
7
|
export function Button({ style, size = "md", variant = "surface", icon, action, oops = console.log, disabled, children }) {
|
|
8
8
|
const [isLoading, setLoading] = useState(false);
|
|
9
9
|
const handleClick = async () => {
|
|
@@ -28,6 +28,7 @@ const styles = tv({
|
|
|
28
28
|
base: "relative flex justify-center items-center border outline-none rounded font-semibold shadow duration-200 cursor-pointer disabled:cursor-not-allowed hover:brightness-125",
|
|
29
29
|
variants: {
|
|
30
30
|
size: {
|
|
31
|
+
icon: "p-2 text-sm rounded-full",
|
|
31
32
|
xs: "gap-2 px-3 py-2 text-xs",
|
|
32
33
|
sm: "gap-2 px-3 py-2 text-xs sm:text-sm",
|
|
33
34
|
md: "gap-2 px-4 py-2 text-xs sm:text-sm",
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Hoverable } from "../../utils/types/element.js";
|
|
2
|
+
type Component = {
|
|
3
|
+
style?: string;
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
};
|
|
2
6
|
type Props = Component & Hoverable & {
|
|
3
7
|
center?: boolean;
|
|
4
8
|
centerX?: boolean;
|
|
5
9
|
centerY?: boolean;
|
|
6
10
|
equal?: boolean;
|
|
7
11
|
};
|
|
8
|
-
/**
|
|
9
|
-
* Flex containers with configurable alignment and styling.
|
|
10
|
-
*
|
|
11
|
-
* @param {string} style - Additional styles for container.
|
|
12
|
-
* @param {ReactNode} children - Child elements to render inside the row.
|
|
13
|
-
* @param {Props} opts - Variant options for styling.
|
|
14
|
-
**/
|
|
15
12
|
export declare function RowBox({ style, children, onHover, onLeave, ...opts }: Props): import("react").JSX.Element | null;
|
|
16
13
|
export declare function ColumnBox({ style, children, onHover, onLeave, ...opts }: Props): import("react").JSX.Element | null;
|
|
17
14
|
export {};
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/functions/style.js";
|
|
3
3
|
import { tv } from "tailwind-variants";
|
|
4
|
-
/**
|
|
5
|
-
* Flex containers with configurable alignment and styling.
|
|
6
|
-
*
|
|
7
|
-
* @param {string} style - Additional styles for container.
|
|
8
|
-
* @param {ReactNode} children - Child elements to render inside the row.
|
|
9
|
-
* @param {Props} opts - Variant options for styling.
|
|
10
|
-
**/
|
|
11
4
|
export function RowBox({ style, children, onHover, onLeave, ...opts }) {
|
|
12
5
|
if (!children)
|
|
13
6
|
return null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type FieldProps<T> = {
|
|
3
|
+
id?: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
style?: string;
|
|
6
|
+
value?: T;
|
|
7
|
+
update?: (x: T) => void;
|
|
8
|
+
updateForm?: (id: string, x: T) => void;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
note?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
type Variant = "normal" | "ghost";
|
|
14
|
+
type DatePickerProps = FieldProps<string> & {
|
|
15
|
+
variant?: Variant;
|
|
16
|
+
size?: "sm" | "md";
|
|
17
|
+
style?: string;
|
|
18
|
+
min?: string;
|
|
19
|
+
max?: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Date picker field component.
|
|
23
|
+
*
|
|
24
|
+
* Wraps the native `input[type="date"]` element with custom styling.
|
|
25
|
+
* It displays an optional label and note, and can be marked as required.
|
|
26
|
+
* The value is controlled externally as an ISO date string (`YYYY-MM-DD`).
|
|
27
|
+
*
|
|
28
|
+
* Variants:
|
|
29
|
+
* - `normal`: Standard input with primary color scheme.
|
|
30
|
+
* - `ghost`: Transparent for injecting input inside another component.
|
|
31
|
+
*
|
|
32
|
+
* @param {Variant} variant - Visual style variant. (optional, default = normal)
|
|
33
|
+
* @param {string} style - Additional class names for styling. (optional)
|
|
34
|
+
* @param {string} min - Earliest selectable date in `YYYY-MM-DD` format. (optional)
|
|
35
|
+
* @param {string} max - Latest selectable date in `YYYY-MM-DD` format. (optional)
|
|
36
|
+
*/
|
|
37
|
+
export declare function DatePicker({ id, variant, size, value, update, updateForm, disabled, min, max, ...props }: DatePickerProps): import("react").JSX.Element;
|
|
38
|
+
export default DatePicker;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../../utils/functions/style.js";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
function Field({ style, label, required = true, note, children }) {
|
|
6
|
+
return (_jsxs("div", { className: cn(fieldStyles.container, style), children: [label && _jsxs("span", { className: fieldStyles.label, children: [label, " ", !required ? _jsx("span", { className: "italic text-slate-400", children: "(optional)" }) : ""] }), children, note && _jsx("span", { className: fieldStyles.note, children: note })] }));
|
|
7
|
+
}
|
|
8
|
+
const fieldStyles = {
|
|
9
|
+
container: "flex flex-col shrink-0 gap-2 overflow-hidden",
|
|
10
|
+
label: "text-xs text-primary-dark",
|
|
11
|
+
note: "mt-1 text-xs text-slate-500 whitespace-pre-line"
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Date picker field component.
|
|
15
|
+
*
|
|
16
|
+
* Wraps the native `input[type="date"]` element with custom styling.
|
|
17
|
+
* It displays an optional label and note, and can be marked as required.
|
|
18
|
+
* The value is controlled externally as an ISO date string (`YYYY-MM-DD`).
|
|
19
|
+
*
|
|
20
|
+
* Variants:
|
|
21
|
+
* - `normal`: Standard input with primary color scheme.
|
|
22
|
+
* - `ghost`: Transparent for injecting input inside another component.
|
|
23
|
+
*
|
|
24
|
+
* @param {Variant} variant - Visual style variant. (optional, default = normal)
|
|
25
|
+
* @param {string} style - Additional class names for styling. (optional)
|
|
26
|
+
* @param {string} min - Earliest selectable date in `YYYY-MM-DD` format. (optional)
|
|
27
|
+
* @param {string} max - Latest selectable date in `YYYY-MM-DD` format. (optional)
|
|
28
|
+
*/
|
|
29
|
+
export function DatePicker({ id, variant, size = "md", value, update, updateForm, disabled = false, min, max, ...props }) {
|
|
30
|
+
const handleUpdate = (value) => {
|
|
31
|
+
if (id && updateForm)
|
|
32
|
+
updateForm(id, value);
|
|
33
|
+
else
|
|
34
|
+
update?.(value);
|
|
35
|
+
};
|
|
36
|
+
return (_jsx(Field, { ...props, children: _jsx("input", { type: "date", className: styles({ variant, size }), value: value || "", onChange: ({ target: { value } }) => handleUpdate(value), autoComplete: "off", disabled: disabled, min: min, max: max }) }));
|
|
37
|
+
}
|
|
38
|
+
const styles = tv({
|
|
39
|
+
base: "px-3 py-2 duration-200 rounded placeholder:text-primary-darker text-primary disabled:brightness-90 disabled:cursor-not-allowed",
|
|
40
|
+
defaultVariants: { variant: "normal", size: "md" },
|
|
41
|
+
variants: {
|
|
42
|
+
variant: {
|
|
43
|
+
normal: "bg-input border border-outline focus:border-outline-light outline-none",
|
|
44
|
+
ghost: "p-0 bg-transparent border-none outline-none"
|
|
45
|
+
},
|
|
46
|
+
size: {
|
|
47
|
+
sm: "px-2 text-xs",
|
|
48
|
+
md: "text-xs sm:text-sm"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
export default DatePicker;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
interface Props {
|
|
2
|
-
size?: "sm" | "md"
|
|
3
|
-
label
|
|
2
|
+
size?: "sm" | "md";
|
|
3
|
+
label?: string;
|
|
4
4
|
value: boolean;
|
|
5
5
|
update: (x: boolean) => void;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
}
|
|
8
|
-
export declare
|
|
8
|
+
export declare function CheckBox({ size, label, value: isChecked, update, disabled }: Props): import("react").JSX.Element;
|
|
9
9
|
export default CheckBox;
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { RowBox } from "
|
|
2
|
+
import { RowBox } from "../../container/flex.js";
|
|
3
3
|
import Check from "../../../assets/icons/check.svg";
|
|
4
4
|
import { cn } from "../../../utils/functions/style.js";
|
|
5
5
|
import { tv } from "tailwind-variants";
|
|
6
|
-
export
|
|
7
|
-
const s =
|
|
6
|
+
export function CheckBox({ size, label, value: isChecked, update, disabled = false }) {
|
|
7
|
+
const s = getStyle({
|
|
8
8
|
size,
|
|
9
9
|
active: isChecked
|
|
10
10
|
});
|
|
11
|
-
return (_jsxs(RowBox, { style: s.container(), children: [_jsx("div", { className: cn(s.box(), disabled && "pointer-events-none"), onClick: () => !disabled ? update(!isChecked) : null, children: isChecked && _jsx(Check, { className: s.check() }) }), _jsx("span", { className: s.label(), children: label })] }));
|
|
12
|
-
}
|
|
13
|
-
const
|
|
11
|
+
return (_jsxs(RowBox, { style: s.container(), children: [_jsx("div", { className: cn(s.box(), disabled && "pointer-events-none"), onClick: () => !disabled ? update(!isChecked) : null, children: isChecked && _jsx(Check, { className: s.check() }) }), label && (_jsx("span", { className: s.label(), children: label }))] }));
|
|
12
|
+
}
|
|
13
|
+
const getStyle = tv({
|
|
14
14
|
slots: {
|
|
15
15
|
container: "items-center",
|
|
16
|
-
box: "flex justify-center items-center
|
|
16
|
+
box: "flex justify-center items-center hover:bg-accent border rounded border-outline duration-200 cursor-pointer",
|
|
17
17
|
check: "text-background",
|
|
18
|
-
label: "text-primary"
|
|
19
|
-
},
|
|
20
|
-
defaultVariants: {
|
|
21
|
-
size: "md"
|
|
18
|
+
label: "text-primary-dark"
|
|
22
19
|
},
|
|
23
20
|
variants: {
|
|
24
21
|
size: {
|
|
@@ -29,12 +26,6 @@ const baseStyles = tv({
|
|
|
29
26
|
label: "text-xs"
|
|
30
27
|
},
|
|
31
28
|
md: {
|
|
32
|
-
container: "gap-3",
|
|
33
|
-
box: "size-5 min-w-5",
|
|
34
|
-
check: "size-3",
|
|
35
|
-
label: "text-sm"
|
|
36
|
-
},
|
|
37
|
-
lg: {
|
|
38
29
|
container: "gap-3",
|
|
39
30
|
box: "size-5 min-w-5",
|
|
40
31
|
check: "size-3",
|
|
@@ -43,9 +34,12 @@ const baseStyles = tv({
|
|
|
43
34
|
},
|
|
44
35
|
active: {
|
|
45
36
|
true: {
|
|
46
|
-
box: "
|
|
37
|
+
box: "bg-accent"
|
|
47
38
|
}
|
|
48
39
|
}
|
|
40
|
+
},
|
|
41
|
+
defaultVariants: {
|
|
42
|
+
size: "md"
|
|
49
43
|
}
|
|
50
44
|
});
|
|
51
45
|
export default CheckBox;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { LabeledItem } from "../../../utils/types/
|
|
2
|
+
import type { LabeledItem } from "../../../utils/types/element.js";
|
|
3
3
|
interface Props {
|
|
4
|
+
size?: "xs" | "sm";
|
|
5
|
+
style?: string;
|
|
4
6
|
prefix?: ReactNode;
|
|
5
7
|
align?: "left" | "right";
|
|
6
8
|
options: LabeledItem[];
|
|
7
9
|
values?: string[];
|
|
8
10
|
update: (x: string) => void;
|
|
9
11
|
}
|
|
10
|
-
export declare function MultiSelect({ prefix, align, options, values, update }: Props): import("react").JSX.Element;
|
|
12
|
+
export declare function MultiSelect({ size, style, prefix, align, options, values, update }: Props): import("react").JSX.Element;
|
|
11
13
|
export default MultiSelect;
|
|
@@ -3,11 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useRef, useEffect, } from "react";
|
|
4
4
|
import ChevronDown from "../../../assets/icons/chevron-down.svg";
|
|
5
5
|
import { AnimatePresence, motion } from "framer-motion";
|
|
6
|
-
import { Button } from "../../button/normal.js";
|
|
7
6
|
import { CheckBox } from "../checkbox/index.js";
|
|
8
|
-
import {
|
|
7
|
+
import { Button } from "../../button/normal.js";
|
|
8
|
+
import { cn } from "../../../utils/functions/style.js";
|
|
9
9
|
;
|
|
10
|
-
export function MultiSelect({ prefix, align = "left", options, values = [], update }) {
|
|
10
|
+
export function MultiSelect({ size = "sm", style, prefix, align = "left", options, values = [], update }) {
|
|
11
11
|
const [isOpen, setOpen] = useState(false);
|
|
12
12
|
const ref = useRef(null);
|
|
13
13
|
const activeOpts = options.filter(({ id }) => (values?.includes(id)));
|
|
@@ -21,7 +21,7 @@ export function MultiSelect({ prefix, align = "left", options, values = [], upda
|
|
|
21
21
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
22
22
|
};
|
|
23
23
|
}, []);
|
|
24
|
-
return (_jsxs("div", { className: s.container, ref: ref, children: [_jsxs(Button, { size:
|
|
24
|
+
return (_jsxs("div", { className: s.container, ref: ref, children: [_jsxs(Button, { size: size, style: style, action: () => setOpen(o => !o), children: [prefix, activeOpts.length > 0 && (_jsx("span", { className: "text-primary-dark", children: activeOpts.map(({ label }) => label).join(", ") })), _jsx(ChevronDown, { className: s.chevron })] }), _jsx(AnimatePresence, { children: isOpen && (_jsx(motion.div, { className: cn(s.dropdown, align === "left" ? "left-0" : "right-0"), initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: options.map(({ id, label }) => (_jsx("div", { className: s.option, onClick: () => update(id), children: _jsx(CheckBox, { size: "sm", label: label, value: !!values?.includes(id), update: () => console.log("hi"), disabled: true }) }, id))) })) })] }));
|
|
25
25
|
}
|
|
26
26
|
const s = {
|
|
27
27
|
container: "relative shrink-0",
|
|
@@ -3,7 +3,8 @@ interface Props {
|
|
|
3
3
|
trigger: ReactNode;
|
|
4
4
|
align?: "left" | "right";
|
|
5
5
|
direction?: "up" | "down";
|
|
6
|
+
style?: string;
|
|
6
7
|
children: ReactNode;
|
|
7
8
|
}
|
|
8
|
-
export declare function Dropdown({ trigger, align, direction, children }: Props): import("react").JSX.Element;
|
|
9
|
+
export declare function Dropdown({ trigger, align, direction, style, children }: Props): import("react").JSX.Element;
|
|
9
10
|
export default Dropdown;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useRef, useEffect } from "react";
|
|
4
4
|
import { AnimatePresence, motion } from "framer-motion";
|
|
5
|
-
import { cn } from "../../../utils/functions/
|
|
5
|
+
import { cn } from "../../../utils/functions/style.js";
|
|
6
6
|
;
|
|
7
|
-
export function Dropdown({ trigger, align = "left", direction = "down", children }) {
|
|
7
|
+
export function Dropdown({ trigger, align = "left", direction = "down", style, children }) {
|
|
8
8
|
const [isOpen, setOpen] = useState(false);
|
|
9
9
|
const ref = useRef(null);
|
|
10
10
|
useEffect(() => {
|
|
@@ -19,10 +19,10 @@ export function Dropdown({ trigger, align = "left", direction = "down", children
|
|
|
19
19
|
}, []);
|
|
20
20
|
return (_jsxs("div", { ref: ref, className: styles.container, children: [_jsx("div", { onClick: () => setOpen(open => !open), children: trigger }), _jsx(AnimatePresence, { children: isOpen && (_jsx(motion.div, { className: cn(styles.dropdown, align === "left" ? "left-0" : "right-0", direction === "down"
|
|
21
21
|
? "top-full mt-2"
|
|
22
|
-
: "bottom-full mb-2"), initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: children })) })] }));
|
|
22
|
+
: "bottom-full mb-2", style), initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: children })) })] }));
|
|
23
23
|
}
|
|
24
24
|
const styles = {
|
|
25
25
|
container: "relative shrink-0",
|
|
26
|
-
dropdown: "absolute z-50 flex flex-col gap-2 min-w-full w-max bg-surface-
|
|
26
|
+
dropdown: "absolute z-50 flex flex-col gap-2 min-w-full w-max bg-surface-dark rounded-md border border-border-dark p-2 text-sm shadow"
|
|
27
27
|
};
|
|
28
28
|
export default Dropdown;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { LabeledItem } from "../../../utils/types/
|
|
2
|
+
import type { LabeledItem } from "../../../utils/types/element.js";
|
|
3
3
|
interface Props {
|
|
4
|
+
size?: "xs" | "sm";
|
|
5
|
+
style?: string;
|
|
4
6
|
prefix?: ReactNode;
|
|
5
7
|
align?: "left" | "right";
|
|
6
8
|
options: LabeledItem[];
|
|
7
9
|
value?: string;
|
|
8
10
|
update: (x: string) => void;
|
|
9
11
|
}
|
|
10
|
-
export declare function Select({ prefix, align, options, value, update }: Props): import("react").JSX.Element;
|
|
12
|
+
export declare function Select({ size, style, prefix, align, options, value, update }: Props): import("react").JSX.Element;
|
|
11
13
|
export default Select;
|
|
@@ -4,9 +4,9 @@ import { useState, useRef, useEffect, } from "react";
|
|
|
4
4
|
import ChevronDown from "../../../assets/icons/chevron-down.svg";
|
|
5
5
|
import { AnimatePresence, motion } from "framer-motion";
|
|
6
6
|
import { Button } from "../../button/normal.js";
|
|
7
|
-
import { cn } from "../../../utils/functions/
|
|
7
|
+
import { cn } from "../../../utils/functions/style.js";
|
|
8
8
|
;
|
|
9
|
-
export function Select({ prefix, align = "left", options, value, update }) {
|
|
9
|
+
export function Select({ size = "sm", style, prefix, align = "left", options, value, update }) {
|
|
10
10
|
const [isOpen, setOpen] = useState(false);
|
|
11
11
|
const ref = useRef(null);
|
|
12
12
|
const active = options.find(o => o.id === value);
|
|
@@ -24,7 +24,7 @@ export function Select({ prefix, align = "left", options, value, update }) {
|
|
|
24
24
|
update(x);
|
|
25
25
|
setOpen(false);
|
|
26
26
|
};
|
|
27
|
-
return (_jsxs("div", { ref: ref, className: s.container, children: [_jsxs(Button, { size:
|
|
27
|
+
return (_jsxs("div", { ref: ref, className: s.container, children: [_jsxs(Button, { size: size, style: style, action: () => setOpen(open => !open), children: [prefix, _jsx("span", { className: "text-primary-dark", children: active && active.label }), _jsx(ChevronDown, { className: s.chevron })] }), _jsx(AnimatePresence, { children: isOpen && (_jsx(motion.div, { className: cn(s.dropdown, align === "left" ? "left-0" : "right-0"), initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: options.map(({ id, label }) => (_jsx("span", { className: cn(s.option, id === value && "text-accent pointer-events-none"), onClick: () => setValue(id), children: label }, id))) })) })] }));
|
|
28
28
|
}
|
|
29
29
|
const s = {
|
|
30
30
|
container: "relative shrink-0",
|
|
@@ -6,6 +6,6 @@ export function Actions({ row, actions }) {
|
|
|
6
6
|
return (_jsx(RowBox, { style: "gap-2 items-center", children: actions.map(action => {
|
|
7
7
|
if (action.hidden?.(row))
|
|
8
8
|
return null;
|
|
9
|
-
return (_jsx(Button, { size: "
|
|
9
|
+
return (_jsx(Button, { size: "icon", variant: action.variant ?? "surface", style: action.style, icon: action.icon, action: () => action.handler(row), children: action.label }, action.id));
|
|
10
10
|
}) }));
|
|
11
11
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Dispatch, SetStateAction } from "react";
|
|
2
|
+
import type { DateRange } from "./utils/types.js";
|
|
3
|
+
type Props = DateRange & {
|
|
4
|
+
update: Dispatch<SetStateAction<DateRange>>;
|
|
5
|
+
initial: DateRange;
|
|
6
|
+
};
|
|
7
|
+
export default function DateRange({ from, to, update, initial }: Props): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Dropdown } from "../miscellaneous/dropdown/normal.js";
|
|
3
|
+
import { DatePicker } from "../date/date-picker.js";
|
|
4
|
+
import { Button } from "../button/normal.js";
|
|
5
|
+
import Trash from "../../assets/icons/trash.svg";
|
|
6
|
+
import { formatDateLabel } from "./utils/date.js";
|
|
7
|
+
export default function DateRange({ from, to, update, initial }) {
|
|
8
|
+
const Trigger = (_jsxs(Button, { size: "xs", style: "bg-surface-dark border-border-darker", children: [formatDateLabel(from), " \u2013 ", formatDateLabel(to)] }));
|
|
9
|
+
return (_jsxs(Dropdown, { trigger: Trigger, align: "right", style: "gap-3 min-w-56 p-4", children: [_jsx(DatePicker, { id: "from", label: "From", size: "sm", style: "gap-1", value: from, update: v => update(prev => ({ ...prev, from: v })) }), _jsx(DatePicker, { id: "to", label: "To", size: "sm", style: "gap-1", value: to, update: v => update(prev => ({ ...prev, to: v })) }), (from || to) && (_jsx(Button, { size: "xs", style: "mx-auto py-1 px-2 gap-1", variant: "error", icon: _jsx(Trash, {}), action: () => update(initial), disabled: from === initial.from && to === initial.to, children: "Clear" }))] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CellValue, Column, TableRow } from "./utils/types.js";
|
|
2
|
+
type Props<T extends TableRow> = {
|
|
3
|
+
columns: Column<T>[];
|
|
4
|
+
items: Map<keyof T, Set<CellValue>>;
|
|
5
|
+
options: Partial<Record<keyof T, CellValue[]>>;
|
|
6
|
+
update: (column: keyof T, value: CellValue) => void;
|
|
7
|
+
clear: (column: keyof T) => void;
|
|
8
|
+
};
|
|
9
|
+
export default function Filters<T extends TableRow>({ columns, items, options, update, clear }: Props<T>): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Dropdown, MultiSelect } from "../miscellaneous/dropdown/_.js";
|
|
3
|
+
import { Button } from "../button/normal.js";
|
|
4
|
+
import FilterIcon from "../../assets/icons/filter.svg";
|
|
5
|
+
import Trash from "../../assets/icons/trash.svg";
|
|
6
|
+
export default function Filters({ columns, items, options, update, clear }) {
|
|
7
|
+
const filterColumns = columns
|
|
8
|
+
.filter(column => column.filter === "select")
|
|
9
|
+
.map(column => column.key);
|
|
10
|
+
const activeFilterCount = Array.from(items.values())
|
|
11
|
+
.filter(values => values.size > 0)
|
|
12
|
+
.length;
|
|
13
|
+
const getColumnLabel = (key) => {
|
|
14
|
+
const col = columns.find(column => column.key === key);
|
|
15
|
+
return col?.label ?? key.toString().replaceAll("_", " ");
|
|
16
|
+
};
|
|
17
|
+
const clearFilters = () => {
|
|
18
|
+
for (const c of filterColumns)
|
|
19
|
+
clear(c);
|
|
20
|
+
};
|
|
21
|
+
const Trigger = (_jsxs(Button, { size: "xs", style: "bg-surface-dark border-border-darker", icon: _jsx(FilterIcon, {}), children: ["Filter", activeFilterCount > 0 && (_jsx("span", { className: s.count, children: activeFilterCount }))] }));
|
|
22
|
+
return (_jsxs(Dropdown, { trigger: Trigger, style: "p-3 gap-3", children: [filterColumns.map(column => {
|
|
23
|
+
const myFilters = items.get(column);
|
|
24
|
+
return (_jsx(MultiSelect, { size: "xs", prefix: getColumnLabel(column).toUpperCase(), options: (options[column] || []).filter(v => v !== null).map(value => ({
|
|
25
|
+
id: String(value),
|
|
26
|
+
label: String(value)
|
|
27
|
+
})), values: Array.from(myFilters || []).map(f => String(f)), update: (x) => update(column, x) }, String(column)));
|
|
28
|
+
}), _jsx(Button, { size: "xs", style: "mx-auto py-1 px-2 gap-1", variant: "error", icon: _jsx(Trash, {}), action: clearFilters, disabled: activeFilterCount === 0, children: "Clear" })] }));
|
|
29
|
+
}
|
|
30
|
+
const s = {
|
|
31
|
+
count: "flex justify-center items-center ml-1 bg-accent border border-accent-dark size-4 rounded-full text-px font-bold"
|
|
32
|
+
};
|
|
@@ -6,6 +6,10 @@ type Props<T> = {
|
|
|
6
6
|
hasActions: boolean;
|
|
7
7
|
sortBy: (x: string | null) => void;
|
|
8
8
|
toggleSortDirection: () => void;
|
|
9
|
+
selection?: {
|
|
10
|
+
allSelected: boolean;
|
|
11
|
+
toggleAll: () => void;
|
|
12
|
+
};
|
|
9
13
|
};
|
|
10
|
-
export declare const Header: <T extends Record<keyof T, CellValue>>({ columnLabels, sortColumn, sortDirection, hasActions, sortBy, toggleSortDirection }: Props<T>) => import("react").JSX.Element;
|
|
14
|
+
export declare const Header: <T extends Record<keyof T, CellValue>>({ columnLabels, sortColumn, sortDirection, hasActions, sortBy, toggleSortDirection, selection }: Props<T>) => import("react").JSX.Element;
|
|
11
15
|
export {};
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Fragment } from "react";
|
|
4
4
|
import { RowBox } from "../container/flex.js";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
5
|
+
import { CheckBox } from "../miscellaneous/checkbox/index.js";
|
|
6
|
+
import { cn } from "../../utils/functions/style.js";
|
|
7
|
+
import ArrowUp from "../static/icons/arrow-up.svg";
|
|
8
|
+
import ArrowDown from "../static/icons/arrow-down.svg";
|
|
9
|
+
export const Header = ({ columnLabels, sortColumn, sortDirection, hasActions, sortBy, toggleSortDirection, selection }) => (_jsxs(RowBox, { style: s.container, children: [selection && (_jsx(RowBox, { style: s.selection, children: _jsx(CheckBox, { size: "sm", value: selection.allSelected, update: selection.toggleAll }) })), Object.entries(columnLabels).map(([col, label], i) => (_jsxs(RowBox, { style: cn(s.column, i === 0 && "justify-start"), children: [_jsx("span", { className: s.label, onClick: () => sortBy(col), children: label }), col === sortColumn && (_jsxs(Fragment, { children: [sortDirection === "desc" && _jsx(ArrowDown, { className: s.sorter, onClick: () => toggleSortDirection() }), sortDirection === "asc" && _jsx(ArrowUp, { className: s.sorter, onClick: () => toggleSortDirection() })] }))] }, i))), hasActions && (_jsx(RowBox, { style: s.column, children: _jsx("span", { children: "Actions" }) }))] }));
|
|
8
10
|
const s = {
|
|
9
|
-
container: "w-full px-4
|
|
11
|
+
container: "w-full px-4 h-11 shrink-0 bg-background-light border-b border-border",
|
|
10
12
|
column: "flex-1 gap-1 first:justify-start justify-center last:justify-end items-center uppercase text-primary-dark font-light text-sm",
|
|
13
|
+
selection: "w-10 shrink-0 flex items-center justify-start",
|
|
11
14
|
label: "hover:brightness-120 duration-200 cursor-pointer",
|
|
12
15
|
sorter: "hover:text-primary cursor-pointer duration-200"
|
|
13
16
|
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import type { Column, TableAction } from "./utils/types.js";
|
|
2
|
+
import type { SetState } from "../../utils/types/state.js";
|
|
2
3
|
import { type Row } from "./row.js";
|
|
3
|
-
type Props<T extends Row> = {
|
|
4
|
+
type Props<T extends Row, K extends keyof T = keyof T> = {
|
|
4
5
|
style?: string;
|
|
5
6
|
title?: string;
|
|
6
7
|
columns: Column<T>[];
|
|
7
8
|
items: T[];
|
|
8
9
|
actions?: TableAction<T>[];
|
|
9
10
|
initialSort?: keyof T;
|
|
11
|
+
selection?: {
|
|
12
|
+
key: K;
|
|
13
|
+
values: T[K][];
|
|
14
|
+
update: SetState<T[K][]>;
|
|
15
|
+
};
|
|
10
16
|
};
|
|
11
|
-
export declare function Table<T extends Row>({ style, title, columns, items, actions, initialSort }: Props<T>): import("react").JSX.Element;
|
|
17
|
+
export declare function Table<T extends Row, K extends keyof T = keyof T>({ style, title, columns, items, actions, initialSort, selection }: Props<T, K>): import("react").JSX.Element;
|
|
12
18
|
export type { TableAction } from "./utils/types.js";
|
|
@@ -1,56 +1,70 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTableData } from "./state/_.js";
|
|
4
|
-
import
|
|
4
|
+
import { condenseArray } from "ts-helper-kit";
|
|
5
5
|
import { ColumnBox, RowBox } from "../container/flex.js";
|
|
6
|
-
import {
|
|
6
|
+
import { Select } from "../miscellaneous/dropdown/select.js";
|
|
7
7
|
import { Header } from "./header.js";
|
|
8
8
|
import { TableRow } from "./row.js";
|
|
9
|
-
import { Dropdown, Select, MultiSelect } from "../miscellaneous/dropdown/_.js";
|
|
10
9
|
import { SearchBar } from "./search-bar.js";
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import { cn } from "../../utils/functions/
|
|
14
|
-
export function Table({ style, title, columns, items, actions, initialSort }) {
|
|
10
|
+
import Filters from "./filters.js";
|
|
11
|
+
import DateRange from "./date-range.js";
|
|
12
|
+
import { cn } from "../../utils/functions/style.js";
|
|
13
|
+
export function Table({ style, title, columns, items, actions, initialSort, selection }) {
|
|
15
14
|
const columnKeys = columns.map(column => column.key);
|
|
16
15
|
const filterColumns = columns
|
|
17
16
|
.filter(column => column.filter === "select")
|
|
18
17
|
.map(column => column.key);
|
|
19
18
|
const dateColumn = columns.find(column => column.filter === "date")?.key;
|
|
20
|
-
const { sortedItems, search, setSearch, dateRange, setDateRange, initialDateRange, sortBy, sortColumn, sortDirection, toggleSortDirection, filterOptions, selectedFilters, toggleFilter } = useTableData(items, columns, initialSort);
|
|
19
|
+
const { sortedItems, search, setSearch, dateRange, setDateRange, initialDateRange, sortBy, sortColumn, sortDirection, toggleSortDirection, filterOptions, selectedFilters, toggleFilter, clearFilter } = useTableData(items, columns, initialSort);
|
|
20
|
+
const toggleSelection = (item) => {
|
|
21
|
+
if (!selection)
|
|
22
|
+
return;
|
|
23
|
+
const value = item[selection.key];
|
|
24
|
+
selection.update(selection.values.includes(value)
|
|
25
|
+
? selection.values.filter(x => x !== value)
|
|
26
|
+
: [...selection.values, value]);
|
|
27
|
+
};
|
|
21
28
|
const condensedItems = condenseArray(sortedItems, columnKeys);
|
|
22
|
-
const
|
|
23
|
-
.
|
|
24
|
-
|
|
29
|
+
const visibleSelectionValues = selection
|
|
30
|
+
? condensedItems.map(({ item }) => item[selection.key])
|
|
31
|
+
: [];
|
|
32
|
+
const allSelected = selection
|
|
33
|
+
? visibleSelectionValues.length > 0 &&
|
|
34
|
+
visibleSelectionValues.every(value => selection.values.includes(value))
|
|
35
|
+
: false;
|
|
36
|
+
const toggleAll = () => {
|
|
37
|
+
if (!selection)
|
|
38
|
+
return;
|
|
39
|
+
selection.update(current => {
|
|
40
|
+
if (allSelected) {
|
|
41
|
+
return current.filter(value => !visibleSelectionValues.includes(value));
|
|
42
|
+
}
|
|
43
|
+
return [
|
|
44
|
+
...current,
|
|
45
|
+
...visibleSelectionValues.filter(value => !current.includes(value))
|
|
46
|
+
];
|
|
47
|
+
});
|
|
48
|
+
};
|
|
25
49
|
const getColumnLabel = (key) => {
|
|
26
50
|
const col = columns.find(column => column.key === key);
|
|
27
51
|
return col?.label ?? key.toString().replaceAll("_", " ");
|
|
28
52
|
};
|
|
29
|
-
return (_jsxs(ColumnBox, { style: cn(s.container, style), children: [title && (_jsx("span", { className: s.title, children: title })), _jsxs(RowBox, { style: "gap-3 mb-1", children: [_jsx(SearchBar, { value: search, update: setSearch, resultCount: sortedItems.length }), filterColumns.length > 0 && (_jsx(
|
|
30
|
-
const myFilters = selectedFilters.get(column);
|
|
31
|
-
return (_jsx(MultiSelect, { prefix: getColumnLabel(column).toUpperCase(), options: (filterOptions[column] || []).map(value => ({
|
|
32
|
-
id: String(value),
|
|
33
|
-
label: String(value)
|
|
34
|
-
})), values: Array.from(myFilters || []).map(f => String(f)), update: (x) => toggleFilter(column, x) }, String(column)));
|
|
35
|
-
}) })), _jsx(Select, { prefix: "Sort By", options: columns.map(column => ({
|
|
53
|
+
return (_jsxs(ColumnBox, { style: cn(s.container, style), children: [title && (_jsx("span", { className: s.title, children: title })), _jsxs(RowBox, { style: "gap-3 items-center mb-1", children: [_jsx(SearchBar, { value: search, update: setSearch, resultCount: sortedItems.length }), filterColumns.length > 0 && (_jsx(Filters, { columns: columns, items: selectedFilters, options: filterOptions, update: toggleFilter, clear: clearFilter })), _jsx(Select, { size: "xs", style: "bg-surface-dark border-border-darker", prefix: "Sort By", options: columns.map(column => ({
|
|
36
54
|
id: column.key.toString(),
|
|
37
55
|
label: getColumnLabel(column.key).toUpperCase()
|
|
38
|
-
})), value: sortColumn?.toString(), update: x => sortBy(x) }), dateColumn && (_jsx(
|
|
39
|
-
? `${formatDateLabel(dateRange.from)} – ${formatDateLabel(dateRange.to)}`
|
|
40
|
-
: getColumnLabel(dateColumn) }), children: _jsxs(ColumnBox, { style: "gap-3 p-3 min-w-64", children: [_jsxs("label", { className: "flex flex-col gap-1 text-sm", children: ["From", _jsx("input", { type: "date", value: dateRange.from, onChange: e => setDateRange(prev => ({
|
|
41
|
-
...prev,
|
|
42
|
-
from: e.target.value
|
|
43
|
-
})), className: "px-3 py-2 bg-input border border-border rounded-md" })] }), _jsxs("label", { className: "flex flex-col gap-1 text-sm", children: ["To", _jsx("input", { type: "date", value: dateRange.to, onChange: e => setDateRange(prev => ({
|
|
44
|
-
...prev,
|
|
45
|
-
to: e.target.value
|
|
46
|
-
})), className: "px-3 py-2 bg-input border border-border rounded-md" })] }), (dateRange.from || dateRange.to) && (_jsx(Button, { size: "sm", action: () => setDateRange(initialDateRange), children: "Clear" }))] }) }))] }), _jsxs(ColumnBox, { style: s.subcontainer, children: [_jsx(Header, { sortBy: sortBy, sortColumn: sortColumn, sortDirection: sortDirection, toggleSortDirection: toggleSortDirection, hasActions: !!actions?.length, columnLabels: Object.fromEntries(columns.map(column => [
|
|
56
|
+
})), value: sortColumn?.toString(), update: x => sortBy(x) }), dateColumn && (_jsx(DateRange, { ...dateRange, update: setDateRange, initial: initialDateRange }))] }), _jsxs(ColumnBox, { style: s.subcontainer, children: [_jsx(Header, { sortBy: sortBy, sortColumn: sortColumn, sortDirection: sortDirection, toggleSortDirection: toggleSortDirection, hasActions: !!actions?.length, columnLabels: Object.fromEntries(columns.map(column => [
|
|
47
57
|
column.key,
|
|
48
58
|
getColumnLabel(column.key)
|
|
49
|
-
])) }), _jsx(ColumnBox, { style: s.rows, children: condensedItems.map(({ item, count }, i) => (_jsx(TableRow, { columns: columns, data: item, count: count, actions: actions
|
|
59
|
+
])), selection: selection ? { allSelected, toggleAll } : undefined }), _jsx(ColumnBox, { style: s.rows, children: condensedItems.map(({ item, count }, i) => (_jsx(TableRow, { columns: columns, data: item, count: count, actions: actions, selected: selection
|
|
60
|
+
? selection.values.includes(item[selection.key])
|
|
61
|
+
: undefined, toggleSelected: selection
|
|
62
|
+
? () => toggleSelection(item)
|
|
63
|
+
: undefined }, i))) })] })] }));
|
|
50
64
|
}
|
|
51
65
|
const s = {
|
|
52
|
-
container: "grow gap-3 overflow-hidden",
|
|
66
|
+
container: "grow gap-3 shadow-md overflow-hidden",
|
|
53
67
|
title: "text-xl font-bold text-primary",
|
|
54
|
-
subcontainer: "
|
|
68
|
+
subcontainer: "w-full grow border border-border-dark rounded-md overflow-hidden",
|
|
55
69
|
rows: "grow overflow-y-auto no-scrollbar"
|
|
56
70
|
};
|
|
@@ -5,7 +5,9 @@ interface Props<T extends TableRowType> {
|
|
|
5
5
|
data: T;
|
|
6
6
|
count: number;
|
|
7
7
|
actions?: TableAction<T>[];
|
|
8
|
+
selected?: boolean;
|
|
9
|
+
toggleSelected?: () => void;
|
|
8
10
|
}
|
|
9
11
|
export type Row = Record<string, CellValue>;
|
|
10
|
-
export declare const TableRow: <T extends Row>({ columns, data, actions, count }: Props<T>) => import("react").JSX.Element;
|
|
12
|
+
export declare const TableRow: <T extends Row>({ columns, data, actions, count, selected, toggleSelected }: Props<T>) => import("react").JSX.Element;
|
|
11
13
|
export {};
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { RowBox } from "
|
|
2
|
+
import { RowBox, cn } from "@bouku/core";
|
|
3
|
+
import { CheckBox } from "../miscellaneous/checkbox/index.js";
|
|
3
4
|
import { Actions } from "./actions.js";
|
|
4
5
|
import CheckCircle from "../../assets/icons/check-circle.svg";
|
|
5
6
|
import XCircle from "../../assets/icons/x-circle.svg";
|
|
6
7
|
export const isDateLike = (val) => !isNaN(Date.parse(val)) && isNaN(Number(val));
|
|
7
|
-
|
|
8
|
-
export const TableRow = ({ columns, data, actions, count }) => (_jsxs(RowBox, { style: s.container, children: [columns.map(({ key, format }, i) => {
|
|
8
|
+
export const TableRow = ({ columns, data, actions, count, selected, toggleSelected }) => (_jsxs(RowBox, { style: s.container, children: [toggleSelected && (_jsx(RowBox, { style: s.selection, children: _jsx(CheckBox, { size: "sm", value: selected ?? false, update: toggleSelected }) })), columns.map(({ key, format }, i) => {
|
|
9
9
|
const rawValue = data[key];
|
|
10
|
-
const value = rawValue === 0
|
|
10
|
+
const value = rawValue === 0
|
|
11
|
+
? 0
|
|
12
|
+
: (data[key] ?? "-");
|
|
11
13
|
let cellValue = value;
|
|
12
|
-
if (format
|
|
14
|
+
if (format === "date" &&
|
|
15
|
+
isDateLike(value.toString())) {
|
|
13
16
|
cellValue = new Date(cellValue.toString()).toLocaleDateString();
|
|
14
|
-
|
|
17
|
+
}
|
|
18
|
+
return (_jsx(RowBox, { style: cn(s.cell, i === 0 && "justify-start"), children: _jsx("span", { className: "truncate", children: value === true
|
|
19
|
+
? _jsx(CheckCircle, { className: "text-success" })
|
|
20
|
+
: value === false
|
|
21
|
+
? _jsx(XCircle, { className: "text-error" })
|
|
22
|
+
: cellValue }) }, i));
|
|
15
23
|
}), actions && (_jsx(RowBox, { style: s.cell, children: _jsx(Actions, { row: data, actions: actions }) })), count > 1 && (_jsx("span", { className: "absolute top-0 left-0 pb-px px-1 bg-surface-light border-b border-r rounded-br border-border-light font-bold font-mono text-px text-accent", children: count }))] }));
|
|
16
24
|
const s = {
|
|
17
|
-
container: "relative w-full px-4
|
|
25
|
+
container: "relative w-full px-4 h-15 shrink-0 bg-background even:bg-surface/50 border-b border-border last:border-b-0",
|
|
26
|
+
selection: "w-10 shrink-0 flex items-center justify-start",
|
|
18
27
|
cell: "flex-1 first-of-type:justify-start justify-center last-of-type:justify-end items-center gap-2 font-mono overflow-hidden"
|
|
19
28
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { RowBox } from "../container/flex.js";
|
|
3
|
+
import { cn } from "../../utils/functions/style.js";
|
|
3
4
|
import MagnifyGlass from "../../assets/icons/magnify-glass.svg";
|
|
4
5
|
export function SearchBar({ value, update, resultCount }) {
|
|
5
|
-
return (_jsxs(RowBox, { style: "items-center gap-
|
|
6
|
+
return (_jsxs(RowBox, { style: "group items-center gap-2 mr-auto w-56 px-3 py-2 bg-surface-dark border border-border-darker shadow rounded", children: [_jsx(MagnifyGlass, { className: cn("text-base text-primary-darker group-focus-within:text-primary duration-200", value && value !== "" && "text-primary") }), _jsx("input", { className: "outline-0 text-primary placeholder:text-primary-dark text-xs", placeholder: `Search ${resultCount} result${resultCount !== 1 ? "s" : ""}...`, value: value, onChange: e => update(e.target.value) })] }));
|
|
6
7
|
}
|
|
7
8
|
export default SearchBar;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
2
|
export function useFilters(items, columns) {
|
|
3
|
-
console.log(items, columns);
|
|
4
3
|
const [selectedFilters, setSelectedFilters] = useState(new Map());
|
|
5
4
|
const filterOptions = useMemo(() => {
|
|
6
5
|
return Object.fromEntries(columns.map(column => [
|
|
@@ -20,16 +20,20 @@ export function useSort(items, initialSort) {
|
|
|
20
20
|
if (valB === undefined || valB === null)
|
|
21
21
|
return -1;
|
|
22
22
|
let comparison;
|
|
23
|
-
if (isDateLike(valA) && isDateLike(valB))
|
|
23
|
+
if (isDateLike(valA) && isDateLike(valB)) {
|
|
24
24
|
comparison =
|
|
25
25
|
parseDate(valA).getTime() -
|
|
26
26
|
parseDate(valB).getTime();
|
|
27
|
-
|
|
27
|
+
}
|
|
28
|
+
else if (typeof valA === "number" &&
|
|
29
|
+
typeof valB === "number") {
|
|
28
30
|
comparison = valA - valB;
|
|
29
|
-
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
30
33
|
comparison = String(valA)
|
|
31
34
|
.toLowerCase()
|
|
32
35
|
.localeCompare(String(valB).toLowerCase());
|
|
36
|
+
}
|
|
33
37
|
return isDescending ? -comparison : comparison;
|
|
34
38
|
});
|
|
35
39
|
}, [items, column, isDescending]);
|
|
@@ -41,6 +41,7 @@ export const formatDateLabel = (value) => {
|
|
|
41
41
|
year: "numeric"
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
-
export const isDateLike = (value) => value instanceof Date ||
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
export const isDateLike = (value) => value instanceof Date ||
|
|
45
|
+
(typeof value === "string" &&
|
|
46
|
+
!Number.isNaN(Date.parse(value)) &&
|
|
47
|
+
Number.isNaN(Number(value)));
|