@sincpro/web-ui 1.1.0 → 1.3.0
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/activity/Activity.js +1 -1
- package/dist/components/activity/Messages.d.ts +4 -2
- package/dist/components/activity/Messages.js +4 -4
- package/dist/components/activity/Timeline.js +1 -1
- package/dist/components/assist/AssistMark.js +1 -1
- package/dist/components/assist/AssistSuggestion.js +1 -1
- package/dist/components/assist/ChangeReview.js +2 -2
- package/dist/components/calendar/RecordCalendar.js +4 -1
- package/dist/components/form/Sheet.js +1 -1
- package/dist/components/lines/LineItems.d.ts +1 -1
- package/dist/components/lines/LineItems.js +2 -2
- package/dist/components/listing/ListingBar.js +1 -1
- package/dist/components/listing/Row.d.ts +7 -0
- package/dist/components/listing/Row.js +7 -1
- package/dist/components/listing/ViewSwitcher.d.ts +7 -2
- package/dist/components/listing/index.d.ts +1 -1
- package/dist/components/listing/set.d.ts +4 -1
- package/dist/components/record/Breadcrumb.js +5 -1
- package/dist/components/search/CriteriaBuilder.d.ts +1 -1
- package/dist/components/search/CriteriaBuilder.js +3 -3
- package/dist/components/search/FilterBuilder.d.ts +1 -18
- package/dist/components/search/FilterBuilder.js +4 -4
- package/dist/components/search/JsonEditor.js +5 -1
- package/dist/components/search/SearchBar.d.ts +1 -2
- package/dist/components/search/SearchBar.js +6 -1
- package/dist/components/search/criteria.d.ts +17 -1
- package/dist/components/search/index.d.ts +2 -1
- package/dist/components/search/index.js +1 -0
- package/dist/components/shell/Panes.d.ts +18 -0
- package/dist/components/shell/Panes.js +36 -0
- package/dist/components/shell/index.d.ts +1 -0
- package/dist/components/shell/index.js +2 -0
- package/dist/foundations/calendar.d.ts +3 -1
- package/dist/foundations/catalog.d.ts +27 -0
- package/dist/foundations/catalog.js +48 -0
- package/dist/foundations/copy.d.ts +33 -62
- package/dist/foundations/copy.es.js +6 -0
- package/dist/foundations/copy.js +21 -93
- package/dist/{primitives/field/context.d.ts → foundations/form.d.ts} +9 -0
- package/dist/{primitives/field/mode.js → foundations/form.js} +11 -0
- package/dist/foundations/grouping.d.ts +9 -3
- package/dist/foundations/index.d.ts +2 -0
- package/dist/foundations/index.js +5 -0
- package/dist/foundations/sticky.d.ts +4 -0
- package/dist/foundations/sticky.js +1 -0
- package/dist/primitives/choice/ComboBox.d.ts +1 -1
- package/dist/primitives/choice/ComboBox.js +4 -4
- package/dist/primitives/choice/RadioGroup.d.ts +1 -1
- package/dist/primitives/choice/RadioGroup.js +3 -3
- package/dist/primitives/choice/SearchField.js +1 -1
- package/dist/primitives/choice/SegmentedControl.d.ts +1 -1
- package/dist/primitives/choice/SegmentedControl.js +2 -2
- package/dist/primitives/choice/Slider.d.ts +1 -1
- package/dist/primitives/choice/Slider.js +2 -2
- package/dist/primitives/choice/Switch.d.ts +1 -1
- package/dist/primitives/choice/Switch.js +2 -2
- package/dist/primitives/date/Calendar.js +1 -1
- package/dist/primitives/date/DatePicker.d.ts +1 -1
- package/dist/primitives/date/DatePicker.js +2 -2
- package/dist/primitives/date/DateTime.d.ts +1 -1
- package/dist/primitives/date/DateTime.js +2 -2
- package/dist/primitives/field/Checks.d.ts +1 -1
- package/dist/primitives/field/Checks.js +4 -4
- package/dist/primitives/field/DropZone.d.ts +1 -1
- package/dist/primitives/field/DropZone.js +2 -2
- package/dist/primitives/field/Field.d.ts +2 -2
- package/dist/primitives/field/Field.js +3 -3
- package/dist/primitives/field/Suggest.d.ts +1 -1
- package/dist/primitives/field/Suggest.js +2 -2
- package/dist/primitives/field/index.d.ts +1 -3
- package/dist/primitives/field/index.js +4 -4
- package/dist/primitives/picker/Picker.d.ts +1 -1
- package/dist/primitives/picker/Picker.js +4 -4
- package/dist/primitives/{tag → picker}/TagsField.d.ts +2 -2
- package/dist/primitives/{tag → picker}/TagsField.js +5 -5
- package/dist/primitives/picker/index.d.ts +2 -0
- package/dist/primitives/picker/index.js +5 -1
- package/dist/primitives/table/CellCopy.js +1 -1
- package/dist/primitives/table/CellEditor.js +2 -4
- package/dist/primitives/table/DataTable.d.ts +11 -1
- package/dist/primitives/table/DataTable.js +137 -38
- package/dist/primitives/table/EditableTable.js +2 -2
- package/dist/primitives/table/PivotTable.js +0 -0
- package/dist/primitives/table/ReportTable.js +4 -3
- package/dist/primitives/table/SheetTable.js +8 -14
- package/dist/primitives/table/Table.d.ts +12 -3
- package/dist/primitives/table/Table.js +7 -7
- package/dist/primitives/table/rowWindow.d.ts +39 -0
- package/dist/primitives/table/rowWindow.js +177 -0
- package/dist/primitives/tabs/Tabs.d.ts +22 -4
- package/dist/primitives/tabs/Tabs.js +77 -23
- package/dist/primitives/tabs/index.d.ts +1 -1
- package/dist/primitives/tabs/index.js +1 -1
- package/dist/primitives/tag/Tags.js +4 -1
- package/dist/primitives/tag/index.d.ts +0 -2
- package/dist/primitives/tag/index.js +0 -3
- package/dist/primitives/value/Avatar.js +8 -1
- package/dist/primitives/value/{Fecha.d.ts → DateValue.d.ts} +5 -2
- package/dist/primitives/value/{Fecha.js → DateValue.js} +9 -8
- package/dist/primitives/value/Relation.d.ts +1 -1
- package/dist/primitives/value/Relation.js +2 -2
- package/dist/primitives/value/Value.js +4 -1
- package/dist/primitives/value/index.d.ts +1 -1
- package/dist/primitives/value/index.js +1 -1
- package/dist/views/list/List.d.ts +14 -1
- package/dist/views/list/List.js +28 -6
- package/dist/views/list/ListView.d.ts +1 -1
- package/dist/views/list/ListView.js +41 -14
- package/dist/views/list/index.d.ts +2 -2
- package/dist/views/list/presentations.js +3 -1
- package/dist/views/list/types.d.ts +49 -1
- package/dist/views/list/useNarrowList.d.ts +2 -2
- package/dist/views/list/useNarrowList.js +5 -5
- package/dist/views/record/Record.js +46 -42
- package/package.json +1 -1
- package/dist/primitives/field/context.js +0 -13
- package/dist/primitives/field/mode.d.ts +0 -9
- /package/dist/primitives/field/{copy.d.ts → clipboard.d.ts} +0 -0
- /package/dist/primitives/field/{copy.js → clipboard.js} +0 -0
- /package/dist/primitives/{tag/choices.d.ts → picker/tagChoices.d.ts} +0 -0
- /package/dist/primitives/{tag/choices.js → picker/tagChoices.js} +0 -0
|
@@ -44,7 +44,7 @@ const YEARS_LISTED = 100;
|
|
|
44
44
|
function Header({ jump }) {
|
|
45
45
|
const { direction } = useLocale();
|
|
46
46
|
const [Prev, Next] = direction === "rtl" ? [ChevronRight, ChevronLeft] : [ChevronLeft, ChevronRight];
|
|
47
|
-
return (_jsxs("
|
|
47
|
+
return (_jsxs("div", { className: "mb-2 flex items-center gap-1", children: [_jsx(AriaButton, { slot: "previous", className: navButton, children: _jsx(Prev, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) }), jump ? (_jsxs("div", { className: "flex flex-1 items-center justify-center", children: [_jsx(CalendarMonthPicker, { format: "long", children: (aria) => _jsx(Jump, { ...aria }) }), _jsx(CalendarYearPicker, { visibleYears: YEARS_LISTED, children: (aria) => _jsx(Jump, { ...aria }) })] })) : (_jsx(CalendarHeading, { className: "flex-1 text-center text-body font-semibold text-ink first-letter:uppercase" })), _jsx(AriaButton, { slot: "next", className: navButton, children: _jsx(Next, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }));
|
|
48
48
|
}
|
|
49
49
|
function WeekDays() {
|
|
50
50
|
return (_jsx(CalendarGridHeader, { children: (day) => (_jsx(CalendarHeaderCell, { className: "pb-1 text-micro font-medium text-muted", children: day })) }));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FormMode } from "
|
|
1
|
+
import { type FormMode } from "../../foundations/form.js";
|
|
2
2
|
/** The typed segments inside the field box — shared with `TimeField` and `DateTimePicker`
|
|
3
3
|
* (internal: not in the family barrel). */
|
|
4
4
|
export declare function Segments({ slot }: {
|
|
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { DateFormatter, parseDate } from "@internationalized/date";
|
|
4
4
|
import { AriaLocale } from "../../foundations/aria.js";
|
|
5
5
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
6
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
7
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
6
8
|
import { tv } from "../../foundations/variants.js";
|
|
7
9
|
import { Calendar, RangeCalendar } from "./Calendar.js";
|
|
8
10
|
import { FieldButton, FieldGroup } from "../field/aria.js";
|
|
9
|
-
import { useFieldIds } from "../field/context.js";
|
|
10
11
|
import { ReadValue } from "../field/Field.js";
|
|
11
|
-
import { useFormMode } from "../field/mode.js";
|
|
12
12
|
import { Popover } from "../overlay/Popover.js";
|
|
13
13
|
import { CalendarDays } from "lucide-react";
|
|
14
14
|
import { DateField as AriaDateField, DateInput, DatePicker as AriaDatePicker, DateRangePicker as AriaDateRangePicker, DateSegment, Dialog, } from "react-aria-components";
|
|
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { DateFormatter, parseDateTime, parseTime, toCalendarDate, } from "@internationalized/date";
|
|
4
4
|
import { AriaLocale } from "../../foundations/aria.js";
|
|
5
5
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
6
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
7
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
6
8
|
import { Calendar } from "./Calendar.js";
|
|
7
9
|
import { Segments } from "./DatePicker.js";
|
|
8
10
|
import { FieldButton, FieldGroup } from "../field/aria.js";
|
|
9
|
-
import { useFieldIds } from "../field/context.js";
|
|
10
11
|
import { ReadValue } from "../field/Field.js";
|
|
11
|
-
import { useFormMode } from "../field/mode.js";
|
|
12
12
|
import { Popover } from "../overlay/Popover.js";
|
|
13
13
|
import { CalendarDays } from "lucide-react";
|
|
14
14
|
import { DatePicker as AriaDatePicker, Dialog, TimeField as AriaTimeField, } from "react-aria-components";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
4
|
-
import { useFieldIds } from "
|
|
5
|
-
import { useFormMode } from "
|
|
4
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
5
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
6
6
|
import { Check, Minus } from "lucide-react";
|
|
7
7
|
import { useId } from "react";
|
|
8
|
-
import { Checkbox as AriaCheckbox, ListBox, ListBoxItem,
|
|
8
|
+
import { Checkbox as AriaCheckbox, ListBox, ListBoxItem, } from "react-aria-components";
|
|
9
9
|
function Mark({ on, mixed, focus = false, invalid = false, }) {
|
|
10
10
|
return (_jsx("span", { "aria-hidden": true, className: `inline-flex size-4 shrink-0 items-center justify-center rounded-mark border transition-colors duration-(--duration-instant) forced-color-adjust-none ${on
|
|
11
11
|
? "border-primary bg-primary text-surface forced-colors:border-[Highlight] forced-colors:bg-[Highlight] forced-colors:text-[HighlightText]"
|
|
@@ -68,5 +68,5 @@ export function CheckList({ label, items, value, onChange, mono = false, emptyLa
|
|
|
68
68
|
}
|
|
69
69
|
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsx("p", { id: labelId, className: "text-micro text-muted", children: label }), _jsx(ListBox, { "aria-labelledby": labelId, "aria-describedby": describedBy, items: items, selectionMode: "multiple", selectedKeys: value, onSelectionChange: (keys) => onChange?.(valuesOf(keys, items)), disabledKeys: items.filter((item) => item.disabled).map((item) => item.value), shouldFocusWrap: true, className: "flex flex-col overflow-hidden rounded-box border border-line bg-surface outline-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: (item) => (_jsx(ListBoxItem, { id: item.value, textValue: item.label, className: ({ isSelected, isFocusVisible, isDisabled }) => `flex min-w-0 cursor-pointer items-center gap-2.5 border-b border-line px-3 py-2 text-left outline-0 last:border-b-0 ${isFocusVisible
|
|
70
70
|
? "outline-2 -outline-offset-2 outline-primary forced-colors:outline-[Highlight]"
|
|
71
|
-
: ""} ${isDisabled ? "cursor-not-allowed opacity-45" : ""} ${isSelected ? "bg-primary/8" : "hover:bg-ink/4"}`, children: ({ isSelected }) => (_jsxs(_Fragment, { children: [_jsx(Mark, { on: isSelected, mixed: false }), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx(
|
|
71
|
+
: ""} ${isDisabled ? "cursor-not-allowed opacity-45" : ""} ${isSelected ? "bg-primary/8" : "hover:bg-ink/4"}`, children: ({ isSelected }) => (_jsxs(_Fragment, { children: [_jsx(Mark, { on: isSelected, mixed: false }), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: `truncate text-meta text-ink ${mono ? "font-mono" : ""}`, children: item.label }), item.detail === undefined ? null : (_jsx("span", { className: "truncate text-micro text-muted", children: item.detail }))] })] })) })) })] }));
|
|
72
72
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
5
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
4
6
|
import { cn } from "../../foundations/variants.js";
|
|
5
7
|
import { Button, buttonClasses } from "../button/Button.js";
|
|
6
|
-
import { useFieldIds } from "./context.js";
|
|
7
8
|
import { ReadValue } from "./Field.js";
|
|
8
|
-
import { useFormMode } from "./mode.js";
|
|
9
9
|
import { Failed } from "../notice/Notice.js";
|
|
10
10
|
import { ProgressBar } from "../value/ProgressBar.js";
|
|
11
11
|
import { Button as AriaButton, DropZone as AriaDropZone, FileTrigger, } from "react-aria-components";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type FieldIds } from "
|
|
2
|
-
import { type FormMode } from "
|
|
1
|
+
import { type FieldIds } from "../../foundations/form.js";
|
|
2
|
+
import { type FormMode } from "../../foundations/form.js";
|
|
3
3
|
import { type FieldNote } from "./note.js";
|
|
4
4
|
import { type ReactNode } from "react";
|
|
5
5
|
export declare function Field({ label, hint, error, required, children, htmlFor, layout, action, mode, note, copy: copyAs, }: {
|
|
@@ -3,12 +3,12 @@ import { jsxs as _jsxs, Fragment as _Fragment, jsx as _jsx } from "react/jsx-run
|
|
|
3
3
|
import { AriaLocale } from "../../foundations/aria.js";
|
|
4
4
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
5
5
|
import { byDensity, useDensity } from "../../foundations/density.js";
|
|
6
|
+
import { FieldIdsContext, useFieldIds, } from "../../foundations/form.js";
|
|
7
|
+
import { FormModeProvider, useFormMode, } from "../../foundations/form.js";
|
|
6
8
|
import { focusRing, tv } from "../../foundations/variants.js";
|
|
7
9
|
import { FieldGroup, listOptionStyles } from "./aria.js";
|
|
8
|
-
import {
|
|
9
|
-
import { FieldCopy, useFieldCopy } from "./copy.js";
|
|
10
|
+
import { FieldCopy, useFieldCopy } from "./clipboard.js";
|
|
10
11
|
import { useSheetLayout } from "./layout.js";
|
|
11
|
-
import { FormModeProvider, useFormMode, } from "./mode.js";
|
|
12
12
|
import { describedByOf, NoteCard, NoteFlag, } from "./note.js";
|
|
13
13
|
import { Hint } from "../hint/Hint.js";
|
|
14
14
|
import { Popover } from "../overlay/Popover.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { AriaLocale } from "../../foundations/aria.js";
|
|
4
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
5
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
4
6
|
import { FieldGroup, listOptionStyles } from "./aria.js";
|
|
5
|
-
import { useFieldIds } from "./context.js";
|
|
6
7
|
import { ReadValue } from "./Field.js";
|
|
7
|
-
import { useFormMode } from "./mode.js";
|
|
8
8
|
import { Popover } from "../overlay/Popover.js";
|
|
9
9
|
import { ComboBox as AriaComboBox, Input, ListBox, ListBoxItem, useFilter, } from "react-aria-components";
|
|
10
10
|
const MAX = 8;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export { Checkbox, type CheckItem, CheckList } from "./Checks.js";
|
|
2
|
-
export {
|
|
3
|
-
export { FieldCopyProvider, shownText, useFieldCopy, } from "./copy.js";
|
|
2
|
+
export { FieldCopyProvider, shownText, useFieldCopy, } from "./clipboard.js";
|
|
4
3
|
export { DropZone, type DropZoneProps } from "./DropZone.js";
|
|
5
4
|
export { controlClasses, DataField, Field, NumberInput, ReadValue, Select, type SelectOption, TextArea, TextInput, } from "./Field.js";
|
|
6
5
|
export { type FieldLayout, FieldLayoutProvider, useSheetLayout, } from "./layout.js";
|
|
7
|
-
export { type FormMode, FormModeProvider, useFormMode, } from "./mode.js";
|
|
8
6
|
export { Suggest, type SuggestProps } from "./Suggest.js";
|
|
9
7
|
export { type FieldNote } from "./note.js";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/* THE CONTROLS a form is written with.
|
|
1
|
+
/* THE CONTROLS a form is written with. The contract they speak with their `Field` —
|
|
2
|
+
`useFieldIds`, `useFormMode`— is in `foundations/form`: an application's own control
|
|
3
|
+
speaks it the same way.
|
|
2
4
|
What ARRANGES them —the sheet, the section, the footer— lives in `components/form`: a
|
|
3
5
|
control does not know a form exists, and is used the same way inside a cell, a filter or
|
|
4
6
|
a dialog. */
|
|
5
7
|
export { Checkbox, CheckList } from "./Checks.js";
|
|
6
|
-
export { useFieldIds } from "./context.js";
|
|
7
8
|
/* The copy button every field carries by its label — on by default, off by field or by form. */
|
|
8
|
-
export { FieldCopyProvider, shownText, useFieldCopy, } from "./
|
|
9
|
+
export { FieldCopyProvider, shownText, useFieldCopy, } from "./clipboard.js";
|
|
9
10
|
export { DropZone } from "./DropZone.js";
|
|
10
11
|
export { controlClasses, DataField, Field, NumberInput, ReadValue, Select, TextArea, TextInput, } from "./Field.js";
|
|
11
12
|
export { FieldLayoutProvider, useSheetLayout, } from "./layout.js";
|
|
12
|
-
export { FormModeProvider, useFormMode, } from "./mode.js";
|
|
13
13
|
export { Suggest } from "./Suggest.js";
|
|
14
14
|
/* A note that belongs to one field: a card in the record's margin, or a flag by its label. */
|
|
15
15
|
export {} from "./note.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type FormMode } from "../../foundations/form.js";
|
|
1
2
|
import type { OpenTarget } from "../../foundations/types.js";
|
|
2
|
-
import { type FormMode } from "../field/mode.js";
|
|
3
3
|
import type { LucideIcon } from "lucide-react";
|
|
4
4
|
import { type ReactNode } from "react";
|
|
5
5
|
export interface PickerItem {
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { AriaLocale } from "../../foundations/aria.js";
|
|
4
4
|
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
5
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
6
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
5
7
|
import { matchedWord, matches } from "../../foundations/match.js";
|
|
6
8
|
import { cn, tv } from "../../foundations/variants.js";
|
|
7
9
|
import { IconButton } from "../button/Button.js";
|
|
8
10
|
import { FieldButton, FieldGroup } from "../field/aria.js";
|
|
9
|
-
import { useFieldIds } from "../field/context.js";
|
|
10
11
|
import { ReadValue } from "../field/Field.js";
|
|
11
|
-
import { useFormMode } from "../field/mode.js";
|
|
12
12
|
import { Menu } from "../menu/Menu.js";
|
|
13
13
|
import { Popover } from "../overlay/Popover.js";
|
|
14
14
|
import { MatchedText } from "./MatchedText.js";
|
|
15
15
|
import { Avatar } from "../value/Person.js";
|
|
16
16
|
import { AppWindow, Check, ChevronDown, Ellipsis, Eye, PanelRight, PictureInPicture2, SquareArrowOutUpRight, } from "lucide-react";
|
|
17
17
|
import { useContext, useMemo, useRef, useState } from "react";
|
|
18
|
-
import { Collection, ComboBox as AriaComboBox, ComboBoxStateContext, Header, Input, ListBox, ListBoxItem, ListBoxLoadMoreItem, ListBoxSection,
|
|
18
|
+
import { Collection, ComboBox as AriaComboBox, ComboBoxStateContext, Header, Input, ListBox, ListBoxItem, ListBoxLoadMoreItem, ListBoxSection, useFilter, } from "react-aria-components";
|
|
19
19
|
/** The icon of each place a record opens — the same in every field and list. */
|
|
20
20
|
const OPEN_ICON = {
|
|
21
21
|
modal: PictureInPicture2,
|
|
@@ -271,7 +271,7 @@ function Row({ item, selected, marks, hints, query, locale, }) {
|
|
|
271
271
|
item.haystack === undefined
|
|
272
272
|
? null
|
|
273
273
|
: matchedWord(item.haystack, query, locale);
|
|
274
|
-
return (_jsxs(_Fragment, { children: [marks ? (_jsx("span", { className: cn(hints ? "mt-0.5" : "", "flex size-(--size-icon-sm) shrink-0 items-center justify-center"), children: selected ? (_jsx(Check, { "aria-hidden": true, className: "size-(--size-icon-sm) text-primary forced-colors:text-[HighlightText]", strokeWidth: 2.5 })) : Icon !== undefined ? (_jsx(Icon, { "aria-hidden": true, className: "size-(--size-icon-sm) text-muted", strokeWidth: 1.75 })) : null })) : null, item.avatar === undefined ? null : (_jsx(Avatar, { name: item.name, size: "sm", ...(typeof item.avatar === "string" ? { src: item.avatar } : {}) })), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx(
|
|
274
|
+
return (_jsxs(_Fragment, { children: [marks ? (_jsx("span", { className: cn(hints ? "mt-0.5" : "", "flex size-(--size-icon-sm) shrink-0 items-center justify-center"), children: selected ? (_jsx(Check, { "aria-hidden": true, className: "size-(--size-icon-sm) text-primary forced-colors:text-[HighlightText]", strokeWidth: 2.5 })) : Icon !== undefined ? (_jsx(Icon, { "aria-hidden": true, className: "size-(--size-icon-sm) text-muted", strokeWidth: 1.75 })) : null })) : null, item.avatar === undefined ? null : (_jsx(Avatar, { name: item.name, size: "sm", ...(typeof item.avatar === "string" ? { src: item.avatar } : {}) })), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: cn("min-w-0 truncate text-meta text-ink", selected ? "font-medium" : ""), children: _jsx(MatchedText, { text: item.name, query: query, locale: locale }) }), item.tags] }), item.meta === undefined && hidden === null ? null : (_jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [item.meta === undefined ? null : (_jsx("span", { className: "truncate text-micro tabular-nums text-muted", children: _jsx(MatchedText, { text: item.meta, query: query, locale: locale }) })), hidden === null ? null : (_jsxs("span", { className: "truncate text-micro text-muted", children: [copy.picker.matched, " ", _jsx("span", { className: "font-mono", children: _jsx(MatchedText, { text: hidden, query: query, locale: locale }) })] }))] }))] }), item.aside === undefined ? null : (_jsx("span", { className: "flex shrink-0 items-center gap-1.5 self-center text-meta text-ink tabular-nums", children: item.aside })), !marks && selected ? (_jsx(Check, { "aria-hidden": true, className: "size-(--size-icon-sm) shrink-0 text-primary forced-colors:text-[HighlightText]", strokeWidth: 2.5 })) : null] }));
|
|
275
275
|
}
|
|
276
276
|
/** Hands the combobox's `close` to the code outside it: React Aria owns the open state and
|
|
277
277
|
* offers no controlled `isOpen`, and the «Buscar más…» row has to close the list it lives in. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { type FormMode } from "../../foundations/form.js";
|
|
1
2
|
import type { OpenTarget } from "../../foundations/types.js";
|
|
2
|
-
import { type
|
|
3
|
-
import { type TagOption } from "./choices.js";
|
|
3
|
+
import { type TagOption } from "./tagChoices.js";
|
|
4
4
|
export type TagsFieldProps = {
|
|
5
5
|
/** The ids of the chosen tags, in the order they were added. */
|
|
6
6
|
value: readonly string[];
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { AriaLocale } from "../../foundations/aria.js";
|
|
4
4
|
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
5
|
+
import { useFieldIds } from "../../foundations/form.js";
|
|
6
|
+
import { useFormMode } from "../../foundations/form.js";
|
|
5
7
|
import { cn } from "../../foundations/variants.js";
|
|
6
8
|
import { fieldGroupStyles, listOptionStyles } from "../field/aria.js";
|
|
7
|
-
import { useFieldIds } from "../field/context.js";
|
|
8
9
|
import { ReadValue } from "../field/Field.js";
|
|
9
|
-
import { useFormMode } from "../field/mode.js";
|
|
10
10
|
import { Popover } from "../overlay/Popover.js";
|
|
11
|
-
import { MatchedText } from "
|
|
12
|
-
import { tagChoices, withoutTag, withTag, } from "./
|
|
13
|
-
import { Tags } from "
|
|
11
|
+
import { MatchedText } from "./MatchedText.js";
|
|
12
|
+
import { tagChoices, withoutTag, withTag, } from "./tagChoices.js";
|
|
13
|
+
import { Tags } from "../tag/Tags.js";
|
|
14
14
|
import { Avatar } from "../value/Person.js";
|
|
15
15
|
import { Plus, Search } from "lucide-react";
|
|
16
16
|
import { useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* CHOOSING RECORDS — one (`Picker`, the many2one) or several (`TagsField`, the many2many).
|
|
2
|
+
The replacement for `<select>` when you have to SEE in order to choose. */
|
|
2
3
|
export { Picker, } from "./Picker.js";
|
|
4
|
+
/* El many2many: las fichas elegidas más una entrada que agrega la siguiente. */
|
|
5
|
+
export {} from "./tagChoices.js";
|
|
6
|
+
export { TagsField } from "./TagsField.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
4
|
-
import { shownText } from "../field/
|
|
4
|
+
import { shownText } from "../field/clipboard.js";
|
|
5
5
|
import { CopyButton } from "../value/Copyable.js";
|
|
6
6
|
import { useRef } from "react";
|
|
7
7
|
/**
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { cn, focusRing } from "../../foundations/variants.js";
|
|
4
4
|
import { Popover } from "../overlay/Popover.js";
|
|
5
5
|
import { Pencil } from "lucide-react";
|
|
6
|
-
import { useEffect,
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
7
|
import { Button as AriaButton, Dialog, DialogTrigger } from "react-aria-components";
|
|
8
8
|
/* ==================================================================== *
|
|
9
9
|
* CELL EDITOR — change one value of a list without leaving the list.
|
|
@@ -75,9 +75,7 @@ function EditorDialog({ label, editor, }) {
|
|
|
75
75
|
* Internal to the table family: a column asks for it with `DataColumn.edit`.
|
|
76
76
|
*/
|
|
77
77
|
export function CellEditor({ label, numeric, editor, children, }) {
|
|
78
|
-
const verb = useId();
|
|
79
|
-
const shown = useId();
|
|
80
78
|
return (
|
|
81
79
|
/* The row may open its record on click; changing one cell must not also open it. */
|
|
82
|
-
_jsx("div", { onClick: (event) => event.stopPropagation(), className: "-mx-1 min-w-0", children: _jsxs(DialogTrigger, { children: [_jsxs(AriaButton, {
|
|
80
|
+
_jsx("div", { onClick: (event) => event.stopPropagation(), className: "-mx-1 min-w-0", children: _jsxs(DialogTrigger, { children: [_jsxs(AriaButton, { className: ({ isFocusVisible }) => cn("group/edit flex w-full min-w-0 cursor-pointer items-center gap-1.5 rounded-mark px-1 text-inherit outline-0 transition-colors duration-(--duration-instant) hover:bg-ink/6 pressed:bg-ink/10 aria-expanded:bg-primary/8", numeric ? "flex-row-reverse text-right" : "text-left", focusRing({ isFocusVisible })), children: [_jsx("span", { className: "sr-only", children: `${label} ` }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: children }), _jsx(Pencil, { "aria-hidden": true, strokeWidth: 2, className: "size-(--size-icon-sm) shrink-0 text-muted opacity-0 transition-opacity duration-(--duration-instant) group-hover/edit:opacity-100 group-focus-visible/edit:opacity-100 group-aria-expanded/edit:opacity-100" })] }), _jsx(Popover, { placement: numeric ? "bottom end" : "bottom start", children: _jsx(EditorDialog, { label: label, editor: editor }) })] }) }));
|
|
83
81
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Pile } from "../../foundations/grouping.js";
|
|
1
2
|
import type { Tone } from "../../foundations/types.js";
|
|
2
3
|
import { type RowAction } from "./RowActions.js";
|
|
3
4
|
import { type SortState } from "./Table.js";
|
|
@@ -148,7 +149,7 @@ export type GroupColumn<T> = {
|
|
|
148
149
|
/** Cómo se lee esa clave. */
|
|
149
150
|
labelOf: (key: string) => string;
|
|
150
151
|
};
|
|
151
|
-
export declare function DataTable<T extends object>({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals, collapsed, onGroupToggle, create, copyable, }: {
|
|
152
|
+
export declare function DataTable<T extends object>({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals, collapsed, onGroupToggle, piles, onMore, create, copyable, }: {
|
|
152
153
|
data: readonly T[];
|
|
153
154
|
columns: readonly DataColumn<T>[];
|
|
154
155
|
getRowId: (row: T) => string;
|
|
@@ -181,6 +182,15 @@ export declare function DataTable<T extends object>({ data, columns, getRowId, s
|
|
|
181
182
|
* table does not reopen what rows just folded. */
|
|
182
183
|
collapsed?: readonly string[];
|
|
183
184
|
onGroupToggle?: (key: string) => void;
|
|
185
|
+
/**
|
|
186
|
+
* The piles ALREADY counted by whoever keeps the data — «187» over the whole set, not
|
|
187
|
+
* the 12 rows of the page. Given, the table draws them as they come (their order, their
|
|
188
|
+
* labels, their `detail`) instead of grouping `data` by `levels`.
|
|
189
|
+
*/
|
|
190
|
+
piles?: readonly Pile<T>[];
|
|
191
|
+
/** A pile shows its first page and counts them all: this asks for the next ones, and
|
|
192
|
+
* the table offers it under each pile that holds fewer than it counts. */
|
|
193
|
+
onMore?: (pile: Pile<T>) => void;
|
|
184
194
|
/** Write a new record as a row of the list. See the file's header. */
|
|
185
195
|
create?: DataTableCreate<T>;
|
|
186
196
|
/** The cells' copy buttons, on by default. `false` takes them off the whole table; a
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
4
|
-
import { pathKey } from "../../foundations/grouping.js";
|
|
4
|
+
import { pathKey, pileItems } from "../../foundations/grouping.js";
|
|
5
5
|
import { CellCopy } from "./CellCopy.js";
|
|
6
6
|
import { CellEditor } from "./CellEditor.js";
|
|
7
7
|
import { EditRow, firstControl } from "./EditableTable.js";
|
|
8
8
|
import { RowActions } from "./RowActions.js";
|
|
9
|
+
import { useRowWindow } from "./rowWindow.js";
|
|
9
10
|
import { SelectBox, Table, Td, Th, THead, Tr, } from "./Table.js";
|
|
10
11
|
import { aggregationFns, columnGroupingFeature, createColumnHelper, createExpandedRowModel, createGroupedRowModel, createSortedRowModel, rowAggregationFeature, rowExpandingFeature, rowSortingFeature, sortFns, tableFeatures, useTable, } from "@tanstack/react-table";
|
|
11
12
|
import { ChevronRight, Plus } from "lucide-react";
|
|
@@ -117,7 +118,47 @@ export function aggregateOf(column, rows) {
|
|
|
117
118
|
return numbers.length;
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
|
-
|
|
121
|
+
/** The piles someone else counted, as the body draws them: open ones with what is inside,
|
|
122
|
+
* the deepest with its records and, when it holds fewer than it counts, its «show more». */
|
|
123
|
+
function entriesOfPiles(piles, keyOf, collapsed, onToggle, onMore) {
|
|
124
|
+
return piles.flatMap((pile) => {
|
|
125
|
+
const key = pathKey(pile.path);
|
|
126
|
+
const open = collapsed?.includes(key) !== true;
|
|
127
|
+
const head = {
|
|
128
|
+
kind: "pile",
|
|
129
|
+
id: `\u0000pile${key}`,
|
|
130
|
+
depth: pile.depth,
|
|
131
|
+
level: pile.field,
|
|
132
|
+
label: pile.label,
|
|
133
|
+
count: pile.count,
|
|
134
|
+
detail: pile.detail,
|
|
135
|
+
open,
|
|
136
|
+
toggle: onToggle === undefined ? undefined : () => onToggle(key),
|
|
137
|
+
};
|
|
138
|
+
if (!open)
|
|
139
|
+
return [head];
|
|
140
|
+
if (pile.piles.length > 0) {
|
|
141
|
+
return [head, ...entriesOfPiles(pile.piles, keyOf, collapsed, onToggle, onMore)];
|
|
142
|
+
}
|
|
143
|
+
const left = pile.count - pile.items.length;
|
|
144
|
+
return [
|
|
145
|
+
head,
|
|
146
|
+
...pile.items.map((item) => ({ kind: "row", id: keyOf(item), item })),
|
|
147
|
+
...(left > 0 && onMore !== undefined
|
|
148
|
+
? [
|
|
149
|
+
{
|
|
150
|
+
kind: "more",
|
|
151
|
+
id: `\u0000more${key}`,
|
|
152
|
+
depth: pile.depth,
|
|
153
|
+
left,
|
|
154
|
+
more: () => onMore(pile),
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
: []),
|
|
158
|
+
];
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export function DataTable({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals = false, collapsed, onGroupToggle, piles, onMore, create, copyable = true, }) {
|
|
121
162
|
const copy = useUiCopy();
|
|
122
163
|
const draft = create?.draft ?? null;
|
|
123
164
|
const draftRow = useRef(null);
|
|
@@ -208,7 +249,8 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
208
249
|
sorting: sort === null || sort === undefined
|
|
209
250
|
? []
|
|
210
251
|
: [{ id: sort.key, desc: sort.direction === "desc" }],
|
|
211
|
-
|
|
252
|
+
/* Counted elsewhere, the tree comes made: grouping the page again would undo it. */
|
|
253
|
+
grouping: piles === undefined ? (levels ?? []).map((level) => level.id) : [],
|
|
212
254
|
},
|
|
213
255
|
// Groups start open: grouping and then having to open each group is two
|
|
214
256
|
// gestures for what the user asked for in one.
|
|
@@ -246,7 +288,7 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
246
288
|
the same thing; with several, folding the outer pile has to take the inner
|
|
247
289
|
ones with it — otherwise closing «Ferretería Sur» leaves its months on screen,
|
|
248
290
|
orphaned under a header that says it is closed. */
|
|
249
|
-
const
|
|
291
|
+
const shown = collapsed === undefined
|
|
250
292
|
? rows
|
|
251
293
|
: rows.filter((row) => {
|
|
252
294
|
let node = row.getParentRow();
|
|
@@ -257,14 +299,56 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
257
299
|
}
|
|
258
300
|
return true;
|
|
259
301
|
});
|
|
302
|
+
/* ONE sequence for the body, whoever grouped: the piles counted elsewhere, or the tree
|
|
303
|
+
TanStack built from the page. Before, the table only knew the second — grouped on the
|
|
304
|
+
server, a list said «187» in rows and «12» in the table. */
|
|
305
|
+
const entries = piles !== undefined
|
|
306
|
+
? entriesOfPiles(piles, getRowId, collapsed, onGroupToggle, onMore)
|
|
307
|
+
: shown.map((row) => {
|
|
308
|
+
if (row.subRows.length === 0)
|
|
309
|
+
return { kind: "row", id: row.id, item: row.original };
|
|
310
|
+
const path = pathOf(row);
|
|
311
|
+
const key = pathKey(path);
|
|
312
|
+
const depth = path.length - 1;
|
|
313
|
+
const level = levels?.find((one) => one.id === row.groupingColumnId);
|
|
314
|
+
/* La CLAVE del montón —la del motor— y el rótulo, que lo pone el nivel: son dos
|
|
315
|
+
textos y sólo el segundo cambia con el idioma. */
|
|
316
|
+
const value = path[depth] ?? key;
|
|
317
|
+
return {
|
|
318
|
+
kind: "pile",
|
|
319
|
+
id: row.id,
|
|
320
|
+
depth,
|
|
321
|
+
level: level?.label,
|
|
322
|
+
label: level === undefined ? value : level.labelOf(value),
|
|
323
|
+
/* Las FILAS de adentro, no los subgrupos: `subRows` de un montón con niveles
|
|
324
|
+
abajo son los montones del nivel siguiente, y contarlos decía «2» donde hay
|
|
325
|
+
cuarenta filas. */
|
|
326
|
+
count: row.getLeafRows().filter((leaf) => leaf.subRows.length === 0).length,
|
|
327
|
+
detail: undefined,
|
|
328
|
+
open: collapsed === undefined ? row.getIsExpanded() : !collapsed.includes(key),
|
|
329
|
+
toggle: () => onGroupToggle === undefined ? row.toggleExpanded() : onGroupToggle(key),
|
|
330
|
+
};
|
|
331
|
+
});
|
|
260
332
|
const selecting = onSelectedChange !== undefined;
|
|
261
|
-
const
|
|
333
|
+
const width = columns.length + (selecting ? 1 : 0) + (rowActions === undefined ? 0 : 1);
|
|
334
|
+
/* Past a few hundred rows only the ones in view are drawn — see `rowWindow.tsx`. It
|
|
335
|
+
windows what is DRAWN, pile headers included and folded rows left out: that is the
|
|
336
|
+
sequence a person scrolls through. */
|
|
337
|
+
const view = useRowWindow({
|
|
338
|
+
count: entries.length,
|
|
339
|
+
getKey: (i) => entries[i]?.id ?? String(i),
|
|
340
|
+
});
|
|
341
|
+
/* Every record the selection can reach — folded ones included: «select all» over a
|
|
342
|
+
grouped list means all of them, not the ones whose pile happens to be open. */
|
|
343
|
+
const leaves = useMemo(() => piles !== undefined
|
|
344
|
+
? pileItems(piles).map(getRowId)
|
|
345
|
+
: rows.filter((r) => r.subRows.length === 0).map((r) => r.id), [piles, rows, getRowId]);
|
|
262
346
|
/* The aggregates are computed by the component and not by TanStack: its
|
|
263
347
|
`aggregationFn` lives in the GROUP model, and a footer exists precisely when
|
|
264
348
|
there are no groups. */
|
|
265
349
|
const footer = useMemo(() => (totals ? columns.map((c) => aggregateOf(c, data)) : null), [totals, columns, data]);
|
|
266
|
-
const all = leaves.length > 0 && leaves.every((
|
|
267
|
-
const some = leaves.some((
|
|
350
|
+
const all = leaves.length > 0 && leaves.every((id) => selected?.has(id) === true);
|
|
351
|
+
const some = leaves.some((id) => selected?.has(id) === true);
|
|
268
352
|
function toggle(id, on) {
|
|
269
353
|
const n = new Set(selected ?? []);
|
|
270
354
|
if (on)
|
|
@@ -273,36 +357,51 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
273
357
|
n.delete(id);
|
|
274
358
|
onSelectedChange?.(n);
|
|
275
359
|
}
|
|
276
|
-
const grid = (_jsxs(Table, { height: height,
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
360
|
+
const grid = (_jsxs(Table, { height: height, ...(view.on
|
|
361
|
+
? {
|
|
362
|
+
rowCount: 1 + entries.length + (draft === null ? 0 : 1) + (totals ? 1 : 0),
|
|
363
|
+
}
|
|
364
|
+
: {}), children: [_jsxs(THead, { ...(view.on ? { rowIndex: 1 } : {}), children: [selecting ? (_jsx(Th, { width: 36, children: _jsx(SelectBox, { label: copy.selection.all, checked: all, indeterminate: some, onChange: (v) => onSelectedChange?.(v ? new Set(leaves) : new Set()) }) })) : null, columns.map((c) => (_jsx(Th, { sortKey: c.sortable === false ? undefined : c.id, sort: sort, onSort: onSortChange, numeric: c.numeric, width: c.width, children: c.header }, c.id))), rowActions === undefined ? null : (_jsx("td", { className: "w-px border-b border-line-strong" }))] }), _jsxs("tbody", { ref: view.body.ref, children: [view.rows.flatMap((slot, at) => {
|
|
365
|
+
const entry = entries[slot.index];
|
|
366
|
+
if (entry === undefined)
|
|
367
|
+
return [];
|
|
368
|
+
const windowed = view.windowed(slot.index);
|
|
369
|
+
/* What is not drawn between the previous drawn row and this one: the rows
|
|
370
|
+
above the view before the first, and the gap before a row kept only because
|
|
371
|
+
it has the focus. */
|
|
372
|
+
const gap = view.pad(at === 0 ? 0 : view.rows[at - 1].end, slot.start, width, `\u0000pad${slot.index}`);
|
|
373
|
+
const indent = entry.kind === "row" || entry.depth === 0
|
|
374
|
+
? undefined
|
|
375
|
+
: { paddingLeft: `calc(var(--spacing-indent) * ${entry.depth} + 0.75rem)` };
|
|
376
|
+
if (entry.kind === "pile") {
|
|
377
|
+
const head = (_jsxs(_Fragment, { children: [entry.toggle === undefined ? null : (_jsx(ChevronRight, { className: `size-(--size-icon-sm) text-muted transition-transform duration-(--duration-instant) ${entry.open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true })), entry.level === undefined ? null : (_jsx("span", { className: "text-micro font-normal text-muted", children: entry.level })), _jsx("span", { children: entry.label }), _jsx("span", { className: "inline-flex shrink-0 items-center rounded-pill border border-line-strong bg-ink/5 px-2 py-0.5 text-micro tabular-nums text-muted", children: entry.count.toLocaleString(copy.locale) }), entry.detail === undefined ? null : (_jsx("span", { className: "text-micro font-normal tabular-nums text-muted", children: entry.detail }))] }));
|
|
378
|
+
return [
|
|
379
|
+
gap,
|
|
380
|
+
/* The pile's row: it spans the whole width, says which value it groups by
|
|
381
|
+
and how many are inside — what makes it useful without opening it. */
|
|
382
|
+
_jsx("tr", { ...windowed, className: "border-b border-line bg-ground", children: _jsx("td", { colSpan: width, className: "px-3 py-1.5", style: indent, children: entry.toggle === undefined ? (_jsx("span", { className: "inline-flex items-center gap-1.5 text-meta font-medium text-ink", children: head })) : (_jsx("button", { type: "button", onClick: entry.toggle, "aria-expanded": entry.open, className: "inline-flex items-center gap-1.5 rounded-control text-meta font-medium text-ink transition-colors duration-(--duration-instant) hover:text-primary focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: head })) }) }, entry.id),
|
|
383
|
+
];
|
|
384
|
+
}
|
|
385
|
+
if (entry.kind === "more") {
|
|
386
|
+
return [
|
|
387
|
+
gap,
|
|
388
|
+
_jsx("tr", { ...windowed, className: "border-b border-line", children: _jsx("td", { colSpan: width, className: "px-3 py-1", style: indent, children: _jsx("button", { type: "button", onClick: entry.more, className: "rounded-control text-meta text-primary transition-colors duration-(--duration-instant) hover:underline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: fillCopy(copy.table.pileMore, {
|
|
389
|
+
count: entry.left.toLocaleString(copy.locale),
|
|
390
|
+
}) }) }) }, entry.id),
|
|
391
|
+
];
|
|
297
392
|
}
|
|
298
|
-
const original =
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
393
|
+
const original = entry.item;
|
|
394
|
+
const id = entry.id;
|
|
395
|
+
return [
|
|
396
|
+
gap,
|
|
397
|
+
_jsxs(Tr, { ...(windowed === undefined ? {} : { windowed }), selected: selected?.has(id) === true, onClick: onRowClick === undefined ? undefined : () => onRowClick(original), children: [selecting ? (_jsx(Td, { children: _jsx(SelectBox, { label: copy.selection.row, checked: selected?.has(id) === true, onChange: (v) => toggle(id, v) }) })) : null, columns.map((c) => {
|
|
398
|
+
const t = c.tone?.(original);
|
|
399
|
+
const n = c.note?.(original);
|
|
400
|
+
const edit = c.edit?.when?.(original) === false ? undefined : c.edit;
|
|
401
|
+
return (_jsx(Td, { numeric: c.numeric, mono: c.mono === true, ...(t === undefined ? {} : { tone: t }), ...(n === undefined ? {} : { title: n }), children: edit !== undefined ? (_jsx(CellEditor, { label: fillCopy(copy.inlineEdit.editCell, { column: c.header }), numeric: c.numeric === true, editor: (done) => edit.render(original, done), children: drawCell(copy, c, original) })) : copyable && c.copy !== false ? (_jsx(CellCopy, { header: c.header, numeric: c.numeric === true, ...(c.copy === undefined ? {} : { value: c.copy(original) }), children: drawCell(copy, c, original) })) : (drawCell(copy, c, original)) }, c.id));
|
|
402
|
+
}), rowActions === undefined ? null : (_jsx(RowActions, { actions: rowActions(original) }))] }, id),
|
|
403
|
+
];
|
|
404
|
+
}), view.tail(width), create === undefined || draft === null ? null : (_jsxs(EditRow, { as: "tr", rowId: "\\u0000draft", rowRef: (node) => {
|
|
306
405
|
draftRow.current = node;
|
|
307
406
|
}, editable: true, editing: true, tabbable: false, className: "group/row", onStart: () => { }, onCommit: saveDraft, onCancel: () => {
|
|
308
407
|
backToAdd.current = true;
|
|
@@ -318,7 +417,7 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
318
417
|
}, children: [selecting ? _jsx("td", { className: DRAFT_CELL }) : null, columns.map((c) => (_jsx("td", { ...(c.input === undefined ? {} : { "data-edit": "" }), className: `${DRAFT_CELL} ${c.numeric === true ? "text-right tabular-nums" : ""}`, children: c.input?.(draft, (next) => {
|
|
319
418
|
touched.current = true;
|
|
320
419
|
create.onChange(next);
|
|
321
|
-
}) ?? null }, c.id))), rowActions === undefined ? null : _jsx("td", { className: DRAFT_CELL })] }))] }), footer === null ? null : (_jsx("tfoot", { className: "sticky bottom-0 z-10 bg-ground", children: _jsxs("tr", { className: "border-t border-line-strong", children: [selecting ? _jsx("td", {}) : null, columns.map((c, i) => {
|
|
420
|
+
}) ?? null }, c.id))), rowActions === undefined ? null : _jsx("td", { className: DRAFT_CELL })] }))] }), footer === null ? null : (_jsx("tfoot", { className: "sticky bottom-0 z-10 bg-ground", children: _jsxs("tr", { className: "border-t border-line-strong", "aria-rowindex": view.on ? 2 + entries.length + (draft === null ? 0 : 1) : undefined, children: [selecting ? _jsx("td", {}) : null, columns.map((c, i) => {
|
|
322
421
|
const value = footer[i] ?? null;
|
|
323
422
|
return (_jsx("td", { className: `px-3 py-1.5 text-meta font-semibold text-ink ${c.numeric === true ? "text-right tabular-nums" : ""}`, children: i === 0 && value === null ? (_jsxs("span", { className: "text-muted", children: [data.length.toLocaleString(copy.locale), " ", data.length === 1 ? copy.table.row : copy.table.rows] })) : value === null ? null : (aggregateLabel(copy, c, value)) }, c.id));
|
|
324
423
|
}), rowActions === undefined ? null : _jsx("td", {})] }) }))] }));
|