@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,232 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { usePagination } from "../listing/pages.js";
|
|
4
|
-
import { Pagination } from "../listing/Pagination.js";
|
|
5
|
-
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
6
|
-
import { Empty } from "../../primitives/notice/Notice.js";
|
|
7
|
-
import { Tabs } from "../../primitives/tabs/Tabs.js";
|
|
8
|
-
import { Tag } from "../../primitives/tag/Tag.js";
|
|
9
|
-
import { DateValue } from "../../primitives/value/Fecha.js";
|
|
10
|
-
import { Avatar } from "../../primitives/value/Person.js";
|
|
11
|
-
import { ArrowRight, ChevronRight, History, MessageSquare, PanelRightClose, PanelRightOpen, Rows3, Zap, } from "lucide-react";
|
|
12
|
-
import { Lock } from "lucide-react";
|
|
13
|
-
import { useState } from "react";
|
|
14
|
-
/* The tab names come from the copy table: the same three words on every record. */
|
|
15
|
-
const TAB_LABEL = {
|
|
16
|
-
all: (copy) => copy.chatter.tabAll,
|
|
17
|
-
messages: (copy) => copy.chatter.tabMessages,
|
|
18
|
-
events: (copy) => copy.chatter.tabEvents,
|
|
19
|
-
};
|
|
20
|
-
/* El ícono de cada pestaña. Con tres, es lo que se reconoce sin leer — y en el
|
|
21
|
-
chatter angosto, lo único que sobrevive cuando el rótulo se trunca. */
|
|
22
|
-
const TAB_ICON = {
|
|
23
|
-
all: Rows3,
|
|
24
|
-
messages: MessageSquare,
|
|
25
|
-
events: History,
|
|
26
|
-
};
|
|
27
|
-
/* UN SOLO ÍCONO para todos los eventos: el rayo, que es lo que pasó — algo se
|
|
28
|
-
ejecutó. Había uno por tono y eso convertía la historia en un muestrario:
|
|
29
|
-
cinco formas y cinco colores para decir cinco veces «pasó algo».
|
|
30
|
-
Lo que SÍ hay que distinguir es si falló, y eso lo dice el contenedor. */
|
|
31
|
-
const EVENT_ICON = Zap;
|
|
32
|
-
/* DOS estados y no seis: pasó, o falló.
|
|
33
|
-
*
|
|
34
|
-
* El rojo de un fallo queda en la fila entera: es el único color, y tiene que
|
|
35
|
-
* verse sin señalar. El hover no lo reemplaza. Es una marca metida adentro,
|
|
36
|
-
* redondeada, para no pintar el riel. */
|
|
37
|
-
const ROW_SKIN = {
|
|
38
|
-
ok: "",
|
|
39
|
-
failed: "bg-bad/6",
|
|
40
|
-
};
|
|
41
|
-
const ROW_HOVER = {
|
|
42
|
-
ok: "group-hover/entry:bg-ink/4",
|
|
43
|
-
failed: "group-hover/entry:bg-bad/10",
|
|
44
|
-
};
|
|
45
|
-
const NODE_SKIN = {
|
|
46
|
-
ok: "bg-ink/8 text-muted",
|
|
47
|
-
failed: "bg-bad/12 text-bad",
|
|
48
|
-
};
|
|
49
|
-
function at(when) {
|
|
50
|
-
return when instanceof Date ? when.getTime() : new Date(when).getTime();
|
|
51
|
-
}
|
|
52
|
-
function MessageEntry({ message }) {
|
|
53
|
-
const copy = useUiCopy();
|
|
54
|
-
/* Mismo relleno que un evento: las dos clases de entrada viven en la misma
|
|
55
|
-
lista y con relleno distinto se leen como dos listas pegadas. Antes el
|
|
56
|
-
mensaje iba a ras del borde y el evento tenía caja, así que el nombre de
|
|
57
|
-
quien escribió quedaba más cerca del filo que el texto de al lado. */
|
|
58
|
-
return (_jsxs("li", { className: "flex min-w-0 gap-2.5 px-2.5 py-2", children: [_jsx(Avatar, { name: message.author, size: "sm" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5", children: [_jsx("span", { className: "text-meta font-medium text-ink", children: message.author }), _jsx(DateValue, { when: message.at, format: "relative" }), message.internal !== true ? null : (_jsxs("span", { className: "inline-flex items-center gap-1 text-micro text-muted", title: copy.chatter.internalNote, children: [_jsx(Lock, { className: "size-3", "aria-hidden": true }), copy.chatter.internal] }))] }), _jsx("p", { className: `mt-0.5 text-body whitespace-pre-line ${message.internal === true
|
|
59
|
-
? "rounded-control border border-warn/40 bg-warn/6 px-2 py-1 text-ink"
|
|
60
|
-
: "text-ink"}`, children: message.body })] })] }));
|
|
61
|
-
}
|
|
62
|
-
/** El cambio de estado, cuando lo hubo: de qué a qué.
|
|
63
|
-
*
|
|
64
|
-
* Es la línea más útil de una historia de gestión y la que peor se contaba: «el
|
|
65
|
-
* proyecto cambió de estado» obliga a buscar el evento anterior para saber de
|
|
66
|
-
* dónde venía. Dos fichas y una flecha se leen sin buscar nada.
|
|
67
|
-
*
|
|
68
|
-
* El ESTADO VIEJO va apagado y el nuevo en tinta: lo que importa es dónde
|
|
69
|
-
* quedó, y el de antes está para dar contexto, no para competir.
|
|
70
|
-
*
|
|
71
|
-
* La cápsula es la de `Tag`: pastilla, pelo, mismo relleno. Dos radios
|
|
72
|
-
* distintos para la misma idea de «una ficha» es una familia de más. */
|
|
73
|
-
const STATE_CHIP = "inline-flex max-w-32 min-w-0 items-center truncate rounded-pill border border-line-strong bg-ink/5 px-2 py-0.5 text-micro";
|
|
74
|
-
function Moved({ from, to }) {
|
|
75
|
-
return (_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1", children: [from === undefined ? null : (_jsxs(_Fragment, { children: [_jsx("span", { className: `${STATE_CHIP} text-muted`, children: from }), _jsx(ArrowRight, { className: "size-3 shrink-0 text-muted/60", strokeWidth: 2.5, "aria-hidden": true })] })), _jsx("span", { className: `${STATE_CHIP} font-medium text-ink`, children: to })] }));
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* UN EVENTO, como línea de una línea de tiempo.
|
|
79
|
-
*
|
|
80
|
-
* ── Por qué hay un riel y no sólo puntos ────────────────────────────
|
|
81
|
-
*
|
|
82
|
-
* El riel —la línea vertical que une los puntos— es lo que convierte una lista
|
|
83
|
-
* en una CRONOLOGÍA. Sin él son renglones sueltos que casualmente tienen fecha;
|
|
84
|
-
* con él se lee que una cosa pasó después de la otra, que es la pregunta que
|
|
85
|
-
* alguien trae cuando abre esto.
|
|
86
|
-
*
|
|
87
|
-
* ── El color es del punto y de nada más ─────────────────────────────
|
|
88
|
-
*
|
|
89
|
-
* El tono pinta el punto, y el texto se queda en tinta normal. Un renglón entero
|
|
90
|
-
* en rojo no se lee más urgente: se lee peor, y cuando hay tres seguidos deja de
|
|
91
|
-
* significar nada. El punto alcanza para encontrar los rojos de un vistazo, que
|
|
92
|
-
* es para lo que el color sirve en una lista larga.
|
|
93
|
-
*/
|
|
94
|
-
/** El error: la primera línea, y el resto a un clic.
|
|
95
|
-
*
|
|
96
|
-
* Un stack de cuarenta renglones adentro de una historia tapa la historia; el
|
|
97
|
-
* primer renglón casi siempre alcanza. Pero el resto tiene que estar —quien
|
|
98
|
-
* mira esta pantalla muchas veces no tiene acceso a los logs— así que se
|
|
99
|
-
* guarda, no se descarta.
|
|
100
|
-
*
|
|
101
|
-
* Sin color propio: el rojo ya lo puso el nodo. Esto es una cita textual de la
|
|
102
|
-
* máquina, así que se ve como lo que es — monoespaciada sobre el piso. */
|
|
103
|
-
function ErrorLine({ error }) {
|
|
104
|
-
const copy = useUiCopy();
|
|
105
|
-
const [open, setOpen] = useState(false);
|
|
106
|
-
const lineas = error.split("\n");
|
|
107
|
-
const primera = lineas[0] ?? "";
|
|
108
|
-
const hayMas = lineas.length > 1 || primera.length > 120;
|
|
109
|
-
return (_jsxs("div", { className: "min-w-0 rounded-control border border-line bg-ground px-2 py-1", children: [_jsx("p", { className: `min-w-0 font-mono text-micro text-ink ${open ? "break-words whitespace-pre-wrap" : "truncate"}`, children: open ? error : primera }), !hayMas ? null : (_jsx("button", { type: "button", onClick: () => setOpen(!open), className: "mt-0.5 cursor-pointer text-micro text-muted underline-offset-2 hover:text-ink hover:underline", children: open ? copy.chatter.less : copy.chatter.more }))] }));
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* UN EVENTO: una FILA que se aprieta, no un renglón.
|
|
113
|
-
*
|
|
114
|
-
* ── Qué se ve sin abrir ─────────────────────────────────────────────
|
|
115
|
-
*
|
|
116
|
-
* El rótulo, quién —si lo hubo— y cuándo. Nada más. Es lo que alguien lee para
|
|
117
|
-
* recorrer la historia; todo lo demás es para cuando ese renglón no alcanzó.
|
|
118
|
-
*
|
|
119
|
-
* ── Un solo ícono, y el contenedor dice si falló ────────────────────
|
|
120
|
-
*
|
|
121
|
-
* El rayo, porque eso es lo que pasó: algo se ejecutó. Había un ícono y un color
|
|
122
|
-
* por tono, y seis colores no señalan nada — una historia de veinte eventos
|
|
123
|
-
* quedaba como una bolsa de caramelos, y el que falló no saltaba más que los
|
|
124
|
-
* otros. El rojo sirve exactamente mientras sea el único.
|
|
125
|
-
*
|
|
126
|
-
* Lo que distingue un error es el TINTE DE LA FILA, translúcido. Y sirve dos
|
|
127
|
-
* veces: dice que falló, y dice que la fila es una zona que se puede apretar —
|
|
128
|
-
* que era lo que no se notaba.
|
|
129
|
-
*/
|
|
130
|
-
function EventEntry({ event }) {
|
|
131
|
-
const failed = event.tone === "bad" || event.error !== undefined;
|
|
132
|
-
const skin = failed ? "failed" : "ok";
|
|
133
|
-
const Icon = event.icon ?? EVENT_ICON;
|
|
134
|
-
/* Arranca CERRADO: lo de adentro es para cuando el renglón no alcanzó. */
|
|
135
|
-
const [open, setOpen] = useState(false);
|
|
136
|
-
const hayMas = event.moved !== undefined || event.detail !== undefined || event.error !== undefined;
|
|
137
|
-
const fila = (_jsxs(_Fragment, { children: [_jsx("span", { className: `relative z-10 flex size-6 shrink-0 items-center justify-center rounded-pill ring-2 ring-surface ${NODE_SKIN[skin]}`, children: _jsx(Icon, { className: "size-3", strokeWidth: 2.5, "aria-hidden": true }) }), _jsx("span", { className: "min-w-0 flex-1 truncate text-meta text-ink", children: event.label }), event.by === undefined ? null : (_jsx("span", { className: "hidden shrink-0 truncate text-micro text-muted @xs:inline", children: event.by })), _jsx("span", { className: "shrink-0", children: _jsx(DateValue, { when: event.at, format: "relative" }) }), hayMas ? (_jsx(ChevronRight, { className: `size-3.5 shrink-0 text-muted transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2.5, "aria-hidden": true })) : (
|
|
138
|
-
/* Hueco del mismo ancho: sin él, las filas que no se abren corren la
|
|
139
|
-
fecha y la columna deja de estar alineada. */
|
|
140
|
-
_jsx("span", { className: "size-3.5 shrink-0", "aria-hidden": true }))] }));
|
|
141
|
-
return (_jsxs("li", { className: "group/event relative @container", children: [_jsx("span", { "aria-hidden": true, className: "absolute top-8 left-5.5 w-px bg-line group-last/event:hidden" }), hayMas ? (_jsxs("button", { type: "button", onClick: () => setOpen(!open), "aria-expanded": open, className: `group/entry relative flex w-full min-w-0 cursor-pointer items-center rounded-box px-2.5 py-2 text-left focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-primary ${ROW_SKIN[skin]}`, children: [_jsx("span", { "aria-hidden": true, className: `pointer-events-none absolute inset-x-1 inset-y-0.5 rounded-control ${ROW_HOVER[skin]}` }), _jsx("span", { className: "relative z-10 flex min-w-0 flex-1 items-center gap-2.5", children: fila })] })) : (_jsx("div", { className: `flex w-full min-w-0 items-center gap-2.5 rounded-box px-2.5 py-2 ${failed ? "bg-bad/6" : ""}`, children: fila })), !open ? null : (
|
|
142
|
-
/* Lo de adentro, alineado bajo el rótulo y sobre el piso: es el DETALLE
|
|
143
|
-
de esa línea, no una línea más de la historia. */
|
|
144
|
-
_jsxs("div", { className: "mt-0.5 mb-1 ml-11 flex min-w-0 flex-col gap-1.5 rounded-box bg-ground px-2.5 py-2", children: [event.moved === undefined ? null : (_jsx(Moved, { ...(event.moved.from === undefined ? {} : { from: event.moved.from }), to: event.moved.to })), event.by === undefined ? null : (_jsx("span", { className: "text-micro text-muted @xs:hidden", children: event.by })), event.detail === undefined ? null : (_jsx("div", { className: "min-w-0 text-micro text-muted", children: event.detail })), event.error === undefined ? null : _jsx(ErrorLine, { error: event.error })] }))] }));
|
|
145
|
-
}
|
|
146
|
-
export function Chatter({ messages, events, composer, current, onCurrentChange, open, onToggle, label: ownLabel, layout = "page", embedded = false, }) {
|
|
147
|
-
const copy = useUiCopy();
|
|
148
|
-
const label = ownLabel ?? copy.chatter.history;
|
|
149
|
-
const [owned, setOwned] = useState("all");
|
|
150
|
-
const view = current ?? owned;
|
|
151
|
-
const setView = onCurrentChange ?? setOwned;
|
|
152
|
-
const hasMessages = messages !== undefined;
|
|
153
|
-
const hasEvents = events !== undefined;
|
|
154
|
-
/* Las tres pestañas sólo cuando hay DOS fuentes: con una sola, «todo» y esa
|
|
155
|
-
una son la misma lista, y tres solapas donde dos dicen lo mismo es un
|
|
156
|
-
control que no decide nada. */
|
|
157
|
-
const tabs = hasMessages && hasEvents ? ["all", "messages", "events"] : [];
|
|
158
|
-
const showing = tabs.length === 0 ? "all" : view;
|
|
159
|
-
const entries = [
|
|
160
|
-
...(showing === "events" ? [] : (messages ?? [])).map((message) => ({
|
|
161
|
-
kind: "message",
|
|
162
|
-
at: at(message.at),
|
|
163
|
-
message,
|
|
164
|
-
})),
|
|
165
|
-
...(showing === "messages" ? [] : (events ?? [])).map((event) => ({
|
|
166
|
-
kind: "event",
|
|
167
|
-
at: at(event.at),
|
|
168
|
-
event,
|
|
169
|
-
})),
|
|
170
|
-
].sort((a, b) => b.at - a.at);
|
|
171
|
-
const total = (messages?.length ?? 0) + (events?.length ?? 0);
|
|
172
|
-
/* Cortado acá y no por quien compone: es la misma lista en todos los
|
|
173
|
-
registros, y una historia que sólo hace scroll es una historia que se
|
|
174
|
-
abandona a la mitad. */
|
|
175
|
-
const pages = usePagination(entries, { pageSize: 15 });
|
|
176
|
-
const page = layout === "page";
|
|
177
|
-
/* Plegado. Embebido es la última fila de la hoja: el marco lo pone la hoja
|
|
178
|
-
y el hover es una marca redondeada adentro, para no pintar la línea que
|
|
179
|
-
la separa del papel. Suelta, o al costado, sí es una tarjeta. */
|
|
180
|
-
if (!open) {
|
|
181
|
-
const strip = page && embedded;
|
|
182
|
-
const face = (_jsxs(_Fragment, { children: [_jsx(MessageSquare, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), _jsx("span", { className: page
|
|
183
|
-
? "min-w-0 truncate text-meta text-muted"
|
|
184
|
-
: "min-w-0 truncate text-meta text-muted @4xl:hidden", children: label }), total === 0 ? null : _jsx(Tag, { tone: "neutral", children: total }), _jsx(PanelRightOpen, { className: page
|
|
185
|
-
? "ml-auto size-4 shrink-0 text-muted"
|
|
186
|
-
: "ml-auto size-4 shrink-0 text-muted @4xl:ml-0", "aria-hidden": true })] }));
|
|
187
|
-
return (_jsxs("button", { type: "button", onClick: onToggle, title: fillCopy(copy.chatter.open, { label }), className: page
|
|
188
|
-
? strip
|
|
189
|
-
? "group/strip relative flex w-full shrink-0 cursor-pointer items-center px-3 py-2.5"
|
|
190
|
-
: "flex w-full shrink-0 cursor-pointer items-center gap-2 rounded-box border border-line bg-surface px-3 py-2.5 hover:bg-ground"
|
|
191
|
-
: "flex w-full shrink-0 cursor-pointer items-center gap-2 self-start rounded-box border border-line bg-surface px-2.5 py-2 hover:bg-ground @4xl:w-auto @4xl:flex-col @4xl:px-1.5 @4xl:py-2.5", children: [strip ? (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-x-1 inset-y-0.5 rounded-control group-hover/strip:bg-ground" })) : null, strip ? (_jsx("span", { className: "relative z-10 flex min-w-0 flex-1 items-center gap-2", children: face })) : (face)] }));
|
|
192
|
-
}
|
|
193
|
-
return (_jsxs("aside", { className: page
|
|
194
|
-
? embedded
|
|
195
|
-
? "flex w-full flex-col"
|
|
196
|
-
: "flex w-full flex-col rounded-box border border-line bg-surface"
|
|
197
|
-
: "flex w-full shrink-0 flex-col self-start rounded-box border border-line bg-surface @4xl:w-(--container-chatter)", children: [_jsxs("header", { className: `flex min-w-0 items-end gap-1 border-b border-line bg-ground px-2 pt-2 ${page && embedded ? "" : "rounded-t-box"}`, children: [tabs.length === 0 ? (_jsxs("span", { className: "px-2 py-1.5 text-meta font-medium text-ink", children: [label, total === 0 ? null : (_jsx("span", { className: "ml-1.5 text-micro text-muted", children: total }))] })) : (_jsx(Tabs, { label: label, current: showing, onSelect: (id) => setView(id), tabs: tabs.map((tab) => ({
|
|
198
|
-
id: tab,
|
|
199
|
-
label: TAB_LABEL[tab](copy),
|
|
200
|
-
icon: TAB_ICON[tab],
|
|
201
|
-
count: tab === "messages"
|
|
202
|
-
? (messages?.length ?? 0)
|
|
203
|
-
: tab === "events"
|
|
204
|
-
? (events?.length ?? 0)
|
|
205
|
-
: total,
|
|
206
|
-
})) })), _jsx("button", { type: "button", onClick: onToggle, title: fillCopy(copy.chatter.fold, { label }), className: "mb-1.5 ml-auto inline-flex size-(--size-control-sm) shrink-0 cursor-pointer items-center justify-center rounded-pill text-muted transition-colors duration-(--duration-instant) hover:bg-ink/6 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: _jsx(PanelRightClose, { className: "size-(--size-icon-md)", "aria-hidden": true }) })] }), _jsxs("div", { className: page
|
|
207
|
-
? "flex w-full min-w-0 max-w-(--container-prose) flex-col gap-3 px-3.5 py-3"
|
|
208
|
-
: "flex min-w-0 flex-col gap-3 px-2.5 py-2.5", children: [composer === undefined || showing === "events" ? null : composer, entries.length === 0 ? (_jsx(Empty, { sentence: copy.chatter.empty })) : (_jsxs(_Fragment, { children: [_jsx("ol", { className: "flex flex-col", children: pages.pageItems.map((entry) => entry.kind === "message" ? (_jsx(MessageEntry, { message: entry.message }, `m:${entry.message.id}`)) : (_jsx(EventEntry, { event: entry.event }, `e:${entry.event.id}`))) }), entries.length <= 15 ? null : _jsx(Pagination, { ...pages.bridge })] }))] })] }));
|
|
209
|
-
}
|
|
210
|
-
/** Whether the chatter is open, when the page has no better answer.
|
|
211
|
-
*
|
|
212
|
-
* **Starts folded.** It began the other way round —a history nobody opens is a
|
|
213
|
-
* history nobody reads— and seeing it took the argument apart: open, it holds a
|
|
214
|
-
* 22rem column of context on every record you open, before anybody asked for
|
|
215
|
-
* any. Folded it still says how many there are, which is the half that has to
|
|
216
|
-
* be free; reading them is a click, and a click you chose.
|
|
217
|
-
*
|
|
218
|
-
* It is the same answer for both layouts. A default that depends on the width
|
|
219
|
-
* would mean the same record shows a different screen depending on how many
|
|
220
|
-
* panels happen to be open next to it, and that is not a default, it is a
|
|
221
|
-
* surprise. */
|
|
222
|
-
export function useChatter(initial = false) {
|
|
223
|
-
const [open, setOpen] = useState(initial);
|
|
224
|
-
return {
|
|
225
|
-
open,
|
|
226
|
-
toggle: () => setOpen((current) => !current),
|
|
227
|
-
/** Open it without closing it if it already was. What a button from outside
|
|
228
|
-
* needs —«events 12»— when pressing it twice must not hide the very thing
|
|
229
|
-
* it promised to show. */
|
|
230
|
-
show: () => setOpen(true),
|
|
231
|
-
};
|
|
232
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export { Chatter, type ChatterEvent, type ChatterLayout, type ChatterProps, type ChatterView, useChatter, } from "./Chatter.js";
|
|
2
|
-
export { type Message, MessageComposer, type MessageComposerProps, type MessageKind, writeANote, } from "./Messages.js";
|
|
3
|
-
export { Timeline, type TimelineItemProps, type TimelineProps, } from "./Timeline.js";
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { type PaletteCommand } from "./commands.js";
|
|
2
|
-
/**
|
|
3
|
-
* The search box and the list, without the modal around them.
|
|
4
|
-
*
|
|
5
|
-
* Context: split out so the filtering can be rendered on its own — in a test, with a query
|
|
6
|
-
* already typed — since the modal is a portal that only exists in a browser.
|
|
7
|
-
*/
|
|
8
|
-
export declare function CommandSearch({ commands, onRun, query, onQueryChange, placeholder, }: {
|
|
9
|
-
commands: readonly PaletteCommand[];
|
|
10
|
-
onRun: (id: string) => void;
|
|
11
|
-
query?: string;
|
|
12
|
-
onQueryChange?: (next: string) => void;
|
|
13
|
-
placeholder?: string;
|
|
14
|
-
}): import("react").JSX.Element;
|
|
15
|
-
/**
|
|
16
|
-
* A modal search over everything the app can do — go to a screen, create a record — opened
|
|
17
|
-
* with ⌘K (Ctrl+K) and run with Enter.
|
|
18
|
-
*
|
|
19
|
-
* Context: it is controlled (`open`, `onOpenChange`) and does not bind the shortcut itself,
|
|
20
|
-
* because an app has ONE palette and many screens: the key is bound once, where the palette
|
|
21
|
-
* lives, with `useCommandPaletteShortcut`. Running a command closes it — the palette is a
|
|
22
|
-
* path, not a place to stay.
|
|
23
|
-
*/
|
|
24
|
-
export declare function CommandPalette({ open, onOpenChange, commands, onRun, placeholder, }: {
|
|
25
|
-
open: boolean;
|
|
26
|
-
onOpenChange: (open: boolean) => void;
|
|
27
|
-
commands: readonly PaletteCommand[];
|
|
28
|
-
/** A command was chosen. The palette closes on its own. */
|
|
29
|
-
onRun: (id: string) => void;
|
|
30
|
-
/** The search box's prompt and its accessible name. By default, the copy table's. */
|
|
31
|
-
placeholder?: string;
|
|
32
|
-
}): import("react").JSX.Element;
|
|
33
|
-
/**
|
|
34
|
-
* Binds ⌘K (Ctrl+K on Windows and Linux) to open the palette, anywhere in the page.
|
|
35
|
-
*
|
|
36
|
-
* Context: both modifiers are accepted on every platform. Telling them apart by the user
|
|
37
|
-
* agent guesses wrong on a Mac with a PC keyboard, and the cost of accepting both is that
|
|
38
|
-
* Ctrl+K on a Mac — «delete to the end of the line» in a text box — opens the palette.
|
|
39
|
-
*/
|
|
40
|
-
export declare function useCommandPaletteShortcut(setOpen: (open: boolean) => void): void;
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { commandMatches, groupCommands, } from "./commands.js";
|
|
4
|
-
import { AriaLocale } from "../../foundations/aria.js";
|
|
5
|
-
import { useUiCopy } from "../../foundations/copy.js";
|
|
6
|
-
import { tv } from "../../foundations/variants.js";
|
|
7
|
-
import { Search, X } from "lucide-react";
|
|
8
|
-
import { useEffect, useMemo } from "react";
|
|
9
|
-
import { Autocomplete, Button as AriaButton, Dialog, Header, Input, Keyboard, Menu, MenuItem, MenuSection, Modal, ModalOverlay, SearchField, useFilter, } from "react-aria-components";
|
|
10
|
-
/* ==================================================================== *
|
|
11
|
-
* COMMAND PALETTE — ⌘K, type three letters, Enter.
|
|
12
|
-
*
|
|
13
|
-
* An ERP has a hundred screens and twenty «new» buttons, and the people who
|
|
14
|
-
* use it all day stop wanting to look for any of them: they know the name of
|
|
15
|
-
* what they want. The palette is the shortest path from knowing the name to
|
|
16
|
-
* being there — one key to open, a few letters, Enter — without leaving the
|
|
17
|
-
* keyboard and without knowing in which menu of which module it lives.
|
|
18
|
-
*
|
|
19
|
-
* ┌──────────────────────────────────────────────┐
|
|
20
|
-
* │ 🔍 fact ✕ │
|
|
21
|
-
* ├──────────────────────────────────────────────┤
|
|
22
|
-
* │ IR A… │
|
|
23
|
-
* │ ▌🧾 Facturas │ ← Enter runs it
|
|
24
|
-
* │ CREAR… │
|
|
25
|
-
* │ + Nueva factura ⌘N │
|
|
26
|
-
* └──────────────────────────────────────────────┘
|
|
27
|
-
*
|
|
28
|
-
* React Aria's `Autocomplete` drives it: focus stays in the search box while
|
|
29
|
-
* the arrows walk the list (virtual focus), the first match is highlighted as
|
|
30
|
-
* you type so Enter always does something, and the modal traps focus, closes
|
|
31
|
-
* on Escape and on a click outside, and gives the focus back.
|
|
32
|
-
*
|
|
33
|
-
* The match is the locale's (`useFilter` inside `AriaLocale`): «facturacion»
|
|
34
|
-
* finds «Facturación» because nobody types the accent when they are in a hurry.
|
|
35
|
-
* ==================================================================== */
|
|
36
|
-
const overlay = tv({
|
|
37
|
-
base: "fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-scrim/40 px-4 pt-24 transition-opacity duration-(--duration-quick) ease-(--ease-out)",
|
|
38
|
-
variants: {
|
|
39
|
-
isEntering: { true: "opacity-0" },
|
|
40
|
-
isExiting: { true: "opacity-0" },
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
const surface = tv({
|
|
44
|
-
base: "w-full max-w-xl overflow-hidden rounded-panel border border-line-strong bg-surface text-ink shadow-window outline-0 transition-[opacity,translate] duration-(--duration-quick) ease-(--ease-out) forced-colors:bg-[Canvas]",
|
|
45
|
-
variants: {
|
|
46
|
-
isEntering: { true: "-translate-y-1 opacity-0" },
|
|
47
|
-
isExiting: { true: "opacity-0" },
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
const item = tv({
|
|
51
|
-
base: "flex cursor-default items-center gap-2 rounded-control px-2 py-1.5 text-meta text-ink outline-0 select-none",
|
|
52
|
-
variants: {
|
|
53
|
-
isFocused: {
|
|
54
|
-
true: "bg-primary/8 forced-colors:bg-[Highlight] forced-colors:text-[HighlightText]",
|
|
55
|
-
},
|
|
56
|
-
isPressed: { true: "bg-primary/12" },
|
|
57
|
-
isDisabled: { true: "text-muted forced-colors:text-[GrayText]" },
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
/**
|
|
61
|
-
* The search box and the list, without the modal around them.
|
|
62
|
-
*
|
|
63
|
-
* Context: split out so the filtering can be rendered on its own — in a test, with a query
|
|
64
|
-
* already typed — since the modal is a portal that only exists in a browser.
|
|
65
|
-
*/
|
|
66
|
-
export function CommandSearch({ commands, onRun, query, onQueryChange, placeholder, }) {
|
|
67
|
-
const copy = useUiCopy();
|
|
68
|
-
const { contains } = useFilter({ sensitivity: "base" });
|
|
69
|
-
const byId = useMemo(() => new Map(commands.map((c) => [c.id, c])), [commands]);
|
|
70
|
-
const groups = useMemo(() => groupCommands(commands), [commands]);
|
|
71
|
-
const hint = placeholder ?? copy.search.placeholder;
|
|
72
|
-
return (_jsxs(Autocomplete, { ...(query === undefined ? {} : { inputValue: query }), ...(onQueryChange === undefined ? {} : { onInputChange: onQueryChange }), filter: (_text, input, node) => {
|
|
73
|
-
const command = byId.get(String(node.key));
|
|
74
|
-
return command === undefined || commandMatches(command, input, contains);
|
|
75
|
-
}, children: [_jsxs(SearchField, { "aria-label": hint, autoFocus: true, className: "group flex items-center gap-2 border-b border-line px-3", children: [_jsx(Search, { className: "size-(--size-icon-md) shrink-0 text-muted", strokeWidth: 2, "aria-hidden": true }), _jsx(Input, { placeholder: hint, className: "h-(--size-control-lg) min-w-0 flex-1 bg-transparent text-body text-ink outline-0 placeholder:text-muted [&::-webkit-search-cancel-button]:hidden" }), _jsx(AriaButton, { className: "inline-flex shrink-0 items-center justify-center rounded-mark p-1 text-muted transition-colors duration-(--duration-instant) group-empty:invisible hover:bg-ink/6 hover:text-ink pressed:bg-ink/10", children: _jsx(X, { className: "size-(--size-icon-md)", strokeWidth: 2, "aria-hidden": true }) })] }), _jsx(Menu, { "aria-label": hint, onAction: (key) => onRun(String(key)), renderEmptyState: () => (_jsx("p", { className: "px-3 py-6 text-center text-meta text-muted italic", children: copy.menu.empty })), className: "max-h-96 overflow-y-auto p-1 outline-0", children: groups.map((group) => (_jsxs(MenuSection, { id: group.section ?? "-", className: "not-first:mt-1", children: [group.section === undefined ? null : (_jsx(Header, { className: "px-2 pt-2 pb-1 text-micro font-medium text-muted", children: group.section })), group.commands.map((command) => {
|
|
76
|
-
const Icon = command.icon;
|
|
77
|
-
return (_jsxs(MenuItem, { id: command.id, textValue: command.label, className: item, children: [_jsx("span", { className: "flex size-(--size-icon-md) shrink-0 items-center justify-center text-muted", children: Icon === undefined ? null : (_jsx(Icon, { className: "size-(--size-icon-md)", strokeWidth: 2, "aria-hidden": true })) }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: command.label }), command.shortcut === undefined ? null : (_jsx(Keyboard, { className: "shrink-0 rounded-mark border border-line px-1.5 font-sans text-micro text-muted tabular-nums", children: command.shortcut }))] }, command.id));
|
|
78
|
-
})] }, group.section ?? ""))) })] }));
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* A modal search over everything the app can do — go to a screen, create a record — opened
|
|
82
|
-
* with ⌘K (Ctrl+K) and run with Enter.
|
|
83
|
-
*
|
|
84
|
-
* Context: it is controlled (`open`, `onOpenChange`) and does not bind the shortcut itself,
|
|
85
|
-
* because an app has ONE palette and many screens: the key is bound once, where the palette
|
|
86
|
-
* lives, with `useCommandPaletteShortcut`. Running a command closes it — the palette is a
|
|
87
|
-
* path, not a place to stay.
|
|
88
|
-
*/
|
|
89
|
-
export function CommandPalette({ open, onOpenChange, commands, onRun, placeholder, }) {
|
|
90
|
-
const copy = useUiCopy();
|
|
91
|
-
return (_jsx(AriaLocale, { children: _jsx(ModalOverlay, { isOpen: open, onOpenChange: onOpenChange, isDismissable: true, className: overlay, children: _jsx(Modal, { className: surface, children: _jsx(Dialog, { "aria-label": placeholder ?? copy.search.placeholder, className: "outline-0", children: _jsx(CommandSearch, { commands: commands, onRun: (id) => {
|
|
92
|
-
onOpenChange(false);
|
|
93
|
-
onRun(id);
|
|
94
|
-
}, ...(placeholder === undefined ? {} : { placeholder }) }) }) }) }) }));
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Binds ⌘K (Ctrl+K on Windows and Linux) to open the palette, anywhere in the page.
|
|
98
|
-
*
|
|
99
|
-
* Context: both modifiers are accepted on every platform. Telling them apart by the user
|
|
100
|
-
* agent guesses wrong on a Mac with a PC keyboard, and the cost of accepting both is that
|
|
101
|
-
* Ctrl+K on a Mac — «delete to the end of the line» in a text box — opens the palette.
|
|
102
|
-
*/
|
|
103
|
-
export function useCommandPaletteShortcut(setOpen) {
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
const onKey = (e) => {
|
|
106
|
-
if (e.key.toLowerCase() !== "k" || !(e.metaKey || e.ctrlKey) || e.altKey)
|
|
107
|
-
return;
|
|
108
|
-
e.preventDefault();
|
|
109
|
-
setOpen(true);
|
|
110
|
-
};
|
|
111
|
-
document.addEventListener("keydown", onKey);
|
|
112
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
113
|
-
}, [setOpen]);
|
|
114
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { LucideIcon } from "lucide-react";
|
|
2
|
-
/** Something the palette can run: go to a screen, create a record, open a report. */
|
|
3
|
-
export interface PaletteCommand {
|
|
4
|
-
id: string;
|
|
5
|
-
label: string;
|
|
6
|
-
/** The heading it is listed under: «Ir a…», «Crear…». Commands without one go first,
|
|
7
|
-
* without a heading. */
|
|
8
|
-
section?: string;
|
|
9
|
-
icon?: LucideIcon;
|
|
10
|
-
/** The key that runs it without the palette, as it should be read: «⌘N», «Alt+F». It is
|
|
11
|
-
* only drawn — the palette is where the shortcut gets learned, not where it is bound. */
|
|
12
|
-
shortcut?: string;
|
|
13
|
-
/** Other words that should find it: «factura» finds «Nueva venta» when the people who use
|
|
14
|
-
* it call it that. */
|
|
15
|
-
keywords?: readonly string[];
|
|
16
|
-
}
|
|
17
|
-
/** The commands under each heading, headings in the order they first appear. */
|
|
18
|
-
export declare function groupCommands(commands: readonly PaletteCommand[]): {
|
|
19
|
-
section: string | undefined;
|
|
20
|
-
commands: PaletteCommand[];
|
|
21
|
-
}[];
|
|
22
|
-
/**
|
|
23
|
-
* Whether a command answers a query. `contains` is the locale's comparison — React Aria's
|
|
24
|
-
* `useFilter` in the palette — so «facturacion» finds «Facturación» and an uppercase query
|
|
25
|
-
* finds a lowercase label.
|
|
26
|
-
*
|
|
27
|
-
* Every word of the query has to appear, in any order, in the label or the keywords:
|
|
28
|
-
* «nueva fac» finds «Nueva factura», and so does «factura nueva».
|
|
29
|
-
*/
|
|
30
|
-
export declare function commandMatches(command: PaletteCommand, query: string, contains: (text: string, part: string) => boolean): boolean;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/** The commands under each heading, headings in the order they first appear. */
|
|
2
|
-
export function groupCommands(commands) {
|
|
3
|
-
const groups = new Map();
|
|
4
|
-
/* The commands with no heading are listed first, wherever they were written. */
|
|
5
|
-
if (commands.some((c) => c.section === undefined))
|
|
6
|
-
groups.set(undefined, []);
|
|
7
|
-
for (const command of commands) {
|
|
8
|
-
const group = groups.get(command.section);
|
|
9
|
-
if (group === undefined)
|
|
10
|
-
groups.set(command.section, [command]);
|
|
11
|
-
else
|
|
12
|
-
group.push(command);
|
|
13
|
-
}
|
|
14
|
-
return [...groups].map(([section, list]) => ({ section, commands: list }));
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Whether a command answers a query. `contains` is the locale's comparison — React Aria's
|
|
18
|
-
* `useFilter` in the palette — so «facturacion» finds «Facturación» and an uppercase query
|
|
19
|
-
* finds a lowercase label.
|
|
20
|
-
*
|
|
21
|
-
* Every word of the query has to appear, in any order, in the label or the keywords:
|
|
22
|
-
* «nueva fac» finds «Nueva factura», and so does «factura nueva».
|
|
23
|
-
*/
|
|
24
|
-
export function commandMatches(command, query, contains) {
|
|
25
|
-
const words = query.trim().split(/\s+/).filter(Boolean);
|
|
26
|
-
if (words.length === 0)
|
|
27
|
-
return true;
|
|
28
|
-
const haystack = [command.label, ...(command.keywords ?? [])].join(" ");
|
|
29
|
-
return words.every((word) => contains(haystack, word));
|
|
30
|
-
}
|
|
File without changes
|
|
File without changes
|