@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
package/dist/tokens.css
CHANGED
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
@import "@fontsource/ibm-plex-mono/latin-ext-400.css";
|
|
12
12
|
@import "@fontsource/ibm-plex-mono/latin-500.css";
|
|
13
13
|
@import "@fontsource/ibm-plex-mono/latin-ext-500.css";
|
|
14
|
+
/* The record calendar (`RecordCalendar`, FullCalendar v7): its structure and the `forma`
|
|
15
|
+
theme. The palette that theme reads is defined below, from web-ui's tokens. */
|
|
16
|
+
@import "@fullcalendar/react/skeleton.css";
|
|
17
|
+
@import "@fullcalendar/react/themes/forma/theme.css";
|
|
18
|
+
/* The record Gantt (`RecordGantt`, SVAR): its styles, Willow theme included. Its variables
|
|
19
|
+
are pointed at web-ui's tokens below. */
|
|
20
|
+
@import "@svar-ui/react-gantt/all.css";
|
|
14
21
|
|
|
15
22
|
@import "tailwindcss";
|
|
16
23
|
|
|
@@ -491,6 +498,20 @@
|
|
|
491
498
|
earlier. */
|
|
492
499
|
--container-name: 12rem;
|
|
493
500
|
|
|
501
|
+
/* The widest a chip of the search bar gets before it truncates. A filter written by hand
|
|
502
|
+
(«cliente contiene Importadora y Exportadora…») would otherwise take a whole line of the
|
|
503
|
+
bar and push the rest down; the full text stays in the chip's title. */
|
|
504
|
+
/* One stage of a board by stages: a card with its name, a tag and a figure, without
|
|
505
|
+
wrapping the name into three lines. Five stages fit a desk; more scroll sideways. */
|
|
506
|
+
--container-lane: 18rem;
|
|
507
|
+
/* The names column of a timeline (an employee with an avatar, a room, a machine): wide enough
|
|
508
|
+
for «Ammy Shalem Aranibar Zambrana» before it truncates. */
|
|
509
|
+
--container-timeline-label: 15rem;
|
|
510
|
+
--container-facet: 16rem;
|
|
511
|
+
/* The grouping chain is several levels in one chip, so it asks for more before it
|
|
512
|
+
truncates — at its end, as one sentence, never level by level. */
|
|
513
|
+
--container-chain: 28rem;
|
|
514
|
+
|
|
494
515
|
/* The minimum width of a metric. It belongs to the system and not to the
|
|
495
516
|
component: three metrics of different widths read as three different
|
|
496
517
|
things, and what they have to let you compare is precisely the number. */
|
|
@@ -501,13 +522,25 @@
|
|
|
501
522
|
—which has to stay clickable— reduced to a useless strip. */
|
|
502
523
|
--container-peek: 30rem;
|
|
503
524
|
|
|
504
|
-
/*
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
is
|
|
510
|
-
|
|
525
|
+
/* THE ACTIVITY COLUMN — at the RIGHT of a record's sheet, joined to it (`Activity`,
|
|
526
|
+
`RecordHeader` THE PAGE). It holds what is true now (the record's
|
|
527
|
+
notices and its fields' and rows' notes, compact) over what happened (the timeline:
|
|
528
|
+
messages, notes and events by time) — both always in sight.
|
|
529
|
+
|
|
530
|
+
`--container-activity` is the column: 22rem holds a notice with its action and a
|
|
531
|
+
message bubble of readable measure.
|
|
532
|
+
|
|
533
|
+
`--container-beside` is how wide the record's PAGE has to be for the column to stand
|
|
534
|
+
beside the sheet: the column and a sheet that keeps 54rem — two groups of fields side by
|
|
535
|
+
side still fit their labels. Narrower (a laptop with a sidebar, a phone, a modal), the
|
|
536
|
+
panel goes BELOW the sheet as a card. Written as a number and not a `calc()` because a container query cannot
|
|
537
|
+
read a variable.
|
|
538
|
+
|
|
539
|
+
`--container-record` is the pair at its widest — the sheet's reading width and the
|
|
540
|
+
column — so on a big screen they are placed together, centred. At 1680 px: 72 + 22. */
|
|
541
|
+
--container-activity: 22rem;
|
|
542
|
+
--container-beside: 76rem;
|
|
543
|
+
--container-record: calc(var(--container-sheet) + var(--container-activity));
|
|
511
544
|
|
|
512
545
|
/* Below this a surface is held in one hand: a phone, or a panel just as narrow. 40rem is
|
|
513
546
|
the width the list already stops offering a table at (`ListView`), and it is the same
|
|
@@ -523,6 +556,12 @@
|
|
|
523
556
|
covering the bottom of the screen, which is exactly where the sheet's actions are. */
|
|
524
557
|
--size-sheet: 90dvh;
|
|
525
558
|
|
|
559
|
+
/* How tall a floating list grows before it scrolls inside: about eight two-line rows,
|
|
560
|
+
the height a capped `Picker` already has, so a list that loads page after page from the
|
|
561
|
+
backend opens at the same size as one that shows the first eight. Taller, and a list of
|
|
562
|
+
two thousand customers fills the window and covers the form it is choosing for. */
|
|
563
|
+
--size-dropdown: 20rem;
|
|
564
|
+
|
|
526
565
|
/* The width of a module's sidebar. 15rem holds an icon, a screen name of two
|
|
527
566
|
words —«Notas de crédito», «Órdenes de compra»— a count and one step of
|
|
528
567
|
nesting without truncating, and still leaves a 1280-wide window more than
|
|
@@ -583,6 +622,25 @@
|
|
|
583
622
|
--duration-panel: 240ms;
|
|
584
623
|
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
585
624
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
625
|
+
|
|
626
|
+
/* The one loop of the system: the bar of a task under way that cannot say how far
|
|
627
|
+
it is (`ProgressBar` without a value). It is not a move between two states — it
|
|
628
|
+
has no end until the task ends — so the 250ms ceiling above does not apply; at
|
|
629
|
+
1.6s a bar that stays a minute on screen moves slowly enough not to flicker in
|
|
630
|
+
the corner of the eye. The bar uses it behind `motion-safe:`: with reduced
|
|
631
|
+
motion it stands still. */
|
|
632
|
+
--animate-progress-sweep: progress-sweep 1.6s var(--ease-in-out) infinite;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/* The segment is two fifths of its track: from just off the left edge (-100 % of
|
|
636
|
+
itself) to just off the right one (250 % of itself = the track's width). */
|
|
637
|
+
@keyframes progress-sweep {
|
|
638
|
+
from {
|
|
639
|
+
translate: -100%;
|
|
640
|
+
}
|
|
641
|
+
to {
|
|
642
|
+
translate: 250%;
|
|
643
|
+
}
|
|
586
644
|
}
|
|
587
645
|
|
|
588
646
|
/* ==================================================================== *
|
|
@@ -729,6 +787,113 @@
|
|
|
729
787
|
0 2px 6px -2px rgba(26, 23, 20, 0.18), 0 18px 40px -20px rgba(26, 23, 20, 0.42);
|
|
730
788
|
}
|
|
731
789
|
|
|
790
|
+
/* ====================================================================
|
|
791
|
+
* THE WORKSPACE (@sincpro/web-composer) dressed in these tokens.
|
|
792
|
+
*
|
|
793
|
+
* Context: the composer depends on no design system and paints its frames, tabs, windows
|
|
794
|
+
* and modals with `--sc-*` variables. Pointing them at web-ui's tokens here means a composer
|
|
795
|
+
* on a web-ui page takes its theme — light, dark, and any brand — with nothing to wire: no
|
|
796
|
+
* import, no dependency, only names. They are aliases, so they follow the dark theme on
|
|
797
|
+
* their own and carry no contrast of their own to measure.
|
|
798
|
+
* ==================================================================== */
|
|
799
|
+
:root {
|
|
800
|
+
--sc-ground: var(--color-ground);
|
|
801
|
+
--sc-surface: var(--color-surface);
|
|
802
|
+
--sc-line: var(--color-line);
|
|
803
|
+
--sc-line-strong: var(--color-line-strong);
|
|
804
|
+
--sc-ink: var(--color-ink);
|
|
805
|
+
--sc-muted: var(--color-muted);
|
|
806
|
+
/* The workspace's accent marks where you are (the active tab, a focused frame): the navy
|
|
807
|
+
of a link, not the gold of the one primary action. */
|
|
808
|
+
--sc-accent: var(--color-primary);
|
|
809
|
+
--sc-danger: var(--color-danger);
|
|
810
|
+
--sc-on-danger: var(--color-surface);
|
|
811
|
+
--sc-scrim: var(--color-scrim);
|
|
812
|
+
--sc-modal-scrim: color-mix(in srgb, var(--color-scrim) 40%, transparent);
|
|
813
|
+
--sc-shadow: var(--shadow-window);
|
|
814
|
+
--sc-radius: var(--radius-panel);
|
|
815
|
+
--sc-radius-control: var(--radius-control);
|
|
816
|
+
--sc-text: var(--text-meta);
|
|
817
|
+
--sc-text-small: var(--text-micro);
|
|
818
|
+
--sc-icon: var(--size-icon-md);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/* ====================================================================
|
|
822
|
+
* THE RECORD CALENDAR (FullCalendar v7, `forma` theme) in these tokens.
|
|
823
|
+
*
|
|
824
|
+
* Context: the theme reads a palette of `--fc-forma-*` variables. Pointed at web-ui's tokens it
|
|
825
|
+
* takes the theme — light, dark, a brand — with no dark block of its own: the tokens already
|
|
826
|
+
* flip. Aliases, so no contrast of their own to measure; the translucent washes are mixed from
|
|
827
|
+
* the ink so they read on either theme.
|
|
828
|
+
* ==================================================================== */
|
|
829
|
+
:root {
|
|
830
|
+
--fc-forma-primary: var(--color-primary);
|
|
831
|
+
--fc-forma-primary-over: var(--color-primary-hover);
|
|
832
|
+
--fc-forma-primary-down: var(--color-primary-hover);
|
|
833
|
+
--fc-forma-primary-foreground: var(--color-surface);
|
|
834
|
+
--fc-forma-event: var(--color-primary);
|
|
835
|
+
--fc-forma-event-contrast: var(--color-surface);
|
|
836
|
+
--fc-forma-background-event: var(--color-primary);
|
|
837
|
+
--fc-forma-highlight: color-mix(in srgb, var(--color-primary) 18%, transparent);
|
|
838
|
+
--fc-forma-background: var(--color-surface);
|
|
839
|
+
--fc-forma-faint: color-mix(in srgb, var(--color-ink) 3%, transparent);
|
|
840
|
+
--fc-forma-muted: color-mix(in srgb, var(--color-ink) 7%, transparent);
|
|
841
|
+
--fc-forma-strong: color-mix(in srgb, var(--color-ink) 12%, transparent);
|
|
842
|
+
--fc-forma-stronger: color-mix(in srgb, var(--color-ink) 16%, transparent);
|
|
843
|
+
--fc-forma-strongest: color-mix(in srgb, var(--color-ink) 20%, transparent);
|
|
844
|
+
--fc-forma-foreground: var(--color-ink);
|
|
845
|
+
--fc-forma-faint-foreground: var(--color-muted);
|
|
846
|
+
--fc-forma-muted-foreground: var(--color-muted);
|
|
847
|
+
--fc-forma-border: var(--color-line);
|
|
848
|
+
--fc-forma-muted-border: var(--color-line);
|
|
849
|
+
--fc-forma-strong-border: var(--color-line-strong);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/* ====================================================================
|
|
853
|
+
* THE RECORD GANTT (SVAR, Willow theme) in these tokens.
|
|
854
|
+
*
|
|
855
|
+
* Context: Willow declares its `--wx-*` on `.wx-willow-theme`; redeclared on the same class
|
|
856
|
+
* here, later, they win. Bars are the navy of the system, a summary the ink, a milestone the
|
|
857
|
+
* info tone; the type is the system's. Aliases: they flip with the theme.
|
|
858
|
+
* ==================================================================== */
|
|
859
|
+
.wx-willow-theme {
|
|
860
|
+
--wx-color-primary: var(--color-primary);
|
|
861
|
+
--wx-color-primary-selected: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
862
|
+
--wx-color-primary-font: var(--color-surface);
|
|
863
|
+
--wx-color-secondary-hover: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
864
|
+
--wx-color-success: var(--color-good);
|
|
865
|
+
--wx-color-warning: var(--color-warn);
|
|
866
|
+
--wx-color-info: var(--color-info);
|
|
867
|
+
--wx-color-danger: var(--color-danger);
|
|
868
|
+
--wx-color-font: var(--color-ink);
|
|
869
|
+
--wx-color-font-alt: var(--color-muted);
|
|
870
|
+
--wx-background: var(--color-surface);
|
|
871
|
+
--wx-background-alt: var(--color-ground);
|
|
872
|
+
--wx-background-hover: color-mix(in srgb, var(--color-ink) 5%, transparent);
|
|
873
|
+
--wx-font-family: inherit;
|
|
874
|
+
--wx-font-size: var(--text-meta);
|
|
875
|
+
--wx-font-size-sm: var(--text-micro);
|
|
876
|
+
--wx-icon-color: var(--color-muted);
|
|
877
|
+
--wx-border-color: var(--color-line);
|
|
878
|
+
--wx-border-radius: var(--radius-control);
|
|
879
|
+
--wx-radius-major: var(--radius-box);
|
|
880
|
+
--wx-gantt-border-color: var(--color-line);
|
|
881
|
+
--wx-gantt-task-color: var(--color-primary);
|
|
882
|
+
--wx-gantt-task-fill-color: var(--color-primary-hover);
|
|
883
|
+
--wx-gantt-task-border-color: var(--color-primary-hover);
|
|
884
|
+
--wx-gantt-task-font-color: var(--color-surface);
|
|
885
|
+
--wx-gantt-summary-color: var(--color-line-strong);
|
|
886
|
+
--wx-gantt-summary-fill-color: color-mix(in srgb, var(--color-ink) 45%, transparent);
|
|
887
|
+
--wx-gantt-summary-border-color: var(--color-line-strong);
|
|
888
|
+
--wx-gantt-summary-font-color: var(--color-ink);
|
|
889
|
+
--wx-gantt-milestone-color: var(--color-info);
|
|
890
|
+
--wx-gantt-select-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
891
|
+
--wx-gantt-link-color: var(--color-muted);
|
|
892
|
+
--wx-gantt-link-color-hovered: var(--color-ink);
|
|
893
|
+
--wx-gantt-holiday-background: var(--color-ground);
|
|
894
|
+
--wx-gantt-holiday-color: var(--color-muted);
|
|
895
|
+
}
|
|
896
|
+
|
|
732
897
|
@layer base {
|
|
733
898
|
/* No forced height anywhere: the document is exactly as tall as its content.
|
|
734
899
|
* The color still has to reach the bottom of the viewport on a short page —
|
|
@@ -1023,3 +1188,10 @@
|
|
|
1023
1188
|
--size-control-lg: 48px;
|
|
1024
1189
|
}
|
|
1025
1190
|
}
|
|
1191
|
+
|
|
1192
|
+
/* A label written down a narrow strip: the name of a folded board column, read bottom-up
|
|
1193
|
+
like a book's spine. Tailwind has no writing-mode utility. */
|
|
1194
|
+
@utility text-vertical {
|
|
1195
|
+
writing-mode: vertical-rl;
|
|
1196
|
+
transform: rotate(180deg);
|
|
1197
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { BoardStages } from "../../components/listing/BoardColumns.js";
|
|
1
2
|
import type { ListIdentity } from "../../components/listing/ListingBar.js";
|
|
2
3
|
import type { SortField } from "../../components/listing/SortMenu.js";
|
|
3
4
|
import type { Surface } from "../../foundations/types.js";
|
|
4
|
-
import type { DataColumn } from "../../primitives/table/DataTable.js";
|
|
5
|
+
import type { DataColumn, DataTableCreate } from "../../primitives/table/DataTable.js";
|
|
5
6
|
import type { RowAction } from "../../primitives/table/RowActions.js";
|
|
6
7
|
import type { SortState } from "../../primitives/table/Table.js";
|
|
7
|
-
import { type ListCrumb, type ListEmpty, type ListGroup, type ListPivot, type ListPresent, type ListReport, type ListSheet, type RowOf } from "./types.js";
|
|
8
|
+
import { type ListCalendar, type ListCrumb, type ListEmpty, type ListGantt, type ListGroup, type ListPivot, type ListPresent, type ListReport, type ListSheet, type ListTimeline, type RowOf } from "./types.js";
|
|
8
9
|
import type { ReactNode } from "react";
|
|
9
10
|
type BarProps = {
|
|
10
11
|
children?: ReactNode;
|
|
@@ -41,12 +42,17 @@ type TableProps<T extends object> = {
|
|
|
41
42
|
/** Lo que se le hace a UNA fila sin entrar. Se dibujan escondidas y aparecen
|
|
42
43
|
* sobre la fila que se está apuntando. */
|
|
43
44
|
rowActions?: (item: T) => readonly RowAction[];
|
|
45
|
+
/** Escribir un registro nuevo como una fila más de la tabla (la lista editable de
|
|
46
|
+
* Odoo). Ver la cabecera de `DataTable`. */
|
|
47
|
+
create?: DataTableCreate<T>;
|
|
44
48
|
};
|
|
45
49
|
/** El tablero. Sin `card`, la tarjeta sale de lo que ya dice `List.Rows`: el
|
|
46
50
|
* nombre, el `meta` y las fichas. Escribirla igual es declarar que ESTA lista
|
|
47
51
|
* se puede ver como tablero. */
|
|
48
52
|
type BoardProps<T> = {
|
|
49
53
|
card?: (item: T) => ReactNode;
|
|
54
|
+
/** One column per stage, with drag (and Alt + ← / →) to move a card between them. */
|
|
55
|
+
stages?: BoardStages<T>;
|
|
50
56
|
};
|
|
51
57
|
/** La planilla. Sin columnas propias, sale de las que declaró `List.Table` — la
|
|
52
58
|
* de datos CRUDOS sí las trae, porque ahí salen del archivo y el código no sabe
|
|
@@ -55,6 +61,9 @@ type SheetProps<T> = Partial<ListSheet<T>>;
|
|
|
55
61
|
export type ListProps<T extends object> = {
|
|
56
62
|
items: readonly T[];
|
|
57
63
|
keyOf: (item: T) => string;
|
|
64
|
+
/** The list is a FIELD of a record — a one2many / many2many — and not a screen: a
|
|
65
|
+
* one-line bar that does not stick. See «A list INSIDE a record» above. */
|
|
66
|
+
embedded?: boolean;
|
|
58
67
|
/** En qué superficie se apoya la BARRA. `canvas` (el default) es una barra
|
|
59
68
|
* de control clara; `chrome` la convierte en una banda oscura, una opción
|
|
60
69
|
* explícita: la barra de la aplicación es la única banda oscura de la
|
|
@@ -69,6 +78,11 @@ export type ListProps<T extends object> = {
|
|
|
69
78
|
count: number;
|
|
70
79
|
clear: () => void;
|
|
71
80
|
actions?: ReactNode;
|
|
81
|
+
/** Every record that matches, not just the page — see `ListViewProps`. */
|
|
82
|
+
matching?: {
|
|
83
|
+
total: number;
|
|
84
|
+
onSelect: () => void;
|
|
85
|
+
};
|
|
72
86
|
};
|
|
73
87
|
/** El orden, que también es del conjunto. */
|
|
74
88
|
sort?: SortState | null;
|
|
@@ -82,6 +96,18 @@ export type ListProps<T extends object> = {
|
|
|
82
96
|
onOpen?: (item: T) => void;
|
|
83
97
|
onPeek?: (item: T) => void;
|
|
84
98
|
isPeeked?: (item: T) => boolean;
|
|
99
|
+
/** What a press on the row's body does when both ways in exist. `peek` (the default) looks
|
|
100
|
+
* without leaving the list; `open` goes to the record — a workspace whose rule is «a row
|
|
101
|
+
* opens the page» (the composer's) says `open`, and 👁 stays for the look. */
|
|
102
|
+
rowClick?: "peek" | "open";
|
|
103
|
+
/** The list CHOOSES instead of opening — a «Buscar más…», a chooser a workspace asked for:
|
|
104
|
+
* a press on a row (its body, its name, a card) calls this, and the row offers no 👁 / ↗.
|
|
105
|
+
* For several at once, give `selection` too and toggle it here. */
|
|
106
|
+
onChoose?: (item: T) => void;
|
|
107
|
+
/** Nothing in it can be acted on: no opening, peeking, selecting, creating nor row
|
|
108
|
+
* actions — a list to read, like the lines of a record in view mode. One switch, so whoever
|
|
109
|
+
* passes the handlers generically does not have to strip them one by one. */
|
|
110
|
+
readOnly?: boolean;
|
|
85
111
|
/** Los tres estados, en un solo lugar. */
|
|
86
112
|
state?: {
|
|
87
113
|
loading?: boolean;
|
|
@@ -93,7 +119,7 @@ export type ListProps<T extends object> = {
|
|
|
93
119
|
empty?: ListEmpty;
|
|
94
120
|
children?: ReactNode;
|
|
95
121
|
};
|
|
96
|
-
export declare function List<T extends object>({ items, keyOf, surface, group, selection, sort, onSortChange, sortable, present, onPresentChange, onOpen, onPeek, isPeeked, state, filtered, empty, children, }: ListProps<T>): import("react").JSX.Element;
|
|
122
|
+
export declare function List<T extends object>({ items, keyOf, embedded, surface, group, selection: selectWith, sort, onSortChange, sortable, present, onPresentChange, onOpen: openWith, onPeek: peekWith, isPeeked, readOnly, rowClick, onChoose, state, filtered, empty, children, }: ListProps<T>): import("react").JSX.Element;
|
|
97
123
|
export declare namespace List {
|
|
98
124
|
var Bar: ((props: BarProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"bar">;
|
|
99
125
|
var Crumbs: ((props: CrumbsProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"crumbs">;
|
|
@@ -109,5 +135,8 @@ export declare namespace List {
|
|
|
109
135
|
var Sheet: <T>(props: SheetProps<T>) => ReactNode;
|
|
110
136
|
var Report: ((props: ListReport) => ReactNode) & import("../../foundations/slots.js").Slotted<"report">;
|
|
111
137
|
var Pivot: <T>(props: ListPivot<T>) => ReactNode;
|
|
138
|
+
var Calendar: <T>(props: ListCalendar<T>) => ReactNode;
|
|
139
|
+
var Gantt: <T>(props: ListGantt<T>) => ReactNode;
|
|
140
|
+
var Timeline: <T>(props: ListTimeline<T>) => ReactNode;
|
|
112
141
|
}
|
|
113
142
|
export {};
|
package/dist/views/list/List.js
CHANGED
|
@@ -22,6 +22,9 @@ const Views = slot("views", (_props) => null);
|
|
|
22
22
|
* arriba de las filas, porque son del conjunto y no del marco. */
|
|
23
23
|
const Metrics = slot("metrics", (_props) => null);
|
|
24
24
|
const Rows = slot("rows", (_props) => null);
|
|
25
|
+
/* Through `unknown`: `create` both receives and hands back a `T` (`onChange`, `draft`),
|
|
26
|
+
so `TableProps` is invariant in `T` and `TableProps<never>` no longer narrows to it. The
|
|
27
|
+
slot only carries the props; `List` reads them back typed against the list's `T`. */
|
|
25
28
|
const TableSlot = slot("table", (_props) => null);
|
|
26
29
|
const Board = slot("board", (_props) => null);
|
|
27
30
|
const SheetSlot = slot("sheet", (_props) => null);
|
|
@@ -37,10 +40,32 @@ const PRESENT_OF = {
|
|
|
37
40
|
sheet: "sheet",
|
|
38
41
|
report: "report",
|
|
39
42
|
pivot: "pivot",
|
|
43
|
+
calendar: "calendar",
|
|
44
|
+
gantt: "gantt",
|
|
45
|
+
timeline: "timeline",
|
|
40
46
|
};
|
|
41
|
-
const FACES = [
|
|
42
|
-
|
|
47
|
+
const FACES = [
|
|
48
|
+
"rows",
|
|
49
|
+
"table",
|
|
50
|
+
"board",
|
|
51
|
+
"sheet",
|
|
52
|
+
"report",
|
|
53
|
+
"pivot",
|
|
54
|
+
"calendar",
|
|
55
|
+
"gantt",
|
|
56
|
+
"timeline",
|
|
57
|
+
];
|
|
58
|
+
/** Records grouped in rows across time, N per row — a planning (`RecordTimeline`). */
|
|
59
|
+
const TimelineSlot = slot("timeline", (_props) => null);
|
|
60
|
+
/** When each record happens, on a month, a week, a day or an agenda (`RecordCalendar`). */
|
|
61
|
+
const CalendarSlot = slot("calendar", (_props) => null);
|
|
62
|
+
/** Each record as a span on a timeline, with its dependencies (`RecordGantt`). */
|
|
63
|
+
const GanttSlot = slot("gantt", (_props) => null);
|
|
64
|
+
export function List({ items, keyOf, embedded, surface, group, selection: selectWith, sort, onSortChange, sortable, present, onPresentChange, onOpen: openWith, onPeek: peekWith, isPeeked, readOnly = false, rowClick, onChoose, state, filtered, empty, children, }) {
|
|
43
65
|
const top = flatten(children);
|
|
66
|
+
const onOpen = readOnly ? undefined : openWith;
|
|
67
|
+
const onPeek = readOnly ? undefined : peekWith;
|
|
68
|
+
const selection = readOnly ? undefined : selectWith;
|
|
44
69
|
const bar = flatten(propsOf(pick(top, "bar"))?.children);
|
|
45
70
|
const crumbs = propsOf(pick(bar, "crumbs"))?.crumbs;
|
|
46
71
|
const identity = propsOf(pick(bar, "identity"));
|
|
@@ -55,6 +80,9 @@ export function List({ items, keyOf, surface, group, selection, sort, onSortChan
|
|
|
55
80
|
const sheet = propsOf(pick(top, "sheet"));
|
|
56
81
|
const report = propsOf(pick(top, "report"));
|
|
57
82
|
const pivot = propsOf(pick(top, "pivot"));
|
|
83
|
+
const calendar = propsOf(pick(top, "calendar"));
|
|
84
|
+
const gantt = propsOf(pick(top, "gantt"));
|
|
85
|
+
const timeline = propsOf(pick(top, "timeline"));
|
|
58
86
|
/* Las caras que hay son las que están escritas, EN ESE ORDEN. El conmutador
|
|
59
87
|
no puede ofrecer una que no se pueda dibujar, porque la que se dibuja y la
|
|
60
88
|
que se ofrece son el mismo nodo. */
|
|
@@ -65,21 +93,34 @@ export function List({ items, keyOf, surface, group, selection, sort, onSortChan
|
|
|
65
93
|
/* La escotilla: lo que el arquetipo no previó se dibuja igual, debajo. Que
|
|
66
94
|
aparezca algo acá es la señal de que al arquetipo le falta una pieza. */
|
|
67
95
|
const rest = loose(top, ["bar", "metrics", ...FACES]);
|
|
68
|
-
return (_jsx(ListView, { items: items, keyOf: keyOf, row: rows
|
|
96
|
+
return (_jsx(ListView, { items: items, keyOf: keyOf, row: rows === undefined
|
|
97
|
+
? () => ({ name: "" })
|
|
98
|
+
: readOnly
|
|
99
|
+
? (item) => {
|
|
100
|
+
const { actions: _actions, ...rest } = rows.row(item);
|
|
101
|
+
return rest;
|
|
102
|
+
}
|
|
103
|
+
: rows.row, ...(rows?.header === undefined ? {} : { rowHeader: rows.header }), ...(identity === undefined ? {} : { identity }), ...(crumbs === undefined ? {} : { crumbs }), ...(embedded === undefined ? {} : { embedded }), ...(surface === undefined ? {} : { surface }), ...(search === undefined ? {} : { search }), ...(actions === undefined ? {} : { actions }), ...(pagination === undefined ? {} : { pagination }), ...(views === undefined ? {} : { views }), ...(metrics === undefined ? {} : { metrics }), ...(table === undefined
|
|
69
104
|
? {}
|
|
70
105
|
: {
|
|
71
|
-
|
|
106
|
+
/* Read-only: a cell that opens an editor is an action too. */
|
|
107
|
+
columns: readOnly
|
|
108
|
+
? table.columns.map(({ edit: _edit, ...c }) => c)
|
|
109
|
+
: table.columns,
|
|
72
110
|
...(table.totals === undefined ? {} : { totals: table.totals }),
|
|
73
|
-
...(table.rowActions === undefined
|
|
111
|
+
...(table.rowActions === undefined || readOnly
|
|
112
|
+
? {}
|
|
113
|
+
: { rowActions: table.rowActions }),
|
|
114
|
+
...(table.create === undefined || readOnly ? {} : { create: table.create }),
|
|
74
115
|
...(table.visibleColumnIds === undefined
|
|
75
116
|
? {}
|
|
76
117
|
: { visibleColumnIds: table.visibleColumnIds }),
|
|
77
118
|
...(table.onVisibleColumnIdsChange === undefined
|
|
78
119
|
? {}
|
|
79
120
|
: { onVisibleColumnIdsChange: table.onVisibleColumnIdsChange }),
|
|
80
|
-
}), ...(board?.card === undefined ? {} : { card: board.card }), ...(sheet?.columns === undefined || sheet.cell === undefined
|
|
121
|
+
}), ...(board?.card === undefined ? {} : { card: board.card }), ...(board?.stages === undefined ? {} : { boardStages: board.stages }), ...(sheet?.columns === undefined || sheet.cell === undefined
|
|
81
122
|
? {}
|
|
82
|
-
: { sheet: sheet }), ...(report === undefined ? {} : { report }), ...(pivot === undefined ? {} : { pivot }), ...(presents.length === 0 ? {} : { presents }), ...(present === undefined ? {} : { present }), ...(onPresentChange === undefined ? {} : { onPresentChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(sortable === undefined ? {} : { sortable }), ...(onOpen === undefined ? {} : { onOpen }), ...(onPeek === undefined ? {} : { onPeek }), ...(isPeeked === undefined ? {} : { isPeeked }), ...(state === undefined ? {} : { state }), ...(filtered === undefined ? {} : { filtered }), ...(empty === undefined ? {} : { empty }), children: rest.length === 0 ? undefined : rest }));
|
|
123
|
+
: { sheet: sheet }), ...(report === undefined ? {} : { report }), ...(pivot === undefined ? {} : { pivot }), ...(calendar === undefined ? {} : { calendar }), ...(gantt === undefined ? {} : { gantt }), ...(timeline === undefined ? {} : { timeline }), ...(presents.length === 0 ? {} : { presents }), ...(present === undefined ? {} : { present }), ...(onPresentChange === undefined ? {} : { onPresentChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(sortable === undefined ? {} : { sortable }), ...(onOpen === undefined ? {} : { onOpen }), ...(onPeek === undefined ? {} : { onPeek }), ...(isPeeked === undefined ? {} : { isPeeked }), ...(rowClick === undefined ? {} : { rowClick }), ...(onChoose === undefined || readOnly ? {} : { onChoose }), ...(state === undefined ? {} : { state }), ...(filtered === undefined ? {} : { filtered }), ...(empty === undefined ? {} : { empty }), children: rest.length === 0 ? undefined : rest }));
|
|
83
124
|
}
|
|
84
125
|
List.Bar = Bar;
|
|
85
126
|
List.Crumbs = Crumbs;
|
|
@@ -95,3 +136,6 @@ List.Board = Board;
|
|
|
95
136
|
List.Sheet = SheetSlot;
|
|
96
137
|
List.Report = Report;
|
|
97
138
|
List.Pivot = Pivot;
|
|
139
|
+
List.Calendar = CalendarSlot;
|
|
140
|
+
List.Gantt = GanttSlot;
|
|
141
|
+
List.Timeline = TimelineSlot;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ListViewProps } from "./types.js";
|
|
2
|
-
export declare function ListView<T extends object>({ items, keyOf, identity, crumbs, surface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, sheet, report, pivot, present: requested, presents, onPresentChange, sort, onSortChange, sortable, totals, rowActions, group, selection, onOpen, onPeek, state, filtered, empty, children, }: ListViewProps<T>): import("react").JSX.Element;
|
|
2
|
+
export declare function ListView<T extends object>({ items, keyOf, identity, crumbs, embedded, surface: requestedSurface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, boardStages, calendar, gantt, timeline, sheet, report, pivot, present: requested, presents, onPresentChange, sort, onSortChange, sortable, totals, rowActions, create, group, selection, onOpen, onPeek, rowClick, onChoose, state, filtered, empty, children, }: ListViewProps<T>): import("react").JSX.Element;
|
|
3
3
|
/** What a list offers when it fails and can be retried. */
|
|
4
4
|
export declare function RetryAction({ onRetry }: {
|
|
5
5
|
onRetry: () => void;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { RecordCalendar } from "../../components/calendar/RecordCalendar.js";
|
|
4
|
+
import { RecordGantt } from "../../components/gantt/RecordGantt.js";
|
|
5
|
+
import { BoardColumns } from "../../components/listing/BoardColumns.js";
|
|
3
6
|
import { useColumnControl, visibleColumns } from "../../components/listing/columns.js";
|
|
4
7
|
import { ColumnsMenu, specsFromColumns, } from "../../components/listing/ColumnsMenu.js";
|
|
5
8
|
import { Listing } from "../../components/listing/Listing.js";
|
|
6
9
|
import { ListingBar } from "../../components/listing/ListingBar.js";
|
|
10
|
+
import { PivotExplorer } from "../../components/listing/PivotExplorer.js";
|
|
7
11
|
import { RecordCard } from "../../components/listing/RecordCard.js";
|
|
8
12
|
import { GroupHeader, Row, RowHeader } from "../../components/listing/Row.js";
|
|
9
13
|
import { sortItems, SortMenu, } from "../../components/listing/SortMenu.js";
|
|
10
14
|
import { ViewSwitcher } from "../../components/listing/ViewSwitcher.js";
|
|
11
15
|
import { Breadcrumb } from "../../components/record/Breadcrumb.js";
|
|
16
|
+
import { RecordTimeline } from "../../components/timeline/RecordTimeline.js";
|
|
12
17
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
13
18
|
import { useDensity } from "../../foundations/density.js";
|
|
14
19
|
import { pathKey, pileIsOpen, pilesOf, } from "../../foundations/grouping.js";
|
|
@@ -18,10 +23,10 @@ import { Empty, Failed, Skeleton } from "../../primitives/notice/Notice.js";
|
|
|
18
23
|
import { PivotTable } from "../../primitives/table/PivotTable.js";
|
|
19
24
|
import { ReportTable } from "../../primitives/table/ReportTable.js";
|
|
20
25
|
import { groupedColumnIds } from "./columns.js";
|
|
21
|
-
import { groupLevelsOf, reportRowsOf, sheetFromColumns, sheetRowsOf, } from "./derive.js";
|
|
26
|
+
import { groupLevelsOf, reportColumnsOf, reportRowsOf, sheetFromColumns, sheetRowsOf, } from "./derive.js";
|
|
22
27
|
import { SheetPresentation, TablePresentation, } from "./presentations.js";
|
|
23
28
|
import { useNarrowList } from "./useNarrowList.js";
|
|
24
|
-
import { Grid3x3, LayoutGrid, List, Sheet, Sigma, Table2 } from "lucide-react";
|
|
29
|
+
import { CalendarDays, CalendarRange, ChartGantt, Grid3x3, LayoutGrid, List, Sheet, Sigma, Table2, } from "lucide-react";
|
|
25
30
|
import { Fragment, useCallback, useMemo } from "react";
|
|
26
31
|
/**
|
|
27
32
|
* EL DIBUJO de una lista. **La puerta es `List`**, que se escribe como árbol y
|
|
@@ -57,9 +62,15 @@ const PRESENT_ICONS = [
|
|
|
57
62
|
{ id: "report", icon: Sigma },
|
|
58
63
|
/* La cuadrícula: dos ejes, y lo que importa está adentro. */
|
|
59
64
|
{ id: "pivot", icon: Grid3x3 },
|
|
65
|
+
{ id: "calendar", icon: CalendarDays },
|
|
66
|
+
{ id: "gantt", icon: ChartGantt },
|
|
67
|
+
{ id: "timeline", icon: CalendarRange },
|
|
60
68
|
];
|
|
61
|
-
export function ListView({ items, keyOf, identity, crumbs, surface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, sheet, report, pivot, present: requested = "rows", presents, onPresentChange, sort, onSortChange, sortable, totals = false, rowActions, group, selection, onOpen, onPeek, state, filtered = false, empty, children, }) {
|
|
69
|
+
export function ListView({ items, keyOf, identity, crumbs, embedded = false, surface: requestedSurface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, boardStages, calendar, gantt, timeline, sheet, report, pivot, present: requested = "rows", presents, onPresentChange, sort, onSortChange, sortable, totals = false, rowActions, create, group, selection, onOpen, onPeek, rowClick = "peek", onChoose, state, filtered = false, empty, children, }) {
|
|
62
70
|
const { frame, offered, present } = useNarrowList(presents, requested);
|
|
71
|
+
/* Embedded, the list sits on the record's paper: there is no dark band to inherit, and a
|
|
72
|
+
navy switcher inside a white sheet would be a second chrome on the screen. */
|
|
73
|
+
const surface = embedded ? undefined : requestedSurface;
|
|
63
74
|
const density = useDensity();
|
|
64
75
|
const copy = useUiCopy();
|
|
65
76
|
const loading = state?.loading === true;
|
|
@@ -128,7 +139,16 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
|
|
|
128
139
|
present === "pivot" ? null : (_jsx(SortMenu, { fields: sortable, sort: sort ?? null, onSortChange: (next) => onSortChange?.(next), ...(surface === undefined ? {} : { surface }) }));
|
|
129
140
|
const rightViews = views ?? switcher;
|
|
130
141
|
const viewsSlot = columnsMenu === null && sortMenu === null && rightViews === undefined ? undefined : (_jsxs(_Fragment, { children: [sortMenu, columnsMenu, rightViews] }));
|
|
131
|
-
|
|
142
|
+
/* Embedded, the line is drawn only if it has something to OFFER: a count alone repeats
|
|
143
|
+
what the rows (and the footer) already say. */
|
|
144
|
+
const barHasControls = !embedded ||
|
|
145
|
+
identity?.title !== undefined ||
|
|
146
|
+
search !== undefined ||
|
|
147
|
+
viewsSlot !== undefined ||
|
|
148
|
+
actions !== undefined ||
|
|
149
|
+
pagination !== undefined ||
|
|
150
|
+
(selection?.count ?? 0) > 0;
|
|
151
|
+
const bar = !barHasControls ? undefined : (_jsx(ListingBar, { surface: surface, ...(embedded ? { inline: true, count: items.length } : {}), ...(embedded || crumbs === undefined || crumbs.length === 0 || density === "compact"
|
|
132
152
|
? {}
|
|
133
153
|
: {
|
|
134
154
|
crumbs: (_jsx(Breadcrumb, { crumbs: crumbs, ...(surface === undefined ? {} : { surface }) })),
|
|
@@ -139,12 +159,26 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
|
|
|
139
159
|
count: selection.count,
|
|
140
160
|
onClear: selection.clear,
|
|
141
161
|
...(selection.actions === undefined ? {} : { actions: selection.actions }),
|
|
162
|
+
...(selection.matching === undefined ? {} : { matching: selection.matching }),
|
|
142
163
|
},
|
|
143
164
|
}), ...(search === undefined ? {} : { search }), ...(pagination === undefined ? {} : { pagination }), ...(viewsSlot === undefined ? {} : { views: viewsSlot }) }));
|
|
165
|
+
/* The body of a row, a card: the look first unless the list says the page. */
|
|
166
|
+
/* Choosing, the press chooses and the ways in (👁 ↗) are not offered: a chooser's row answers. */
|
|
167
|
+
const choosing = onChoose !== undefined;
|
|
168
|
+
const press = choosing
|
|
169
|
+
? onChoose
|
|
170
|
+
: rowClick === "open"
|
|
171
|
+
? (onOpen ?? onPeek)
|
|
172
|
+
: (onPeek ?? onOpen);
|
|
144
173
|
const renderRow = (item) => {
|
|
145
174
|
const d = row(item);
|
|
146
175
|
const id = keyOf(item);
|
|
147
|
-
return (_jsx(Row, { ...(d.icon === undefined ? {} : { icon: d.icon }),
|
|
176
|
+
return (_jsx(Row, { ...(d.icon === undefined ? {} : { icon: d.icon }), ...(d.avatar === undefined ? {} : { avatar: d.avatar }), name: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(d.actions === undefined ? {} : { actions: d.actions }), ...(choosing
|
|
177
|
+
? { onOpen: () => onChoose(item), openers: false }
|
|
178
|
+
: {
|
|
179
|
+
...(onOpen === undefined ? {} : { onOpen: () => onOpen(item) }),
|
|
180
|
+
...(onPeek === undefined ? {} : { onPeek: () => onPeek(item) }),
|
|
181
|
+
}), click: rowClick, ...(selection === undefined
|
|
148
182
|
? {}
|
|
149
183
|
: { selected: selection.has(id), onSelectedChange: () => selection.toggle(id) }), children: d.children }, id));
|
|
150
184
|
};
|
|
@@ -183,7 +217,10 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
|
|
|
183
217
|
error === null &&
|
|
184
218
|
(present === "pivot" && pivotShown !== undefined
|
|
185
219
|
? pivotIsEmpty(pivotShown)
|
|
186
|
-
: items.length === 0)
|
|
220
|
+
: items.length === 0) &&
|
|
221
|
+
/* The first record of an empty list is written in the table, not under a sentence
|
|
222
|
+
saying there is nothing. */
|
|
223
|
+
!(present === "table" && create?.draft != null);
|
|
187
224
|
/* La única línea que hace agrupable a una vista: el árbol que le pasaron, o el
|
|
188
225
|
que se arma con las filas en la mano. */
|
|
189
226
|
const piles = useMemo(() => pilesOf(ordered, group), [group, ordered]);
|
|
@@ -221,11 +258,11 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
|
|
|
221
258
|
return _jsx(Fragment, { children: card(item) }, keyOf(item));
|
|
222
259
|
}
|
|
223
260
|
const d = row(item);
|
|
224
|
-
return (_jsx(RecordCard, { title: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(
|
|
261
|
+
return (_jsx(RecordCard, { title: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(press === undefined ? {} : { onOpen: () => press(item) }) }, keyOf(item)));
|
|
225
262
|
};
|
|
226
263
|
const body = present === "table" && shownColumns !== undefined ? (
|
|
227
264
|
/* The table receives EVERYTHING that belongs to the set: see `TablePresentation`. */
|
|
228
|
-
_jsx(TablePresentation, { items: items, columns: shownColumns, keyOf: keyOf, levels: groupLevels, ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), totals: totals, ...(rowActions === undefined ? {} : { rowActions }), ...(onPeek === undefined ? {} : { onPeek }), ...(onOpen === undefined ? {} : { onOpen }) })) : present === "sheet" && sheetSpec !== undefined ? (
|
|
265
|
+
_jsx(TablePresentation, { items: items, columns: shownColumns, keyOf: keyOf, levels: groupLevels, ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), totals: totals, ...(rowActions === undefined ? {} : { rowActions }), ...(create === undefined ? {} : { create }), ...(onPeek === undefined || choosing ? {} : { onPeek }), ...(onOpen === undefined || choosing ? {} : { onOpen }), ...(choosing ? { onChoose } : {}), rowClick: rowClick })) : present === "sheet" && sheetSpec !== undefined ? (
|
|
229
266
|
/* La planilla recibe el conjunto por ÍNDICE y no una matriz ya armada: ver `SheetPresentation`. */
|
|
230
267
|
_jsx(SheetPresentation, { spec: sheetSpec, entries: sheetRows, ordered: ordered, ...(group === undefined ? {} : { group }) })) : present === "report" && report !== undefined ? (
|
|
231
268
|
/* El informe declarado por el dominio no se ORDENA desde la barra: el
|
|
@@ -233,28 +270,48 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
|
|
|
233
270
|
Activos» en el medio. Pero sí se AGRUPA cuando las líneas no vienen
|
|
234
271
|
declaradas: ahí el informe es este conjunto contado por los niveles que
|
|
235
272
|
estén puestos. */
|
|
236
|
-
_jsx(ReportTable
|
|
273
|
+
_jsx(ReportTable
|
|
274
|
+
/* Built from the piles, the numbers are the list's columns' — and so is how they
|
|
275
|
+
are written. Declared by the domain, the columns are the domain's. */
|
|
276
|
+
, {
|
|
277
|
+
/* Built from the piles, the numbers are the list's columns' — and so is how they
|
|
278
|
+
are written. Declared by the domain, the columns are the domain's. */
|
|
279
|
+
columns: report.rows === undefined
|
|
280
|
+
? reportColumnsOf(report.columns, columns)
|
|
281
|
+
: report.columns, rows: report.rows ?? reportRows, ...(report.defaultOpen === undefined ? {} : { defaultOpen: report.defaultOpen }), ...(report.height === undefined ? {} : { height: report.height }) })) : present === "pivot" && pivot?.spec !== undefined && pivot.fields !== undefined ? (
|
|
282
|
+
/* Explored: the person moves the axes. Its rows follow the search bar's grouping. */
|
|
283
|
+
_jsx("div", { className: "p-3", children: _jsx(PivotExplorer, { items: ordered, of: pivot.spec.of, fields: pivot.fields, measures: pivot.spec.measures, ...(pivot.defaultMeasures === undefined
|
|
284
|
+
? {}
|
|
285
|
+
: { defaultMeasures: pivot.defaultMeasures }), ...((group?.levels.length ?? 0) > 0 ? { rows: group.levels } : {}), defaultRows: pivot.spec.rows, defaultColumns: pivot.spec.columns, ...(pivot.onCell === undefined ? {} : { onCell: pivot.onCell }), ...(pivot.height === undefined ? {} : { height: pivot.height }) }) })) : present === "pivot" && pivotShown !== undefined ? (
|
|
237
286
|
/* El pivote recibe la grilla y nada más: no hay selección que pasarle
|
|
238
287
|
—una casilla por montón elegiría cosas que no son registros—, no hay
|
|
239
288
|
orden —cada eje viene en el orden de sus claves— y no hay menú de
|
|
240
289
|
columnas, porque las columnas son un eje y no una lista de campos que
|
|
241
290
|
se puedan esconder de a uno. */
|
|
242
|
-
_jsx(PivotTable, { pivot: pivotShown, ...(pivot?.onCell === undefined ? {} : { onCell: pivot.onCell }), ...(pivot?.height === undefined ? {} : { height: pivot.height }) })) : present === "board" ? (_jsx("div", { className: "flex flex-col", children: wrapGroups((inside) => (_jsx("div", { className: "grid gap-3 p-3 @2xl:grid-cols-2 @5xl:grid-cols-3", children: inside.map(renderCard) }))) })) : (
|
|
291
|
+
_jsx(PivotTable, { pivot: pivotShown, ...(pivot?.onCell === undefined ? {} : { onCell: pivot.onCell }), ...(pivot?.height === undefined ? {} : { height: pivot.height }) })) : present === "calendar" && calendar !== undefined ? (_jsx("div", { className: "p-3", children: _jsx(RecordCalendar, { ...calendar, items: ordered, keyOf: keyOf, ...(press === undefined ? {} : { onOpen: press }) }) })) : present === "gantt" && gantt !== undefined ? (_jsx("div", { className: "p-3", children: _jsx(RecordGantt, { ...gantt, items: ordered, keyOf: keyOf, ...(press === undefined ? {} : { onOpen: press }) }) })) : present === "timeline" && timeline !== undefined ? (_jsx("div", { className: "p-3", children: _jsx(RecordTimeline, { ...timeline, items: ordered, keyOf: keyOf, ...(press === undefined ? {} : { onOpen: press }) }) })) : present === "board" && boardStages !== undefined ? (_jsx(BoardColumns, { items: ordered, keyOf: keyOf, stages: boardStages, card: renderCard })) : present === "board" ? (_jsx("div", { className: "flex flex-col", children: wrapGroups((inside) => (_jsx("div", { className: "grid gap-3 p-3 @2xl:grid-cols-2 @5xl:grid-cols-3", children: inside.map(renderCard) }))) })) : (
|
|
243
292
|
/* El encabezado va ANTES de los montones y no adentro de cada uno:
|
|
244
293
|
rotula la fila, y la fila es la misma en los cinco montones. */
|
|
245
294
|
_jsxs(_Fragment, { children: [rowHeader === undefined || items.length === 0 ? null : (_jsx(RowHeader, { name: rowHeader.name, ...(rowHeader.meta === undefined ? {} : { meta: rowHeader.meta }), ...(rowHeader.tags === undefined ? {} : { tags: rowHeader.tags }), withSelection: selection !== undefined,
|
|
246
295
|
/* Los espaciadores se preguntan por la PRIMERA fila: el ícono y las
|
|
247
296
|
acciones son de la lista entera, no de una fila — si la primera no
|
|
248
297
|
tiene ícono, ninguna lo tiene. */
|
|
249
|
-
withIcon: firstRow?.icon !== undefined,
|
|
298
|
+
withIcon: firstRow?.icon !== undefined, withAvatar: firstRow?.avatar !== undefined, trail: {
|
|
299
|
+
...(firstRow?.actions === undefined ? {} : { actions: firstRow.actions }),
|
|
300
|
+
peek: onPeek !== undefined && !choosing,
|
|
301
|
+
open: onOpen !== undefined && !choosing,
|
|
302
|
+
} })), wrapGroups((inside) => inside.map(renderRow))] }));
|
|
250
303
|
return (
|
|
251
304
|
/* The list is its own container (`@container`: the presentations adapt to the width
|
|
252
305
|
the LIST has, not the window) and fills the space it is given. Before, every page
|
|
253
306
|
wrapped it in a `div` to get both. */
|
|
254
307
|
_jsxs("div", { ref: frame, className: "@container flex min-w-0 flex-1 flex-col bg-surface", children: [_jsxs(Listing, { bar: bar,
|
|
255
308
|
/* La barra se queda arriba: con 187 filas, scrollear para ver la última
|
|
256
|
-
dejaba sin buscador, sin paginador y sin el conmutador.
|
|
257
|
-
|
|
309
|
+
dejaba sin buscador, sin paginador y sin el conmutador.
|
|
310
|
+
Embedded it does NOT: the record has its own sticky parts (its rail, its tabs),
|
|
311
|
+
and a second band stuck under them — or carried up into the frame's header slot,
|
|
312
|
+
which is what a sticky list bar does under a sidebar — would cover the record
|
|
313
|
+
for a collection of a dozen rows. */
|
|
314
|
+
sticky: !embedded, ...(surface === undefined ? {} : { surface }), ...(metrics === undefined
|
|
258
315
|
? {}
|
|
259
316
|
: {
|
|
260
317
|
metrics: _jsx("div", { className: "px-3.5", children: metrics }),
|
|
@@ -16,7 +16,14 @@ export function groupedColumnIds(group, columns) {
|
|
|
16
16
|
.filter((id) => id !== undefined);
|
|
17
17
|
}
|
|
18
18
|
/** The presentations that need width: a grid of columns. */
|
|
19
|
-
export const WIDE_PRESENTS = [
|
|
19
|
+
export const WIDE_PRESENTS = [
|
|
20
|
+
"table",
|
|
21
|
+
"sheet",
|
|
22
|
+
"report",
|
|
23
|
+
"pivot",
|
|
24
|
+
"gantt",
|
|
25
|
+
"timeline",
|
|
26
|
+
];
|
|
20
27
|
/** The id of the column that answers for a model field, when there is one. */
|
|
21
28
|
function columnAnswering(field, columns) {
|
|
22
29
|
if (columns === undefined)
|