sharpcodes-heroui 1.0.1 → 1.0.2
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/components/fields/autocomplete.js +44 -0
- package/dist/components/fields/breadcrumbs.js +13 -0
- package/dist/components/fields/button.js +9 -0
- package/dist/components/fields/date-range.js +42 -0
- package/dist/components/fields/index.js +10 -0
- package/dist/components/fields/modal.js +13 -0
- package/dist/components/fields/number.js +7 -0
- package/dist/components/fields/scroll.js +3 -0
- package/dist/components/fields/search.js +15 -0
- package/dist/components/fields/table/base.js +130 -0
- package/dist/components/fields/table/empty.js +8 -0
- package/dist/components/fields/table/index.js +70 -0
- package/dist/components/fields/table/pagination.js +51 -0
- package/dist/components/fields/tabs.js +27 -0
- package/dist/components/form-fields/autocomplete.js +45 -0
- package/dist/components/form-fields/date-range.js +46 -0
- package/dist/components/form-fields/date.js +37 -0
- package/dist/components/form-fields/error.js +7 -0
- package/dist/components/form-fields/field.js +7 -0
- package/dist/components/form-fields/index.js +6 -0
- package/dist/components/form-fields/input.js +15 -0
- package/dist/components/index.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/libs/index.js +2 -0
- package/dist/libs/next/axios-client.js +18 -0
- package/dist/libs/next/axios-server.js +49 -0
- package/dist/libs/next/index.js +2 -0
- package/dist/libs/utils/date.js +5 -0
- package/dist/libs/utils/form.js +42 -0
- package/dist/libs/utils/index.js +3 -0
- package/dist/libs/utils/response.js +70 -0
- package/package.json +4 -4
- package/dist/index.cjs.js +0 -75
- package/dist/index.es.js +0 -23088
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { Autocomplete as HeroUIAutoComplete, EmptyState, Label, ListBox, SearchField, Tag, TagGroup, useFilter, } from "@heroui/react";
|
|
5
|
+
// prettier-ignore
|
|
6
|
+
export const AutoComplete = ({ emptyMessage, items, idKey, nameKey, properties, label, onChange, value }) => {
|
|
7
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
8
|
+
const isSingle = properties?.base?.autoComplete?.selectionMode === "single";
|
|
9
|
+
// @ts-expect-error values
|
|
10
|
+
return _jsxs(HeroUIAutoComplete, { selectionMode: "multiple", variant: "secondary", value: isSingle ? value :
|
|
11
|
+
!value ? undefined : _.isArray(value) ? value : [value], ...properties?.base?.autoComplete, onChange: (keys) => {
|
|
12
|
+
if (isSingle)
|
|
13
|
+
// @ts-expect-error keys
|
|
14
|
+
return onChange(keys);
|
|
15
|
+
if (!_.isArray(keys))
|
|
16
|
+
return onChange([]);
|
|
17
|
+
// @ts-expect-error keys
|
|
18
|
+
return onChange(keys);
|
|
19
|
+
}, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(HeroUIAutoComplete.Trigger, { ...properties?.base?.trigger, children: [_jsx(HeroUIAutoComplete.Value, { ...properties?.base?.value, children: ({ defaultChildren, isPlaceholder, state }) => {
|
|
20
|
+
if (isPlaceholder || !state.selectedItems.length)
|
|
21
|
+
return defaultChildren;
|
|
22
|
+
const selectedItemKeys = state.selectedItems.map((item) => item.key);
|
|
23
|
+
return _jsx(TagGroup, { size: "sm", "aria-label": "autocomplete tags", ...properties?.tag?.group, onRemove: (keys) => {
|
|
24
|
+
const keysToRemove = _.toArray(keys);
|
|
25
|
+
const keysCurrent = _.isArray(value) ? value : [value];
|
|
26
|
+
return onChange(keysCurrent.filter((key) => !keysToRemove.includes(key)));
|
|
27
|
+
}, children: _jsx(TagGroup.List, { ...properties?.tag?.list, children: selectedItemKeys.map((selectedItemKey) => {
|
|
28
|
+
const item = items.find((s) => _.get(s, idKey) == selectedItemKey);
|
|
29
|
+
if (!item)
|
|
30
|
+
return null;
|
|
31
|
+
const current = {
|
|
32
|
+
id: _.get(item, idKey),
|
|
33
|
+
name: _.get(item, nameKey)
|
|
34
|
+
};
|
|
35
|
+
return _jsx(Tag, { id: current.id, ...properties?.tag?.item, children: current.name }, current.id);
|
|
36
|
+
}) }) });
|
|
37
|
+
} }), _jsx(HeroUIAutoComplete.Indicator, {})] }), _jsx(HeroUIAutoComplete.Popover, { isNonModal: true, placement: "bottom left", ...properties?.popover?.popover, children: _jsxs(HeroUIAutoComplete.Filter, { filter: contains, ...properties?.popover?.filter, children: [_jsx(SearchField, { autoFocus: true, fullWidth: true, "aria-label": "autocomplete search", name: "search", variant: "secondary", ...properties?.search?.field, children: _jsxs(SearchField.Group, { ...properties?.search?.group, children: [_jsx(SearchField.SearchIcon, { ...properties?.search?.icon }), _jsx(SearchField.Input, { placeholder: "Search...", ...properties?.search?.input }), _jsx(SearchField.ClearButton, { ...properties?.search?.clear })] }) }), _jsx(ListBox, { renderEmptyState: () => _jsx(EmptyState, { ...properties?.list?.empty, children: _jsx("span", { children: emptyMessage || "No results found" }) }), children: items.map((item) => {
|
|
38
|
+
const current = {
|
|
39
|
+
id: _.get(item, idKey),
|
|
40
|
+
name: _.get(item, nameKey)
|
|
41
|
+
};
|
|
42
|
+
return _jsxs(ListBox.Item, { id: current.id, textValue: current.name, "aria-label": current.name, ...properties?.list?.item, children: [_jsx("span", { children: current.name }), _jsx(ListBox.ItemIndicator, { ...properties?.list?.check })] }, current.id);
|
|
43
|
+
}) })] }) })] });
|
|
44
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Breadcrumbs } from "@heroui/react";
|
|
4
|
+
// prettier-ignore
|
|
5
|
+
export const BreadCrumbs = ({ data }) => {
|
|
6
|
+
return _jsx(Breadcrumbs, { children: !data ? null :
|
|
7
|
+
Object.keys(data).map((key, index) => {
|
|
8
|
+
const item = data[key];
|
|
9
|
+
if (!item)
|
|
10
|
+
return;
|
|
11
|
+
return _jsx(Breadcrumbs.Item, { ...item, children: _jsx("span", { children: key }) }, index);
|
|
12
|
+
}) });
|
|
13
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, cn, Spinner, Tooltip, } from "@heroui/react";
|
|
4
|
+
import { Ripple } from "m3-ripple";
|
|
5
|
+
// prettier-ignore
|
|
6
|
+
export const RippledButton = ({ onPress, isLoading, type, tooltipContent, children, properties }) => {
|
|
7
|
+
return _jsxs(Tooltip, { delay: 0, isDisabled: !tooltipContent, ...properties?.tooltip, children: [_jsx(Tooltip.Content, { ...properties?.tooltipContent, children: tooltipContent }), _jsxs(Button, { type: type, variant: "secondary", isPending: isLoading, ...properties?.button, className: cn("font-semibold text-xs active:scale-95 shadow transition-all", properties?.button?.className), onPress: onPress, children: [_jsx(Ripple, { ...properties?.ripple }), isLoading ?
|
|
8
|
+
_jsx(Spinner, { size: "sm", ...properties?.spinner, className: cn("text-accent-foreground", properties?.spinner?.className) }) : null, isLoading && properties?.button?.isIconOnly ? null : children] })] });
|
|
9
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { DateField, DateRangePicker, Label, RangeCalendar } from "@heroui/react";
|
|
5
|
+
import { parseAbsoluteToLocal } from "@internationalized/date";
|
|
6
|
+
import { toMoment } from "../../libs/utils/date";
|
|
7
|
+
// prettier-ignore
|
|
8
|
+
export const DateRange = ({ onChange, value, properties, label }) => {
|
|
9
|
+
const parsed = {
|
|
10
|
+
start: _.isDate(value?.from) ? parseAbsoluteToLocal(value.from.toISOString()) : undefined,
|
|
11
|
+
end: _.isDate(value?.to) ? parseAbsoluteToLocal(value.to.toISOString()) : undefined,
|
|
12
|
+
};
|
|
13
|
+
return _jsxs(DateRangePicker, { hideTimeZone: true, "aria-label": "date range picker", granularity: "day",
|
|
14
|
+
// defaultValue={parsed.start && parsed.end ? parsed : undefined}
|
|
15
|
+
value: parsed, ...properties?.picker, startName: "from", endName: "to", onChange: (value) => {
|
|
16
|
+
if (!value)
|
|
17
|
+
return onChange?.(null);
|
|
18
|
+
const { start, end } = value;
|
|
19
|
+
const from = toMoment().set({
|
|
20
|
+
month: start.month - 1,
|
|
21
|
+
date: start.day,
|
|
22
|
+
year: start.year,
|
|
23
|
+
hour: start.hour,
|
|
24
|
+
minute: start.minute,
|
|
25
|
+
second: start.second,
|
|
26
|
+
millisecond: start.millisecond
|
|
27
|
+
});
|
|
28
|
+
const to = toMoment().set({
|
|
29
|
+
month: end.month - 1,
|
|
30
|
+
date: end.day,
|
|
31
|
+
year: end.year,
|
|
32
|
+
hour: end.hour,
|
|
33
|
+
minute: end.minute,
|
|
34
|
+
second: end.second,
|
|
35
|
+
millisecond: end.millisecond
|
|
36
|
+
});
|
|
37
|
+
return onChange?.({
|
|
38
|
+
from: from.toDate(),
|
|
39
|
+
to: to.toDate()
|
|
40
|
+
});
|
|
41
|
+
}, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(DateField.Group, { variant: "secondary", ...properties?.input?.group, children: [_jsx(DateField.Input, { ...properties?.input?.input, slot: "start", children: (segment) => _jsx(DateField.Segment, { segment: segment, ...properties?.input?.segment }) }), _jsx(DateRangePicker.RangeSeparator, { ...properties?.input?.separator }), _jsx(DateField.Input, { ...properties?.input?.input, slot: "end", children: (segment) => _jsx(DateField.Segment, { segment: segment, ...properties?.input?.segment }) }), _jsx(DateField.Suffix, { ...properties?.input?.trigger?.suffix, children: _jsx(DateRangePicker.Trigger, { ...properties?.input?.trigger?.trigger, children: _jsx(DateRangePicker.TriggerIndicator, { ...properties?.input?.trigger?.indicator }) }) })] }), _jsx(DateRangePicker.Popover, { isNonModal: true, placement: "bottom end", ...properties?.calendar?.popover, children: _jsxs(RangeCalendar, { "aria-label": "calendar period", ...properties?.calendar?.calendar, children: [_jsxs(RangeCalendar.Header, { ...properties?.calendar?.header, children: [_jsxs(RangeCalendar.YearPickerTrigger, { ...properties?.calendar?.yearPicker?.tigger, children: [_jsx(RangeCalendar.YearPickerTriggerHeading, { ...properties?.calendar?.yearPicker?.heading }), _jsx(RangeCalendar.YearPickerTriggerIndicator, { ...properties?.calendar?.yearPicker?.indicator })] }), _jsx(RangeCalendar.NavButton, { slot: "previous", ...properties?.calendar?.previous }), _jsx(RangeCalendar.NavButton, { slot: "next", ...properties?.calendar?.next })] }), _jsxs(RangeCalendar.Grid, { children: [_jsx(RangeCalendar.GridHeader, { children: (day) => _jsx(RangeCalendar.HeaderCell, { children: day }) }), _jsx(RangeCalendar.GridBody, { children: (date) => _jsx(RangeCalendar.Cell, { date: date }) })] }), _jsx(RangeCalendar.YearPickerGrid, { children: _jsx(RangeCalendar.YearPickerGridBody, { children: ({ year }) => _jsx(RangeCalendar.YearPickerCell, { year: year }) }) })] }) })] });
|
|
42
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./autocomplete";
|
|
2
|
+
export * from "./breadcrumbs";
|
|
3
|
+
export * from "./button";
|
|
4
|
+
export * from "./date-range";
|
|
5
|
+
export * from "./modal";
|
|
6
|
+
export * from "./number";
|
|
7
|
+
export * from "./scroll";
|
|
8
|
+
export * from "./search";
|
|
9
|
+
export * from "./table";
|
|
10
|
+
export * from "./tabs";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn, Description, Modal as HeroUIModal, Separator, } from "@heroui/react";
|
|
4
|
+
import { createElement } from "react";
|
|
5
|
+
// prettier-ignore
|
|
6
|
+
export const Modal = ({ isOpen, icon, footer, onOpenChange, properties, showCloseButton, description, heading, children }) => {
|
|
7
|
+
return _jsx(HeroUIModal, { isOpen: isOpen, onOpenChange: onOpenChange, ...properties?.modal, children: _jsx(HeroUIModal.Backdrop, { isDismissable: false, ...properties?.backdrop, children: _jsx(HeroUIModal.Container, { size: "lg", ...properties?.container, children: _jsxs(HeroUIModal.Dialog, { ...properties?.dialog, children: [!showCloseButton ? null :
|
|
8
|
+
_jsx(HeroUIModal.CloseTrigger, { ...properties?.close, className: cn("text-danger bg-danger-soft", properties?.close?.className) }), _jsxs(HeroUIModal.Header, { ...properties?.header, children: [!icon ? null :
|
|
9
|
+
_jsx(HeroUIModal.Icon, { ...properties?.icon, className: cn("bg-default text-foreground", properties?.icon?.className), children: createElement(icon) }), !heading ? null :
|
|
10
|
+
_jsx(HeroUIModal.Heading, { ...properties?.heading, children: heading }), !description ? null :
|
|
11
|
+
_jsx(Description, { ...properties?.description, className: cn("text-muted text-sm", properties?.description?.className), children: description })] }), _jsx(Separator, { ...properties?.separator, className: cn("mt-5 mb-3", properties?.separator?.className) }), _jsx(HeroUIModal.Body, { ...properties?.body, children: children }), !footer ? null :
|
|
12
|
+
_jsx(HeroUIModal.Footer, { ...properties?.footer, children: footer })] }) }) }) });
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn, Label, NumberField, } from "@heroui/react";
|
|
4
|
+
// prettier-ignore
|
|
5
|
+
export const NumericInput = ({ label, onChange, properties, value }) => {
|
|
6
|
+
return _jsxs(NumberField, { minValue: 0, fullWidth: true, ...properties?.field, className: cn("max-w-32", properties?.field?.className), value: value, onChange: onChange, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(NumberField.Group, { ...properties?.group, children: [_jsx(NumberField.DecrementButton, { ...properties?.decrement }), _jsx(NumberField.Input, { ...properties?.input }), _jsx(NumberField.IncrementButton, { ...properties?.increment })] })] });
|
|
7
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn, Label, SearchField, } from "@heroui/react";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { useDebounce } from "react-haiku";
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
export const SearchInputField = ({ label, delay, onDebounce, properties }) => {
|
|
8
|
+
const [text, setText] = useState("");
|
|
9
|
+
const debounced = useDebounce(text, delay || 300);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
onDebounce?.(debounced);
|
|
12
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
13
|
+
}, [debounced]);
|
|
14
|
+
return _jsxs(SearchField, { variant: "secondary", "aria-label": "search for keyword", ...properties?.field, value: text, onChange: setText, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(SearchField.Group, { ...properties?.group, children: [_jsx(SearchField.SearchIcon, { ...properties?.icon }), _jsx(SearchField.Input, { placeholder: "Search...", ...properties?.input, className: cn("w-70", properties?.input?.className) }), _jsx(SearchField.ClearButton, { ...properties?.clear })] })] });
|
|
15
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import z from "zod";
|
|
5
|
+
import { DataTablePagination } from "./pagination";
|
|
6
|
+
import useLocalStorageState from "use-local-storage-state";
|
|
7
|
+
import { Checkbox, cn, Table, TableLayout, Virtualizer } from "@heroui/react";
|
|
8
|
+
import { createColumnHelper, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
|
9
|
+
import { ChevronUp } from "lucide-react";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
11
|
+
// prettier-ignore
|
|
12
|
+
export const DataTable = ({ name, onSelectRows, columns, items, properties, rowIdKey, renderCell, storageBaseName, ...pagination }) => {
|
|
13
|
+
const enableRowSelection = Boolean(onSelectRows);
|
|
14
|
+
const [columnWidths, setColumnWidths] = useLocalStorageState([storageBaseName || "DataTable", name].join("::"), {
|
|
15
|
+
defaultValue: Object.fromEntries(columns.map(({ key }) => [key, null]))
|
|
16
|
+
});
|
|
17
|
+
const [sorting, setSorting] = useState([]);
|
|
18
|
+
const [rowSelection, setRowSelection] = useState({});
|
|
19
|
+
const table = useReactTable({
|
|
20
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
21
|
+
getCoreRowModel: getCoreRowModel(),
|
|
22
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
23
|
+
getSortedRowModel: getSortedRowModel(),
|
|
24
|
+
columns: createColumns({ columns }),
|
|
25
|
+
enableRowSelection,
|
|
26
|
+
data: items,
|
|
27
|
+
initialState: {
|
|
28
|
+
pagination: { pageSize: pagination.count.value }
|
|
29
|
+
},
|
|
30
|
+
state: {
|
|
31
|
+
sorting,
|
|
32
|
+
rowSelection,
|
|
33
|
+
columnVisibility: Object.fromEntries(columns.map((col) => [col.key, !col.hide]))
|
|
34
|
+
},
|
|
35
|
+
columnResizeMode: "onEnd",
|
|
36
|
+
onSortingChange: setSorting,
|
|
37
|
+
onRowSelectionChange: setRowSelection,
|
|
38
|
+
});
|
|
39
|
+
const sortDescriptor = toSortDescriptor(sorting);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!onSelectRows)
|
|
42
|
+
return;
|
|
43
|
+
const arr = z.coerce.number().array().safeParse(Object.keys(rowSelection));
|
|
44
|
+
if (arr.success) {
|
|
45
|
+
const keys = arr.data
|
|
46
|
+
.map((index) => _.get(items[index], rowIdKey) ?? null)
|
|
47
|
+
.filter((item) => item !== null);
|
|
48
|
+
// @ts-expect-error keys
|
|
49
|
+
onSelectRows(keys);
|
|
50
|
+
}
|
|
51
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
+
}, [rowSelection, items, rowIdKey]);
|
|
53
|
+
return _jsx(Virtualizer, { layout: TableLayout, layoutOptions: {
|
|
54
|
+
headingHeight: 42,
|
|
55
|
+
rowHeight: 52,
|
|
56
|
+
...properties?.virtualization
|
|
57
|
+
}, children: _jsxs(Table, { ...properties?.table, className: cn("min-h-[50dvh]", properties?.table?.className), children: [_jsx(Table.ScrollContainer, { ...properties?.scrollContainer, children: _jsx(Table.ResizableContainer, { onResize: (...map) => {
|
|
58
|
+
const next = Object.fromEntries(Array.from(map.entries())
|
|
59
|
+
.map(([key, value]) => [String(key),
|
|
60
|
+
Object.fromEntries(value)
|
|
61
|
+
]));
|
|
62
|
+
// @ts-expect-error next[0]
|
|
63
|
+
setColumnWidths(next[0]);
|
|
64
|
+
}, ...properties?.tableReizeableContainer, children: _jsxs(Table.Content, { "aria-label": "tanstack usable data table", sortDescriptor: sortDescriptor, onSortChange: (s) => setSorting(toSortingState(s)), ...properties?.tableContent, className: cn("w-full overflow-x-auto", properties?.tableContent?.className), children: [_jsxs(Table.Header, { ...properties?.tableHeader, children: [!enableRowSelection ? null :
|
|
65
|
+
_jsx(Table.Column, { maxWidth: 20, children: _jsx(InditerminateCheckbox, { id: "all", properties: {
|
|
66
|
+
checkbox: {
|
|
67
|
+
"aria-label": "Select all",
|
|
68
|
+
variant: "primary",
|
|
69
|
+
className: "w-fit"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
// onChange={table.getToggleAllRowsSelectedHandler()}
|
|
73
|
+
onChange: table.toggleAllRowsSelected, isSelected: table.getIsAllRowsSelected(), isIndeterminate: table.getIsSomeRowsSelected() }) }), table.getHeaderGroups().at(0)?.headers.map((header, index) => {
|
|
74
|
+
// @ts-expect-error width
|
|
75
|
+
return _jsx(Table.Column, { id: header.id, isRowHeader: index === 0, allowsSorting: header.column.getCanSort(), ...properties?.tableColumn, defaultWidth: columnWidths?.[header.id], className: cn("rounded-none p-0", properties?.tableColumn?.className), children: ({ sortDirection }) => _jsxs(SortableColumnHeader, { direction: sortDirection, children: [flexRender(header.column.columnDef.header, header.getContext()), _jsx(Table.ColumnResizer, { ...properties?.tableColumnResizer })] }) }, header.id);
|
|
76
|
+
})] }), _jsx(Table.Body, { ...properties?.tableBody, children: table.getRowModel().rows.map((row, rowIndex) => {
|
|
77
|
+
return _jsxs(Table.Row, { id: row.id, ...properties?.tableRow, children: [!enableRowSelection ? null :
|
|
78
|
+
_jsx(Table.Cell, { className: "pr-0", children: _jsx(InditerminateCheckbox, { id: row.id, label: String(_.get(row, rowIdKey)), onChange: row.getToggleSelectedHandler(), isSelected: row.getIsSelected(), isIndeterminate: row.getIsSomeSelected(), properties: {
|
|
79
|
+
checkbox: {
|
|
80
|
+
className: "w-fit"
|
|
81
|
+
}
|
|
82
|
+
} }) }, row.id), row.getVisibleCells().map((cell) => {
|
|
83
|
+
const original = row.original;
|
|
84
|
+
// @ts-expect-error cell
|
|
85
|
+
return _jsx(Table.Cell, { ...properties?.tableCell, className: cn("whitespace-nowrap flex items-center", properties?.tableCell?.className), children: renderCell?.({ row: original, rowIndex })?.[cell.column.id] ??
|
|
86
|
+
flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
|
|
87
|
+
})] }, row.id);
|
|
88
|
+
}) })] }) }) }), _jsx(Table.Footer, { ...properties?.tableFooter, className: cn("flex items-end", properties?.tableFooter?.className), children: _jsx(DataTablePagination, { ...pagination, properties: properties, prev: {
|
|
89
|
+
...pagination.prev,
|
|
90
|
+
onPress: pagination.prev?.onPress ?? table.previousPage,
|
|
91
|
+
canPrev: pagination.prev?.canPrev ?? table.getCanPreviousPage(),
|
|
92
|
+
}, next: {
|
|
93
|
+
...pagination.next,
|
|
94
|
+
onPress: pagination.next?.onPress ?? table.nextPage,
|
|
95
|
+
canNext: pagination.next?.canNext ?? table.getCanNextPage(),
|
|
96
|
+
} }) })] }) });
|
|
97
|
+
};
|
|
98
|
+
// prettier-ignore
|
|
99
|
+
const createColumns = ({ columns }) => {
|
|
100
|
+
const helper = createColumnHelper();
|
|
101
|
+
// @ts-expect-error columnKey
|
|
102
|
+
return columns.map((column) => helper.accessor(column.key, {
|
|
103
|
+
header: column.title
|
|
104
|
+
}));
|
|
105
|
+
};
|
|
106
|
+
// prettier-ignore
|
|
107
|
+
const toSortDescriptor = (sorting) => {
|
|
108
|
+
const first = _.head(sorting);
|
|
109
|
+
if (!first)
|
|
110
|
+
return undefined;
|
|
111
|
+
return {
|
|
112
|
+
column: first.id,
|
|
113
|
+
direction: first.desc ? "descending" : "ascending",
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
// prettier-ignore
|
|
117
|
+
const toSortingState = (descriptor) => {
|
|
118
|
+
return [{ desc: descriptor.direction === "descending", id: String(descriptor.column) }];
|
|
119
|
+
};
|
|
120
|
+
// prettier-ignore
|
|
121
|
+
const SortableColumnHeader = ({ children, direction }) => {
|
|
122
|
+
return _jsxs("div", { className: "flex items-center gap-x-2 p-4 header-cell truncate", children: [children, !direction ? null :
|
|
123
|
+
_jsx(ChevronUp, { className: cn("transform size-3 transition-transform duration-100 ease-out",
|
|
124
|
+
// @ts-expect-error overlap
|
|
125
|
+
direction === "descending" ? "rotate-180" : "") })] });
|
|
126
|
+
};
|
|
127
|
+
// prettier-ignore
|
|
128
|
+
const InditerminateCheckbox = ({ label, properties, ...rest }) => {
|
|
129
|
+
return _jsx(Checkbox, { "aria-label": `Select row ${label || ""}`.trim(), slot: "selection", variant: "secondary", isDisabled: false, ...properties?.checkbox, ...rest, children: _jsx(Checkbox.Control, { children: _jsx(Checkbox.Indicator, {}) }) });
|
|
130
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Surface } from "@heroui/react";
|
|
4
|
+
import { PackageOpenIcon } from "lucide-react";
|
|
5
|
+
// prettier-ignore
|
|
6
|
+
export const RenderEmptyDataTable = ({ isDropTarget, isEmpty }) => {
|
|
7
|
+
return _jsxs(Surface, { variant: "secondary", className: "h-full rounded-2xl flex items-center text-center justify-center flex-col", children: [_jsx(PackageOpenIcon, { className: "size-15 mb-3" }), _jsx("h5", { className: "text-lg", children: "No records found." }), _jsx("p", { className: "text-sm text-zinc-500", children: "Try adjusting your filters or add new entries." })] });
|
|
8
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import z from "zod";
|
|
5
|
+
import { cn } from "@heroui/react";
|
|
6
|
+
import { FilesIcon, InfoIcon, PencilIcon, ShieldAlertIcon, TrashIcon } from "lucide-react";
|
|
7
|
+
import { createElement } from "react";
|
|
8
|
+
import { RippledButton } from "../button";
|
|
9
|
+
import { toMoment } from "../../../libs/utils/date";
|
|
10
|
+
export { DataTable } from "./base";
|
|
11
|
+
export { RenderEmptyDataTable } from "./empty";
|
|
12
|
+
export { DataTablePagination } from "./pagination";
|
|
13
|
+
// prettier-ignore
|
|
14
|
+
export const RenderDateRangeDataTableValue = ({ dates: [from, to] }) => {
|
|
15
|
+
return _jsxs("div", { className: "flex gap-x-2 items-center", children: [_jsx("div", { className: "w-20.5", children: toMoment(from).format("ll") }), _jsx("span", { className: "mr-1", children: "-" }), _jsx("div", { children: toMoment(to).format("ll") })] });
|
|
16
|
+
};
|
|
17
|
+
// prettier-ignore
|
|
18
|
+
export const RenderDateTimeDataTableValue = ({ date }) => {
|
|
19
|
+
return _jsxs("div", { className: "flex gap-x-2 items-center", children: [_jsx("div", { className: "w-20.5", children: toMoment(date).format("ll") }), _jsx("span", { className: "mr-1", children: "-" }), _jsx("div", { children: toMoment(date).format("hh:mm A") })] });
|
|
20
|
+
};
|
|
21
|
+
// prettier-ignore
|
|
22
|
+
export const RenderCurrencyDataTableValue = ({ isNegativeDanger, value }) => {
|
|
23
|
+
if (_.isNil(value))
|
|
24
|
+
return null;
|
|
25
|
+
const schema = z.coerce.number().safeParse(value);
|
|
26
|
+
if (!schema.success)
|
|
27
|
+
return value;
|
|
28
|
+
return _jsx("div", { className: "flex gap-x-2 items-center", children: _jsx("div", { className: cn("w-20.5", isNegativeDanger && schema.data < 0 ? "text-danger" : ""), children: Intl.NumberFormat("en-PH", {
|
|
29
|
+
style: "currency",
|
|
30
|
+
currency: "PHP"
|
|
31
|
+
}).format(schema.data) }) });
|
|
32
|
+
};
|
|
33
|
+
// prettier-ignore
|
|
34
|
+
export const RenderActionDataTableValue = (props) => {
|
|
35
|
+
const options = {
|
|
36
|
+
view: {
|
|
37
|
+
icon: InfoIcon,
|
|
38
|
+
variant: "tertiary"
|
|
39
|
+
},
|
|
40
|
+
edit: {
|
|
41
|
+
icon: PencilIcon,
|
|
42
|
+
variant: "tertiary"
|
|
43
|
+
},
|
|
44
|
+
delete: {
|
|
45
|
+
icon: TrashIcon,
|
|
46
|
+
variant: "danger-soft"
|
|
47
|
+
},
|
|
48
|
+
audit: {
|
|
49
|
+
icon: ShieldAlertIcon,
|
|
50
|
+
variant: "tertiary"
|
|
51
|
+
},
|
|
52
|
+
files: {
|
|
53
|
+
icon: FilesIcon,
|
|
54
|
+
variant: "secondary"
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const items = Object.entries(props).reduce((acc, [k, value]) => {
|
|
58
|
+
const key = k;
|
|
59
|
+
const { onPress, tooltip } = value;
|
|
60
|
+
acc[key] = _jsx(RippledButton, { properties: {
|
|
61
|
+
button: {
|
|
62
|
+
size: "sm",
|
|
63
|
+
isIconOnly: true,
|
|
64
|
+
variant: options[key].variant
|
|
65
|
+
}
|
|
66
|
+
}, tooltipContent: tooltip, onPress: onPress, type: "button", children: createElement(options[key].icon, { className: "size-4" }) }, key);
|
|
67
|
+
return acc;
|
|
68
|
+
}, {});
|
|
69
|
+
return _jsx("div", { className: "flex gap-2", children: Object.values(items) });
|
|
70
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn, Pagination } from "@heroui/react";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { NumericInput } from "../number";
|
|
6
|
+
import { Ripple } from "m3-ripple";
|
|
7
|
+
// prettier-ignore
|
|
8
|
+
export const DataTablePagination = ({ renderPaginationSummary, gotoPage, locale, count, page, totalPages, properties, next, prev, hidePageNumbers }) => {
|
|
9
|
+
const { displayPage, take } = useMemo(() => ({
|
|
10
|
+
take: page * count.value + 1,
|
|
11
|
+
displayPage: page,
|
|
12
|
+
}), [count.value, page]);
|
|
13
|
+
const getPageNumbers = () => {
|
|
14
|
+
const content = [];
|
|
15
|
+
if (totalPages <= 7) {
|
|
16
|
+
for (let index = 1; index <= totalPages; index++) {
|
|
17
|
+
content.push(index);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
content.push(1);
|
|
22
|
+
if (page > 3) {
|
|
23
|
+
content.push("...");
|
|
24
|
+
}
|
|
25
|
+
const start = Math.max(2, page - 1);
|
|
26
|
+
const end = Math.min(totalPages - 1, page + 1);
|
|
27
|
+
for (let index = start; index <= end; index++) {
|
|
28
|
+
content.push(index);
|
|
29
|
+
}
|
|
30
|
+
if (page < totalPages - 2) {
|
|
31
|
+
content.push("...");
|
|
32
|
+
}
|
|
33
|
+
content.push(totalPages);
|
|
34
|
+
}
|
|
35
|
+
return content;
|
|
36
|
+
};
|
|
37
|
+
return _jsxs(Pagination, { size: "sm", ...properties?.pagination, className: cn("items-center", properties?.pagination?.className), children: [_jsx(NumericInput, { onChange: count.onChange, value: count.value, properties: {
|
|
38
|
+
field: {
|
|
39
|
+
maxValue: count.max,
|
|
40
|
+
minValue: 1,
|
|
41
|
+
},
|
|
42
|
+
label: { className: "text-zinc-500 text-xs" }
|
|
43
|
+
}, label: "Row count" }), _jsx(Pagination.Summary, { ...properties?.paginationSummary, children: renderPaginationSummary?.(take) ||
|
|
44
|
+
_jsx("div", { className: "flex flex-col", children: _jsx("span", { children: `Page ${displayPage.toLocaleString(locale)} of ${totalPages.toLocaleString(locale)}` }) }) }), _jsxs(Pagination.Content, { ...properties?.paginationContent, children: [_jsx(Pagination.Item, { ...properties?.paginationItem, children: _jsxs(Pagination.Previous, { isDisabled: !prev?.canPrev, onPress: prev?.onPress, ...properties?.paginationPrevButton, children: [_jsx(Ripple, {}), _jsx(Pagination.PreviousIcon, { ...properties?.paginationPrevIcon }), _jsx("span", { children: prev?.text || "Prev" })] }) }), hidePageNumbers ? null :
|
|
45
|
+
getPageNumbers().map((current, index) => {
|
|
46
|
+
const isActive = current === page;
|
|
47
|
+
if (current === "...")
|
|
48
|
+
return _jsx(Pagination.Item, { ...properties?.paginationItem, children: _jsx(Pagination.Ellipsis, {}) }, ["e", index].join(":"));
|
|
49
|
+
return _jsx(Pagination.Item, { ...properties?.paginationItem, children: _jsx(Pagination.Link, { isActive: isActive, onPress: () => gotoPage?.(current), className: cn(isActive ? "bg-accent/5" : ""), children: _jsx("span", { className: isActive ? "text-accent font-bold" : "font-normal", children: current }) }) }, current);
|
|
50
|
+
}), _jsx(Pagination.Item, { ...properties?.paginationItem, children: _jsxs(Pagination.Next, { isDisabled: !next?.canNext, onPress: next?.onPress, ...properties?.paginationNextButton, children: [_jsx(Ripple, {}), _jsx("span", { children: next?.text || "Next" }), _jsx(Pagination.NextIcon, { ...properties?.paginationNextIcon })] }) })] })] });
|
|
51
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { cn, Tabs as HeroUITabs } from "@heroui/react";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
export const Tabs = ({ pathname, renderTab, data, properties }) => {
|
|
8
|
+
const activeKey = useMemo(() => {
|
|
9
|
+
const paths = pathname.split("/");
|
|
10
|
+
const path = paths[paths.length - 1];
|
|
11
|
+
const activeKey = Object.keys(data).find((key) => {
|
|
12
|
+
const item = _.get(data, key);
|
|
13
|
+
if (item.href)
|
|
14
|
+
return item.href.startsWith(pathname);
|
|
15
|
+
return key.toLowerCase() == path.toLowerCase();
|
|
16
|
+
});
|
|
17
|
+
return activeKey;
|
|
18
|
+
}, [pathname, data]);
|
|
19
|
+
return _jsxs(HeroUITabs, { defaultSelectedKey: activeKey, ...properties?.tabs, children: [_jsx(HeroUITabs.ListContainer, { ...properties?.listContainer, children: _jsx(HeroUITabs.List, { "aria-label": "list options", ...properties?.list, children: Object.keys(data).map((k) => {
|
|
20
|
+
const key = k;
|
|
21
|
+
const { href } = data[key];
|
|
22
|
+
return _jsxs(HeroUITabs.Tab, { id: key, href: href, ...properties?.tab, className: cn("w-fit", properties?.tab?.className), children: [renderTab?.[key] ?? key, _jsx(HeroUITabs.Indicator, { ...properties?.indicator })] }, key);
|
|
23
|
+
}) }) }), Object.keys(data).map((key) => {
|
|
24
|
+
const { component } = data[key];
|
|
25
|
+
return _jsx(HeroUITabs.Panel, { id: key, ...properties?.panel, children: component }, key);
|
|
26
|
+
})] });
|
|
27
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { Autocomplete, cn, EmptyState, Label, ListBox, SearchField, Tag, TagGroup, useFilter, } from "@heroui/react";
|
|
5
|
+
import { FormField } from "./field";
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
export const AutoCompleteFormField = ({ emptyMessage, items, idKey, nameKey, properties, label, ...form }) => {
|
|
8
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
9
|
+
const isSingle = properties?.base?.autoComplete?.selectionMode === "single";
|
|
10
|
+
return _jsx(FormField, { ...form, children: ({ field: { value, onChange, ...field }, fieldState }) => {
|
|
11
|
+
// @ts-expect-error keys
|
|
12
|
+
return _jsxs(Autocomplete, { isInvalid: fieldState.invalid, selectionMode: "multiple", variant: "secondary", value: isSingle ? value :
|
|
13
|
+
!value ? undefined : _.isArray(value) ? value : [value], ...properties?.base?.autoComplete, ...field, onChange: (keys) => {
|
|
14
|
+
if (isSingle)
|
|
15
|
+
return onChange(keys);
|
|
16
|
+
if (!_.isArray(keys))
|
|
17
|
+
return onChange([]);
|
|
18
|
+
return onChange(keys);
|
|
19
|
+
}, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(Autocomplete.Trigger, { ...properties?.base?.trigger, className: cn("py-0 items-center", properties?.base?.trigger?.className), children: [_jsx(Autocomplete.Value, { ...properties?.base?.value, children: ({ defaultChildren, isPlaceholder, state }) => {
|
|
20
|
+
if (isPlaceholder || !state.selectedItems.length)
|
|
21
|
+
return defaultChildren;
|
|
22
|
+
const selectedItemKeys = state.selectedItems.map((item) => item.key);
|
|
23
|
+
return _jsx(TagGroup, { size: "lg", "aria-label": "autocomplete tags", ...properties?.tag?.group, onRemove: (keys) => {
|
|
24
|
+
const keysToRemove = _.toArray(keys);
|
|
25
|
+
const keysCurrent = _.isArray(value) ? value : [value];
|
|
26
|
+
return onChange(keysCurrent.filter((key) => !keysToRemove.includes(key)));
|
|
27
|
+
}, children: _jsx(TagGroup.List, { ...properties?.tag?.list, children: selectedItemKeys.map((selectedItemKey) => {
|
|
28
|
+
const item = items.find((s) => _.get(s, idKey) == selectedItemKey);
|
|
29
|
+
if (!item)
|
|
30
|
+
return null;
|
|
31
|
+
const current = {
|
|
32
|
+
id: _.get(item, idKey),
|
|
33
|
+
name: _.get(item, nameKey)
|
|
34
|
+
};
|
|
35
|
+
return _jsx(Tag, { id: current.id, ...properties?.tag?.item, className: cn(isSingle ? "[&_.close-button]:hidden" : undefined, "bg-transparent!", properties?.tag?.item?.className), textValue: current.name, children: _jsx("span", { children: current.name }) }, current.id);
|
|
36
|
+
}) }) });
|
|
37
|
+
} }), _jsx(Autocomplete.Indicator, {})] }), _jsx(Autocomplete.Popover, { isNonModal: true, placement: "bottom left", ...properties?.popover?.popover, children: _jsxs(Autocomplete.Filter, { filter: contains, ...properties?.popover?.filter, children: [_jsx(SearchField, { autoFocus: true, fullWidth: true, "aria-label": "autocomplete search", name: "search", variant: "secondary", ...properties?.search?.field, children: _jsxs(SearchField.Group, { ...properties?.search?.group, children: [_jsx(SearchField.SearchIcon, { ...properties?.search?.icon }), _jsx(SearchField.Input, { placeholder: "Search...", ...properties?.search?.input }), _jsx(SearchField.ClearButton, { ...properties?.search?.clear })] }) }), _jsx(ListBox, { renderEmptyState: () => _jsx(EmptyState, { ...properties?.list?.empty, children: _jsx("span", { children: emptyMessage || "No results found" }) }), children: items.map((item) => {
|
|
38
|
+
const current = {
|
|
39
|
+
id: _.get(item, idKey),
|
|
40
|
+
name: _.get(item, nameKey)
|
|
41
|
+
};
|
|
42
|
+
return _jsxs(ListBox.Item, { id: current.id, textValue: current.name, "aria-label": current.name, ...properties?.list?.item, children: [_jsx("span", { children: current.name }), _jsx(ListBox.ItemIndicator, { ...properties?.list?.check })] }, current.id);
|
|
43
|
+
}) })] }) })] });
|
|
44
|
+
} });
|
|
45
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { FormField } from "./field";
|
|
5
|
+
import { DateField, DateRangePicker, Label, RangeCalendar } from "@heroui/react";
|
|
6
|
+
import { parseAbsoluteToLocal } from "@internationalized/date";
|
|
7
|
+
import { FormFieldErrorMessage } from "./error";
|
|
8
|
+
import { toMoment } from "../../libs/utils/date";
|
|
9
|
+
// prettier-ignore
|
|
10
|
+
export const DateRangeFormField = ({ startName, endName, properties, label, ...form }) => {
|
|
11
|
+
return _jsx(FormField, { ...form, children: ({ field: { onChange, value, ...field }, fieldState }) => {
|
|
12
|
+
const start = _.get(value, startName.split(".").slice(1).join("."));
|
|
13
|
+
const end = _.get(value, startName.split(".").slice(1).join("."));
|
|
14
|
+
const parsed = {
|
|
15
|
+
start: _.isDate(start) ? parseAbsoluteToLocal(start.toISOString()) : undefined,
|
|
16
|
+
end: _.isDate(end) ? parseAbsoluteToLocal(end.toISOString()) : undefined,
|
|
17
|
+
};
|
|
18
|
+
return _jsxs(DateRangePicker, { hideTimeZone: true, isInvalid: fieldState.invalid, "aria-label": "date range picker", granularity: "day", defaultValue: parsed.start && parsed.end ? parsed : undefined, ...properties?.picker, ...field, startName: startName, endName: endName, onChange: (value) => {
|
|
19
|
+
if (!value)
|
|
20
|
+
return onChange(null);
|
|
21
|
+
const { start, end } = value;
|
|
22
|
+
const from = toMoment().set({
|
|
23
|
+
month: start.month - 1,
|
|
24
|
+
date: start.day,
|
|
25
|
+
year: start.year,
|
|
26
|
+
hour: start.hour,
|
|
27
|
+
minute: start.minute,
|
|
28
|
+
second: start.second,
|
|
29
|
+
millisecond: start.millisecond
|
|
30
|
+
});
|
|
31
|
+
const to = toMoment().set({
|
|
32
|
+
month: end.month - 1,
|
|
33
|
+
date: end.day,
|
|
34
|
+
year: end.year,
|
|
35
|
+
hour: end.hour,
|
|
36
|
+
minute: end.minute,
|
|
37
|
+
second: end.second,
|
|
38
|
+
millisecond: end.millisecond
|
|
39
|
+
});
|
|
40
|
+
return onChange({
|
|
41
|
+
from: from.toDate(),
|
|
42
|
+
to: to.toDate()
|
|
43
|
+
});
|
|
44
|
+
}, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(DateField.Group, { variant: "secondary", ...properties?.input?.group, children: [_jsx(DateField.Input, { ...properties?.input?.input, slot: "start", children: (segment) => _jsx(DateField.Segment, { segment: segment, ...properties?.input?.segment }) }), _jsx(DateRangePicker.RangeSeparator, { ...properties?.input?.separator }), _jsx(DateField.Input, { ...properties?.input?.input, slot: "end", children: (segment) => _jsx(DateField.Segment, { segment: segment, ...properties?.input?.segment }) }), _jsx(DateField.Suffix, { ...properties?.input?.trigger?.suffix, children: _jsx(DateRangePicker.Trigger, { ...properties?.input?.trigger?.trigger, children: _jsx(DateRangePicker.TriggerIndicator, { ...properties?.input?.trigger?.indicator }) }) })] }), _jsx(FormFieldErrorMessage, { fieldState: fieldState, properties: { error: properties?.error } }), _jsx(DateRangePicker.Popover, { isNonModal: true, placement: "bottom end", ...properties?.calendar?.popover, children: _jsxs(RangeCalendar, { "aria-label": "calendar period", ...properties?.calendar?.calendar, children: [_jsxs(RangeCalendar.Header, { ...properties?.calendar?.header, children: [_jsxs(RangeCalendar.YearPickerTrigger, { ...properties?.calendar?.yearPicker?.tigger, children: [_jsx(RangeCalendar.YearPickerTriggerHeading, { ...properties?.calendar?.yearPicker?.heading }), _jsx(RangeCalendar.YearPickerTriggerIndicator, { ...properties?.calendar?.yearPicker?.indicator })] }), _jsx(RangeCalendar.NavButton, { slot: "previous", ...properties?.calendar?.previous }), _jsx(RangeCalendar.NavButton, { slot: "next", ...properties?.calendar?.next })] }), _jsxs(RangeCalendar.Grid, { children: [_jsx(RangeCalendar.GridHeader, { children: (day) => _jsx(RangeCalendar.HeaderCell, { children: day }) }), _jsx(RangeCalendar.GridBody, { children: (date) => _jsx(RangeCalendar.Cell, { date: date }) })] }), _jsx(RangeCalendar.YearPickerGrid, { children: _jsx(RangeCalendar.YearPickerGridBody, { children: ({ year }) => _jsx(RangeCalendar.YearPickerCell, { year: year }) }) })] }) })] });
|
|
45
|
+
} });
|
|
46
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FormField } from "./field";
|
|
4
|
+
import { DateField, DatePicker, Calendar, Label, TimeField, cn } from "@heroui/react";
|
|
5
|
+
import { parseAbsoluteToLocal } from "@internationalized/date";
|
|
6
|
+
import { FormFieldErrorMessage } from "./error";
|
|
7
|
+
import { toMoment } from "../../libs/utils/date";
|
|
8
|
+
// prettier-ignore
|
|
9
|
+
export const DateFormField = ({ label, properties, ...form }) => {
|
|
10
|
+
const showTimePicker = properties?.picker?.granularity && ["hour", "minute", "second"].includes(properties.picker.granularity);
|
|
11
|
+
return _jsx(FormField, { ...form, children: ({ field: { value, onChange, ...field }, fieldState }) => _jsxs(DatePicker, { hideTimeZone: true, isInvalid: fieldState.invalid, "aria-label": label || "date picker", value: !value ? undefined : parseAbsoluteToLocal(value.toISOString()), shouldCloseOnSelect: !showTimePicker, ...properties?.picker, ...field, onChange: (value) => {
|
|
12
|
+
if (!value)
|
|
13
|
+
return onChange(null);
|
|
14
|
+
const date = toMoment().set({
|
|
15
|
+
month: value.month - 1,
|
|
16
|
+
date: value.day,
|
|
17
|
+
year: value.year,
|
|
18
|
+
hour: value.hour,
|
|
19
|
+
minute: value.minute,
|
|
20
|
+
second: value.second,
|
|
21
|
+
millisecond: value.millisecond
|
|
22
|
+
});
|
|
23
|
+
return onChange(date.toDate());
|
|
24
|
+
}, children: [_jsx(Label, { ...properties?.label, children: label }), _jsxs(DateField.Group, { variant: "secondary", ...properties?.input?.group, children: [_jsx(DateField.Input, { ...properties?.input?.input, children: (segment) => _jsx(DateField.Segment, { segment: segment, ...properties?.input?.segment }) }), _jsx(DateField.Suffix, { ...properties?.input?.trigger?.suffix, children: _jsx(DatePicker.Trigger, { ...properties?.input?.trigger?.trigger, children: _jsx(DatePicker.TriggerIndicator, { ...properties?.input?.trigger?.indicator }) }) })] }), _jsx(FormFieldErrorMessage, { fieldState: fieldState, properties: { error: properties?.error } }), _jsxs(DatePicker.Popover, { isNonModal: true, placement: "bottom end", ...properties?.calendar?.popover, className: cn("min-w-64", properties?.calendar?.popover?.className), children: [_jsxs(Calendar, { "aria-label": "calendar period", ...properties?.calendar?.calendar, children: [_jsxs(Calendar.Header, { ...properties?.calendar?.header, children: [_jsxs(Calendar.YearPickerTrigger, { ...properties?.calendar?.yearPicker?.tigger, children: [_jsx(Calendar.YearPickerTriggerHeading, { ...properties?.calendar?.yearPicker?.heading }), _jsx(Calendar.YearPickerTriggerIndicator, { ...properties?.calendar?.yearPicker?.indicator })] }), _jsx(Calendar.NavButton, { slot: "previous", ...properties?.calendar?.previous }), _jsx(Calendar.NavButton, { slot: "next", ...properties?.calendar?.next })] }), _jsxs(Calendar.Grid, { children: [_jsx(Calendar.GridHeader, { children: (day) => _jsx(Calendar.HeaderCell, { children: day }) }), _jsx(Calendar.GridBody, { children: (date) => _jsx(Calendar.Cell, { date: date }) })] }), _jsx(Calendar.YearPickerGrid, { children: _jsx(Calendar.YearPickerGridBody, { children: ({ year }) => _jsx(Calendar.YearPickerCell, { year: year }) }) })] }), showTimePicker ?
|
|
25
|
+
_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, {}), _jsx(TimeField, { defaultValue: value, shouldForceLeadingZeros: properties?.picker?.shouldForceLeadingZeros, hideTimeZone: properties?.picker?.hideTimeZone, "aria-label": label?.concat(" time ") || "time picker", hourCycle: properties?.picker?.hourCycle, onChange: (timeValue) => {
|
|
26
|
+
if (!timeValue)
|
|
27
|
+
return;
|
|
28
|
+
const date = toMoment(value).set({
|
|
29
|
+
hour: timeValue.hour,
|
|
30
|
+
minute: timeValue.minute,
|
|
31
|
+
second: timeValue.second,
|
|
32
|
+
millisecond: timeValue.millisecond
|
|
33
|
+
});
|
|
34
|
+
return onChange(date.toDate());
|
|
35
|
+
}, children: _jsx(TimeField.Group, { variant: "secondary", children: _jsx(TimeField.Input, { children: (segment) => _jsx(TimeField.Segment, { segment: segment }) }) }) })] })
|
|
36
|
+
: null] })] }) });
|
|
37
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FieldError } from "@heroui/react";
|
|
4
|
+
// prettier-ignore
|
|
5
|
+
export const FormFieldErrorMessage = ({ fieldState, properties }) => {
|
|
6
|
+
return _jsxs(FieldError, { ...properties?.error, children: [" ", fieldState.error?.message || fieldState.error?.root?.message] });
|
|
7
|
+
};
|