@sincpro/web-ui 0.1.0 → 1.0.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/README.md +40 -13
- package/dist/components/activity/Activity.d.ts +69 -0
- package/dist/components/activity/Activity.js +279 -0
- package/dist/components/activity/ActivityBadge.d.ts +16 -0
- package/dist/components/activity/ActivityBadge.js +29 -0
- package/dist/components/{chatter → activity}/Messages.d.ts +2 -2
- package/dist/components/{chatter → activity}/Messages.js +9 -6
- package/dist/components/activity/index.d.ts +4 -0
- package/dist/components/activity/index.js +5 -0
- package/dist/components/assist/AssistMark.d.ts +3 -3
- package/dist/components/assist/AssistMark.js +1 -1
- package/dist/components/assist/assist.d.ts +1 -1
- package/dist/components/assist/assist.js +1 -1
- package/dist/components/calendar/RecordCalendar.d.ts +48 -0
- package/dist/components/calendar/RecordCalendar.js +101 -0
- package/dist/components/calendar/index.d.ts +1 -0
- package/dist/components/calendar/index.js +1 -0
- package/dist/components/form/Sheet.d.ts +4 -0
- package/dist/components/form/Sheet.js +16 -5
- package/dist/components/gantt/RecordGantt.d.ts +44 -0
- package/dist/components/gantt/RecordGantt.js +110 -0
- package/dist/components/gantt/index.d.ts +1 -0
- package/dist/components/gantt/index.js +1 -0
- package/dist/components/index.d.ts +4 -1
- package/dist/components/index.js +4 -1
- package/dist/components/lines/LineItems.d.ts +45 -5
- package/dist/components/lines/LineItems.js +106 -18
- package/dist/components/listing/BoardColumns.d.ts +39 -0
- package/dist/components/listing/BoardColumns.js +138 -0
- package/dist/components/listing/Listing.js +1 -1
- package/dist/components/listing/ListingBar.d.ts +46 -2
- package/dist/components/listing/ListingBar.js +47 -5
- package/dist/components/listing/PivotExplorer.d.ts +34 -0
- package/dist/components/listing/PivotExplorer.js +86 -0
- package/dist/components/listing/RecordCard.d.ts +6 -8
- package/dist/components/listing/RecordCard.js +13 -7
- package/dist/components/listing/Row.d.ts +25 -2
- package/dist/components/listing/Row.js +30 -13
- package/dist/components/listing/index.d.ts +2 -0
- package/dist/components/listing/index.js +2 -0
- package/dist/components/listing/set.d.ts +1 -1
- package/dist/components/navigation/index.d.ts +0 -2
- package/dist/components/navigation/index.js +2 -3
- package/dist/components/record/RecordAudit.d.ts +17 -0
- package/dist/components/record/RecordAudit.js +27 -0
- package/dist/components/record/RecordHeader.d.ts +27 -17
- package/dist/components/record/RecordHeader.js +64 -20
- package/dist/components/record/RecordNotices.d.ts +37 -0
- package/dist/components/record/RecordNotices.js +75 -0
- package/dist/components/record/RecordRibbon.d.ts +13 -0
- package/dist/components/record/RecordRibbon.js +22 -0
- package/dist/components/record/ThumbBar.js +1 -1
- package/dist/components/record/index.d.ts +2 -0
- package/dist/components/record/index.js +2 -0
- package/dist/components/search/CriteriaChips.d.ts +6 -1
- package/dist/components/search/CriteriaChips.js +71 -20
- package/dist/components/search/SearchBar.d.ts +4 -1
- package/dist/components/search/SearchBar.js +15 -7
- package/dist/components/timeline/RecordTimeline.d.ts +60 -0
- package/dist/components/timeline/RecordTimeline.js +214 -0
- package/dist/components/timeline/TimelineToolbar.d.ts +30 -0
- package/dist/components/timeline/TimelineToolbar.js +31 -0
- package/dist/components/timeline/index.d.ts +3 -0
- package/dist/components/timeline/index.js +3 -0
- package/dist/components/timeline/time.d.ts +43 -0
- package/dist/components/timeline/time.js +135 -0
- package/dist/foundations/copy.d.ts +194 -18
- package/dist/foundations/copy.es.js +142 -15
- package/dist/foundations/copy.js +133 -11
- package/dist/foundations/index.d.ts +1 -1
- package/dist/foundations/match.d.ts +23 -0
- package/dist/foundations/match.js +63 -0
- package/dist/foundations/slots.d.ts +1 -1
- package/dist/foundations/slots.js +8 -3
- package/dist/foundations/types.d.ts +7 -0
- package/dist/foundations/types.js +0 -36
- package/dist/primitives/block/index.d.ts +1 -1
- package/dist/primitives/block/index.js +1 -1
- package/dist/primitives/cell/CellStack.d.ts +7 -1
- package/dist/primitives/cell/CellStack.js +5 -2
- package/dist/primitives/choice/ComboBox.d.ts +3 -0
- package/dist/primitives/choice/ComboBox.js +10 -6
- package/dist/primitives/choice/KanbanState.d.ts +14 -0
- package/dist/primitives/choice/KanbanState.js +37 -0
- package/dist/primitives/choice/Priority.d.ts +20 -0
- package/dist/primitives/choice/Priority.js +41 -0
- package/dist/primitives/choice/SearchField.d.ts +10 -1
- package/dist/primitives/choice/SearchField.js +21 -2
- package/dist/primitives/choice/Slider.d.ts +6 -1
- package/dist/primitives/choice/Slider.js +2 -2
- package/dist/primitives/choice/index.d.ts +2 -0
- package/dist/primitives/choice/index.js +2 -0
- package/dist/primitives/date/Calendar.js +30 -6
- package/dist/primitives/date/DatePicker.d.ts +11 -3
- package/dist/primitives/date/DatePicker.js +12 -8
- package/dist/primitives/date/DateTime.d.ts +35 -0
- package/dist/primitives/date/DateTime.js +100 -0
- package/dist/primitives/date/index.d.ts +1 -0
- package/dist/primitives/date/index.js +4 -2
- package/dist/primitives/dialog/Dialog.d.ts +9 -3
- package/dist/primitives/dialog/Dialog.js +41 -5
- package/dist/primitives/field/DropZone.js +1 -1
- package/dist/primitives/field/Field.d.ts +14 -2
- package/dist/primitives/field/Field.js +25 -14
- package/dist/primitives/field/copy.d.ts +16 -0
- package/dist/primitives/field/copy.js +69 -0
- package/dist/primitives/field/index.d.ts +2 -0
- package/dist/primitives/field/index.js +4 -0
- package/dist/primitives/field/note.d.ts +55 -0
- package/dist/primitives/field/note.js +117 -0
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/menu/Menu.d.ts +28 -0
- package/dist/primitives/menu/Menu.js +47 -17
- package/dist/primitives/notice/Callout.js +1 -1
- package/dist/primitives/notice/Flag.d.ts +13 -0
- package/dist/primitives/notice/Flag.js +31 -0
- package/dist/primitives/notice/index.d.ts +2 -1
- package/dist/primitives/notice/index.js +2 -1
- package/dist/primitives/overlay/Preview.d.ts +19 -0
- package/dist/primitives/overlay/Preview.js +69 -0
- package/dist/primitives/overlay/index.d.ts +1 -0
- package/dist/primitives/overlay/index.js +3 -1
- package/dist/primitives/picker/MatchedText.d.ts +10 -0
- package/dist/primitives/picker/MatchedText.js +12 -0
- package/dist/primitives/picker/Picker.d.ts +67 -4
- package/dist/primitives/picker/Picker.js +142 -23
- package/dist/primitives/table/CellCopy.d.ts +20 -0
- package/dist/primitives/table/CellCopy.js +23 -0
- package/dist/primitives/table/DataTable.d.ts +55 -1
- package/dist/primitives/table/DataTable.js +126 -40
- package/dist/primitives/table/EditableTable.d.ts +102 -2
- package/dist/primitives/table/EditableTable.js +287 -32
- package/dist/primitives/table/ReportTable.js +2 -1
- package/dist/primitives/table/RowActions.js +7 -2
- package/dist/primitives/table/SheetTable.d.ts +4 -1
- package/dist/primitives/table/SheetTable.js +38 -5
- package/dist/primitives/table/Table.d.ts +5 -0
- package/dist/primitives/table/Table.js +17 -8
- package/dist/primitives/table/index.d.ts +2 -2
- package/dist/primitives/table/index.js +1 -1
- package/dist/primitives/tabs/Tabs.d.ts +2 -2
- package/dist/primitives/tabs/Tabs.js +1 -1
- package/dist/primitives/tag/Tags.d.ts +2 -0
- package/dist/primitives/tag/Tags.js +2 -1
- package/dist/primitives/tag/TagsField.d.ts +13 -0
- package/dist/primitives/tag/TagsField.js +39 -10
- package/dist/primitives/tag/choices.d.ts +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/Avatar.d.ts +16 -0
- package/dist/primitives/value/Avatar.js +91 -0
- package/dist/primitives/value/Copyable.d.ts +11 -0
- package/dist/primitives/value/Copyable.js +27 -6
- package/dist/primitives/value/Fecha.js +10 -1
- package/dist/primitives/value/Person.d.ts +6 -51
- package/dist/primitives/value/Person.js +9 -148
- package/dist/primitives/value/ProgressBar.d.ts +31 -0
- package/dist/primitives/value/ProgressBar.js +53 -0
- package/dist/primitives/value/Relation.d.ts +22 -5
- package/dist/primitives/value/Relation.js +42 -14
- package/dist/primitives/value/Totals.d.ts +102 -7
- package/dist/primitives/value/Totals.js +52 -2
- package/dist/primitives/value/Value.d.ts +0 -37
- package/dist/primitives/value/Value.js +0 -39
- package/dist/primitives/value/contact.d.ts +30 -0
- package/dist/primitives/value/contact.js +51 -0
- package/dist/primitives/value/index.d.ts +5 -4
- package/dist/primitives/value/index.js +5 -4
- package/dist/tokens.css +179 -7
- package/dist/views/list/List.d.ts +32 -3
- package/dist/views/list/List.js +51 -7
- package/dist/views/list/ListView.d.ts +1 -1
- package/dist/views/list/ListView.js +70 -13
- package/dist/views/list/columns.js +8 -1
- package/dist/views/list/derive.d.ts +12 -1
- package/dist/views/list/derive.js +29 -3
- package/dist/views/list/presentations.d.ts +5 -2
- package/dist/views/list/presentations.js +35 -3
- package/dist/views/list/types.d.ts +57 -1
- package/dist/views/list/useNarrowList.d.ts +10 -0
- package/dist/views/list/useNarrowList.js +20 -6
- package/dist/views/record/Record.d.ts +41 -8
- package/dist/views/record/Record.js +113 -28
- package/dist/views/record/index.d.ts +1 -1
- package/package.json +15 -4
- package/dist/components/chatter/Chatter.d.ts +0 -161
- package/dist/components/chatter/Chatter.js +0 -232
- package/dist/components/chatter/index.d.ts +0 -3
- package/dist/components/chatter/index.js +0 -4
- package/dist/components/navigation/CommandPalette.d.ts +0 -40
- package/dist/components/navigation/CommandPalette.js +0 -114
- package/dist/components/navigation/commands.d.ts +0 -30
- package/dist/components/navigation/commands.js +0 -30
- /package/dist/components/{chatter → activity}/Timeline.d.ts +0 -0
- /package/dist/components/{chatter → activity}/Timeline.js +0 -0
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Activity } from "../../components/activity/Activity.js";
|
|
3
4
|
import { Sheet, } from "../../components/form/Sheet.js";
|
|
4
5
|
import { Pager as PagerControl } from "../../components/record/Pager.js";
|
|
6
|
+
import { RecordAudit } from "../../components/record/RecordAudit.js";
|
|
5
7
|
import { RecordHeader } from "../../components/record/RecordHeader.js";
|
|
8
|
+
import { RecordBlocking, RecordNoticeIndicator, splitNotices, } from "../../components/record/RecordNotices.js";
|
|
9
|
+
import { RecordRibbon } from "../../components/record/RecordRibbon.js";
|
|
6
10
|
import { RecordSkeleton } from "../../components/record/RecordSkeleton.js";
|
|
7
11
|
import { SmartButton, SmartButtons } from "../../components/record/SmartButtons.js";
|
|
8
12
|
import { StatusBar } from "../../components/record/StatusBar.js";
|
|
9
13
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
10
14
|
import { byDensity, useDensity } from "../../foundations/density.js";
|
|
11
|
-
import { flatten, pick as pickSlot, propsOf, slot, slotOf, } from "../../foundations/slots.js";
|
|
12
|
-
import {
|
|
15
|
+
import { flatten, pick as pickSlot, pickAll, propsOf, slot, slotOf, } from "../../foundations/slots.js";
|
|
16
|
+
import { createNoteRegistry, NoteRegistryContext, } from "../../primitives/field/note.js";
|
|
13
17
|
import { Tabs } from "../../primitives/tabs/Tabs.js";
|
|
18
|
+
import { useEffect, useId, useState, useSyncExternalStore } from "react";
|
|
14
19
|
const pick = (nodes, name) => pickSlot(nodes, name);
|
|
15
20
|
/** El riel: mi lugar delante del registro. Sin él, no hay banda — y eso
|
|
16
21
|
* es exactamente lo que un diálogo quiere. */
|
|
@@ -31,16 +36,31 @@ const Actions = slot("actions", (_props) => null);
|
|
|
31
36
|
* quien la pasaba armada tenía que acordarse de la superficie del riel. Olvidarlo
|
|
32
37
|
* pintaba la paleta del lienzo encima del chrome, y pasó. */
|
|
33
38
|
const Status = slot("status", (_props) => null);
|
|
39
|
+
/** What has to be known about THIS record before acting on it: «este cliente tiene
|
|
40
|
+
* Bs. 1.200 en créditos pendientes · Aplicar», «posible factura duplicada», «vencida
|
|
41
|
+
* hace 12 días», a blocking error from the backend. A `Callout` — the tone, one
|
|
42
|
+
* sentence, an optional `title`, an optional `action` (secondary or ghost: the primary
|
|
43
|
+
* belongs to the rail) and `onDismiss` when it can be put away.
|
|
44
|
+
*
|
|
45
|
+
* Context: written as a child of `Record` or of `Record.Paper`, as many as are true —
|
|
46
|
+
* `{overdue ? <Record.Notice tone="warn">…</Record.Notice> : null}`. Where they go is
|
|
47
|
+
* the record's, and NONE goes among the fields: a `bad` one (blocking) stays visible
|
|
48
|
+
* between the rail and the paper; the rest gather in one indicator in the rail —
|
|
49
|
+
* «⚠ 2 avisos», toned by the most urgent — that opens their list on press. Both in
|
|
50
|
+
* urgency order. A note about ONE field is that field's `note`. See `RecordNotices`.
|
|
51
|
+
*
|
|
52
|
+
* Named `Notice` and not `Alert`: an ARIA `alert` interrupts the screen reader, and a
|
|
53
|
+
* notice that was already true when the record opened interrupts nothing (`Callout`
|
|
54
|
+
* is `role="status"`). */
|
|
55
|
+
const NoticeSlot = slot("notice", (_props) => null);
|
|
56
|
+
/** What the record IS now, across the paper's corner: «PAGADA», «ARCHIVADA». A word with its
|
|
57
|
+
* tone, written as a child of `Record` or `Record.Paper`; see `RecordRibbon`. */
|
|
58
|
+
const RibbonSlot = slot("ribbon", (_props) => null);
|
|
59
|
+
/** Who created the record and who changed it last, on the paper's last line. */
|
|
60
|
+
const AuditSlot = slot("audit", (_props) => null);
|
|
34
61
|
/** El papel: el registro. El `<sheet>` blanco sobre el piso. */
|
|
35
62
|
const Paper = slot("paper", (_props) => null);
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
* Va al lado y no abajo porque la historia es el contexto de todo lo demás que
|
|
39
|
-
* está en pantalla — leer «la factura fue rechazada» mirando los datos que la
|
|
40
|
-
* produjeron es justamente para lo que sirve, y una sección más abajo obliga a
|
|
41
|
-
* ver una cosa o la otra. Lo que va adentro lo arma quien compone: el kit no
|
|
42
|
-
* sabe qué es un evento de este producto. */
|
|
43
|
-
const ChatterSlot = slot("chatter", (_props) => null);
|
|
63
|
+
const ActivitySlot = slot("activity", (_props) => null);
|
|
44
64
|
/** Los hechos globales del registro. Están en TODAS las páginas, y por eso van
|
|
45
65
|
* arriba del cuaderno y no adentro de una. */
|
|
46
66
|
const Facts = slot("facts", (_props) => null);
|
|
@@ -54,11 +74,10 @@ export function Record({ identity, error = null, dirty, children }) {
|
|
|
54
74
|
const top = flatten(children);
|
|
55
75
|
const railNode = pick(top, "rail");
|
|
56
76
|
const paperNode = pick(top, "paper");
|
|
57
|
-
const
|
|
58
|
-
const chatter = chatterSlot?.children;
|
|
77
|
+
const activitySlot = propsOf(pick(top, "activity"));
|
|
59
78
|
/* La escotilla: lo que el arquetipo no previó se dibuja igual, en el papel.
|
|
60
79
|
Que aparezca algo acá es la señal de que al arquetipo le falta una pieza. */
|
|
61
|
-
const loose = top.filter((node) => slotOf(node) !== "rail" && slotOf(node) !== "paper" && slotOf(node) !== "
|
|
80
|
+
const loose = top.filter((node) => slotOf(node) !== "rail" && slotOf(node) !== "paper" && slotOf(node) !== "activity");
|
|
62
81
|
const rail = flatten(propsOf(railNode)?.children);
|
|
63
82
|
const crumbs = propsOf(pick(rail, "crumbs"))?.crumbs;
|
|
64
83
|
const pager = propsOf(pick(rail, "pager"));
|
|
@@ -77,10 +96,43 @@ export function Record({ identity, error = null, dirty, children }) {
|
|
|
77
96
|
/* Apiladas en un bloque: arriba dónde está, abajo qué pasó para llegar.
|
|
78
97
|
Lado a lado competían por el ancho con los botones y quedaban las dos
|
|
79
98
|
truncadas; una debajo de la otra, cada una tiene su línea entera. */
|
|
80
|
-
_jsxs("div", { className: "flex min-w-0 flex-col items-end gap-1.5", children: [status.stages === undefined ? null : (_jsx(StatusBar, { stages: status.stages, current: status.current ?? status.stages[0]?.id ?? "", failed: status.failed ?? false, surface: surface })), status.children] }));
|
|
99
|
+
_jsxs("div", { className: "flex min-w-0 flex-col items-end gap-1.5", children: [status.stages === undefined ? null : (_jsx(StatusBar, { stages: status.stages, current: status.current ?? status.stages[0]?.id ?? "", failed: status.failed ?? false, surface: surface, ...(status.onSelect === undefined ? {} : { onSelect: status.onSelect }) })), status.children] }));
|
|
81
100
|
const paper = propsOf(paperNode);
|
|
82
101
|
const inPaper = [...flatten(paper?.children), ...loose];
|
|
83
102
|
const fields = propsOf(pick(inPaper, "facts"))?.fields;
|
|
103
|
+
const ribbon = propsOf(pick(inPaper, "ribbon"));
|
|
104
|
+
const audit = propsOf(pick(inPaper, "audit"));
|
|
105
|
+
/* Written at the top of the record or inside the paper: both are «on the record». */
|
|
106
|
+
const notices = pickAll(inPaper, "notice")
|
|
107
|
+
.map((node) => propsOf(node))
|
|
108
|
+
.filter((notice) => notice !== undefined);
|
|
109
|
+
const { blocking, rest } = splitNotices(notices);
|
|
110
|
+
/* The notes of the record's fields and rows, which the cards register as they mount. */
|
|
111
|
+
const [registry] = useState(createNoteRegistry);
|
|
112
|
+
const notes = useSyncExternalStore(registry.subscribe, registry.snapshot, registry.snapshot);
|
|
113
|
+
/* The panel's open state is the record's, so the rail's indicator can open it. */
|
|
114
|
+
const [ownOpen, setOwnOpen] = useState(activitySlot?.defaultOpen ?? true);
|
|
115
|
+
const activityOpen = activitySlot?.open ?? ownOpen;
|
|
116
|
+
const setActivityOpen = (next) => {
|
|
117
|
+
activitySlot?.onOpenChange?.(next);
|
|
118
|
+
if (activitySlot?.open === undefined)
|
|
119
|
+
setOwnOpen(next);
|
|
120
|
+
};
|
|
121
|
+
const alertsId = useId();
|
|
122
|
+
/* «⚠ 2 avisos» with a panel to go to: open it, then — once it is drawn — bring its
|
|
123
|
+
notices into view and put the focus on them. On a wide record they are beside the
|
|
124
|
+
sheet already; on a narrow one the panel is below and this is the scroll that finds
|
|
125
|
+
it. A counter and not a flag, so pressing it twice moves the focus twice. */
|
|
126
|
+
const [reveal, setReveal] = useState(0);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (reveal === 0)
|
|
129
|
+
return;
|
|
130
|
+
const section = document.getElementById(alertsId);
|
|
131
|
+
if (section === null)
|
|
132
|
+
return;
|
|
133
|
+
section.scrollIntoView({ block: "start" });
|
|
134
|
+
section.focus({ preventScroll: true });
|
|
135
|
+
}, [reveal, alertsId]);
|
|
84
136
|
const notebook = propsOf(pick(inPaper, "pages"));
|
|
85
137
|
const pages = flatten(notebook?.children).filter((node) => slotOf(node) === "page");
|
|
86
138
|
const tabs = pages
|
|
@@ -113,19 +165,49 @@ export function Record({ identity, error = null, dirty, children }) {
|
|
|
113
165
|
bar (last in it, `mt-auto`) reaches the bottom of the screen even when the record is
|
|
114
166
|
shorter than the screen. The extra height has no paint of its own: on a desk
|
|
115
167
|
nothing changes. */
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
168
|
+
/* The record knows the notes of its fields and rows: their cards register here. */
|
|
169
|
+
_jsx(NoteRegistryContext, { value: registry, children: _jsx("article", { className: "flex min-w-0 flex-1 flex-col", children: _jsx(RecordHeader, { railSurface: surface, ...(ribbon === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: {
|
|
172
|
+
ribbon: (_jsx(RecordRibbon, { label: ribbon.label, ...(ribbon.tone === undefined ? {} : { tone: ribbon.tone }) })),
|
|
173
|
+
}), ...(paper?.width === undefined ? {} : { width: paper.width }), ...(dirty === undefined ? {} : { dirty }), ...(crumbs === undefined ? {} : { crumbs }), ...(pager === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: {
|
|
176
|
+
pager: (_jsx(PagerControl, { index: pager.index, total: pager.total, surface: surface, ...(pager.onPrev === undefined ? {} : { onPrev: pager.onPrev }), ...(pager.onNext === undefined ? {} : { onNext: pager.onNext }) })),
|
|
177
|
+
}), ...(actions === undefined ? {} : { actions }), ...(statusBlock === undefined ? {} : { status: statusBlock }), ...(identity.icon === undefined ? {} : { icon: identity.icon }), title: identity.title, ...(identity.subtitle === undefined ? {} : { subtitle: identity.subtitle }), ...(identity.figure === undefined ? {} : { figure: identity.figure }), ...(identity.tag === undefined ? {} : { tag: identity.tag }), ...(smart === undefined || smart.length === 0
|
|
178
|
+
? {}
|
|
179
|
+
: {
|
|
180
|
+
smart: (_jsx(SmartButtons, { children: smart.map((s) => (_jsx(SmartButton, { icon: s.icon, ...(s.count === undefined ? {} : { count: s.count }), label: s.label, layout: onTheRail && d === "comfortable" ? "inline" : "stacked", ...(s.onClick === undefined ? {} : { onClick: s.onClick }) }, s.label))) })),
|
|
181
|
+
}), ...(aspectsNode === undefined || globalSheet ? {} : { aspects: aspectsNode }), ...(activitySlot === undefined
|
|
182
|
+
? {}
|
|
183
|
+
: {
|
|
184
|
+
activity: (_jsx(Activity, { ...activitySlot, alerts: rest, alertsId: alertsId, open: activityOpen, onToggle: () => setActivityOpen(!activityOpen) })),
|
|
185
|
+
activityLayout: activitySlot.layout ?? "auto",
|
|
186
|
+
}), ...(blocking.length === 0 && error === null
|
|
187
|
+
? {}
|
|
188
|
+
: {
|
|
189
|
+
blocking: (_jsx(RecordBlocking, { notices: blocking, error: error, errorHeading: copy.failed.operation })),
|
|
190
|
+
}), ...(rest.length === 0 && (activitySlot === undefined || notes.length === 0)
|
|
191
|
+
? {}
|
|
192
|
+
: {
|
|
193
|
+
indicator: (_jsx(RecordNoticeIndicator, { notices: rest, anchored: notes.map((entry) => ({ tone: entry.note.tone ?? "info" })), ...(activitySlot === undefined
|
|
194
|
+
? {}
|
|
195
|
+
: {
|
|
196
|
+
/* With a panel, the notices live there: the indicator goes to them
|
|
197
|
+
instead of floating a second copy in a popover. */
|
|
198
|
+
reveal: {
|
|
199
|
+
target: alertsId,
|
|
200
|
+
onReveal: () => {
|
|
201
|
+
setActivityOpen(true);
|
|
202
|
+
setReveal((n) => n + 1);
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
}) })),
|
|
206
|
+
}), children: _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-(--spacing-block) px-4 pt-(--spacing-block) pb-4", "gap-2.5 pt-2.5 pb-2")}`, children: _jsxs(Sheet, { layout: paper?.layout ?? "stacked", columns: paper?.columns ?? 2, ...(hasFacts ? { fields } : {}), ...(above.length > 0 || (globalSheet && aspectsNode !== undefined)
|
|
207
|
+
? {
|
|
208
|
+
head: (_jsxs(_Fragment, { children: [above, globalSheet && aspectsNode !== undefined ? (_jsx("div", { className: `border-b border-line-strong ${byDensity(d, "-mx-4 px-4", "")}`, children: aspectsNode })) : null] })),
|
|
209
|
+
}
|
|
210
|
+
: {}), children: [pageBody, below] }) }), audit === undefined ? null : _jsx(RecordAudit, { ...audit })] }) }) }) }));
|
|
129
211
|
}
|
|
130
212
|
Record.Skeleton = RecordSkeleton;
|
|
131
213
|
Record.Rail = Rail;
|
|
@@ -135,7 +217,10 @@ Record.Smart = Smart;
|
|
|
135
217
|
Record.Actions = Actions;
|
|
136
218
|
Record.Status = Status;
|
|
137
219
|
Record.Paper = Paper;
|
|
138
|
-
Record.
|
|
220
|
+
Record.Activity = ActivitySlot;
|
|
139
221
|
Record.Facts = Facts;
|
|
140
222
|
Record.Pages = Pages;
|
|
141
223
|
Record.Page = Page;
|
|
224
|
+
Record.Notice = NoticeSlot;
|
|
225
|
+
Record.Ribbon = RibbonSlot;
|
|
226
|
+
Record.Audit = AuditSlot;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Record, type RecordIdentity, type RecordPager, type RecordProps, type SmartLink, } from "./Record.js";
|
|
1
|
+
export { Record, type RecordIdentity, type RecordNotice, type RecordPager, type RecordProps, type SmartLink, } from "./Record.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sincpro/web-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "An enterprise web design system for business software — ERPs, back offices, management tools — in React and Tailwind: tokens, primitives, components and the two views every management screen is made of.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,19 +53,24 @@
|
|
|
53
53
|
"@codemirror/lint": "^6.9.7",
|
|
54
54
|
"@fontsource/ibm-plex-mono": "^5.3.0",
|
|
55
55
|
"@fontsource/ibm-plex-sans": "^5.3.0",
|
|
56
|
+
"@fullcalendar/react": "7.1.3",
|
|
56
57
|
"@internationalized/date": "^3.12.4",
|
|
58
|
+
"@svar-ui/react-gantt": "2.8.2",
|
|
57
59
|
"@uiw/react-codemirror": "^4.25.11",
|
|
58
60
|
"lucide-react": "^1.45.0",
|
|
59
61
|
"react-aria": "^3.53.1",
|
|
60
62
|
"react-aria-components": "^1.22.1",
|
|
61
63
|
"tailwind-variants": "^4.0.0",
|
|
62
|
-
"tailwindcss-react-aria-components": "^2.2.0"
|
|
64
|
+
"tailwindcss-react-aria-components": "^2.2.0",
|
|
65
|
+
"temporal-polyfill": "^1.0.6"
|
|
63
66
|
},
|
|
64
67
|
"devDependencies": {
|
|
65
68
|
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@sincpro/web-composer": "^1.4.0",
|
|
66
70
|
"@storybook/addon-a11y": "10.6.0",
|
|
67
71
|
"@storybook/addon-docs": "10.6.0",
|
|
68
72
|
"@storybook/addon-mcp": "10.6.0",
|
|
73
|
+
"@storybook/addon-vitest": "10.6.0",
|
|
69
74
|
"@storybook/react-vite": "10.6.0",
|
|
70
75
|
"@tailwindcss/vite": "4.3.3",
|
|
71
76
|
"@tanstack/react-table": "^9.2.4",
|
|
@@ -74,12 +79,13 @@
|
|
|
74
79
|
"@types/react": "^19.3.0",
|
|
75
80
|
"@types/react-dom": "^19.3.0",
|
|
76
81
|
"@vitejs/plugin-react": "6.1.1",
|
|
82
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
77
83
|
"eslint": "^10.11.0",
|
|
78
84
|
"eslint-config-prettier": "^10.1.8",
|
|
79
85
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
80
86
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
87
|
+
"playwright": "1.64.1",
|
|
81
88
|
"prettier": "^3.9.8",
|
|
82
|
-
"puppeteer-core": "^23.11.1",
|
|
83
89
|
"react": "19.3.0",
|
|
84
90
|
"react-dom": "19.3.0",
|
|
85
91
|
"storybook": "10.6.0",
|
|
@@ -88,7 +94,8 @@
|
|
|
88
94
|
"tsx": "^4.23.13",
|
|
89
95
|
"typescript": "~5.9.3",
|
|
90
96
|
"typescript-eslint": "^8.70.0",
|
|
91
|
-
"vite": "8.3.0"
|
|
97
|
+
"vite": "8.3.0",
|
|
98
|
+
"vitest": "5.0.5"
|
|
92
99
|
},
|
|
93
100
|
"keywords": [
|
|
94
101
|
"design-system",
|
|
@@ -99,10 +106,14 @@
|
|
|
99
106
|
"ui",
|
|
100
107
|
"sincpro"
|
|
101
108
|
],
|
|
109
|
+
"homepage": "https://github.com/Sincpro-SRL/sincpro_web_ui#readme",
|
|
102
110
|
"repository": {
|
|
103
111
|
"type": "git",
|
|
104
112
|
"url": "git+https://github.com/Sincpro-SRL/sincpro_web_ui.git"
|
|
105
113
|
},
|
|
114
|
+
"bugs": {
|
|
115
|
+
"url": "https://github.com/Sincpro-SRL/sincpro_web_ui/issues"
|
|
116
|
+
},
|
|
106
117
|
"author": "Gutierrez, Andres <andres.gutierrez@sincpro.com.bo>",
|
|
107
118
|
"license": "MIT",
|
|
108
119
|
"engines": {
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import type { Message } from "./Messages.js";
|
|
2
|
-
import type { Tone } from "../../foundations/types.js";
|
|
3
|
-
import type { LucideIcon } from "lucide-react";
|
|
4
|
-
import { type ReactNode } from "react";
|
|
5
|
-
/**
|
|
6
|
-
* What a record has been through: what the system logged and what people wrote,
|
|
7
|
-
* in one place.
|
|
8
|
-
*
|
|
9
|
-
* Odoo calls it the chatter and the idea is the one worth taking: a record's
|
|
10
|
-
* history is not one more section of the record — it is the CONTEXT for
|
|
11
|
-
* everything else on the screen. Reading «the assessment failed» while looking
|
|
12
|
-
* at the inputs that produced it is the whole point.
|
|
13
|
-
*
|
|
14
|
-
* ── The merge is the default, and that is the decision ──────────────
|
|
15
|
-
*
|
|
16
|
-
* **Everything together comes first**, because the question somebody actually
|
|
17
|
-
* has is «what happened here», not «what happened here *according to the
|
|
18
|
-
* system*». A payment that bounced and the message somebody left about it are one
|
|
19
|
-
* story; splitting them by who wrote them makes the reader join them back by
|
|
20
|
-
* hand, comparing two timestamps in two lists.
|
|
21
|
-
*
|
|
22
|
-
* The other two tabs are for when you already know which half you want: «who
|
|
23
|
-
* said what» and «what ran». They are a narrowing of the first, never a
|
|
24
|
-
* different content.
|
|
25
|
-
*
|
|
26
|
-
* ── Why the tabs live HERE and not in whoever composes ──────────────
|
|
27
|
-
*
|
|
28
|
-
* They used to be a generic `tabs` array and every screen decided its own: same
|
|
29
|
-
* component, a different chatter on each record. What a record has been through
|
|
30
|
-
* is the same question everywhere, so the answer is the component's, not the
|
|
31
|
-
* screen's. The screen passes what it HAS —messages, events, a composer— and
|
|
32
|
-
* this decides how it is read.
|
|
33
|
-
*
|
|
34
|
-
* A tab with no source behind it is not drawn: with only events there is
|
|
35
|
-
* nothing to merge and nothing to separate, so it shows the stream and no tab
|
|
36
|
-
* bar at all.
|
|
37
|
-
*
|
|
38
|
-
* ── Cut into pages, like any other list ─────────────────────────────
|
|
39
|
-
*
|
|
40
|
-
* A record with six months behind it has hundreds of entries, and a column that
|
|
41
|
-
* only scrolls turns the history into something you give up on. The pages are
|
|
42
|
-
* this component's and not the screen's for the same reason the tabs are: it is
|
|
43
|
-
* the same list on every record.
|
|
44
|
-
*
|
|
45
|
-
* ── Beside on a wide panel, underneath on a narrow one ──────────────
|
|
46
|
-
*
|
|
47
|
-
* It asks `@container`, not `lg:`, for the reason `SectionGrid` already wrote
|
|
48
|
-
* down: with four panels open each one measures 380 px on a 1.600 screen, and a
|
|
49
|
-
* window rule would put a 22rem column inside a 380 px panel. **Whoever mounts
|
|
50
|
-
* it has to declare `@container` on the container** — `Record` does.
|
|
51
|
-
*/
|
|
52
|
-
/**
|
|
53
|
-
* Algo que el sistema anotó.
|
|
54
|
-
*
|
|
55
|
-
* Deliberadamente NO es el evento del producto: lo que una historia necesita
|
|
56
|
-
* para dibujar uno es qué pasó, cuándo, quién, y —cuando las hay— las tres cosas
|
|
57
|
-
* que uno termina yendo a buscar a otro lado.
|
|
58
|
-
*
|
|
59
|
-
* ── Las tres que faltaban, y por qué cada una ───────────────────────
|
|
60
|
-
*
|
|
61
|
-
* by QUIÉN. «se publicó la factura» no dice si la publicó una persona o un
|
|
62
|
-
* cron, y esa es la primera pregunta cuando algo cambió sin aviso.
|
|
63
|
-
* Ausente = lo hizo el sistema, que también es una respuesta.
|
|
64
|
-
*
|
|
65
|
-
* moved DE QUÉ A QUÉ. «el proyecto cambió de estado» obliga a mirar el
|
|
66
|
-
* evento anterior para saber de dónde venía. «borrador →
|
|
67
|
-
* confirmado» se lee de una y es la línea más útil de una historia
|
|
68
|
-
* de gestión.
|
|
69
|
-
*
|
|
70
|
-
* error QUÉ falló, entero y textual. Un evento rojo que dice «falló» sin
|
|
71
|
-
* decir por qué manda a alguien a los logs — y quien mira esta
|
|
72
|
-
* pantalla muchas veces no tiene acceso a los logs.
|
|
73
|
-
*/
|
|
74
|
-
export type ChatterEvent = {
|
|
75
|
-
id: string;
|
|
76
|
-
/** Qué pasó, en una oración y en pasado. */
|
|
77
|
-
label: ReactNode;
|
|
78
|
-
at: Date | string | number;
|
|
79
|
-
tone?: Tone;
|
|
80
|
-
/** Quién lo hizo. Ausente = el sistema. */
|
|
81
|
-
by?: string;
|
|
82
|
-
/** El cambio de estado, cuando lo hubo. `from` ausente = nació así. */
|
|
83
|
-
moved?: {
|
|
84
|
-
from?: string;
|
|
85
|
-
to: string;
|
|
86
|
-
};
|
|
87
|
-
/** Lo que hay que saber además del título, cuando el título no alcanza. */
|
|
88
|
-
detail?: ReactNode;
|
|
89
|
-
/** Lo que falló, textual. Se muestra la PRIMERA línea y el resto se abre: un
|
|
90
|
-
* stack de cuarenta renglones adentro de una historia tapa la historia, y lo
|
|
91
|
-
* que casi siempre alcanza es el primer renglón. */
|
|
92
|
-
error?: string;
|
|
93
|
-
/** El ícono del punto, cuando el tono no dice lo suficiente. Sin esto sale del
|
|
94
|
-
* tono, que es lo que evita un evento rojo con un tilde verde adentro. */
|
|
95
|
-
icon?: LucideIcon;
|
|
96
|
-
};
|
|
97
|
-
export type ChatterProps = {
|
|
98
|
-
/** What people wrote. Absent —not empty— while there is no service behind it:
|
|
99
|
-
* an empty thread says «nobody wrote yet», which is a different claim. */
|
|
100
|
-
messages?: readonly Message[];
|
|
101
|
-
/** What the system logged. */
|
|
102
|
-
events?: readonly ChatterEvent[];
|
|
103
|
-
/** The box to write in, when this record takes messages. It stays on top of
|
|
104
|
-
* the stream: writing is the thing you came to do, reading is what you do
|
|
105
|
-
* next. */
|
|
106
|
-
composer?: ReactNode;
|
|
107
|
-
/** Which tab is showing. Uncontrolled from «everything» when absent. */
|
|
108
|
-
current?: ChatterView;
|
|
109
|
-
onCurrentChange?: (next: ChatterView) => void;
|
|
110
|
-
open: boolean;
|
|
111
|
-
onToggle: () => void;
|
|
112
|
-
label?: string;
|
|
113
|
-
/** Dentro de la hoja del registro: no lleva marco propio, la hoja ya lo
|
|
114
|
-
* tiene. Sin esto es una tarjeta, que es como se ve cuando vive solo. */
|
|
115
|
-
embedded?: boolean;
|
|
116
|
-
/**
|
|
117
|
-
* Dónde vive el chatter respecto del registro.
|
|
118
|
-
*
|
|
119
|
-
* page DEBAJO y a TODO EL ANCHO, como una hoja propia. **Es el
|
|
120
|
-
* default**, y no por comodidad: un formulario necesita su ancho
|
|
121
|
-
* para que los campos no se apilen, y el de al costado se lo come
|
|
122
|
-
* en cuanto la pantalla no sobra. Abajo, la conversación no compite
|
|
123
|
-
* con el registro — es la otra mitad de la página.
|
|
124
|
-
* aside AL COSTADO cuando hay ancho, DEBAJO cuando no. Es para un
|
|
125
|
-
* registro que se LEE más de lo que se edita, donde tener la
|
|
126
|
-
* historia a la vista mientras se mira el dato vale el ancho que
|
|
127
|
-
* cuesta.
|
|
128
|
-
*
|
|
129
|
-
* La diferencia no es cosmética. En `aside` el ancho es escaso, así que el
|
|
130
|
-
* mensaje se lee en una columna de veintipico de caracteres; en `page` hay
|
|
131
|
-
* ancho de sobra y el texto se acota a una medida legible en vez de estirarse
|
|
132
|
-
* de punta a punta, que es donde un párrafo deja de poder seguirse.
|
|
133
|
-
*
|
|
134
|
-
* **Plegar es de pantalla chica.** En `page` el chatter no se pliega cuando
|
|
135
|
-
* hay ancho: plegarlo ahí esconde media pantalla para no ganar nada.
|
|
136
|
-
*/
|
|
137
|
-
layout?: ChatterLayout;
|
|
138
|
-
};
|
|
139
|
-
export type ChatterLayout = "aside" | "page";
|
|
140
|
-
export type ChatterView = "all" | "messages" | "events";
|
|
141
|
-
export declare function Chatter({ messages, events, composer, current, onCurrentChange, open, onToggle, label: ownLabel, layout, embedded, }: ChatterProps): import("react").JSX.Element;
|
|
142
|
-
/** Whether the chatter is open, when the page has no better answer.
|
|
143
|
-
*
|
|
144
|
-
* **Starts folded.** It began the other way round —a history nobody opens is a
|
|
145
|
-
* history nobody reads— and seeing it took the argument apart: open, it holds a
|
|
146
|
-
* 22rem column of context on every record you open, before anybody asked for
|
|
147
|
-
* any. Folded it still says how many there are, which is the half that has to
|
|
148
|
-
* be free; reading them is a click, and a click you chose.
|
|
149
|
-
*
|
|
150
|
-
* It is the same answer for both layouts. A default that depends on the width
|
|
151
|
-
* would mean the same record shows a different screen depending on how many
|
|
152
|
-
* panels happen to be open next to it, and that is not a default, it is a
|
|
153
|
-
* surprise. */
|
|
154
|
-
export declare function useChatter(initial?: boolean): {
|
|
155
|
-
open: boolean;
|
|
156
|
-
toggle: () => void;
|
|
157
|
-
/** Open it without closing it if it already was. What a button from outside
|
|
158
|
-
* needs —«events 12»— when pressing it twice must not hide the very thing
|
|
159
|
-
* it promised to show. */
|
|
160
|
-
show: () => void;
|
|
161
|
-
};
|