@sincpro/web-ui 1.2.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/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 +29 -62
- package/dist/foundations/copy.es.js +4 -0
- package/dist/foundations/copy.js +19 -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 +111 -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.js +2 -2
- 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
|
@@ -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,7 +1,7 @@
|
|
|
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";
|
|
@@ -118,7 +118,47 @@ export function aggregateOf(column, rows) {
|
|
|
118
118
|
return numbers.length;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
|
|
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, }) {
|
|
122
162
|
const copy = useUiCopy();
|
|
123
163
|
const draft = create?.draft ?? null;
|
|
124
164
|
const draftRow = useRef(null);
|
|
@@ -209,7 +249,8 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
209
249
|
sorting: sort === null || sort === undefined
|
|
210
250
|
? []
|
|
211
251
|
: [{ id: sort.key, desc: sort.direction === "desc" }],
|
|
212
|
-
|
|
252
|
+
/* Counted elsewhere, the tree comes made: grouping the page again would undo it. */
|
|
253
|
+
grouping: piles === undefined ? (levels ?? []).map((level) => level.id) : [],
|
|
213
254
|
},
|
|
214
255
|
// Groups start open: grouping and then having to open each group is two
|
|
215
256
|
// gestures for what the user asked for in one.
|
|
@@ -247,7 +288,7 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
247
288
|
the same thing; with several, folding the outer pile has to take the inner
|
|
248
289
|
ones with it — otherwise closing «Ferretería Sur» leaves its months on screen,
|
|
249
290
|
orphaned under a header that says it is closed. */
|
|
250
|
-
const
|
|
291
|
+
const shown = collapsed === undefined
|
|
251
292
|
? rows
|
|
252
293
|
: rows.filter((row) => {
|
|
253
294
|
let node = row.getParentRow();
|
|
@@ -258,22 +299,56 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
258
299
|
}
|
|
259
300
|
return true;
|
|
260
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
|
+
});
|
|
261
332
|
const selecting = onSelectedChange !== undefined;
|
|
262
333
|
const width = columns.length + (selecting ? 1 : 0) + (rowActions === undefined ? 0 : 1);
|
|
263
334
|
/* Past a few hundred rows only the ones in view are drawn — see `rowWindow.tsx`. It
|
|
264
|
-
windows what is DRAWN,
|
|
335
|
+
windows what is DRAWN, pile headers included and folded rows left out: that is the
|
|
265
336
|
sequence a person scrolls through. */
|
|
266
337
|
const view = useRowWindow({
|
|
267
|
-
count:
|
|
268
|
-
getKey: (i) =>
|
|
338
|
+
count: entries.length,
|
|
339
|
+
getKey: (i) => entries[i]?.id ?? String(i),
|
|
269
340
|
});
|
|
270
|
-
|
|
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]);
|
|
271
346
|
/* The aggregates are computed by the component and not by TanStack: its
|
|
272
347
|
`aggregationFn` lives in the GROUP model, and a footer exists precisely when
|
|
273
348
|
there are no groups. */
|
|
274
349
|
const footer = useMemo(() => (totals ? columns.map((c) => aggregateOf(c, data)) : null), [totals, columns, data]);
|
|
275
|
-
const all = leaves.length > 0 && leaves.every((
|
|
276
|
-
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);
|
|
277
352
|
function toggle(id, on) {
|
|
278
353
|
const n = new Set(selected ?? []);
|
|
279
354
|
if (on)
|
|
@@ -284,49 +359,47 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
284
359
|
}
|
|
285
360
|
const grid = (_jsxs(Table, { height: height, ...(view.on
|
|
286
361
|
? {
|
|
287
|
-
rowCount: 1 +
|
|
362
|
+
rowCount: 1 + entries.length + (draft === null ? 0 : 1) + (totals ? 1 : 0),
|
|
288
363
|
}
|
|
289
|
-
: {}), 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
|
|
290
|
-
const
|
|
291
|
-
if (
|
|
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)
|
|
292
367
|
return [];
|
|
293
|
-
const isGroup = row.subRows.length > 0;
|
|
294
368
|
const windowed = view.windowed(slot.index);
|
|
295
369
|
/* What is not drawn between the previous drawn row and this one: the rows
|
|
296
370
|
above the view before the first, and the gap before a row kept only because
|
|
297
371
|
it has the focus. */
|
|
298
372
|
const gap = view.pad(at === 0 ? 0 : view.rows[at - 1].end, slot.start, width, `\u0000pad${slot.index}`);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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") {
|
|
311
386
|
return [
|
|
312
387
|
gap,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
_jsx("tr", { ...windowed, className: "border-b border-line bg-ground", children: _jsx("td", { colSpan: width, className: "px-3 py-1.5", style: depth === 0
|
|
317
|
-
? undefined
|
|
318
|
-
: { paddingLeft: `calc(var(--spacing-indent) * ${depth} + 0.75rem)` }, children: _jsxs("button", { type: "button", onClick: () => onGroupToggle === undefined ? row.toggleExpanded() : onGroupToggle(key), "aria-expanded": 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: [_jsx(ChevronRight, { className: `size-(--size-icon-sm) text-muted transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true }), level === undefined ? null : (_jsx("span", { className: "text-micro font-normal text-muted/70", children: level.label })), _jsx("span", { children: level === undefined ? value : level.labelOf(value) }), _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: row.getLeafRows().length.toLocaleString(copy.locale) })] }) }) }, row.id),
|
|
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),
|
|
319
391
|
];
|
|
320
392
|
}
|
|
321
|
-
const original =
|
|
393
|
+
const original = entry.item;
|
|
394
|
+
const id = entry.id;
|
|
322
395
|
return [
|
|
323
396
|
gap,
|
|
324
|
-
_jsxs(Tr, { ...(windowed === undefined ? {} : { windowed }), selected: selected?.has(
|
|
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) => {
|
|
325
398
|
const t = c.tone?.(original);
|
|
326
399
|
const n = c.note?.(original);
|
|
327
400
|
const edit = c.edit?.when?.(original) === false ? undefined : c.edit;
|
|
328
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));
|
|
329
|
-
}), rowActions === undefined ? null : (_jsx(RowActions, { actions: rowActions(original) }))] },
|
|
402
|
+
}), rowActions === undefined ? null : (_jsx(RowActions, { actions: rowActions(original) }))] }, id),
|
|
330
403
|
];
|
|
331
404
|
}), view.tail(width), create === undefined || draft === null ? null : (_jsxs(EditRow, { as: "tr", rowId: "\\u0000draft", rowRef: (node) => {
|
|
332
405
|
draftRow.current = node;
|
|
@@ -344,7 +417,7 @@ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels,
|
|
|
344
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) => {
|
|
345
418
|
touched.current = true;
|
|
346
419
|
create.onChange(next);
|
|
347
|
-
}) ?? 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 +
|
|
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) => {
|
|
348
421
|
const value = footer[i] ?? null;
|
|
349
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));
|
|
350
423
|
}), rowActions === undefined ? null : _jsx("td", {})] }) }))] }));
|
|
@@ -50,7 +50,7 @@ function says(node) {
|
|
|
50
50
|
/** The label of a group band — its field, its value, how many rows it holds. The same
|
|
51
51
|
* in the table's band and the stacked list's. */
|
|
52
52
|
function pileTag(pile, locale) {
|
|
53
|
-
return (_jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "shrink-0 text-micro text-muted
|
|
53
|
+
return (_jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "shrink-0 text-micro text-muted", children: pile.field }), _jsx("span", { className: "truncate text-micro font-medium text-ink", children: pile.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: pile.count.toLocaleString(locale) })] }));
|
|
54
54
|
}
|
|
55
55
|
/** The focus ring of an operable row: drawn on the row, inset, so it is not cut by the
|
|
56
56
|
* box and does not move the cells. */
|
|
@@ -602,7 +602,7 @@ groupTotal, height, layout = "auto", onOpen, onDiscard, bands, adds, flag, onReo
|
|
|
602
602
|
return null;
|
|
603
603
|
return (_jsxs("div", { className: "flex items-baseline justify-between gap-3 text-meta", children: [_jsx("dt", { className: "text-muted", children: c.header }), _jsx("dd", { className: "font-semibold text-ink tabular-nums", children: total })] }, c.id));
|
|
604
604
|
}) }))] }));
|
|
605
|
-
return (_jsxs("div", { ref: frame, className: "min-w-0", children: [_jsx("div", { ref: box.ref, className: `min-w-0 ${box.className}`, style: box.style, children: stacked ? (list) : (_jsxs("table", { className: "w-full border-separate border-spacing-0 px-1 text-left", children: [_jsx("thead", { className: "sticky z-10 bg-ground", style: stickyTop, children: _jsxs("tr", { children: [reorderable ? _jsx("
|
|
605
|
+
return (_jsxs("div", { ref: frame, className: "min-w-0", children: [_jsx("div", { ref: box.ref, tabIndex: box.tabIndex, className: `min-w-0 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary ${box.className}`, style: box.style, children: stacked ? (list) : (_jsxs("table", { className: "w-full border-separate border-spacing-0 px-1 text-left", children: [_jsx("thead", { className: "sticky z-10 bg-ground", style: stickyTop, children: _jsxs("tr", { children: [reorderable ? _jsx("td", { className: "w-0 border-b border-line-strong" }) : null, columns.map((c) => c.header === "" ? (_jsx("td", { className: "w-0 border-b border-line-strong" }, c.id)) : (_jsx("th", { scope: "col", style: c.width === undefined ? undefined : { width: c.width }, className: `border-b border-line-strong ${HEADER_TEXT} ${cell} ${c.numeric === true ? "text-right" : "text-left"}`, children: c.header }, c.id))), onRemove === undefined ? null : (_jsx("td", { className: "w-0 border-b border-line-strong" }))] }) }), _jsx("tbody", { ...(reorderable
|
|
606
606
|
? {
|
|
607
607
|
onDragOver: overRow,
|
|
608
608
|
onDrop: (event) => {
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
4
4
|
import { byDensity, useDensity } from "../../foundations/density.js";
|
|
5
5
|
import { stickyTop, useScrollBox } from "../../foundations/sticky.js";
|
|
6
6
|
import { HEADER_TEXT } from "./Table.js";
|
|
@@ -44,11 +44,12 @@ const SKIN = {
|
|
|
44
44
|
},
|
|
45
45
|
};
|
|
46
46
|
function Amount({ value, format }) {
|
|
47
|
+
const { locale } = useUiCopy();
|
|
47
48
|
if (value === null)
|
|
48
49
|
return _jsx("span", { className: "text-line-strong", children: "\u00B7" });
|
|
49
50
|
const zero = value === 0;
|
|
50
51
|
const negative = value < 0;
|
|
51
|
-
return (_jsx("span", { className: `tabular-nums ${zero ? "text-muted" : negative ? "text-bad" : "text-ink"}`, children: format === undefined ? amountFormat(
|
|
52
|
+
return (_jsx("span", { className: `tabular-nums ${zero ? "text-muted" : negative ? "text-bad" : "text-ink"}`, children: format === undefined ? amountFormat(locale).format(value) : format(value) }));
|
|
52
53
|
}
|
|
53
54
|
function ReportLine({ row, columns, open, onToggle, compact, }) {
|
|
54
55
|
const copy = useUiCopy();
|
|
@@ -103,5 +104,5 @@ defaultOpen, caption, height, }) {
|
|
|
103
104
|
desplazamiento de la barra y quedaba empujado 68 px hacia adentro de su
|
|
104
105
|
propia caja — banda en blanco arriba y títulos de columna cayendo entre
|
|
105
106
|
las filas. */
|
|
106
|
-
_jsx("div", { ref: caja.ref, className: `min-w-0 ${caja.className}`, style: caja.style, children: _jsxs("table", { className: "w-full border-collapse px-1", children: [caption === undefined ? null : (_jsx("caption", { className: `text-left text-meta text-muted ${byDensity(d, "px-3 pb-2", "px-2 pb-1.5")}`, children: caption })), _jsx("thead", { className: "sticky z-20 bg-ground", style: stickyTop, children: _jsxs("tr", { className: "border-b border-line-strong", children: [_jsx("th", { className: `sticky left-0 z-10 bg-ground text-left ${compact ? "px-2 py-1.5" : "px-3 py-2"}`, children: _jsx("span", { className: "sr-only", children: copy.report.concept }) }), columns.map((c) => (_jsx("th", { scope: "col", className: `text-right ${HEADER_TEXT} ${compact ? "px-2 py-1.5" : "px-3 py-2"}`, children: c.header }, c.id))), _jsx("
|
|
107
|
+
_jsx("div", { ref: caja.ref, tabIndex: caja.tabIndex, className: `min-w-0 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary ${caja.className}`, style: caja.style, children: _jsxs("table", { className: "w-full border-collapse px-1", children: [caption === undefined ? null : (_jsx("caption", { className: `text-left text-meta text-muted ${byDensity(d, "px-3 pb-2", "px-2 pb-1.5")}`, children: caption })), _jsx("thead", { className: "sticky z-20 bg-ground", style: stickyTop, children: _jsxs("tr", { className: "border-b border-line-strong", children: [_jsx("th", { className: `sticky left-0 z-10 bg-ground text-left ${compact ? "px-2 py-1.5" : "px-3 py-2"}`, children: _jsx("span", { className: "sr-only", children: copy.report.concept }) }), columns.map((c) => (_jsx("th", { scope: "col", className: `text-right ${HEADER_TEXT} ${compact ? "px-2 py-1.5" : "px-3 py-2"}`, children: c.header }, c.id))), _jsx("td", { className: "w-0" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsx(ReportLine, { row: r, columns: columns, open: open, onToggle: toggle, compact: compact }, r.id))) })] }) }));
|
|
107
108
|
}
|