@sincpro/web-ui 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +184 -2
- package/dist/components/activity/Activity.d.ts +69 -0
- package/dist/components/activity/Activity.js +279 -0
- package/dist/components/activity/Messages.d.ts +66 -0
- package/dist/components/activity/Messages.js +46 -0
- package/dist/components/activity/Timeline.d.ts +21 -0
- package/dist/components/activity/Timeline.js +30 -0
- package/dist/components/activity/index.d.ts +3 -0
- package/dist/components/activity/index.js +4 -0
- package/dist/components/assist/AssistMark.d.ts +64 -0
- package/dist/components/assist/AssistMark.js +108 -0
- package/dist/components/assist/AssistPending.d.ts +24 -0
- package/dist/components/assist/AssistPending.js +26 -0
- package/dist/components/assist/AssistSuggestion.d.ts +42 -0
- package/dist/components/assist/AssistSuggestion.js +55 -0
- package/dist/components/assist/ChangeReview.d.ts +56 -0
- package/dist/components/assist/ChangeReview.js +121 -0
- package/dist/components/assist/assist.d.ts +77 -0
- package/dist/components/assist/assist.js +87 -0
- package/dist/components/assist/index.d.ts +5 -0
- package/dist/components/assist/index.js +7 -0
- package/dist/components/form/Form.d.ts +52 -0
- package/dist/components/form/Form.js +49 -0
- package/dist/components/form/Sheet.d.ts +47 -0
- package/dist/components/form/Sheet.js +90 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.js +4 -0
- package/dist/components/frame/Chart.d.ts +13 -0
- package/dist/components/frame/Chart.js +24 -0
- package/dist/components/frame/FrameView.d.ts +33 -0
- package/dist/components/frame/FrameView.js +125 -0
- package/dist/components/frame/index.d.ts +3 -0
- package/dist/components/frame/index.js +3 -0
- package/dist/components/frame/model.d.ts +43 -0
- package/dist/components/frame/model.js +101 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.js +11 -0
- package/dist/components/lines/LineItems.d.ts +111 -0
- package/dist/components/lines/LineItems.js +317 -0
- package/dist/components/lines/index.d.ts +1 -0
- package/dist/components/lines/index.js +2 -0
- package/dist/components/lines/lines.d.ts +23 -0
- package/dist/components/lines/lines.js +26 -0
- package/dist/components/listing/ColumnsMenu.d.ts +20 -0
- package/dist/components/listing/ColumnsMenu.js +46 -0
- package/dist/components/listing/GroupingLevels.d.ts +35 -0
- package/dist/components/listing/GroupingLevels.js +45 -0
- package/dist/components/listing/Listing.d.ts +27 -0
- package/dist/components/listing/Listing.js +86 -0
- package/dist/components/listing/ListingBar.d.ts +112 -0
- package/dist/components/listing/ListingBar.js +144 -0
- package/dist/components/listing/Pagination.d.ts +36 -0
- package/dist/components/listing/Pagination.js +69 -0
- package/dist/components/listing/RecordCard.d.ts +33 -0
- package/dist/components/listing/RecordCard.js +22 -0
- package/dist/components/listing/RecordLink.d.ts +7 -0
- package/dist/components/listing/RecordLink.js +22 -0
- package/dist/components/listing/Row.d.ts +110 -0
- package/dist/components/listing/Row.js +123 -0
- package/dist/components/listing/SortMenu.d.ts +62 -0
- package/dist/components/listing/SortMenu.js +105 -0
- package/dist/components/listing/ViewSwitcher.d.ts +24 -0
- package/dist/components/listing/ViewSwitcher.js +16 -0
- package/dist/components/listing/columns.d.ts +47 -0
- package/dist/components/listing/columns.js +72 -0
- package/dist/components/listing/grouping.d.ts +35 -0
- package/dist/components/listing/grouping.js +47 -0
- package/dist/components/listing/index.d.ts +17 -0
- package/dist/components/listing/index.js +20 -0
- package/dist/components/listing/modelColumns.d.ts +50 -0
- package/dist/components/listing/modelColumns.js +103 -0
- package/dist/components/listing/pages.d.ts +78 -0
- package/dist/components/listing/pages.js +135 -0
- package/dist/components/listing/selection.d.ts +21 -0
- package/dist/components/listing/selection.js +43 -0
- package/dist/components/listing/set.d.ts +48 -0
- package/dist/components/listing/set.js +24 -0
- package/dist/components/listing/tree.d.ts +28 -0
- package/dist/components/listing/tree.js +23 -0
- package/dist/components/navigation/CommandPalette.d.ts +40 -0
- package/dist/components/navigation/CommandPalette.js +114 -0
- package/dist/components/navigation/NavigationTree.d.ts +31 -0
- package/dist/components/navigation/NavigationTree.js +79 -0
- package/dist/components/navigation/commands.d.ts +30 -0
- package/dist/components/navigation/commands.js +30 -0
- package/dist/components/navigation/index.d.ts +3 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/record/Breadcrumb.d.ts +42 -0
- package/dist/components/record/Breadcrumb.js +98 -0
- package/dist/components/record/DirtyActions.d.ts +26 -0
- package/dist/components/record/DirtyActions.js +22 -0
- package/dist/components/record/Pager.d.ts +18 -0
- package/dist/components/record/Pager.js +33 -0
- package/dist/components/record/RecordHeader.d.ts +75 -0
- package/dist/components/record/RecordHeader.js +104 -0
- package/dist/components/record/RecordNotices.d.ts +37 -0
- package/dist/components/record/RecordNotices.js +75 -0
- package/dist/components/record/RecordSkeleton.d.ts +3 -0
- package/dist/components/record/RecordSkeleton.js +21 -0
- package/dist/components/record/SmartButtons.d.ts +35 -0
- package/dist/components/record/SmartButtons.js +38 -0
- package/dist/components/record/StatusBar.d.ts +51 -0
- package/dist/components/record/StatusBar.js +168 -0
- package/dist/components/record/ThumbBar.d.ts +36 -0
- package/dist/components/record/ThumbBar.js +86 -0
- package/dist/components/record/index.d.ts +6 -0
- package/dist/components/record/index.js +8 -0
- package/dist/components/search/CriteriaBuilder.d.ts +58 -0
- package/dist/components/search/CriteriaBuilder.js +278 -0
- package/dist/components/search/CriteriaChips.d.ts +22 -0
- package/dist/components/search/CriteriaChips.js +64 -0
- package/dist/components/search/FilterBuilder.d.ts +142 -0
- package/dist/components/search/FilterBuilder.js +421 -0
- package/dist/components/search/JoinWord.d.ts +8 -0
- package/dist/components/search/JoinWord.js +47 -0
- package/dist/components/search/JsonEditor.d.ts +19 -0
- package/dist/components/search/JsonEditor.js +78 -0
- package/dist/components/search/SearchBar.d.ts +78 -0
- package/dist/components/search/SearchBar.js +387 -0
- package/dist/components/search/criteria.d.ts +116 -0
- package/dist/components/search/criteria.js +159 -0
- package/dist/components/search/datePeriods.d.ts +122 -0
- package/dist/components/search/datePeriods.js +134 -0
- package/dist/components/search/dateRanges.d.ts +54 -0
- package/dist/components/search/dateRanges.js +89 -0
- package/dist/components/search/index.d.ts +9 -0
- package/dist/components/search/index.js +20 -0
- package/dist/components/search/model.d.ts +54 -0
- package/dist/components/search/model.js +37 -0
- package/dist/components/search/query.d.ts +166 -0
- package/dist/components/search/query.js +436 -0
- package/dist/components/shell/AppFrame.d.ts +14 -0
- package/dist/components/shell/AppFrame.js +80 -0
- package/dist/components/shell/TopBar.d.ts +73 -0
- package/dist/components/shell/TopBar.js +54 -0
- package/dist/components/shell/headerSlot.d.ts +17 -0
- package/dist/components/shell/headerSlot.js +21 -0
- package/dist/components/shell/index.d.ts +2 -0
- package/dist/components/shell/index.js +3 -0
- package/dist/foundations/aria.d.ts +14 -0
- package/dist/foundations/aria.js +18 -0
- package/dist/foundations/calendar.d.ts +28 -0
- package/dist/foundations/calendar.js +183 -0
- package/dist/foundations/copy.d.ts +703 -0
- package/dist/foundations/copy.es.d.ts +2 -0
- package/dist/foundations/copy.es.js +430 -0
- package/dist/foundations/copy.js +520 -0
- package/dist/foundations/density.d.ts +16 -0
- package/dist/foundations/density.js +21 -0
- package/dist/foundations/grouping.d.ts +281 -0
- package/dist/foundations/grouping.js +0 -0
- package/dist/foundations/index.d.ts +14 -0
- package/dist/foundations/index.js +30 -0
- package/dist/foundations/layout.d.ts +9 -0
- package/dist/foundations/layout.js +9 -0
- package/dist/foundations/pivot.d.ts +222 -0
- package/dist/foundations/pivot.js +198 -0
- package/dist/foundations/pivotSheet.d.ts +61 -0
- package/dist/foundations/pivotSheet.js +0 -0
- package/dist/foundations/slots.d.ts +67 -0
- package/dist/foundations/slots.js +63 -0
- package/dist/foundations/sticky.d.ts +53 -0
- package/dist/foundations/sticky.js +150 -0
- package/dist/foundations/surface.d.ts +9 -0
- package/dist/foundations/surface.js +26 -0
- package/dist/foundations/theme.d.ts +42 -0
- package/dist/foundations/theme.js +88 -0
- package/dist/foundations/types.d.ts +23 -0
- package/dist/foundations/types.js +50 -0
- package/dist/foundations/variants.d.ts +19 -0
- package/dist/foundations/variants.js +48 -0
- package/dist/foundations/xlsx.d.ts +86 -0
- package/dist/foundations/xlsx.js +0 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/primitives/block/Block.d.ts +66 -0
- package/dist/primitives/block/Block.js +34 -0
- package/dist/primitives/block/Line.d.ts +18 -0
- package/dist/primitives/block/Line.js +45 -0
- package/dist/primitives/block/SectionGrid.d.ts +30 -0
- package/dist/primitives/block/SectionGrid.js +29 -0
- package/dist/primitives/block/Steps.d.ts +76 -0
- package/dist/primitives/block/Steps.js +43 -0
- package/dist/primitives/block/index.d.ts +4 -0
- package/dist/primitives/block/index.js +7 -0
- package/dist/primitives/button/Button.d.ts +137 -0
- package/dist/primitives/button/Button.js +234 -0
- package/dist/primitives/button/index.d.ts +1 -0
- package/dist/primitives/button/index.js +2 -0
- package/dist/primitives/cell/CellStack.d.ts +47 -0
- package/dist/primitives/cell/CellStack.js +33 -0
- package/dist/primitives/cell/index.d.ts +1 -0
- package/dist/primitives/cell/index.js +3 -0
- package/dist/primitives/choice/ComboBox.d.ts +19 -0
- package/dist/primitives/choice/ComboBox.js +52 -0
- package/dist/primitives/choice/RadioGroup.d.ts +18 -0
- package/dist/primitives/choice/RadioGroup.js +50 -0
- package/dist/primitives/choice/SearchField.d.ts +33 -0
- package/dist/primitives/choice/SearchField.js +46 -0
- package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
- package/dist/primitives/choice/SegmentedControl.js +69 -0
- package/dist/primitives/choice/Slider.d.ts +16 -0
- package/dist/primitives/choice/Slider.js +46 -0
- package/dist/primitives/choice/Switch.d.ts +14 -0
- package/dist/primitives/choice/Switch.js +65 -0
- package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
- package/dist/primitives/choice/ThemeSwitcher.js +25 -0
- package/dist/primitives/choice/index.d.ts +8 -0
- package/dist/primitives/choice/index.js +11 -0
- package/dist/primitives/choice/options.d.ts +28 -0
- package/dist/primitives/choice/options.js +6 -0
- package/dist/primitives/date/Calendar.d.ts +31 -0
- package/dist/primitives/date/Calendar.js +134 -0
- package/dist/primitives/date/DatePicker.d.ts +42 -0
- package/dist/primitives/date/DatePicker.js +103 -0
- package/dist/primitives/date/DateTime.d.ts +35 -0
- package/dist/primitives/date/DateTime.js +100 -0
- package/dist/primitives/date/index.d.ts +3 -0
- package/dist/primitives/date/index.js +6 -0
- package/dist/primitives/dialog/Dialog.d.ts +118 -0
- package/dist/primitives/dialog/Dialog.js +121 -0
- package/dist/primitives/dialog/Drill.d.ts +90 -0
- package/dist/primitives/dialog/Drill.js +172 -0
- package/dist/primitives/dialog/index.d.ts +2 -0
- package/dist/primitives/dialog/index.js +4 -0
- package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
- package/dist/primitives/disclosure/Disclosure.js +52 -0
- package/dist/primitives/disclosure/index.d.ts +1 -0
- package/dist/primitives/disclosure/index.js +2 -0
- package/dist/primitives/field/Checks.d.ts +57 -0
- package/dist/primitives/field/Checks.js +72 -0
- package/dist/primitives/field/DropZone.d.ts +42 -0
- package/dist/primitives/field/DropZone.js +52 -0
- package/dist/primitives/field/Field.d.ts +178 -0
- package/dist/primitives/field/Field.js +251 -0
- package/dist/primitives/field/Suggest.d.ts +48 -0
- package/dist/primitives/field/Suggest.js +43 -0
- package/dist/primitives/field/aria.d.ts +61 -0
- package/dist/primitives/field/aria.js +70 -0
- package/dist/primitives/field/context.d.ts +30 -0
- package/dist/primitives/field/context.js +13 -0
- package/dist/primitives/field/copy.d.ts +16 -0
- package/dist/primitives/field/copy.js +69 -0
- package/dist/primitives/field/index.d.ts +9 -0
- package/dist/primitives/field/index.js +15 -0
- package/dist/primitives/field/layout.d.ts +24 -0
- package/dist/primitives/field/layout.js +10 -0
- package/dist/primitives/field/mode.d.ts +9 -0
- package/dist/primitives/field/mode.js +13 -0
- package/dist/primitives/field/note.d.ts +55 -0
- package/dist/primitives/field/note.js +117 -0
- package/dist/primitives/hint/Hint.d.ts +31 -0
- package/dist/primitives/hint/Hint.js +42 -0
- package/dist/primitives/hint/index.d.ts +1 -0
- package/dist/primitives/hint/index.js +1 -0
- package/dist/primitives/index.d.ts +20 -0
- package/dist/primitives/index.js +21 -0
- package/dist/primitives/menu/Menu.d.ts +128 -0
- package/dist/primitives/menu/Menu.js +89 -0
- package/dist/primitives/menu/index.d.ts +1 -0
- package/dist/primitives/menu/index.js +2 -0
- package/dist/primitives/notice/Callout.d.ts +49 -0
- package/dist/primitives/notice/Callout.js +28 -0
- package/dist/primitives/notice/Flag.d.ts +13 -0
- package/dist/primitives/notice/Flag.js +31 -0
- package/dist/primitives/notice/Notice.d.ts +45 -0
- package/dist/primitives/notice/Notice.js +61 -0
- package/dist/primitives/notice/Toast.d.ts +55 -0
- package/dist/primitives/notice/Toast.js +142 -0
- package/dist/primitives/notice/index.d.ts +4 -0
- package/dist/primitives/notice/index.js +6 -0
- package/dist/primitives/overlay/Popover.d.ts +6 -0
- package/dist/primitives/overlay/Popover.js +27 -0
- package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
- package/dist/primitives/overlay/PopoverTrigger.js +38 -0
- package/dist/primitives/overlay/Preview.d.ts +19 -0
- package/dist/primitives/overlay/Preview.js +60 -0
- package/dist/primitives/overlay/Tooltip.d.ts +38 -0
- package/dist/primitives/overlay/Tooltip.js +74 -0
- package/dist/primitives/overlay/index.d.ts +4 -0
- package/dist/primitives/overlay/index.js +7 -0
- package/dist/primitives/picker/Picker.d.ts +89 -0
- package/dist/primitives/picker/Picker.js +204 -0
- package/dist/primitives/picker/index.d.ts +1 -0
- package/dist/primitives/picker/index.js +2 -0
- package/dist/primitives/surface/Peek.d.ts +60 -0
- package/dist/primitives/surface/Peek.js +71 -0
- package/dist/primitives/surface/index.d.ts +1 -0
- package/dist/primitives/surface/index.js +2 -0
- package/dist/primitives/table/CellCopy.d.ts +20 -0
- package/dist/primitives/table/CellCopy.js +23 -0
- package/dist/primitives/table/CellEditor.d.ts +14 -0
- package/dist/primitives/table/CellEditor.js +83 -0
- package/dist/primitives/table/DataTable.d.ts +189 -0
- package/dist/primitives/table/DataTable.js +330 -0
- package/dist/primitives/table/EditableTable.d.ts +191 -0
- package/dist/primitives/table/EditableTable.js +617 -0
- package/dist/primitives/table/PivotTable.d.ts +14 -0
- package/dist/primitives/table/PivotTable.js +0 -0
- package/dist/primitives/table/ReportTable.d.ts +38 -0
- package/dist/primitives/table/ReportTable.js +106 -0
- package/dist/primitives/table/RowActions.d.ts +16 -0
- package/dist/primitives/table/RowActions.js +28 -0
- package/dist/primitives/table/SheetTable.d.ts +73 -0
- package/dist/primitives/table/SheetTable.js +230 -0
- package/dist/primitives/table/Table.d.ts +106 -0
- package/dist/primitives/table/Table.js +116 -0
- package/dist/primitives/table/index.d.ts +7 -0
- package/dist/primitives/table/index.js +9 -0
- package/dist/primitives/tabs/Tabs.d.ts +91 -0
- package/dist/primitives/tabs/Tabs.js +56 -0
- package/dist/primitives/tabs/index.d.ts +1 -0
- package/dist/primitives/tabs/index.js +1 -0
- package/dist/primitives/tag/Tag.d.ts +36 -0
- package/dist/primitives/tag/Tag.js +68 -0
- package/dist/primitives/tag/Tags.d.ts +57 -0
- package/dist/primitives/tag/Tags.js +104 -0
- package/dist/primitives/tag/TagsField.d.ts +35 -0
- package/dist/primitives/tag/TagsField.js +186 -0
- package/dist/primitives/tag/choices.d.ts +38 -0
- package/dist/primitives/tag/choices.js +26 -0
- package/dist/primitives/tag/index.d.ts +4 -0
- package/dist/primitives/tag/index.js +8 -0
- package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
- package/dist/primitives/toolbar/Toolbar.js +36 -0
- package/dist/primitives/toolbar/index.d.ts +1 -0
- package/dist/primitives/toolbar/index.js +2 -0
- package/dist/primitives/tree/Tree.d.ts +56 -0
- package/dist/primitives/tree/Tree.js +74 -0
- package/dist/primitives/tree/index.d.ts +1 -0
- package/dist/primitives/tree/index.js +3 -0
- package/dist/primitives/value/Copyable.d.ts +48 -0
- package/dist/primitives/value/Copyable.js +71 -0
- package/dist/primitives/value/Fecha.d.ts +12 -0
- package/dist/primitives/value/Fecha.js +89 -0
- package/dist/primitives/value/Meter.d.ts +29 -0
- package/dist/primitives/value/Meter.js +46 -0
- package/dist/primitives/value/Metric.d.ts +42 -0
- package/dist/primitives/value/Metric.js +38 -0
- package/dist/primitives/value/Person.d.ts +90 -0
- package/dist/primitives/value/Person.js +208 -0
- package/dist/primitives/value/ProgressBar.d.ts +31 -0
- package/dist/primitives/value/ProgressBar.js +53 -0
- package/dist/primitives/value/Relation.d.ts +74 -0
- package/dist/primitives/value/Relation.js +74 -0
- package/dist/primitives/value/Totals.d.ts +121 -0
- package/dist/primitives/value/Totals.js +54 -0
- package/dist/primitives/value/Value.d.ts +40 -0
- package/dist/primitives/value/Value.js +80 -0
- package/dist/primitives/value/index.d.ts +9 -0
- package/dist/primitives/value/index.js +10 -0
- package/dist/tokens.css +1062 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +3 -0
- package/dist/views/list/List.d.ts +119 -0
- package/dist/views/list/List.js +101 -0
- package/dist/views/list/ListView.d.ts +6 -0
- package/dist/views/list/ListView.js +295 -0
- package/dist/views/list/columns.d.ts +18 -0
- package/dist/views/list/columns.js +27 -0
- package/dist/views/list/derive.d.ts +87 -0
- package/dist/views/list/derive.js +207 -0
- package/dist/views/list/index.d.ts +2 -0
- package/dist/views/list/index.js +4 -0
- package/dist/views/list/presentations.d.ts +39 -0
- package/dist/views/list/presentations.js +105 -0
- package/dist/views/list/types.d.ts +284 -0
- package/dist/views/list/types.js +1 -0
- package/dist/views/list/useNarrowList.d.ts +17 -0
- package/dist/views/list/useNarrowList.js +32 -0
- package/dist/views/record/Record.d.ts +145 -0
- package/dist/views/record/Record.js +211 -0
- package/dist/views/record/index.d.ts +1 -0
- package/dist/views/record/index.js +2 -0
- package/package.json +113 -4
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { currentCopy, useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
import { Button } from "../../primitives/button/Button.js";
|
|
5
|
+
import { TextArea } from "../../primitives/field/Field.js";
|
|
6
|
+
import { Send, Users, X } from "lucide-react";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
/**
|
|
9
|
+
* ESCRIBIR UNA NOTA, la manera que toda lista tiene.
|
|
10
|
+
*
|
|
11
|
+
* Es una función y no una constante porque sus palabras salen de la copia, y una
|
|
12
|
+
* constante las congelaría con las que hubiera al cargar el módulo —o sea, las
|
|
13
|
+
* de fábrica— por más que después se instale otra.
|
|
14
|
+
*/
|
|
15
|
+
export function writeANote() {
|
|
16
|
+
const copy = currentCopy();
|
|
17
|
+
return {
|
|
18
|
+
id: "note",
|
|
19
|
+
label: copy.activity.writeNote,
|
|
20
|
+
internal: true,
|
|
21
|
+
placeholder: copy.activity.notePlaceholder,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function MessageComposer({ kinds, onSend, sending = false, footer, }) {
|
|
25
|
+
const copy = useUiCopy();
|
|
26
|
+
/* La manera por default se arma acá adentro y no en la firma: un default de
|
|
27
|
+
parámetro se evalúa en cada render, sí, pero escrito como constante del
|
|
28
|
+
módulo congelaría las palabras de fábrica. */
|
|
29
|
+
const ways = kinds ?? [writeANote()];
|
|
30
|
+
const [writing, setWriting] = useState(undefined);
|
|
31
|
+
const [body, setBody] = useState("");
|
|
32
|
+
const ready = body.trim() !== "" && !sending;
|
|
33
|
+
function close() {
|
|
34
|
+
setWriting(undefined);
|
|
35
|
+
setBody("");
|
|
36
|
+
}
|
|
37
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [ways.map((kind) => (_jsx(Button, {
|
|
38
|
+
/* Chosen is `secondary`, not `primary`: the gold is the record's one action,
|
|
39
|
+
and a way of writing that is merely selected is not an action. */
|
|
40
|
+
role: writing?.id === kind.id ? "secondary" : "ghost", size: "sm", onClick: () => (writing?.id === kind.id ? close() : setWriting(kind)), children: kind.label }, kind.id))), footer === undefined ? null : _jsx("div", { className: "ml-auto min-w-0", children: footer })] }), writing === undefined ? null : (_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [writing.internal === true || writing.recipients === undefined ? null : (_jsxs("p", { className: "flex min-w-0 items-center gap-1.5 text-micro text-muted", title: copy.activity.recipients, children: [_jsx(Users, { className: "size-3.5 shrink-0", "aria-hidden": true }), _jsx("span", { className: "min-w-0 truncate", children: writing.recipients.length === 0
|
|
41
|
+
? copy.activity.noRecipients
|
|
42
|
+
: writing.recipients.join(" · ") })] })), _jsx(TextArea, { value: body, onChange: setBody, placeholder: writing.placeholder ?? copy.activity.messagePlaceholder, rows: 3 }), _jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Button, { role: "secondary", size: "sm", icon: Send, disabled: !ready, onClick: () => {
|
|
43
|
+
onSend(body.trim(), writing);
|
|
44
|
+
close();
|
|
45
|
+
}, children: sending ? copy.activity.sending : copy.activity.send }), _jsx(Button, { role: "ghost", size: "sm", icon: X, onClick: close, children: copy.activity.cancel })] })] }))] }));
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Surface, Tone } from "../../foundations/types.js";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export type TimelineItemProps = {
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
at: Date | string | number;
|
|
6
|
+
tone?: Tone;
|
|
7
|
+
action?: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
export type TimelineProps = {
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
empty?: {
|
|
12
|
+
sentence: string;
|
|
13
|
+
action?: ReactNode;
|
|
14
|
+
};
|
|
15
|
+
surface?: Surface;
|
|
16
|
+
};
|
|
17
|
+
/** Renders items in the order given — this kit's convention is newest first. */
|
|
18
|
+
export declare function Timeline({ children, empty, surface }: TimelineProps): import("react").JSX.Element | null;
|
|
19
|
+
export declare namespace Timeline {
|
|
20
|
+
var Item: ({ label, at, tone, action }: TimelineItemProps) => import("react").JSX.Element;
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { SurfaceProvider, useSurface } from "../../foundations/surface.js";
|
|
4
|
+
import { Empty } from "../../primitives/notice/Notice.js";
|
|
5
|
+
import { DateValue } from "../../primitives/value/Fecha.js";
|
|
6
|
+
import { Children } from "react";
|
|
7
|
+
const DOT_TONE = {
|
|
8
|
+
neutral: { canvas: "bg-muted", chrome: "bg-chrome-muted" },
|
|
9
|
+
info: { canvas: "bg-info", chrome: "bg-chrome-link" },
|
|
10
|
+
good: { canvas: "bg-good", chrome: "bg-chrome-good" },
|
|
11
|
+
warn: { canvas: "bg-warn", chrome: "bg-chrome-warn" },
|
|
12
|
+
bad: { canvas: "bg-bad", chrome: "bg-chrome-bad" },
|
|
13
|
+
accent: { canvas: "bg-accent", chrome: "bg-chrome-accent" },
|
|
14
|
+
};
|
|
15
|
+
function Item({ label, at, tone = "neutral", action }) {
|
|
16
|
+
const surface = useSurface();
|
|
17
|
+
const isChrome = surface === "chrome";
|
|
18
|
+
const text = isChrome ? "text-chrome-ink" : "text-ink";
|
|
19
|
+
const rail = isChrome ? "bg-chrome-ink/20" : "bg-line";
|
|
20
|
+
return (_jsxs("li", { className: "group/item relative flex gap-3 pb-4 last:pb-0", children: [_jsxs("span", { className: "relative flex w-2 shrink-0 justify-center", children: [_jsx("span", { className: `mt-1 size-2 shrink-0 rounded-pill ${DOT_TONE[tone][surface]}` }), _jsx("span", { className: `absolute top-3 bottom-0 w-px group-last/item:hidden ${rail}` })] }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-0.5", children: [_jsx("span", { className: `min-w-0 text-meta ${text}`, children: label }), _jsx(DateValue, { when: at, format: "exact" })] }), action === undefined ? null : _jsx("div", { className: "mt-1", children: action })] })] }));
|
|
21
|
+
}
|
|
22
|
+
/** Renders items in the order given — this kit's convention is newest first. */
|
|
23
|
+
export function Timeline({ children, empty, surface }) {
|
|
24
|
+
const activeSurface = useSurface(surface);
|
|
25
|
+
if (Children.toArray(children).length === 0) {
|
|
26
|
+
return empty === undefined ? null : (_jsx(Empty, { sentence: empty.sentence, ...(empty.action === undefined ? {} : { action: empty.action }) }));
|
|
27
|
+
}
|
|
28
|
+
return (_jsx(SurfaceProvider, { value: activeSurface, children: _jsx("ol", { className: "flex flex-col", children: children }) }));
|
|
29
|
+
}
|
|
30
|
+
Timeline.Item = Item;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { Activity, type ActivityAlert, type ActivityEvent, type ActivityLayout, type ActivityProps, useActivity, } from "./Activity.js";
|
|
2
|
+
export { type Message, MessageComposer, type MessageComposerProps, type MessageKind, writeANote, } from "./Messages.js";
|
|
3
|
+
export { Timeline, type TimelineItemProps, type TimelineProps, } from "./Timeline.js";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ActivityEvent } from "../activity/Activity.js";
|
|
2
|
+
import { type AssistOrigin, type AssistSource } from "./assist.js";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
/** The robot and the assistant's name: the header of a suggestion, a review, a pending
|
|
5
|
+
* state. `children` replaces the name — «Asistente sugiere». */
|
|
6
|
+
export declare function AssistLabel({ children, id }: {
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
id?: string;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* Where the assistant got it: «Fuente: orden de compra PO/2026/0042».
|
|
12
|
+
*
|
|
13
|
+
* Context: a value without its source is a value nobody can check, and an unchecked value
|
|
14
|
+
* from a machine is the one people stop trusting after the first mistake. The source is a
|
|
15
|
+
* LINK to the record it was read from — with `href` a real `<a>` (middle-click, copy), with
|
|
16
|
+
* `onOpen` a link the screen answers — so checking it is one press, not a search.
|
|
17
|
+
*/
|
|
18
|
+
export declare function AssistSourceLink({ source }: {
|
|
19
|
+
source: AssistSource;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
export type AssistMarkProps = {
|
|
22
|
+
/** When, from what, and who accepted it. Each one that is present is said in the card. */
|
|
23
|
+
origin?: AssistOrigin;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The small robot next to a value the assistant set — provenance, after the fact.
|
|
27
|
+
*
|
|
28
|
+
* Context: once a suggestion is accepted the value looks like any other, and a week later
|
|
29
|
+
* nobody remembers whether «30 días» was typed or proposed. The mark keeps that answer next
|
|
30
|
+
* to the value: its tooltip says «Lo completó el asistente», and pressing it opens WHO
|
|
31
|
+
* (the assistant, and the person who accepted), WHEN and FROM WHAT — with the source as a
|
|
32
|
+
* link. It is a popover and not a tooltip because the source is a link, and a tooltip can
|
|
33
|
+
* never be entered with the keyboard.
|
|
34
|
+
*
|
|
35
|
+
* Next to a field, write it in the field's `action`; next to a read value, use
|
|
36
|
+
* `AssistedValue`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function AssistMark({ origin }: AssistMarkProps): import("react").JSX.Element;
|
|
39
|
+
/** A read value with the assistant's mark after it: a `Record.Facts` value, a table cell. */
|
|
40
|
+
export declare function AssistedValue({ children, origin, }: {
|
|
41
|
+
/** The value, drawn the way it would be without the mark. */
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
origin?: AssistOrigin;
|
|
44
|
+
}): import("react").JSX.Element;
|
|
45
|
+
/**
|
|
46
|
+
* The assistant's entry in a record's history: the activity's event, with the robot as its
|
|
47
|
+
* node, the assistant (and who accepted) as its author, and the source as its detail.
|
|
48
|
+
*
|
|
49
|
+
* Context: provenance has two places. The mark answers «where did THIS value come from»
|
|
50
|
+
* while looking at it; the history answers «what has the assistant done on this record»,
|
|
51
|
+
* in the order it happened, next to what people did. Same mark in both, so the eye learns
|
|
52
|
+
* it once.
|
|
53
|
+
*
|
|
54
|
+
* Call it when the change happens (in the handler), not at module load: it reads the
|
|
55
|
+
* installed copy, like the formatters do.
|
|
56
|
+
*/
|
|
57
|
+
export declare function assistEvent({ id, label, at, source, acceptedBy, }: {
|
|
58
|
+
id: string;
|
|
59
|
+
/** What happened, in the past: «completó 5 líneas desde la orden de compra». */
|
|
60
|
+
label: ReactNode;
|
|
61
|
+
at: Date | string | number;
|
|
62
|
+
source?: AssistSource;
|
|
63
|
+
acceptedBy?: string;
|
|
64
|
+
}): ActivityEvent;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ASSIST_ICON, } from "./assist.js";
|
|
4
|
+
import { currentCopy, fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
5
|
+
import { cn, focusRing } from "../../foundations/variants.js";
|
|
6
|
+
import { PopoverTrigger } from "../../primitives/overlay/PopoverTrigger.js";
|
|
7
|
+
import { Tooltip, TooltipTrigger } from "../../primitives/overlay/Tooltip.js";
|
|
8
|
+
import { DateValue } from "../../primitives/value/Fecha.js";
|
|
9
|
+
import { Link } from "react-aria-components";
|
|
10
|
+
/* ==================================================================== *
|
|
11
|
+
* THE ASSISTANT'S MARK — how a screen says «this came from the assistant».
|
|
12
|
+
*
|
|
13
|
+
* Three pieces, one look:
|
|
14
|
+
*
|
|
15
|
+
* AssistLabel the robot and the word «Asistente», in the info tone:
|
|
16
|
+
* the header of anything the assistant proposes
|
|
17
|
+
* AssistSourceLink «Fuente: orden de compra PO/2026/0042», a real link
|
|
18
|
+
* AssistMark the small robot next to a value it SET, which opens
|
|
19
|
+
* who, when and from what
|
|
20
|
+
*
|
|
21
|
+
* ── Why the info tone, and never the gold ───────────────────────────
|
|
22
|
+
*
|
|
23
|
+
* The gold is the ONE main action of a screen (PRINCIPLES §2–3). The assistant
|
|
24
|
+
* is not an action, it is a source of information — and `info` already means
|
|
25
|
+
* «a fact that changes how you read what is below». It is also never the color
|
|
26
|
+
* alone: the robot and the word are always there, so a person who does not tell
|
|
27
|
+
* teal from navy still reads «Asistente».
|
|
28
|
+
*
|
|
29
|
+
* ── Why a robot and not sparkles ────────────────────────────────────
|
|
30
|
+
*
|
|
31
|
+
* Sparkles celebrate; a business screen states. «An automated assistant did
|
|
32
|
+
* this» is the fact a person needs to weigh a value, and the robot says exactly
|
|
33
|
+
* that. The icon lives in `ASSIST_ICON`, so changing it is one line.
|
|
34
|
+
* ==================================================================== */
|
|
35
|
+
/** The robot and the assistant's name: the header of a suggestion, a review, a pending
|
|
36
|
+
* state. `children` replaces the name — «Asistente sugiere». */
|
|
37
|
+
export function AssistLabel({ children, id }) {
|
|
38
|
+
const copy = useUiCopy();
|
|
39
|
+
const Icon = ASSIST_ICON;
|
|
40
|
+
return (_jsxs("span", { id: id, "data-assist": "label", className: "inline-flex shrink-0 items-center gap-1 text-micro font-medium text-info", children: [_jsx(Icon, { className: "size-(--size-icon-sm) shrink-0", strokeWidth: 2, "aria-hidden": true }), children ?? copy.assist.assistant] }));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Where the assistant got it: «Fuente: orden de compra PO/2026/0042».
|
|
44
|
+
*
|
|
45
|
+
* Context: a value without its source is a value nobody can check, and an unchecked value
|
|
46
|
+
* from a machine is the one people stop trusting after the first mistake. The source is a
|
|
47
|
+
* LINK to the record it was read from — with `href` a real `<a>` (middle-click, copy), with
|
|
48
|
+
* `onOpen` a link the screen answers — so checking it is one press, not a search.
|
|
49
|
+
*/
|
|
50
|
+
export function AssistSourceLink({ source }) {
|
|
51
|
+
const copy = useUiCopy();
|
|
52
|
+
const goes = source.href !== undefined || source.onOpen !== undefined;
|
|
53
|
+
return (_jsxs("span", { className: "inline-flex max-w-full min-w-0 items-baseline gap-1 text-micro text-muted", children: [_jsxs("span", { className: "shrink-0", children: [copy.assist.source, ":"] }), goes ? (_jsx(Link, { ...(source.href === undefined ? {} : { href: source.href }), ...(source.onOpen === undefined ? {} : { onPress: source.onOpen }), className: ({ isFocusVisible }) => cn("min-w-0 cursor-pointer truncate rounded-mark text-primary underline-offset-2 hover:underline", focusRing({ isFocusVisible })), children: source.label })) : (_jsx("span", { className: "min-w-0 truncate text-ink", children: source.label }))] }));
|
|
54
|
+
}
|
|
55
|
+
/** The mark's own button. It forwards `ref` and spreads native props, so `PopoverTrigger`
|
|
56
|
+
* anchors to it and `TooltipTrigger` names it — the same contract as `IconButton`, in the
|
|
57
|
+
* assistant's tone instead of the three an icon button has. */
|
|
58
|
+
function MarkButton({ label, expanded, haspopup, ref, ...native }) {
|
|
59
|
+
const Icon = ASSIST_ICON;
|
|
60
|
+
return (_jsxs(TooltipTrigger, { children: [_jsx("button", { ...native, ref: ref, type: "button", "aria-label": label, ...(haspopup === undefined ? {} : { "aria-haspopup": haspopup }), ...(expanded === undefined ? {} : { "aria-expanded": expanded }), "data-assist": "mark", className: "inline-flex size-(--size-control-sm) shrink-0 items-center justify-center rounded-pill text-info transition-colors duration-(--duration-instant) hover:bg-info/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: _jsx(Icon, { className: "size-(--size-icon-sm)", strokeWidth: 2, "aria-hidden": true }) }), _jsx(Tooltip, { children: label })] }));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The small robot next to a value the assistant set — provenance, after the fact.
|
|
64
|
+
*
|
|
65
|
+
* Context: once a suggestion is accepted the value looks like any other, and a week later
|
|
66
|
+
* nobody remembers whether «30 días» was typed or proposed. The mark keeps that answer next
|
|
67
|
+
* to the value: its tooltip says «Lo completó el asistente», and pressing it opens WHO
|
|
68
|
+
* (the assistant, and the person who accepted), WHEN and FROM WHAT — with the source as a
|
|
69
|
+
* link. It is a popover and not a tooltip because the source is a link, and a tooltip can
|
|
70
|
+
* never be entered with the keyboard.
|
|
71
|
+
*
|
|
72
|
+
* Next to a field, write it in the field's `action`; next to a read value, use
|
|
73
|
+
* `AssistedValue`.
|
|
74
|
+
*/
|
|
75
|
+
export function AssistMark({ origin = {} }) {
|
|
76
|
+
const copy = useUiCopy();
|
|
77
|
+
return (_jsx(PopoverTrigger, { title: copy.assist.madeBy, placement: "bottom end", trigger: _jsx(MarkButton, { label: copy.assist.madeBy }), children: _jsxs("div", { className: "flex max-w-72 min-w-0 flex-col gap-1.5 text-micro text-muted", children: [origin.source === undefined ? null : _jsx(AssistSourceLink, { source: origin.source }), origin.at === undefined ? null : _jsx(DateValue, { when: origin.at, format: "exact" }), origin.acceptedBy === undefined ? null : (_jsx("span", { children: fillCopy(copy.assist.acceptedBy, { name: origin.acceptedBy }) }))] }) }));
|
|
78
|
+
}
|
|
79
|
+
/** A read value with the assistant's mark after it: a `Record.Facts` value, a table cell. */
|
|
80
|
+
export function AssistedValue({ children, origin, }) {
|
|
81
|
+
return (_jsxs("span", { className: "inline-flex max-w-full min-w-0 items-center gap-0.5", children: [_jsx("span", { className: "min-w-0 truncate", children: children }), _jsx(AssistMark, { ...(origin === undefined ? {} : { origin }) })] }));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The assistant's entry in a record's history: the activity's event, with the robot as its
|
|
85
|
+
* node, the assistant (and who accepted) as its author, and the source as its detail.
|
|
86
|
+
*
|
|
87
|
+
* Context: provenance has two places. The mark answers «where did THIS value come from»
|
|
88
|
+
* while looking at it; the history answers «what has the assistant done on this record»,
|
|
89
|
+
* in the order it happened, next to what people did. Same mark in both, so the eye learns
|
|
90
|
+
* it once.
|
|
91
|
+
*
|
|
92
|
+
* Call it when the change happens (in the handler), not at module load: it reads the
|
|
93
|
+
* installed copy, like the formatters do.
|
|
94
|
+
*/
|
|
95
|
+
export function assistEvent({ id, label, at, source, acceptedBy, }) {
|
|
96
|
+
const copy = currentCopy();
|
|
97
|
+
const by = acceptedBy === undefined
|
|
98
|
+
? copy.assist.assistant
|
|
99
|
+
: `${copy.assist.assistant} · ${fillCopy(copy.assist.acceptedBy, { name: acceptedBy })}`;
|
|
100
|
+
return {
|
|
101
|
+
id,
|
|
102
|
+
label,
|
|
103
|
+
at,
|
|
104
|
+
by,
|
|
105
|
+
icon: ASSIST_ICON,
|
|
106
|
+
...(source === undefined ? {} : { detail: _jsx(AssistSourceLink, { source: source }) }),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type AssistPendingProps = {
|
|
2
|
+
/** What the assistant is doing, in the screen's words: «Leyendo la orden de compra
|
|
3
|
+
* PO/2026/0042…». The copy's «El asistente está trabajando…» when absent. */
|
|
4
|
+
label?: string;
|
|
5
|
+
/** Stops it. Present = a «Cancelar» next to the sentence. */
|
|
6
|
+
onCancel?: () => void;
|
|
7
|
+
/** Skeleton bars under the sentence, standing in for the proposal that is coming —
|
|
8
|
+
* `0` (the default) is the one-line form, for under a field. */
|
|
9
|
+
rows?: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The assistant is working on a suggestion, and the screen goes on.
|
|
13
|
+
*
|
|
14
|
+
* Context: the assistant is slower than a form, and a screen that locks while it thinks
|
|
15
|
+
* teaches people to wait for it — or to close it. So this BLOCKS NOTHING: it is a line in
|
|
16
|
+
* the place where the suggestion will appear (under a field, above the lines), the fields
|
|
17
|
+
* stay editable, and «Cancelar» stops it. What the person types in the meantime wins; the
|
|
18
|
+
* screen decides whether a suggestion that arrives late still makes sense.
|
|
19
|
+
*
|
|
20
|
+
* The sentence is a polite live region (`role="status"`), so a screen reader hears that
|
|
21
|
+
* the assistant started without being interrupted; the bars are silent. The spinner and
|
|
22
|
+
* the bars' pulse stop under reduced motion, by the global rule in `tokens.css`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function AssistPending({ label, onCancel, rows }: AssistPendingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AssistLabel } from "./AssistMark.js";
|
|
4
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
5
|
+
import { Button } from "../../primitives/button/Button.js";
|
|
6
|
+
import { LoaderCircle } from "lucide-react";
|
|
7
|
+
/** The widths of the skeleton's bars: uneven, so it reads as lines of content and not as
|
|
8
|
+
* a striped box. */
|
|
9
|
+
const BAR = ["w-2/3", "w-1/2", "w-3/4", "w-2/5"];
|
|
10
|
+
/**
|
|
11
|
+
* The assistant is working on a suggestion, and the screen goes on.
|
|
12
|
+
*
|
|
13
|
+
* Context: the assistant is slower than a form, and a screen that locks while it thinks
|
|
14
|
+
* teaches people to wait for it — or to close it. So this BLOCKS NOTHING: it is a line in
|
|
15
|
+
* the place where the suggestion will appear (under a field, above the lines), the fields
|
|
16
|
+
* stay editable, and «Cancelar» stops it. What the person types in the meantime wins; the
|
|
17
|
+
* screen decides whether a suggestion that arrives late still makes sense.
|
|
18
|
+
*
|
|
19
|
+
* The sentence is a polite live region (`role="status"`), so a screen reader hears that
|
|
20
|
+
* the assistant started without being interrupted; the bars are silent. The spinner and
|
|
21
|
+
* the bars' pulse stop under reduced motion, by the global rule in `tokens.css`.
|
|
22
|
+
*/
|
|
23
|
+
export function AssistPending({ label, onCancel, rows = 0 }) {
|
|
24
|
+
const copy = useUiCopy();
|
|
25
|
+
return (_jsxs("div", { "data-assist": "pending", "aria-busy": "true", className: "flex min-w-0 flex-col gap-2", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [_jsx(LoaderCircle, { className: "size-(--size-icon-sm) shrink-0 animate-spin text-info", strokeWidth: 2, "aria-hidden": true }), _jsx(AssistLabel, {}), _jsx("span", { role: "status", className: "min-w-0 text-micro text-muted", children: label ?? copy.assist.working }), onCancel === undefined ? null : (_jsx(Button, { role: "ghost", size: "sm", onClick: onCancel, children: copy.assist.cancel }))] }), rows <= 0 ? null : (_jsx("span", { "aria-hidden": true, className: "flex flex-col gap-2 pl-5", children: Array.from({ length: rows }, (_, i) => (_jsx("span", { className: `h-3 rounded-control bg-ink/8 motion-safe:animate-pulse ${BAR[i % BAR.length]}` }, i))) }))] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type AssistApply, type AssistSource } from "./assist.js";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export type AssistSuggestionProps = {
|
|
4
|
+
/** The proposed value, drawn as it will read once applied: «30 días», «15/11/2026», a
|
|
5
|
+
* product with its code. */
|
|
6
|
+
value: ReactNode;
|
|
7
|
+
/** Where the assistant got it — a link to the record it read. */
|
|
8
|
+
source?: AssistSource;
|
|
9
|
+
/** Applies the value. Return the function that reverts it and the screen gets a
|
|
10
|
+
* «Deshacer» notice; the suggestion itself is the screen's to remove. */
|
|
11
|
+
onAccept: AssistApply;
|
|
12
|
+
/** «No, gracias»: the screen forgets the suggestion. */
|
|
13
|
+
onDismiss: () => void;
|
|
14
|
+
/** The field's name, for the notice after accepting. Inside a `Field` the accessible
|
|
15
|
+
* name already takes the field's label; the notice is read elsewhere, so it needs it. */
|
|
16
|
+
field?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The assistant proposes a value for ONE field: the customer's payment term, the product of
|
|
20
|
+
* a scanned line, the due date. It goes right under the control it would change.
|
|
21
|
+
*
|
|
22
|
+
* Context: a suggestion has to be noticed and must not be mistaken for the value. So it is
|
|
23
|
+
* NOT written into the control — a pre-filled field reads as «someone already decided» and
|
|
24
|
+
* gets saved without a look. It sits under the control, in a dashed box (dashed: proposed,
|
|
25
|
+
* not yet real), with the robot and «Asistente sugiere», the value, and its source as a
|
|
26
|
+
* link. «Aceptar» applies it at once, and the notice carries «Deshacer» — undo over
|
|
27
|
+
* confirm, the system's rule. The × forgets it.
|
|
28
|
+
*
|
|
29
|
+
* Written inside a `Field`, after the control:
|
|
30
|
+
*
|
|
31
|
+
* <Field label="Plazo de pago">
|
|
32
|
+
* <NumberInput … />
|
|
33
|
+
* <AssistSuggestion value="30 días" source={…} onAccept={…} onDismiss={…} />
|
|
34
|
+
* </Field>
|
|
35
|
+
*
|
|
36
|
+
* It is a group named «Sugerencia del asistente» + the field's label, so a screen reader
|
|
37
|
+
* hears which field it is about before the value. Both buttons are plain buttons: Tab
|
|
38
|
+
* reaches them, Enter and Space press them. After pressing either, the focus goes back to
|
|
39
|
+
* the field's control — the suggestion that held it is gone, and the control is what the
|
|
40
|
+
* person was working on.
|
|
41
|
+
*/
|
|
42
|
+
export declare function AssistSuggestion({ value, source, onAccept, onDismiss, field, }: AssistSuggestionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { acceptedMessage, applyWithUndo, } from "./assist.js";
|
|
4
|
+
import { AssistLabel, AssistSourceLink } from "./AssistMark.js";
|
|
5
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
6
|
+
import { Button, IconButton } from "../../primitives/button/Button.js";
|
|
7
|
+
import { FieldIdsContext } from "../../primitives/field/context.js";
|
|
8
|
+
import { useToastIfAny } from "../../primitives/notice/Toast.js";
|
|
9
|
+
import { Check, X } from "lucide-react";
|
|
10
|
+
import { useContext, useId } from "react";
|
|
11
|
+
/**
|
|
12
|
+
* The assistant proposes a value for ONE field: the customer's payment term, the product of
|
|
13
|
+
* a scanned line, the due date. It goes right under the control it would change.
|
|
14
|
+
*
|
|
15
|
+
* Context: a suggestion has to be noticed and must not be mistaken for the value. So it is
|
|
16
|
+
* NOT written into the control — a pre-filled field reads as «someone already decided» and
|
|
17
|
+
* gets saved without a look. It sits under the control, in a dashed box (dashed: proposed,
|
|
18
|
+
* not yet real), with the robot and «Asistente sugiere», the value, and its source as a
|
|
19
|
+
* link. «Aceptar» applies it at once, and the notice carries «Deshacer» — undo over
|
|
20
|
+
* confirm, the system's rule. The × forgets it.
|
|
21
|
+
*
|
|
22
|
+
* Written inside a `Field`, after the control:
|
|
23
|
+
*
|
|
24
|
+
* <Field label="Plazo de pago">
|
|
25
|
+
* <NumberInput … />
|
|
26
|
+
* <AssistSuggestion value="30 días" source={…} onAccept={…} onDismiss={…} />
|
|
27
|
+
* </Field>
|
|
28
|
+
*
|
|
29
|
+
* It is a group named «Sugerencia del asistente» + the field's label, so a screen reader
|
|
30
|
+
* hears which field it is about before the value. Both buttons are plain buttons: Tab
|
|
31
|
+
* reaches them, Enter and Space press them. After pressing either, the focus goes back to
|
|
32
|
+
* the field's control — the suggestion that held it is gone, and the control is what the
|
|
33
|
+
* person was working on.
|
|
34
|
+
*/
|
|
35
|
+
export function AssistSuggestion({ value, source, onAccept, onDismiss, field, }) {
|
|
36
|
+
const copy = useUiCopy();
|
|
37
|
+
const toasts = useToastIfAny();
|
|
38
|
+
const ids = useContext(FieldIdsContext);
|
|
39
|
+
const nameId = useId();
|
|
40
|
+
/* Back to the control once this box is gone. A frame later: the screen removes the
|
|
41
|
+
suggestion in the same update, and the control is only focusable after it. */
|
|
42
|
+
const refocus = () => {
|
|
43
|
+
const target = ids?.id;
|
|
44
|
+
if (target === undefined || typeof document === "undefined")
|
|
45
|
+
return;
|
|
46
|
+
requestAnimationFrame(() => document.getElementById(target)?.focus());
|
|
47
|
+
};
|
|
48
|
+
return (_jsxs("div", { role: "group", "aria-labelledby": ids === null ? nameId : `${nameId} ${ids.labelId}`, "data-assist": "suggestion", className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5 rounded-control border border-dashed border-info/40 bg-info/5 py-1 pr-1 pl-2.5", children: [_jsx("span", { id: nameId, hidden: true, children: copy.assist.suggestion }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5 py-0.5", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5", children: [_jsxs(AssistLabel, { children: [copy.assist.assistant, " ", copy.assist.suggests] }), _jsx("span", { className: "min-w-0 text-meta font-semibold text-ink tabular-nums", children: value })] }), source === undefined ? null : _jsx(AssistSourceLink, { source: source })] }), _jsxs("span", { className: "flex shrink-0 items-center gap-0.5", children: [_jsx(Button, { role: "secondary", size: "sm", icon: Check, onClick: () => {
|
|
49
|
+
applyWithUndo(onAccept, toasts, acceptedMessage(copy, field), copy.assist.undo);
|
|
50
|
+
refocus();
|
|
51
|
+
}, children: copy.assist.accept }), _jsx(IconButton, { icon: X, label: copy.assist.dismiss, onClick: () => {
|
|
52
|
+
onDismiss();
|
|
53
|
+
refocus();
|
|
54
|
+
} })] })] }));
|
|
55
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type AssistApply, type AssistSource, type ProposedChange } from "./assist.js";
|
|
2
|
+
import { type DialogContext } from "../../primitives/dialog/Dialog.js";
|
|
3
|
+
import { type ReactNode } from "react";
|
|
4
|
+
type GroupProps = {
|
|
5
|
+
/** «Campos», «Líneas». Without it the changes are listed without a heading. */
|
|
6
|
+
title?: string;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
type ReviewProps = {
|
|
10
|
+
/** What the proposal is, as a heading. The copy's «Cambios propuestos» when absent. */
|
|
11
|
+
title?: string;
|
|
12
|
+
/** One sentence under the title: what the assistant read and what it would do. */
|
|
13
|
+
summary?: string;
|
|
14
|
+
/** Where the whole proposal comes from. A change may carry its own. */
|
|
15
|
+
source?: AssistSource;
|
|
16
|
+
/** Applies the chosen changes — their ids, in the listed order. Return the function that
|
|
17
|
+
* reverts them, and the screen gets «Deshacer». */
|
|
18
|
+
onApply: AssistApply<[ids: string[]]>;
|
|
19
|
+
/** Rejects the proposal. */
|
|
20
|
+
onDiscard: () => void;
|
|
21
|
+
/** The chosen ids, controlled. Uncontrolled from «all but the `unchosen` ones». */
|
|
22
|
+
chosen?: readonly string[];
|
|
23
|
+
onChosenChange?: (ids: string[]) => void;
|
|
24
|
+
/** `ChangeReview.Group` and `ChangeReview.Change`. */
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
};
|
|
27
|
+
export type ChangeReviewProps = ReviewProps & {
|
|
28
|
+
/** The role of «Aplicar». `primary` by default; `secondary` inside a record whose rail
|
|
29
|
+
* already shows the screen's one primary. */
|
|
30
|
+
emphasis?: "primary" | "secondary";
|
|
31
|
+
};
|
|
32
|
+
/** The review inside the page: a dashed card in the record it would change. */
|
|
33
|
+
export declare function ChangeReview(props: ChangeReviewProps): import("react").JSX.Element;
|
|
34
|
+
export declare namespace ChangeReview {
|
|
35
|
+
var Group: ((props: GroupProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"group">;
|
|
36
|
+
var Change: ((props: ProposedChange) => ReactNode) & import("../../foundations/slots.js").Slotted<"change">;
|
|
37
|
+
}
|
|
38
|
+
export type ChangeReviewDialogProps = ReviewProps & {
|
|
39
|
+
open: boolean;
|
|
40
|
+
/** Every way out ends here: ×, Escape, the scrim — «later», the proposal stays — and
|
|
41
|
+
* also after «Aplicar» and «Descartar». */
|
|
42
|
+
onClose: () => void;
|
|
43
|
+
/** What the dialog is about, in its header: «factura · FAC/2026/0120». */
|
|
44
|
+
context?: DialogContext;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The same review, in a `Dialog`: opened on demand — from a notice that says «el asistente
|
|
48
|
+
* encontró la orden de compra» — and a bottom sheet on a phone. «Aplicar» is the dialog's
|
|
49
|
+
* primary; closing without choosing keeps the proposal for later.
|
|
50
|
+
*/
|
|
51
|
+
export declare function ChangeReviewDialog(props: ChangeReviewDialogProps): import("react").JSX.Element;
|
|
52
|
+
export declare namespace ChangeReviewDialog {
|
|
53
|
+
var Group: ((props: GroupProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"group">;
|
|
54
|
+
var Change: ((props: ProposedChange) => ReactNode) & import("../../foundations/slots.js").Slotted<"change">;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { appliedMessage, applyLabel, applyWithUndo, changeKind, chosenState, initiallyChosen, toggleChosen, } from "./assist.js";
|
|
4
|
+
import { AssistLabel, AssistSourceLink } from "./AssistMark.js";
|
|
5
|
+
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
6
|
+
import { flatten, propsOf, slot, slotOf } from "../../foundations/slots.js";
|
|
7
|
+
import { Button } from "../../primitives/button/Button.js";
|
|
8
|
+
import { Dialog } from "../../primitives/dialog/Dialog.js";
|
|
9
|
+
import { Checkbox } from "../../primitives/field/Checks.js";
|
|
10
|
+
import { useToastIfAny } from "../../primitives/notice/Toast.js";
|
|
11
|
+
import { Tag } from "../../primitives/tag/Tag.js";
|
|
12
|
+
import { ArrowRight } from "lucide-react";
|
|
13
|
+
import { useId, useState } from "react";
|
|
14
|
+
const Group = slot("group", (_props) => null);
|
|
15
|
+
const Change = slot("change", (_props) => null);
|
|
16
|
+
/** The groups as written. Changes written outside a group form an untitled one in their
|
|
17
|
+
* place, so a short review is just its changes. */
|
|
18
|
+
function readTree(children) {
|
|
19
|
+
const out = [];
|
|
20
|
+
let loose = null;
|
|
21
|
+
for (const node of flatten(children)) {
|
|
22
|
+
const name = slotOf(node);
|
|
23
|
+
if (name === "group") {
|
|
24
|
+
loose = null;
|
|
25
|
+
const props = propsOf(node);
|
|
26
|
+
out.push({
|
|
27
|
+
...(props.title === undefined ? {} : { title: props.title }),
|
|
28
|
+
changes: flatten(props.children)
|
|
29
|
+
.filter((one) => slotOf(one) === "change")
|
|
30
|
+
.map((one) => propsOf(one)),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else if (name === "change") {
|
|
34
|
+
if (loose === null) {
|
|
35
|
+
loose = { changes: [] };
|
|
36
|
+
out.push(loose);
|
|
37
|
+
}
|
|
38
|
+
loose.changes.push(propsOf(node));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
/** What both faces share: the tree read once, the chosen ids, and «apply». */
|
|
44
|
+
function useReview({ children, chosen, onChosenChange, onApply }) {
|
|
45
|
+
const copy = useUiCopy();
|
|
46
|
+
const toasts = useToastIfAny();
|
|
47
|
+
const groups = readTree(children);
|
|
48
|
+
const all = groups.flatMap((g) => g.changes);
|
|
49
|
+
const order = all.map((c) => c.id);
|
|
50
|
+
const [own, setOwn] = useState(() => initiallyChosen(all));
|
|
51
|
+
const current = chosen === undefined ? own : order.filter((id) => chosen.includes(id));
|
|
52
|
+
const choose = (next) => {
|
|
53
|
+
if (chosen === undefined)
|
|
54
|
+
setOwn(next);
|
|
55
|
+
onChosenChange?.(next);
|
|
56
|
+
};
|
|
57
|
+
const apply = () => {
|
|
58
|
+
const ids = current;
|
|
59
|
+
applyWithUndo(() => onApply(ids), toasts, appliedMessage(copy, ids.length), copy.assist.undo);
|
|
60
|
+
};
|
|
61
|
+
return { groups, order, current, choose, apply };
|
|
62
|
+
}
|
|
63
|
+
/** One change: its check (named by its label), the kind in words, and before → after. */
|
|
64
|
+
function ChangeRow({ change, on, onToggle, }) {
|
|
65
|
+
const copy = useUiCopy();
|
|
66
|
+
const valuesId = useId();
|
|
67
|
+
const kind = changeKind(change);
|
|
68
|
+
const word = {
|
|
69
|
+
add: copy.assist.added,
|
|
70
|
+
remove: copy.assist.removed,
|
|
71
|
+
change: copy.assist.changed,
|
|
72
|
+
}[kind];
|
|
73
|
+
const empty = _jsx("span", { className: "text-muted italic", children: copy.assist.empty });
|
|
74
|
+
const before = change.before === null ? empty : change.before;
|
|
75
|
+
return (_jsxs("li", { "data-change": kind, className: "flex min-w-0 flex-col gap-1 border-b border-line px-4 py-2.5 last:border-b-0 @lg:flex-row @lg:items-start @lg:gap-4", children: [_jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx(Checkbox, { checked: on, onChange: onToggle, label: change.label, describedBy: valuesId }), _jsx(Tag, { children: word })] }), change.detail === undefined ? null : (_jsx("span", { className: "pl-6.5 text-micro text-muted", children: change.detail })), change.source === undefined ? null : (_jsx("span", { className: "pl-6.5", children: _jsx(AssistSourceLink, { source: change.source }) }))] }), _jsxs("span", { id: valuesId, className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5 pl-6.5 text-meta tabular-nums @lg:w-2/5 @lg:shrink-0 @lg:justify-end @lg:pl-0 @lg:text-right", children: [kind === "add" ? null : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "sr-only", children: [copy.assist.from, " "] }), _jsx("span", { className: `text-muted ${kind === "remove" ? "line-through" : ""}`, children: before })] })), kind === "remove" ? null : (_jsxs(_Fragment, { children: [kind === "add" ? null : (_jsx(ArrowRight, { className: "size-(--size-icon-sm) shrink-0 self-center text-muted", strokeWidth: 2, "aria-hidden": true })), _jsxs("span", { className: "sr-only", children: [copy.assist.to, " "] }), _jsx("span", { className: "font-medium text-ink", children: change.after })] }))] })] }));
|
|
76
|
+
}
|
|
77
|
+
/** «Todos los cambios», the count, and the groups. */
|
|
78
|
+
function ReviewBody({ review }) {
|
|
79
|
+
const copy = useUiCopy();
|
|
80
|
+
const { groups, order, current, choose } = review;
|
|
81
|
+
const state = chosenState(order, current);
|
|
82
|
+
return (_jsxs("div", { className: "@container flex min-w-0 flex-col", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center justify-between gap-x-3 gap-y-1 border-b border-line px-4 py-2", children: [_jsx(Checkbox, { checked: state === "all", mixed: state === "some", label: copy.assist.all, onChange: (on) => choose(on ? [...order] : []) }), _jsx("span", { className: "text-micro text-muted tabular-nums", children: fillCopy(copy.assist.chosen, { count: current.length, total: order.length }) })] }), groups.map((group, i) => (_jsxs("section", { className: "flex min-w-0 flex-col", children: [group.title === undefined ? null : (_jsx("h4", { className: "px-4 pt-3 pb-1 text-micro font-medium text-muted", children: group.title })), _jsx("ul", { className: "flex min-w-0 flex-col", children: group.changes.map((change) => (_jsx(ChangeRow, { change: change, on: current.includes(change.id), onToggle: (on) => choose(toggleChosen(order, current, change.id, on)) }, change.id))) })] }, group.title ?? i)))] }));
|
|
83
|
+
}
|
|
84
|
+
/** «Elegí al menos un cambio.», «Descartar» and «Aplicar N cambios». */
|
|
85
|
+
function ReviewActions({ review, emphasis, onDiscard, after, }) {
|
|
86
|
+
const copy = useUiCopy();
|
|
87
|
+
const count = review.current.length;
|
|
88
|
+
return (_jsxs(_Fragment, { children: [count > 0 ? null : (
|
|
89
|
+
/* The reason next to the disabled button: a grey «Aplicar» with no explanation is a
|
|
90
|
+
dead end (the same rule as the wizard's «siguiente»). */
|
|
91
|
+
_jsx("span", { className: "mr-auto min-w-0 text-micro text-warn", children: copy.assist.noneChosen })), _jsx(Button, { role: "ghost", size: "sm", onClick: () => {
|
|
92
|
+
onDiscard();
|
|
93
|
+
after?.();
|
|
94
|
+
}, children: copy.assist.discard }), _jsx(Button, { role: emphasis, size: "sm", disabled: count === 0, onClick: () => {
|
|
95
|
+
review.apply();
|
|
96
|
+
after?.();
|
|
97
|
+
}, children: applyLabel(copy, count) })] }));
|
|
98
|
+
}
|
|
99
|
+
/** The review inside the page: a dashed card in the record it would change. */
|
|
100
|
+
export function ChangeReview(props) {
|
|
101
|
+
const { title, summary, source, onDiscard, emphasis = "primary" } = props;
|
|
102
|
+
const copy = useUiCopy();
|
|
103
|
+
const review = useReview(props);
|
|
104
|
+
const titleId = useId();
|
|
105
|
+
return (_jsxs("section", { "aria-labelledby": titleId, "data-assist": "review", className: "flex min-w-0 flex-col overflow-clip rounded-box border border-dashed border-info/40 bg-surface", children: [_jsxs("header", { className: "flex min-w-0 flex-col gap-1 border-b border-line px-4 py-3", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-1", children: [_jsx(AssistLabel, {}), source === undefined ? null : _jsx(AssistSourceLink, { source: source })] }), _jsx("h3", { id: titleId, className: "text-lead font-semibold text-ink", children: title ?? copy.assist.review }), summary === undefined ? null : (_jsx("p", { className: "max-w-prose text-meta text-muted", children: summary }))] }), _jsx(ReviewBody, { review: review }), _jsx("footer", { className: "flex min-w-0 flex-wrap items-center justify-end gap-2 border-t border-line bg-ground px-4 py-2.5", children: _jsx(ReviewActions, { review: review, emphasis: emphasis, onDiscard: onDiscard }) })] }));
|
|
106
|
+
}
|
|
107
|
+
ChangeReview.Group = Group;
|
|
108
|
+
ChangeReview.Change = Change;
|
|
109
|
+
/**
|
|
110
|
+
* The same review, in a `Dialog`: opened on demand — from a notice that says «el asistente
|
|
111
|
+
* encontró la orden de compra» — and a bottom sheet on a phone. «Aplicar» is the dialog's
|
|
112
|
+
* primary; closing without choosing keeps the proposal for later.
|
|
113
|
+
*/
|
|
114
|
+
export function ChangeReviewDialog(props) {
|
|
115
|
+
const { open, onClose, context, title, summary, source, onDiscard } = props;
|
|
116
|
+
const copy = useUiCopy();
|
|
117
|
+
const review = useReview(props);
|
|
118
|
+
return (_jsx(Dialog, { open: open, onClose: onClose, title: title ?? copy.assist.review, ...(summary === undefined ? {} : { subtitle: summary }), ...(context === undefined ? {} : { context }), footer: _jsx(ReviewActions, { review: review, emphasis: "primary", onDiscard: onDiscard, after: onClose }), children: _jsxs("div", { className: "-mx-4 -my-4 flex min-w-0 flex-col", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-1 border-b border-line px-4 py-2.5", children: [_jsx(AssistLabel, {}), source === undefined ? null : _jsx(AssistSourceLink, { source: source })] }), _jsx(ReviewBody, { review: review })] }) }));
|
|
119
|
+
}
|
|
120
|
+
ChangeReviewDialog.Group = Group;
|
|
121
|
+
ChangeReviewDialog.Change = Change;
|