@sincpro/web-ui 0.0.0-stage → 0.1.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 +157 -2
- 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/chatter/Chatter.d.ts +161 -0
- package/dist/components/chatter/Chatter.js +232 -0
- package/dist/components/chatter/Messages.d.ts +66 -0
- package/dist/components/chatter/Messages.js +43 -0
- package/dist/components/chatter/Timeline.d.ts +21 -0
- package/dist/components/chatter/Timeline.js +30 -0
- package/dist/components/chatter/index.d.ts +3 -0
- package/dist/components/chatter/index.js +4 -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 +43 -0
- package/dist/components/form/Sheet.js +83 -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 +74 -0
- package/dist/components/lines/LineItems.js +229 -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 +73 -0
- package/dist/components/listing/ListingBar.js +105 -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 +106 -0
- package/dist/components/listing/Row.js +122 -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 +71 -0
- package/dist/components/record/RecordHeader.js +72 -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 +619 -0
- package/dist/foundations/copy.es.d.ts +2 -0
- package/dist/foundations/copy.es.js +377 -0
- package/dist/foundations/copy.js +472 -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 +16 -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 +24 -0
- package/dist/primitives/choice/SearchField.js +27 -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 +110 -0
- package/dist/primitives/date/DatePicker.d.ts +34 -0
- package/dist/primitives/date/DatePicker.js +99 -0
- package/dist/primitives/date/index.d.ts +2 -0
- package/dist/primitives/date/index.js +4 -0
- package/dist/primitives/dialog/Dialog.d.ts +118 -0
- package/dist/primitives/dialog/Dialog.js +121 -0
- package/dist/primitives/dialog/index.d.ts +1 -0
- package/dist/primitives/dialog/index.js +2 -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 +166 -0
- package/dist/primitives/field/Field.js +240 -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/index.d.ts +7 -0
- package/dist/primitives/field/index.js +11 -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/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 +19 -0
- package/dist/primitives/index.js +20 -0
- package/dist/primitives/menu/Menu.d.ts +100 -0
- package/dist/primitives/menu/Menu.js +59 -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/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 +3 -0
- package/dist/primitives/notice/index.js +5 -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/Tooltip.d.ts +38 -0
- package/dist/primitives/overlay/Tooltip.js +74 -0
- package/dist/primitives/overlay/index.d.ts +3 -0
- package/dist/primitives/overlay/index.js +5 -0
- package/dist/primitives/picker/Picker.d.ts +48 -0
- package/dist/primitives/picker/Picker.js +150 -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/CellEditor.d.ts +14 -0
- package/dist/primitives/table/CellEditor.js +83 -0
- package/dist/primitives/table/DataTable.d.ts +135 -0
- package/dist/primitives/table/DataTable.js +244 -0
- package/dist/primitives/table/EditableTable.d.ts +96 -0
- package/dist/primitives/table/EditableTable.js +423 -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 +70 -0
- package/dist/primitives/table/SheetTable.js +197 -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 +32 -0
- package/dist/primitives/tag/TagsField.js +182 -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/value/Copyable.d.ts +37 -0
- package/dist/primitives/value/Copyable.js +50 -0
- package/dist/primitives/value/Fecha.d.ts +12 -0
- package/dist/primitives/value/Fecha.js +80 -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/Relation.d.ts +69 -0
- package/dist/primitives/value/Relation.js +57 -0
- package/dist/primitives/value/Totals.d.ts +26 -0
- package/dist/primitives/value/Totals.js +4 -0
- package/dist/primitives/value/Value.d.ts +77 -0
- package/dist/primitives/value/Value.js +119 -0
- package/dist/primitives/value/index.d.ts +8 -0
- package/dist/primitives/value/index.js +9 -0
- package/dist/tokens.css +1025 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +3 -0
- package/dist/views/list/List.d.ts +113 -0
- package/dist/views/list/List.js +97 -0
- package/dist/views/list/ListView.d.ts +6 -0
- package/dist/views/list/ListView.js +269 -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 +76 -0
- package/dist/views/list/derive.js +181 -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 +103 -0
- package/dist/views/list/types.d.ts +272 -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 +126 -0
- package/dist/views/record/Record.js +141 -0
- package/dist/views/record/index.d.ts +1 -0
- package/dist/views/record/index.js +2 -0
- package/package.json +109 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { tv } from "../../foundations/variants.js";
|
|
4
|
+
import { Separator as AriaSeparator, Toolbar as AriaToolbar, } from "react-aria-components";
|
|
5
|
+
/**
|
|
6
|
+
* A row of controls that is ONE stop for the keyboard: the actions above an editable table,
|
|
7
|
+
* the bar of a rich-text editor.
|
|
8
|
+
*
|
|
9
|
+
* Context: a table with eight actions above it costs eight Tabs before reaching the first
|
|
10
|
+
* cell. Inside a toolbar Tab enters and leaves the group once, and the arrows move between
|
|
11
|
+
* its controls — the way every desktop application has always worked. React Aria does the
|
|
12
|
+
* moving by walking the DOM for focusable elements, so the system's own `Button` and
|
|
13
|
+
* `IconButton` (plain native buttons) work inside it as they are, with nothing to forward.
|
|
14
|
+
*
|
|
15
|
+
* It draws no box of its own: it sits on whatever surface it is placed, like the actions of a
|
|
16
|
+
* record header. The gap is the one between buttons of a row; a `ToolbarSeparator` splits
|
|
17
|
+
* the groups (edit · insert · export) so a long bar still reads as a few decisions.
|
|
18
|
+
*/
|
|
19
|
+
const toolbar = tv({
|
|
20
|
+
base: "flex gap-1",
|
|
21
|
+
variants: {
|
|
22
|
+
orientation: {
|
|
23
|
+
horizontal: "flex-row flex-wrap items-center",
|
|
24
|
+
vertical: "flex-col items-start",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
export function Toolbar({ label, children, orientation = "horizontal", }) {
|
|
29
|
+
return (_jsx(AriaToolbar, { "aria-label": label, orientation: orientation, className: (state) => toolbar({ orientation: state.orientation }), children: children }));
|
|
30
|
+
}
|
|
31
|
+
/** A hairline between two groups of a `Toolbar`, across its direction. */
|
|
32
|
+
export function ToolbarSeparator({ orientation = "vertical", }) {
|
|
33
|
+
return (_jsx(AriaSeparator, { orientation: orientation, className: orientation === "vertical"
|
|
34
|
+
? "mx-1 h-(--size-icon-lg) w-px shrink-0 border-none bg-line-strong forced-colors:bg-[ButtonBorder]"
|
|
35
|
+
: "my-1 h-px w-full border-none bg-line-strong forced-colors:bg-[ButtonBorder]" }));
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Toolbar, ToolbarSeparator } from "./Toolbar.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* UN VALOR QUE SE COPIA.
|
|
4
|
+
*
|
|
5
|
+
* Context: la mitad de los valores de este producto existen para pegarse en otro
|
|
6
|
+
* lado —un NIT, un CUF de factura, una referencia bancaria, un código de
|
|
7
|
+
* autorización de 26 caracteres—. Seleccionarlos a mano falla justo con los que
|
|
8
|
+
* más importa no equivocar: son largos, van en monoespaciada y se cortan.
|
|
9
|
+
*
|
|
10
|
+
* ── Confirma, y por eso el estado vive acá ──────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* Copiar no tiene efecto visible: el portapapeles está afuera de la pantalla. Un
|
|
13
|
+
* botón que no contesta se aprieta tres veces «por las dudas». Por eso el ícono
|
|
14
|
+
* pasa a un tilde por dos segundos — lo mínimo para creerle sin quedarse
|
|
15
|
+
* mirando.
|
|
16
|
+
*
|
|
17
|
+
* ── Y puede fallar ──────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* `navigator.clipboard` no existe fuera de un contexto seguro, y en algunos
|
|
20
|
+
* navegadores exige un permiso que se puede negar. Se contempla: si falla, el
|
|
21
|
+
* botón lo dice en vez de fingir que copió. Una confirmación falsa es peor que
|
|
22
|
+
* ninguna, porque alguien pega lo que tenía antes y no se entera.
|
|
23
|
+
*/
|
|
24
|
+
export type CopyableProps = {
|
|
25
|
+
/** Lo que se copia. Siempre texto: el portapapeles no tiene tipos. */
|
|
26
|
+
value: string;
|
|
27
|
+
/** Cómo se ve, si no es el valor tal cual. Un fingerprint se muestra cortado y
|
|
28
|
+
* se copia entero, que es justamente por qué las dos cosas son props
|
|
29
|
+
* distintas. */
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
/** El valor es de máquina: id, hash, clave. Sin esto, tipografía normal. */
|
|
32
|
+
mono?: boolean;
|
|
33
|
+
/** Sólo el botón, sin el valor al lado. Para una celda o un encabezado donde
|
|
34
|
+
* el valor ya está dibujado. */
|
|
35
|
+
bare?: boolean;
|
|
36
|
+
};
|
|
37
|
+
export declare function Copyable({ value, children, mono, bare }: CopyableProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
import { Check, Copy } from "lucide-react";
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
/** Cuánto dura el tilde. Dos segundos: alcanza para verlo sin que el botón se
|
|
7
|
+
* quede en un estado que ya no es cierto. */
|
|
8
|
+
const CONFIRMED_FOR = 2000;
|
|
9
|
+
export function Copyable({ value, children, mono = false, bare = false }) {
|
|
10
|
+
const copy = useUiCopy();
|
|
11
|
+
const [state, setState] = useState("idle");
|
|
12
|
+
const timer = useRef(null);
|
|
13
|
+
/* Se limpia al desmontar: sin esto, copiar y cerrar el panel antes de los dos
|
|
14
|
+
segundos deja un `setState` apuntando a un componente que ya no existe. */
|
|
15
|
+
useEffect(() => () => {
|
|
16
|
+
if (timer.current !== null)
|
|
17
|
+
clearTimeout(timer.current);
|
|
18
|
+
}, []);
|
|
19
|
+
async function run() {
|
|
20
|
+
if (timer.current !== null)
|
|
21
|
+
clearTimeout(timer.current);
|
|
22
|
+
try {
|
|
23
|
+
await navigator.clipboard.writeText(value);
|
|
24
|
+
setState("done");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* Sin portapapeles —contexto no seguro, permiso negado— se DICE. Fingir
|
|
28
|
+
que copió hace que alguien pegue lo que tenía antes sin enterarse. */
|
|
29
|
+
setState("failed");
|
|
30
|
+
}
|
|
31
|
+
timer.current = setTimeout(() => setState("idle"), CONFIRMED_FOR);
|
|
32
|
+
}
|
|
33
|
+
const label = state === "done"
|
|
34
|
+
? copy.copyable.done
|
|
35
|
+
: state === "failed"
|
|
36
|
+
? copy.copyable.failed
|
|
37
|
+
: copy.copyable.label;
|
|
38
|
+
const button = (_jsx("button", { type: "button", onClick: run, "aria-label": label, title: label, className: `inline-flex shrink-0 items-center justify-center rounded-control p-0.5 transition-colors duration-(--duration-instant) ${state === "done"
|
|
39
|
+
? "text-good"
|
|
40
|
+
: state === "failed"
|
|
41
|
+
? "text-bad"
|
|
42
|
+
: "text-muted hover:bg-ink/6 hover:text-ink"}`, children: state === "done" ? (_jsx(Check, { className: "size-(--size-icon-sm)", strokeWidth: 2.5 })) : (_jsx(Copy, { className: "size-(--size-icon-sm)", strokeWidth: 2 })) }));
|
|
43
|
+
if (bare)
|
|
44
|
+
return button;
|
|
45
|
+
return (
|
|
46
|
+
/* El botón aparece con el puntero y ocupa lugar SIEMPRE: si apareciera de la
|
|
47
|
+
nada, el valor se correría al pasar por encima, y un texto que se mueve
|
|
48
|
+
cuando vas a seleccionarlo es peor que no tener botón. */
|
|
49
|
+
_jsxs("span", { className: "group/copy inline-flex min-w-0 items-center gap-1", children: [_jsx("span", { className: `min-w-0 truncate ${mono ? "font-mono" : ""}`, children: children ?? value }), _jsx("span", { className: "opacity-0 transition-opacity duration-(--duration-instant) group-hover/copy:opacity-100 focus-within:opacity-100", children: button })] }));
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function formatRelative(when: Date, now?: Date): string;
|
|
2
|
+
export declare function formatDateTime(when: Date): string;
|
|
3
|
+
export declare function DateValue({ when, format,
|
|
4
|
+
/** So it can be tested: without this, a story with «hace 2 min» changes its
|
|
5
|
+
* text every minute and no snapshot works as a reference. */
|
|
6
|
+
now, }: {
|
|
7
|
+
when: Date | string | number;
|
|
8
|
+
/** `relative` for lists and logs; `exact` when the datum gets compared or
|
|
9
|
+
* copied; `date` when the time of day means nothing. */
|
|
10
|
+
format?: "relative" | "exact" | "date";
|
|
11
|
+
now?: Date;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { currentCopy, currentCopyLocale, useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
/* ==================================================================== *
|
|
5
|
+
* WHEN — a date says two things and they can't both be said together.
|
|
6
|
+
*
|
|
7
|
+
* «2 min ago» how recent it is. Understood without thinking.
|
|
8
|
+
* «14/09/2026 06:21» which one it is. It gets compared, copied, cited.
|
|
9
|
+
*
|
|
10
|
+
* The first is the one that helps in a list —what is being asked is «is this
|
|
11
|
+
* from now?»— and the second the one that helps when something has to be done
|
|
12
|
+
* with the datum. Writing both spends twice the width to answer a single
|
|
13
|
+
* question, so the relative one is drawn and the exact one goes in the `title`:
|
|
14
|
+
* a hover away, taking up nothing.
|
|
15
|
+
*
|
|
16
|
+
* ── Why there is no «4 months ago» ──────────────────────────────────
|
|
17
|
+
*
|
|
18
|
+
* Past the week, the relative stops informing: «4 months ago» doesn't let you
|
|
19
|
+
* order two things or remember which one it was. From there on the date is
|
|
20
|
+
* drawn, which is what at that distance is really being asked.
|
|
21
|
+
* ==================================================================== */
|
|
22
|
+
function relativeFormat(locale) {
|
|
23
|
+
return new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
|
24
|
+
}
|
|
25
|
+
function dateFormat(locale) {
|
|
26
|
+
return new Intl.DateTimeFormat(locale, {
|
|
27
|
+
day: "2-digit",
|
|
28
|
+
month: "2-digit",
|
|
29
|
+
year: "numeric",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function timeFormat(locale) {
|
|
33
|
+
return new Intl.DateTimeFormat(locale, {
|
|
34
|
+
hour: "2-digit",
|
|
35
|
+
minute: "2-digit",
|
|
36
|
+
hourCycle: "h23",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const MINUTE = 60_000;
|
|
40
|
+
const HOUR = 60 * MINUTE;
|
|
41
|
+
const DAY = 24 * HOUR;
|
|
42
|
+
/** The steps, from finest to coarsest. The first one that fits, wins. */
|
|
43
|
+
const STEPS = [
|
|
44
|
+
[MINUTE, "minute"],
|
|
45
|
+
[HOUR, "hour"],
|
|
46
|
+
[DAY, "day"],
|
|
47
|
+
];
|
|
48
|
+
export function formatRelative(when, now = new Date()) {
|
|
49
|
+
const locale = currentCopyLocale();
|
|
50
|
+
const delta = when.getTime() - now.getTime();
|
|
51
|
+
const abs = Math.abs(delta);
|
|
52
|
+
if (abs < MINUTE)
|
|
53
|
+
return currentCopy().time.justNow;
|
|
54
|
+
if (abs > 7 * DAY)
|
|
55
|
+
return dateFormat(locale).format(when);
|
|
56
|
+
const step = STEPS.find(([ms]) => abs < ms * 60) ?? STEPS[STEPS.length - 1];
|
|
57
|
+
const [ms, unit] = step;
|
|
58
|
+
return relativeFormat(locale).format(Math.round(delta / ms), unit);
|
|
59
|
+
}
|
|
60
|
+
export function formatDateTime(when) {
|
|
61
|
+
const locale = currentCopyLocale();
|
|
62
|
+
return `${dateFormat(locale).format(when)} ${timeFormat(locale).format(when)}`;
|
|
63
|
+
}
|
|
64
|
+
export function DateValue({ when, format = "relative",
|
|
65
|
+
/** So it can be tested: without this, a story with «hace 2 min» changes its
|
|
66
|
+
* text every minute and no snapshot works as a reference. */
|
|
67
|
+
now, }) {
|
|
68
|
+
const copy = useUiCopy();
|
|
69
|
+
const d = when instanceof Date ? when : new Date(when);
|
|
70
|
+
if (Number.isNaN(d.getTime())) {
|
|
71
|
+
return (_jsx("span", { className: "text-meta text-muted italic", title: copy.time.invalid, children: copy.time.noDate }));
|
|
72
|
+
}
|
|
73
|
+
const exact = formatDateTime(d);
|
|
74
|
+
const text = format === "exact"
|
|
75
|
+
? exact
|
|
76
|
+
: format === "date"
|
|
77
|
+
? dateFormat(copy.locale).format(d)
|
|
78
|
+
: formatRelative(d, now);
|
|
79
|
+
return (_jsx("time", { dateTime: d.toISOString(), title: exact, className: "text-meta whitespace-nowrap tabular-nums text-muted", children: text }));
|
|
80
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type MeterLevel = "good" | "warn" | "danger";
|
|
3
|
+
/** Percentages of the range where the color changes. */
|
|
4
|
+
export type MeterThresholds = {
|
|
5
|
+
warn: number;
|
|
6
|
+
danger: number;
|
|
7
|
+
};
|
|
8
|
+
/** The level of a percentage: under `warn` is good, up to `danger` is warn, above is danger. */
|
|
9
|
+
export declare function meterLevel(percentage: number, thresholds?: MeterThresholds): MeterLevel;
|
|
10
|
+
export type MeterProps = {
|
|
11
|
+
/** What is measured: «Crédito usado», «Stock». */
|
|
12
|
+
label: string;
|
|
13
|
+
value: number;
|
|
14
|
+
minValue?: number;
|
|
15
|
+
/** The capacity. 100 by default, so a plain percentage needs nothing else. */
|
|
16
|
+
maxValue?: number;
|
|
17
|
+
/** How the figure is written. Absent: the percentage of the range. */
|
|
18
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
19
|
+
/** The figure written by hand, when a single formatted number does not say it:
|
|
20
|
+
* «Bs 42.500 de 50.000». It is also what a screen reader reads. */
|
|
21
|
+
valueLabel?: ReactNode;
|
|
22
|
+
/** Where the color changes, in percent of the range. */
|
|
23
|
+
thresholds?: MeterThresholds;
|
|
24
|
+
/** Off when the figure is already beside it — a cell with its column. */
|
|
25
|
+
showValue?: boolean;
|
|
26
|
+
/** The width of the track. A meter in a table cell is shorter than one in a card. */
|
|
27
|
+
width?: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function Meter({ label, value, minValue, maxValue, formatOptions, valueLabel, thresholds, showValue, width, }: MeterProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { AriaLocale } from "../../foundations/aria.js";
|
|
4
|
+
import { tv } from "../../foundations/variants.js";
|
|
5
|
+
import { Meter as AriaMeter } from "react-aria-components";
|
|
6
|
+
/**
|
|
7
|
+
* Where a quantity stands against a known range: the credit used, the stock against the
|
|
8
|
+
* capacity of the warehouse, the budget spent.
|
|
9
|
+
*
|
|
10
|
+
* Context: it looks like `ProgressBar` and it is the opposite thing. A progress bar is a
|
|
11
|
+
* TASK advancing toward done — an import, a stock count — and full is good news. A meter is a
|
|
12
|
+
* MEASUREMENT that just is, and full is often the alarm: a credit line at 95 % is a client
|
|
13
|
+
* who cannot buy tomorrow. That is why its color is not a tone the consumer picks but the
|
|
14
|
+
* reading of the value — `good` under 70 %, `warn` from 70 to 90, `danger` above — so the
|
|
15
|
+
* one that needs a look is the red one in a column of thirty. The thresholds move for the
|
|
16
|
+
* measure where full is fine (a warehouse meant to run at 95 %).
|
|
17
|
+
*
|
|
18
|
+
* React Aria writes `role="meter"` and the `aria-value*`; the visible figure is formatted by
|
|
19
|
+
* React Aria in the locale of the copy table (`AriaLocale`), in tabular figures so a column
|
|
20
|
+
* of meters keeps its digits aligned.
|
|
21
|
+
*/
|
|
22
|
+
const fill = tv({
|
|
23
|
+
base: "block h-full rounded-pill transition-[width] duration-(--duration-panel) ease-(--ease-out) forced-colors:bg-[Highlight]",
|
|
24
|
+
variants: {
|
|
25
|
+
level: {
|
|
26
|
+
good: "bg-good",
|
|
27
|
+
warn: "bg-warn",
|
|
28
|
+
danger: "bg-danger",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const DEFAULT_THRESHOLDS = { warn: 70, danger: 90 };
|
|
33
|
+
/** The level of a percentage: under `warn` is good, up to `danger` is warn, above is danger. */
|
|
34
|
+
export function meterLevel(percentage, thresholds = DEFAULT_THRESHOLDS) {
|
|
35
|
+
if (percentage > thresholds.danger)
|
|
36
|
+
return "danger";
|
|
37
|
+
if (percentage >= thresholds.warn)
|
|
38
|
+
return "warn";
|
|
39
|
+
return "good";
|
|
40
|
+
}
|
|
41
|
+
export function Meter({ label, value, minValue = 0, maxValue = 100, formatOptions, valueLabel, thresholds = DEFAULT_THRESHOLDS, showValue = true, width = "w-64", }) {
|
|
42
|
+
return (_jsx(AriaLocale, { children: _jsx(AriaMeter, { "aria-label": label, value: value, minValue: minValue, maxValue: maxValue, formatOptions: formatOptions, valueLabel: valueLabel, className: `flex max-w-full flex-col gap-1.5 ${width}`, children: ({ percentage, valueText }) => {
|
|
43
|
+
const level = meterLevel(percentage, thresholds);
|
|
44
|
+
return (_jsxs(_Fragment, { children: [_jsxs("span", { className: "flex items-baseline justify-between gap-2", children: [_jsx("span", { className: "truncate text-meta text-ink", children: label }), showValue ? (_jsx("span", { "data-level": level, className: `shrink-0 text-meta tabular-nums ${level === "danger" ? "font-medium text-danger" : "text-muted"}`, children: valueText })) : null] }), _jsx("span", { className: "block h-(--size-track) overflow-hidden rounded-pill bg-ink/10 forced-colors:outline forced-colors:outline-[ButtonBorder]", children: _jsx("span", { "data-level": level, className: fill({ level }), style: { width: `${percentage}%` } }) })] }));
|
|
45
|
+
} }) }));
|
|
46
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Tone } from "../../foundations/types.js";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
3
|
+
/**
|
|
4
|
+
* A metric that also FILTERS: «2 por confirmar», «41 por entregar».
|
|
5
|
+
*
|
|
6
|
+
* Context: it is different from `KeyValue` and from a `ListingBar` counter, and
|
|
7
|
+
* the difference is that this one gets pressed. A number that leads somewhere has
|
|
8
|
+
* to look like something that gets pressed, or nobody discovers that the list can
|
|
9
|
+
* be narrowed from there — which is the most common case of «I didn't know you
|
|
10
|
+
* could do that».
|
|
11
|
+
*
|
|
12
|
+
* ── The color shows up when the narrowing is APPLIED ─────────────────
|
|
13
|
+
*
|
|
14
|
+
* At rest the cards are all the same: sheet background and a hair of border. The
|
|
15
|
+
* only tinted thing is the FIGURE, which is what the card is about. Four
|
|
16
|
+
* different color backgrounds on top of a list are not four filters, they are
|
|
17
|
+
* four stains competing with the rows that don't say which one is applied.
|
|
18
|
+
*
|
|
19
|
+
* The tone paints the background only when the metric is active. There the color
|
|
20
|
+
* does mean something —«I'm only seeing this»— and with a single card lit up
|
|
21
|
+
* there is nothing to compete with.
|
|
22
|
+
*
|
|
23
|
+
* The figure goes big on top and the label small underneath, the other way around
|
|
24
|
+
* from a smart button: here what gets compared at a glance across three cards is
|
|
25
|
+
* the NUMBER.
|
|
26
|
+
*/
|
|
27
|
+
export type MetricProps = {
|
|
28
|
+
value: number | string;
|
|
29
|
+
label: string;
|
|
30
|
+
tone?: Tone;
|
|
31
|
+
icon?: LucideIcon;
|
|
32
|
+
/** Present = narrows the list. Absent = only informs, and doesn't get pressed. */
|
|
33
|
+
onClick?: () => void;
|
|
34
|
+
/** This narrowing is applied. */
|
|
35
|
+
active?: boolean;
|
|
36
|
+
};
|
|
37
|
+
export declare function Metric({ value, label, tone, icon: Icon, onClick, active, }: MetricProps): import("react").JSX.Element;
|
|
38
|
+
/** The row of metrics. It wraps if they don't fit; it doesn't scroll: a hidden
|
|
39
|
+
* metric is a filter nobody finds. */
|
|
40
|
+
export declare function Metrics({ children }: {
|
|
41
|
+
children: React.ReactNode;
|
|
42
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
import { bySurface, useSurface } from "../../foundations/surface.js";
|
|
5
|
+
const TONE = {
|
|
6
|
+
neutral: { background: "bg-ink/6", text: "text-ink", edge: "border-ink/20" },
|
|
7
|
+
info: { background: "bg-info/10", text: "text-info", edge: "border-info/35" },
|
|
8
|
+
good: { background: "bg-good/10", text: "text-good", edge: "border-good/35" },
|
|
9
|
+
warn: { background: "bg-warn/12", text: "text-warn", edge: "border-warn/40" },
|
|
10
|
+
bad: { background: "bg-bad/10", text: "text-bad", edge: "border-bad/35" },
|
|
11
|
+
/* The gold can't be text over the canvas (2,43:1), so here the figure goes in
|
|
12
|
+
ink and the tone is only visible when the card is lit up. */
|
|
13
|
+
accent: { background: "bg-accent/20", text: "text-ink", edge: "border-accent/50" },
|
|
14
|
+
};
|
|
15
|
+
export function Metric({ value, label, tone = "neutral", icon: Icon, onClick, active = false, }) {
|
|
16
|
+
const copy = useUiCopy();
|
|
17
|
+
const sup = useSurface();
|
|
18
|
+
const t = TONE[tone];
|
|
19
|
+
/* At rest the box is the sheet with a hair of border: that is what makes four
|
|
20
|
+
metrics read as one row and not as four notices. Lit up it gets tinted, and
|
|
21
|
+
there the color says which one is applied. */
|
|
22
|
+
const skin = active
|
|
23
|
+
? `${t.background} ${t.edge}`
|
|
24
|
+
: bySurface(sup, "bg-surface border-line", "bg-chrome-ink/6 border-chrome-ink/15");
|
|
25
|
+
const box = `flex min-w-(--container-metric) shrink-0 flex-col justify-center gap-0.5 rounded-box border px-3.5 py-2 text-left transition-colors duration-(--duration-instant) ${skin} ${onClick === undefined
|
|
26
|
+
? ""
|
|
27
|
+
: "cursor-pointer hover:border-line-strong focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"}`;
|
|
28
|
+
const content = (_jsxs(_Fragment, { children: [_jsxs("span", { className: `flex items-center gap-1.5 text-title font-semibold tabular-nums ${t.text}`, children: [Icon === undefined ? null : (_jsx(Icon, { className: "size-(--size-icon-md)", strokeWidth: 2, "aria-hidden": true })), typeof value === "number" ? value.toLocaleString(copy.locale) : value] }), _jsx("span", { className: `text-micro ${bySurface(sup, "text-muted", "text-chrome-muted")}`, children: label })] }));
|
|
29
|
+
if (onClick === undefined) {
|
|
30
|
+
return (_jsx("div", { className: box, "aria-label": `${value} ${label}`, children: content }));
|
|
31
|
+
}
|
|
32
|
+
return (_jsx("button", { type: "button", onClick: onClick, "aria-pressed": active, className: box, children: content }));
|
|
33
|
+
}
|
|
34
|
+
/** The row of metrics. It wraps if they don't fit; it doesn't scroll: a hidden
|
|
35
|
+
* metric is a filter nobody finds. */
|
|
36
|
+
export function Metrics({ children }) {
|
|
37
|
+
return _jsx("div", { className: "flex min-w-0 flex-wrap items-stretch gap-2", children: children });
|
|
38
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
declare const SIZE: {
|
|
3
|
+
readonly sm: "size-5 text-micro";
|
|
4
|
+
readonly md: "size-6 text-micro";
|
|
5
|
+
readonly lg: "size-8 text-meta";
|
|
6
|
+
readonly xl: "size-12 text-body";
|
|
7
|
+
};
|
|
8
|
+
export type AvatarSize = keyof typeof SIZE;
|
|
9
|
+
export declare function Avatar({ name, size,
|
|
10
|
+
/** The photo, if there is one. Without it the initials go, which is the normal
|
|
11
|
+
* case here. */
|
|
12
|
+
src, }: {
|
|
13
|
+
name: string;
|
|
14
|
+
size?: AvatarSize;
|
|
15
|
+
src?: string;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
/** The ways to reach a person. Each one that is present becomes a row of the contact card. */
|
|
18
|
+
export type PersonContact = {
|
|
19
|
+
email?: string;
|
|
20
|
+
/** As people write it — «+591 7 123-4567». The `tel:` link keeps only the digits and `+`. */
|
|
21
|
+
phone?: string;
|
|
22
|
+
/** The WhatsApp number with its country code, written any way; `wa.me` gets the digits. */
|
|
23
|
+
whatsapp?: string;
|
|
24
|
+
};
|
|
25
|
+
export type ContactCardProps = {
|
|
26
|
+
name: string;
|
|
27
|
+
/** The photo. Without it, the initials — in the same color the person has everywhere. */
|
|
28
|
+
src?: string;
|
|
29
|
+
/** What the person is here: «Vendedora · Sucursal La Paz». */
|
|
30
|
+
role?: string;
|
|
31
|
+
contact?: PersonContact;
|
|
32
|
+
/** Go to the person's own record — «Ver ficha». Without it the card only reaches them. */
|
|
33
|
+
onOpen?: () => void;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Who a person is and how to reach them: the face, the name, the role, and one row per way
|
|
37
|
+
* of contact — write, call, WhatsApp — then their record.
|
|
38
|
+
*
|
|
39
|
+
* Context: in an ERP the question after «who is the salesperson of this invoice?» is almost
|
|
40
|
+
* always «how do I reach her?». Copying an email out of another screen to paste it into the
|
|
41
|
+
* mail client is the detour this card removes. Each way of contact is a ROW and not a bare
|
|
42
|
+
* icon: the address and the number are drawn, because often what the person wants is to read
|
|
43
|
+
* the number aloud, not to dial it from the browser. Every row is a real link (`mailto:`,
|
|
44
|
+
* `tel:`, `wa.me`), so middle-click, «copy address» and the phone's own dialer work.
|
|
45
|
+
*
|
|
46
|
+
* It is what opens from a `UserChip` with `contact`, and it is exported on its own for the
|
|
47
|
+
* side panel of a record, where the same person is shown without a chip to open.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ContactCard({ name, src, role, contact, onOpen }: ContactCardProps): import("react").JSX.Element;
|
|
50
|
+
export type UserChipProps = {
|
|
51
|
+
name: string;
|
|
52
|
+
size?: AvatarSize;
|
|
53
|
+
src?: string;
|
|
54
|
+
/** What goes next to the name in the chip: a role, an email, «hace 2 min». */
|
|
55
|
+
detail?: ReactNode;
|
|
56
|
+
/** What the person is, shown in the contact card: «Vendedora · Sucursal La Paz». */
|
|
57
|
+
role?: string;
|
|
58
|
+
/** The ways to reach them. Present = the chip opens a contact card. */
|
|
59
|
+
contact?: PersonContact;
|
|
60
|
+
/** Go to the person's record. Present = the chip opens a card with «Ver ficha». */
|
|
61
|
+
onOpen?: () => void;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The person with their name next to them. For a cell, a list row, a field in read mode or
|
|
65
|
+
* a fact of a record.
|
|
66
|
+
*
|
|
67
|
+
* Context: a chip is a button ONLY when it has somewhere to go. With `contact` or `onOpen` it
|
|
68
|
+
* opens the `ContactCard` (press, Enter or Space — React Aria's `DialogTrigger` through
|
|
69
|
+
* `PopoverTrigger`: focus goes into the card, Escape and a click outside close it, and focus
|
|
70
|
+
* comes back to the chip). Without them it stays text: a button that does nothing is a lie a
|
|
71
|
+
* keyboard user pays for with one more Tab stop on every row.
|
|
72
|
+
*
|
|
73
|
+
* It opens on PRESS and not on hover: the card holds links, and content that has to be
|
|
74
|
+
* reached with the pointer or the keyboard cannot disappear when the pointer leaves the name.
|
|
75
|
+
*/
|
|
76
|
+
export declare function UserChip({ name, size, src, detail, role, contact, onOpen, }: UserChipProps): import("react").JSX.Element;
|
|
77
|
+
/** Several people in little width: the widget of a to-many relation.
|
|
78
|
+
*
|
|
79
|
+
* Context: they overlap and get cut off at `max`. Five avatars in a row are five
|
|
80
|
+
* things; five overlapped with a «+3» are ONE thing that has eight inside, which
|
|
81
|
+
* is what the cell means to say.
|
|
82
|
+
*
|
|
83
|
+
* The `title` carries the whole list: whoever needs the names has them without
|
|
84
|
+
* the column paying for their width. */
|
|
85
|
+
export declare function UserStack({ names, max, size, }: {
|
|
86
|
+
names: readonly string[];
|
|
87
|
+
max?: number;
|
|
88
|
+
size?: AvatarSize;
|
|
89
|
+
}): import("react").JSX.Element;
|
|
90
|
+
export {};
|