@sincpro/web-ui 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +184 -2
- package/dist/components/activity/Activity.d.ts +69 -0
- package/dist/components/activity/Activity.js +279 -0
- package/dist/components/activity/Messages.d.ts +66 -0
- package/dist/components/activity/Messages.js +46 -0
- package/dist/components/activity/Timeline.d.ts +21 -0
- package/dist/components/activity/Timeline.js +30 -0
- package/dist/components/activity/index.d.ts +3 -0
- package/dist/components/activity/index.js +4 -0
- package/dist/components/assist/AssistMark.d.ts +64 -0
- package/dist/components/assist/AssistMark.js +108 -0
- package/dist/components/assist/AssistPending.d.ts +24 -0
- package/dist/components/assist/AssistPending.js +26 -0
- package/dist/components/assist/AssistSuggestion.d.ts +42 -0
- package/dist/components/assist/AssistSuggestion.js +55 -0
- package/dist/components/assist/ChangeReview.d.ts +56 -0
- package/dist/components/assist/ChangeReview.js +121 -0
- package/dist/components/assist/assist.d.ts +77 -0
- package/dist/components/assist/assist.js +87 -0
- package/dist/components/assist/index.d.ts +5 -0
- package/dist/components/assist/index.js +7 -0
- package/dist/components/form/Form.d.ts +52 -0
- package/dist/components/form/Form.js +49 -0
- package/dist/components/form/Sheet.d.ts +47 -0
- package/dist/components/form/Sheet.js +90 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.js +4 -0
- package/dist/components/frame/Chart.d.ts +13 -0
- package/dist/components/frame/Chart.js +24 -0
- package/dist/components/frame/FrameView.d.ts +33 -0
- package/dist/components/frame/FrameView.js +125 -0
- package/dist/components/frame/index.d.ts +3 -0
- package/dist/components/frame/index.js +3 -0
- package/dist/components/frame/model.d.ts +43 -0
- package/dist/components/frame/model.js +101 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.js +11 -0
- package/dist/components/lines/LineItems.d.ts +111 -0
- package/dist/components/lines/LineItems.js +317 -0
- package/dist/components/lines/index.d.ts +1 -0
- package/dist/components/lines/index.js +2 -0
- package/dist/components/lines/lines.d.ts +23 -0
- package/dist/components/lines/lines.js +26 -0
- package/dist/components/listing/ColumnsMenu.d.ts +20 -0
- package/dist/components/listing/ColumnsMenu.js +46 -0
- package/dist/components/listing/GroupingLevels.d.ts +35 -0
- package/dist/components/listing/GroupingLevels.js +45 -0
- package/dist/components/listing/Listing.d.ts +27 -0
- package/dist/components/listing/Listing.js +86 -0
- package/dist/components/listing/ListingBar.d.ts +112 -0
- package/dist/components/listing/ListingBar.js +144 -0
- package/dist/components/listing/Pagination.d.ts +36 -0
- package/dist/components/listing/Pagination.js +69 -0
- package/dist/components/listing/RecordCard.d.ts +33 -0
- package/dist/components/listing/RecordCard.js +22 -0
- package/dist/components/listing/RecordLink.d.ts +7 -0
- package/dist/components/listing/RecordLink.js +22 -0
- package/dist/components/listing/Row.d.ts +110 -0
- package/dist/components/listing/Row.js +123 -0
- package/dist/components/listing/SortMenu.d.ts +62 -0
- package/dist/components/listing/SortMenu.js +105 -0
- package/dist/components/listing/ViewSwitcher.d.ts +24 -0
- package/dist/components/listing/ViewSwitcher.js +16 -0
- package/dist/components/listing/columns.d.ts +47 -0
- package/dist/components/listing/columns.js +72 -0
- package/dist/components/listing/grouping.d.ts +35 -0
- package/dist/components/listing/grouping.js +47 -0
- package/dist/components/listing/index.d.ts +17 -0
- package/dist/components/listing/index.js +20 -0
- package/dist/components/listing/modelColumns.d.ts +50 -0
- package/dist/components/listing/modelColumns.js +103 -0
- package/dist/components/listing/pages.d.ts +78 -0
- package/dist/components/listing/pages.js +135 -0
- package/dist/components/listing/selection.d.ts +21 -0
- package/dist/components/listing/selection.js +43 -0
- package/dist/components/listing/set.d.ts +48 -0
- package/dist/components/listing/set.js +24 -0
- package/dist/components/listing/tree.d.ts +28 -0
- package/dist/components/listing/tree.js +23 -0
- package/dist/components/navigation/CommandPalette.d.ts +40 -0
- package/dist/components/navigation/CommandPalette.js +114 -0
- package/dist/components/navigation/NavigationTree.d.ts +31 -0
- package/dist/components/navigation/NavigationTree.js +79 -0
- package/dist/components/navigation/commands.d.ts +30 -0
- package/dist/components/navigation/commands.js +30 -0
- package/dist/components/navigation/index.d.ts +3 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/record/Breadcrumb.d.ts +42 -0
- package/dist/components/record/Breadcrumb.js +98 -0
- package/dist/components/record/DirtyActions.d.ts +26 -0
- package/dist/components/record/DirtyActions.js +22 -0
- package/dist/components/record/Pager.d.ts +18 -0
- package/dist/components/record/Pager.js +33 -0
- package/dist/components/record/RecordHeader.d.ts +75 -0
- package/dist/components/record/RecordHeader.js +104 -0
- package/dist/components/record/RecordNotices.d.ts +37 -0
- package/dist/components/record/RecordNotices.js +75 -0
- package/dist/components/record/RecordSkeleton.d.ts +3 -0
- package/dist/components/record/RecordSkeleton.js +21 -0
- package/dist/components/record/SmartButtons.d.ts +35 -0
- package/dist/components/record/SmartButtons.js +38 -0
- package/dist/components/record/StatusBar.d.ts +51 -0
- package/dist/components/record/StatusBar.js +168 -0
- package/dist/components/record/ThumbBar.d.ts +36 -0
- package/dist/components/record/ThumbBar.js +86 -0
- package/dist/components/record/index.d.ts +6 -0
- package/dist/components/record/index.js +8 -0
- package/dist/components/search/CriteriaBuilder.d.ts +58 -0
- package/dist/components/search/CriteriaBuilder.js +278 -0
- package/dist/components/search/CriteriaChips.d.ts +22 -0
- package/dist/components/search/CriteriaChips.js +64 -0
- package/dist/components/search/FilterBuilder.d.ts +142 -0
- package/dist/components/search/FilterBuilder.js +421 -0
- package/dist/components/search/JoinWord.d.ts +8 -0
- package/dist/components/search/JoinWord.js +47 -0
- package/dist/components/search/JsonEditor.d.ts +19 -0
- package/dist/components/search/JsonEditor.js +78 -0
- package/dist/components/search/SearchBar.d.ts +78 -0
- package/dist/components/search/SearchBar.js +387 -0
- package/dist/components/search/criteria.d.ts +116 -0
- package/dist/components/search/criteria.js +159 -0
- package/dist/components/search/datePeriods.d.ts +122 -0
- package/dist/components/search/datePeriods.js +134 -0
- package/dist/components/search/dateRanges.d.ts +54 -0
- package/dist/components/search/dateRanges.js +89 -0
- package/dist/components/search/index.d.ts +9 -0
- package/dist/components/search/index.js +20 -0
- package/dist/components/search/model.d.ts +54 -0
- package/dist/components/search/model.js +37 -0
- package/dist/components/search/query.d.ts +166 -0
- package/dist/components/search/query.js +436 -0
- package/dist/components/shell/AppFrame.d.ts +14 -0
- package/dist/components/shell/AppFrame.js +80 -0
- package/dist/components/shell/TopBar.d.ts +73 -0
- package/dist/components/shell/TopBar.js +54 -0
- package/dist/components/shell/headerSlot.d.ts +17 -0
- package/dist/components/shell/headerSlot.js +21 -0
- package/dist/components/shell/index.d.ts +2 -0
- package/dist/components/shell/index.js +3 -0
- package/dist/foundations/aria.d.ts +14 -0
- package/dist/foundations/aria.js +18 -0
- package/dist/foundations/calendar.d.ts +28 -0
- package/dist/foundations/calendar.js +183 -0
- package/dist/foundations/copy.d.ts +703 -0
- package/dist/foundations/copy.es.d.ts +2 -0
- package/dist/foundations/copy.es.js +430 -0
- package/dist/foundations/copy.js +520 -0
- package/dist/foundations/density.d.ts +16 -0
- package/dist/foundations/density.js +21 -0
- package/dist/foundations/grouping.d.ts +281 -0
- package/dist/foundations/grouping.js +0 -0
- package/dist/foundations/index.d.ts +14 -0
- package/dist/foundations/index.js +30 -0
- package/dist/foundations/layout.d.ts +9 -0
- package/dist/foundations/layout.js +9 -0
- package/dist/foundations/pivot.d.ts +222 -0
- package/dist/foundations/pivot.js +198 -0
- package/dist/foundations/pivotSheet.d.ts +61 -0
- package/dist/foundations/pivotSheet.js +0 -0
- package/dist/foundations/slots.d.ts +67 -0
- package/dist/foundations/slots.js +63 -0
- package/dist/foundations/sticky.d.ts +53 -0
- package/dist/foundations/sticky.js +150 -0
- package/dist/foundations/surface.d.ts +9 -0
- package/dist/foundations/surface.js +26 -0
- package/dist/foundations/theme.d.ts +42 -0
- package/dist/foundations/theme.js +88 -0
- package/dist/foundations/types.d.ts +23 -0
- package/dist/foundations/types.js +50 -0
- package/dist/foundations/variants.d.ts +19 -0
- package/dist/foundations/variants.js +48 -0
- package/dist/foundations/xlsx.d.ts +86 -0
- package/dist/foundations/xlsx.js +0 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/primitives/block/Block.d.ts +66 -0
- package/dist/primitives/block/Block.js +34 -0
- package/dist/primitives/block/Line.d.ts +18 -0
- package/dist/primitives/block/Line.js +45 -0
- package/dist/primitives/block/SectionGrid.d.ts +30 -0
- package/dist/primitives/block/SectionGrid.js +29 -0
- package/dist/primitives/block/Steps.d.ts +76 -0
- package/dist/primitives/block/Steps.js +43 -0
- package/dist/primitives/block/index.d.ts +4 -0
- package/dist/primitives/block/index.js +7 -0
- package/dist/primitives/button/Button.d.ts +137 -0
- package/dist/primitives/button/Button.js +234 -0
- package/dist/primitives/button/index.d.ts +1 -0
- package/dist/primitives/button/index.js +2 -0
- package/dist/primitives/cell/CellStack.d.ts +47 -0
- package/dist/primitives/cell/CellStack.js +33 -0
- package/dist/primitives/cell/index.d.ts +1 -0
- package/dist/primitives/cell/index.js +3 -0
- package/dist/primitives/choice/ComboBox.d.ts +19 -0
- package/dist/primitives/choice/ComboBox.js +52 -0
- package/dist/primitives/choice/RadioGroup.d.ts +18 -0
- package/dist/primitives/choice/RadioGroup.js +50 -0
- package/dist/primitives/choice/SearchField.d.ts +33 -0
- package/dist/primitives/choice/SearchField.js +46 -0
- package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
- package/dist/primitives/choice/SegmentedControl.js +69 -0
- package/dist/primitives/choice/Slider.d.ts +16 -0
- package/dist/primitives/choice/Slider.js +46 -0
- package/dist/primitives/choice/Switch.d.ts +14 -0
- package/dist/primitives/choice/Switch.js +65 -0
- package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
- package/dist/primitives/choice/ThemeSwitcher.js +25 -0
- package/dist/primitives/choice/index.d.ts +8 -0
- package/dist/primitives/choice/index.js +11 -0
- package/dist/primitives/choice/options.d.ts +28 -0
- package/dist/primitives/choice/options.js +6 -0
- package/dist/primitives/date/Calendar.d.ts +31 -0
- package/dist/primitives/date/Calendar.js +134 -0
- package/dist/primitives/date/DatePicker.d.ts +42 -0
- package/dist/primitives/date/DatePicker.js +103 -0
- package/dist/primitives/date/DateTime.d.ts +35 -0
- package/dist/primitives/date/DateTime.js +100 -0
- package/dist/primitives/date/index.d.ts +3 -0
- package/dist/primitives/date/index.js +6 -0
- package/dist/primitives/dialog/Dialog.d.ts +118 -0
- package/dist/primitives/dialog/Dialog.js +121 -0
- package/dist/primitives/dialog/Drill.d.ts +90 -0
- package/dist/primitives/dialog/Drill.js +172 -0
- package/dist/primitives/dialog/index.d.ts +2 -0
- package/dist/primitives/dialog/index.js +4 -0
- package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
- package/dist/primitives/disclosure/Disclosure.js +52 -0
- package/dist/primitives/disclosure/index.d.ts +1 -0
- package/dist/primitives/disclosure/index.js +2 -0
- package/dist/primitives/field/Checks.d.ts +57 -0
- package/dist/primitives/field/Checks.js +72 -0
- package/dist/primitives/field/DropZone.d.ts +42 -0
- package/dist/primitives/field/DropZone.js +52 -0
- package/dist/primitives/field/Field.d.ts +178 -0
- package/dist/primitives/field/Field.js +251 -0
- package/dist/primitives/field/Suggest.d.ts +48 -0
- package/dist/primitives/field/Suggest.js +43 -0
- package/dist/primitives/field/aria.d.ts +61 -0
- package/dist/primitives/field/aria.js +70 -0
- package/dist/primitives/field/context.d.ts +30 -0
- package/dist/primitives/field/context.js +13 -0
- package/dist/primitives/field/copy.d.ts +16 -0
- package/dist/primitives/field/copy.js +69 -0
- package/dist/primitives/field/index.d.ts +9 -0
- package/dist/primitives/field/index.js +15 -0
- package/dist/primitives/field/layout.d.ts +24 -0
- package/dist/primitives/field/layout.js +10 -0
- package/dist/primitives/field/mode.d.ts +9 -0
- package/dist/primitives/field/mode.js +13 -0
- package/dist/primitives/field/note.d.ts +55 -0
- package/dist/primitives/field/note.js +117 -0
- package/dist/primitives/hint/Hint.d.ts +31 -0
- package/dist/primitives/hint/Hint.js +42 -0
- package/dist/primitives/hint/index.d.ts +1 -0
- package/dist/primitives/hint/index.js +1 -0
- package/dist/primitives/index.d.ts +20 -0
- package/dist/primitives/index.js +21 -0
- package/dist/primitives/menu/Menu.d.ts +128 -0
- package/dist/primitives/menu/Menu.js +89 -0
- package/dist/primitives/menu/index.d.ts +1 -0
- package/dist/primitives/menu/index.js +2 -0
- package/dist/primitives/notice/Callout.d.ts +49 -0
- package/dist/primitives/notice/Callout.js +28 -0
- package/dist/primitives/notice/Flag.d.ts +13 -0
- package/dist/primitives/notice/Flag.js +31 -0
- package/dist/primitives/notice/Notice.d.ts +45 -0
- package/dist/primitives/notice/Notice.js +61 -0
- package/dist/primitives/notice/Toast.d.ts +55 -0
- package/dist/primitives/notice/Toast.js +142 -0
- package/dist/primitives/notice/index.d.ts +4 -0
- package/dist/primitives/notice/index.js +6 -0
- package/dist/primitives/overlay/Popover.d.ts +6 -0
- package/dist/primitives/overlay/Popover.js +27 -0
- package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
- package/dist/primitives/overlay/PopoverTrigger.js +38 -0
- package/dist/primitives/overlay/Preview.d.ts +19 -0
- package/dist/primitives/overlay/Preview.js +60 -0
- package/dist/primitives/overlay/Tooltip.d.ts +38 -0
- package/dist/primitives/overlay/Tooltip.js +74 -0
- package/dist/primitives/overlay/index.d.ts +4 -0
- package/dist/primitives/overlay/index.js +7 -0
- package/dist/primitives/picker/Picker.d.ts +89 -0
- package/dist/primitives/picker/Picker.js +204 -0
- package/dist/primitives/picker/index.d.ts +1 -0
- package/dist/primitives/picker/index.js +2 -0
- package/dist/primitives/surface/Peek.d.ts +60 -0
- package/dist/primitives/surface/Peek.js +71 -0
- package/dist/primitives/surface/index.d.ts +1 -0
- package/dist/primitives/surface/index.js +2 -0
- package/dist/primitives/table/CellCopy.d.ts +20 -0
- package/dist/primitives/table/CellCopy.js +23 -0
- package/dist/primitives/table/CellEditor.d.ts +14 -0
- package/dist/primitives/table/CellEditor.js +83 -0
- package/dist/primitives/table/DataTable.d.ts +189 -0
- package/dist/primitives/table/DataTable.js +330 -0
- package/dist/primitives/table/EditableTable.d.ts +191 -0
- package/dist/primitives/table/EditableTable.js +617 -0
- package/dist/primitives/table/PivotTable.d.ts +14 -0
- package/dist/primitives/table/PivotTable.js +0 -0
- package/dist/primitives/table/ReportTable.d.ts +38 -0
- package/dist/primitives/table/ReportTable.js +106 -0
- package/dist/primitives/table/RowActions.d.ts +16 -0
- package/dist/primitives/table/RowActions.js +28 -0
- package/dist/primitives/table/SheetTable.d.ts +73 -0
- package/dist/primitives/table/SheetTable.js +230 -0
- package/dist/primitives/table/Table.d.ts +106 -0
- package/dist/primitives/table/Table.js +116 -0
- package/dist/primitives/table/index.d.ts +7 -0
- package/dist/primitives/table/index.js +9 -0
- package/dist/primitives/tabs/Tabs.d.ts +91 -0
- package/dist/primitives/tabs/Tabs.js +56 -0
- package/dist/primitives/tabs/index.d.ts +1 -0
- package/dist/primitives/tabs/index.js +1 -0
- package/dist/primitives/tag/Tag.d.ts +36 -0
- package/dist/primitives/tag/Tag.js +68 -0
- package/dist/primitives/tag/Tags.d.ts +57 -0
- package/dist/primitives/tag/Tags.js +104 -0
- package/dist/primitives/tag/TagsField.d.ts +35 -0
- package/dist/primitives/tag/TagsField.js +186 -0
- package/dist/primitives/tag/choices.d.ts +38 -0
- package/dist/primitives/tag/choices.js +26 -0
- package/dist/primitives/tag/index.d.ts +4 -0
- package/dist/primitives/tag/index.js +8 -0
- package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
- package/dist/primitives/toolbar/Toolbar.js +36 -0
- package/dist/primitives/toolbar/index.d.ts +1 -0
- package/dist/primitives/toolbar/index.js +2 -0
- package/dist/primitives/tree/Tree.d.ts +56 -0
- package/dist/primitives/tree/Tree.js +74 -0
- package/dist/primitives/tree/index.d.ts +1 -0
- package/dist/primitives/tree/index.js +3 -0
- package/dist/primitives/value/Copyable.d.ts +48 -0
- package/dist/primitives/value/Copyable.js +71 -0
- package/dist/primitives/value/Fecha.d.ts +12 -0
- package/dist/primitives/value/Fecha.js +89 -0
- package/dist/primitives/value/Meter.d.ts +29 -0
- package/dist/primitives/value/Meter.js +46 -0
- package/dist/primitives/value/Metric.d.ts +42 -0
- package/dist/primitives/value/Metric.js +38 -0
- package/dist/primitives/value/Person.d.ts +90 -0
- package/dist/primitives/value/Person.js +208 -0
- package/dist/primitives/value/ProgressBar.d.ts +31 -0
- package/dist/primitives/value/ProgressBar.js +53 -0
- package/dist/primitives/value/Relation.d.ts +74 -0
- package/dist/primitives/value/Relation.js +74 -0
- package/dist/primitives/value/Totals.d.ts +121 -0
- package/dist/primitives/value/Totals.js +54 -0
- package/dist/primitives/value/Value.d.ts +40 -0
- package/dist/primitives/value/Value.js +80 -0
- package/dist/primitives/value/index.d.ts +9 -0
- package/dist/primitives/value/index.js +10 -0
- package/dist/tokens.css +1062 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +3 -0
- package/dist/views/list/List.d.ts +119 -0
- package/dist/views/list/List.js +101 -0
- package/dist/views/list/ListView.d.ts +6 -0
- package/dist/views/list/ListView.js +295 -0
- package/dist/views/list/columns.d.ts +18 -0
- package/dist/views/list/columns.js +27 -0
- package/dist/views/list/derive.d.ts +87 -0
- package/dist/views/list/derive.js +207 -0
- package/dist/views/list/index.d.ts +2 -0
- package/dist/views/list/index.js +4 -0
- package/dist/views/list/presentations.d.ts +39 -0
- package/dist/views/list/presentations.js +105 -0
- package/dist/views/list/types.d.ts +284 -0
- package/dist/views/list/types.js +1 -0
- package/dist/views/list/useNarrowList.d.ts +17 -0
- package/dist/views/list/useNarrowList.js +32 -0
- package/dist/views/record/Record.d.ts +145 -0
- package/dist/views/record/Record.js +211 -0
- package/dist/views/record/index.d.ts +1 -0
- package/dist/views/record/index.js +2 -0
- package/package.json +113 -4
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { BlockStatus } from "./Block.js";
|
|
2
|
+
import { type LucideIcon } from "lucide-react";
|
|
3
|
+
import { type ReactNode } from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Context: a procedure laid flat — every stage of one piece of work on a single
|
|
6
|
+
* page, each collapsed to its title until you need it.
|
|
7
|
+
*
|
|
8
|
+
* ── Why not a `Wizard` ──────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* A wizard shows one step and hides the rest, which is right when the steps are
|
|
11
|
+
* a single sitting and wrong when they are the work itself. This is for work
|
|
12
|
+
* that spans days: you come back to a half-done piece and the first thing you
|
|
13
|
+
* need is **where it stands** — which stages are settled, which are waiting on
|
|
14
|
+
* you, which cannot start yet — and a wizard answers that with one screen and a
|
|
15
|
+
* «back» button.
|
|
16
|
+
*
|
|
17
|
+
* ── Why collapsed and not a `Block` per stage ───────────────────────
|
|
18
|
+
*
|
|
19
|
+
* `Block` already carries the half of this that matters — a header that says
|
|
20
|
+
* «Required» or «(Incomplete)» so you can see what is missing WITHOUT opening
|
|
21
|
+
* anything. What it cannot do is *close*. Seven open blocks is a page nobody
|
|
22
|
+
* scrolls; seven closed ones with their state on the line is an index of the
|
|
23
|
+
* work, and that index IS the view.
|
|
24
|
+
*
|
|
25
|
+
* ── Locked is a sentence, never a disabled box ──────────────────────
|
|
26
|
+
*
|
|
27
|
+
* A stage that cannot start yet says **what would unlock it**, because «you
|
|
28
|
+
* cannot do this» without «do that first» is the failure mode that makes people
|
|
29
|
+
* click every section looking for the one that works.
|
|
30
|
+
*/
|
|
31
|
+
export type StepProps = {
|
|
32
|
+
title: string;
|
|
33
|
+
/** The stage's own mark, to the left of the title — what the eye finds when
|
|
34
|
+
* scanning a closed stack. */
|
|
35
|
+
icon?: LucideIcon;
|
|
36
|
+
/** «Required», «(Incomplete)», «(Complete)» — whatever this stage's state is
|
|
37
|
+
* called by whoever composes; the kit does not know what complete means. */
|
|
38
|
+
status?: BlockStatus;
|
|
39
|
+
/** Present when the stage cannot be opened yet. The sentence says what would
|
|
40
|
+
* unlock it, and it is shown in place of the contents. */
|
|
41
|
+
locked?: string;
|
|
42
|
+
/** What can be done with this stage, on its header line. Hidden while closed:
|
|
43
|
+
* an action you cannot see the context of is an action you press by mistake. */
|
|
44
|
+
actions?: ReactNode;
|
|
45
|
+
open: boolean;
|
|
46
|
+
onToggle: () => void;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* One stage. Controlled on purpose: which stage is open is a fact about where
|
|
51
|
+
* the work stands, so the page owns it — completing one can open the next, and
|
|
52
|
+
* arriving at a half-done piece can open the one that is waiting.
|
|
53
|
+
*/
|
|
54
|
+
export declare function Step({ title, icon: Icon, status, locked, actions, open, onToggle, children, }: StepProps): import("react").JSX.Element;
|
|
55
|
+
export type StepsProps = {
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
};
|
|
58
|
+
/** The stack. One frame: the outer corners are the only rounded ones, and the
|
|
59
|
+
* stages share a hairline. A box around a box would be two frames arguing. */
|
|
60
|
+
export declare function Steps({ children }: StepsProps): import("react").JSX.Element;
|
|
61
|
+
/**
|
|
62
|
+
* Which stage is open, when the page has no better answer than «one at a time».
|
|
63
|
+
*
|
|
64
|
+
* Returns the current id and a toggle: pressing the open one closes it, so a
|
|
65
|
+
* fully collapsed stack —the index of the work— is always one click away.
|
|
66
|
+
*
|
|
67
|
+
* `openStep` exists for the rail: the button that says what to do next lives up
|
|
68
|
+
* there with the other actions, and pressing it has to open the stage down here
|
|
69
|
+
* that does it.
|
|
70
|
+
*/
|
|
71
|
+
export declare function useSteps(initial?: string): {
|
|
72
|
+
open: string | undefined;
|
|
73
|
+
isOpen: (id: string) => boolean;
|
|
74
|
+
toggle: (id: string) => void;
|
|
75
|
+
openStep: (id: string) => void;
|
|
76
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Tag } from "../tag/Tag.js";
|
|
4
|
+
import { ChevronDown, Lock } from "lucide-react";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
/**
|
|
7
|
+
* One stage. Controlled on purpose: which stage is open is a fact about where
|
|
8
|
+
* the work stands, so the page owns it — completing one can open the next, and
|
|
9
|
+
* arriving at a half-done piece can open the one that is waiting.
|
|
10
|
+
*/
|
|
11
|
+
export function Step({ title, icon: Icon, status, locked, actions, open, onToggle, children, }) {
|
|
12
|
+
const isLocked = locked !== undefined;
|
|
13
|
+
const expanded = open && !isLocked;
|
|
14
|
+
return (_jsxs("section", { className: "min-w-0 border-t border-line bg-surface first:border-t-0", children: [_jsx("h3", { className: "min-w-0", children: _jsxs("button", { type: "button", onClick: onToggle, "aria-expanded": expanded,
|
|
15
|
+
/* Open is a ground bar: the stage you are in is the only row with a
|
|
16
|
+
fill. Closed rows stay on the paper, so the stack still reads as
|
|
17
|
+
an index. */
|
|
18
|
+
className: `group/step relative flex w-full min-w-0 items-center px-3 py-2.5 text-left ${expanded ? "border-b border-line bg-ground" : ""} ${isLocked ? "cursor-default" : "cursor-pointer"}`, children: [isLocked ? null : (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-x-1 inset-y-0.5 rounded-control group-hover/step:bg-ground" })), _jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 items-center gap-3", children: [Icon === undefined ? null : (_jsx(Icon, { className: `size-(--size-icon-md) shrink-0 ${isLocked ? "text-muted/60" : "text-muted"}`, "aria-hidden": true })), _jsx("span", { className: `min-w-0 truncate text-meta font-medium ${isLocked ? "text-muted" : "text-ink"}`, children: title }), isLocked ? (_jsx(Lock, { className: "size-(--size-icon-sm) shrink-0 text-muted/60", "aria-hidden": true })) : status === undefined ? null : (_jsx(Tag, { tone: status.tone ?? "neutral", children: status.label })), _jsx(ChevronDown, { className: `ml-auto size-(--size-icon-md) shrink-0 text-muted transition-transform ${expanded ? "rotate-180" : ""}`, "aria-hidden": true })] })] }) }), !expanded ? null : (_jsxs("div", { className: "px-3 py-3", children: [actions === undefined ? null : (_jsx("div", { className: "mb-3 flex flex-wrap items-center gap-1", children: actions })), children] })), !isLocked ? null : (_jsx("p", { className: "border-t border-line px-3 py-2 text-micro text-muted", children: locked }))] }));
|
|
19
|
+
}
|
|
20
|
+
/** The stack. One frame: the outer corners are the only rounded ones, and the
|
|
21
|
+
* stages share a hairline. A box around a box would be two frames arguing. */
|
|
22
|
+
export function Steps({ children }) {
|
|
23
|
+
return (_jsx("div", { className: "flex min-w-0 flex-col overflow-hidden rounded-box border border-line bg-surface", children: children }));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Which stage is open, when the page has no better answer than «one at a time».
|
|
27
|
+
*
|
|
28
|
+
* Returns the current id and a toggle: pressing the open one closes it, so a
|
|
29
|
+
* fully collapsed stack —the index of the work— is always one click away.
|
|
30
|
+
*
|
|
31
|
+
* `openStep` exists for the rail: the button that says what to do next lives up
|
|
32
|
+
* there with the other actions, and pressing it has to open the stage down here
|
|
33
|
+
* that does it.
|
|
34
|
+
*/
|
|
35
|
+
export function useSteps(initial) {
|
|
36
|
+
const [open, setOpen] = useState(initial);
|
|
37
|
+
return {
|
|
38
|
+
open,
|
|
39
|
+
isOpen: (id) => open === id,
|
|
40
|
+
toggle: (id) => setOpen((current) => (current === id ? undefined : id)),
|
|
41
|
+
openStep: (id) => setOpen(id),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Block, type BlockProps, type BlockStatus, Rule, SectionHeader, } from "./Block.js";
|
|
2
|
+
export { FullWidth, SectionGrid } from "./SectionGrid.js";
|
|
3
|
+
export { Line } from "./Line.js";
|
|
4
|
+
export { Step, type StepProps, Steps, type StepsProps, useSteps, } from "./Steps.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* The structure of a view: where each section starts and ends. */
|
|
2
|
+
export { Block, Rule, SectionHeader, } from "./Block.js";
|
|
3
|
+
export { FullWidth, SectionGrid } from "./SectionGrid.js";
|
|
4
|
+
/* La línea de tres zonas: lo que centra el centro contra la LÍNEA y no contra
|
|
5
|
+
lo que sobra. La usan las dos bandas — la del registro y la de la lista. */
|
|
6
|
+
export { Line } from "./Line.js";
|
|
7
|
+
export { Step, Steps, useSteps, } from "./Steps.js";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Surface } from "../../foundations/types.js";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
3
|
+
import type { ComponentPropsWithRef, ReactNode } from "react";
|
|
4
|
+
export type ButtonRole = "primary" | "secondary" | "ghost" | "danger";
|
|
5
|
+
export type ButtonSize = "sm" | "md" | "lg";
|
|
6
|
+
export type { Surface };
|
|
7
|
+
/** The icon accompanies the label and never replaces it.
|
|
8
|
+
*
|
|
9
|
+
* Size `sm` (12px) on the small button and `md` (16px) on the medium one, with
|
|
10
|
+
* the stroke that belongs to each — see the iconography scale: what stays
|
|
11
|
+
* constant is the optical weight, not the number. */
|
|
12
|
+
/** The look of a button, without the button.
|
|
13
|
+
*
|
|
14
|
+
* Context: it exists because there are things that LOOK like a button and are
|
|
15
|
+
* not — a menu trigger is the case. Without this, each one copies the classes
|
|
16
|
+
* and by the third copy they no longer match: that is exactly what happened
|
|
17
|
+
* with «+ Crear», which was the only primary button in the product with a
|
|
18
|
+
* different height. */
|
|
19
|
+
export declare function buttonClasses(role?: ButtonRole, size?: ButtonSize, surface?: Surface): string;
|
|
20
|
+
/** What kind of thing a trigger opens. `menu` is the default because it is what the
|
|
21
|
+
* `expanded` prop always announced; a `PopoverTrigger` opens a `dialog`, a picker a
|
|
22
|
+
* `listbox`. */
|
|
23
|
+
export type ButtonPopup = "menu" | "dialog" | "listbox";
|
|
24
|
+
/** The native attributes a system button lets through.
|
|
25
|
+
*
|
|
26
|
+
* Context: React Aria's triggers (`TooltipTrigger`, `DialogTrigger`, a `Toolbar`) talk to
|
|
27
|
+
* the control by handing it a `ref` and DOM props — pointer and focus handlers,
|
|
28
|
+
* `aria-describedby`, `aria-expanded`. A button that drops them is a button the tooltip
|
|
29
|
+
* never learns was hovered. So everything native goes through, and `ref` is a plain prop
|
|
30
|
+
* (React 19).
|
|
31
|
+
*
|
|
32
|
+
* What stays out on purpose: `className` and `style` — the look is the role, the size and
|
|
33
|
+
* the surface, and a primitive that takes classes from outside stops being the one place
|
|
34
|
+
* where a button is decided; `role`, because here it is the button's ROLE in the hierarchy
|
|
35
|
+
* (`primary`…), not an ARIA role; and the attributes the props below already own. */
|
|
36
|
+
type NativeProps<E extends "button" | "a", Own extends string> = Omit<ComponentPropsWithRef<E>, "children" | "className" | "style" | "role" | Own>;
|
|
37
|
+
export type ButtonProps = NativeProps<"button", "type" | "disabled" | "title" | "aria-haspopup" | "aria-expanded"> & {
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
role?: ButtonRole;
|
|
40
|
+
size?: ButtonSize;
|
|
41
|
+
surface?: Surface;
|
|
42
|
+
/** To the left of the label. Never alone: a button with only an icon is an
|
|
43
|
+
* `IconButton` and has its own piece, with its mandatory `aria-label`. */
|
|
44
|
+
icon?: LucideIcon;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
title?: string;
|
|
47
|
+
/** `submit` for the button that sends the form it is in. `button` by default, so a
|
|
48
|
+
* button dropped inside a `<form>` never submits it by accident. */
|
|
49
|
+
type?: "button" | "submit";
|
|
50
|
+
/** Context: the button that opens a dropdown has to say so. Without
|
|
51
|
+
* `aria-expanded`, a screen reader announces it as an ordinary button and
|
|
52
|
+
* whoever uses it does not know there is something open underneath. */
|
|
53
|
+
expanded?: boolean;
|
|
54
|
+
/** What it opens, written to `aria-haspopup`. `menu` when absent. */
|
|
55
|
+
haspopup?: ButtonPopup;
|
|
56
|
+
};
|
|
57
|
+
export declare function Button({ children, role, size, surface: surfaceProp, icon: Icon, disabled, title, type, expanded, haspopup, ref, ...native }: ButtonProps): import("react").JSX.Element;
|
|
58
|
+
export type ButtonLinkProps = NativeProps<"a", "href" | "download" | "title"> & {
|
|
59
|
+
children: ReactNode;
|
|
60
|
+
href: string;
|
|
61
|
+
role?: ButtonRole;
|
|
62
|
+
size?: ButtonSize;
|
|
63
|
+
surface?: Surface;
|
|
64
|
+
icon?: LucideIcon;
|
|
65
|
+
/** The name it is saved with. `true` lets the server decide it. */
|
|
66
|
+
download?: string | boolean;
|
|
67
|
+
title?: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The same button, but it is a real link.
|
|
71
|
+
*
|
|
72
|
+
* Context: it exists because a `<button>` that navigates breaks middle-click,
|
|
73
|
+
* «open in a new tab» and «save as» — and downloading a file is exactly that.
|
|
74
|
+
* The look is shared entirely with `Button`: two different silhouettes for
|
|
75
|
+
* «download» and «synthesize» in the same bar is the problem this file exists
|
|
76
|
+
* in order not to have.
|
|
77
|
+
*
|
|
78
|
+
* It does not carry `inline-block`: a flex item gets blockified anyway, and with
|
|
79
|
+
* that class the `<a>`'s box measured its padding with zero content and the text
|
|
80
|
+
* was painted on top of its neighbor.
|
|
81
|
+
*/
|
|
82
|
+
export declare function ButtonLink({ children, href, role, size, surface: surfaceProp, icon: Icon, download, title, ref, ...native }: ButtonLinkProps): import("react").JSX.Element;
|
|
83
|
+
/** The `IconButton` is SQUARE: its side is the height of its step. An icon
|
|
84
|
+
* button narrower than it is tall has less area than it appears to, and misses
|
|
85
|
+
* when you aim right at the edges. */
|
|
86
|
+
export type IconTone = "normal" | "action" | "danger";
|
|
87
|
+
export type IconButtonProps = NativeProps<"button", "type" | "disabled" | "title" | "aria-label" | "aria-haspopup" | "aria-expanded"> & {
|
|
88
|
+
icon: LucideIcon;
|
|
89
|
+
/** What is read aloud and shown in the tooltip. It is not optional: an icon
|
|
90
|
+
* alone does not say what it does, and whoever knows already knew what to
|
|
91
|
+
* press. */
|
|
92
|
+
label: string;
|
|
93
|
+
size?: ButtonSize;
|
|
94
|
+
surface?: Surface;
|
|
95
|
+
tone?: IconTone;
|
|
96
|
+
disabled?: boolean;
|
|
97
|
+
type?: "button" | "submit";
|
|
98
|
+
/** Opens a menu and says whether it is open. Without this, a screen reader
|
|
99
|
+
* announces «button» where there is a dropdown. */
|
|
100
|
+
expanded?: boolean;
|
|
101
|
+
/** What it opens, written to `aria-haspopup`. `menu` when absent. */
|
|
102
|
+
haspopup?: ButtonPopup;
|
|
103
|
+
/** The system's `Tooltip` with the `label`, on hover and focus. On by default; `false`
|
|
104
|
+
* falls back to the native `title`. */
|
|
105
|
+
tooltip?: boolean;
|
|
106
|
+
};
|
|
107
|
+
/** Only an icon, and that is why the label is mandatory even though it is not
|
|
108
|
+
* drawn.
|
|
109
|
+
*
|
|
110
|
+
* It exists because «download» was a grey 11 px `↓` character with no box: the
|
|
111
|
+
* most frequent action of a row was the hardest one to hit. Here it has its
|
|
112
|
+
* full control height and a background on hover — the target is the same as
|
|
113
|
+
* that of a button with text, only without the text. */
|
|
114
|
+
export declare function IconButton({ icon: Icon, label, size, surface: surfaceProp, tone, disabled, type, expanded, haspopup, tooltip, ref, ...native }: IconButtonProps): import("react").JSX.Element;
|
|
115
|
+
export type IconLinkProps = NativeProps<"a", "href" | "download" | "title" | "aria-label"> & {
|
|
116
|
+
icon: LucideIcon;
|
|
117
|
+
label: string;
|
|
118
|
+
href: string;
|
|
119
|
+
/** The name it is saved with. Present = the browser downloads the file
|
|
120
|
+
* instead of navigating. */
|
|
121
|
+
download?: string;
|
|
122
|
+
size?: ButtonSize;
|
|
123
|
+
surface?: Surface;
|
|
124
|
+
tone?: IconTone;
|
|
125
|
+
/** As in `IconButton`: the system's `Tooltip` by default, the native `title` with
|
|
126
|
+
* `false`. */
|
|
127
|
+
tooltip?: boolean;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* The same icon, but it is a real link.
|
|
131
|
+
*
|
|
132
|
+
* Context: it exists for the same reason as `ButtonLink` — downloading a file or
|
|
133
|
+
* going to a view is NOT pressing a button. With a `<button>` you lose
|
|
134
|
+
* middle-click, «copy the address» and the browser's `download`, which is the
|
|
135
|
+
* only thing that keeps a 150 MB CSV from going through the tab's memory.
|
|
136
|
+
*/
|
|
137
|
+
export declare function IconLink({ icon: Icon, label, href, download, size, surface: surfaceProp, tone, tooltip, ref, ...native }: IconLinkProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/* ==================================================================== *
|
|
4
|
+
* BUTTON — DO.
|
|
5
|
+
*
|
|
6
|
+
* The product had THREE contradictory primaries at the same time: a blue
|
|
7
|
+
* `+ Crear`, a golden `Registrar pago` and a blue `+ Importar facturas`. Three equal
|
|
8
|
+
* weights saying three different things, and therefore no hierarchy at all.
|
|
9
|
+
*
|
|
10
|
+
* The rule that fixes it is four roles and not one more:
|
|
11
|
+
*
|
|
12
|
+
* primary DO — solid gold, `ink` on top (6.98:1)
|
|
13
|
+
* secondary do without urgency — a box with an edge
|
|
14
|
+
* ghost goes along — no weight, only the color of «go»
|
|
15
|
+
* danger DESTROY — and it cannot be undone
|
|
16
|
+
*
|
|
17
|
+
* **Only one solid gold per surface.** `+ Crear` is the chrome's CTA,
|
|
18
|
+
* `Registrar pago` the panel's: they do not compete because they do not share a
|
|
19
|
+
* surface.
|
|
20
|
+
*
|
|
21
|
+
* ── Why a labeled button is a box, and an icon target is a circle ──
|
|
22
|
+
*
|
|
23
|
+
* `pill` is «what is a capsule and not a box» — a tag, a chip, a counter.
|
|
24
|
+
* A labeled button is the opposite: a box that gets pressed. A row of actions
|
|
25
|
+
* in capsules reads as a row of tags, and then what has to be pressed and
|
|
26
|
+
* what merely informs have the same silhouette. `radius/control` is the step
|
|
27
|
+
* for «what is touched and measures one line of text».
|
|
28
|
+
*
|
|
29
|
+
* An icon-only target has no line of text. It is a square hit area, and a
|
|
30
|
+
* square with an 8px corner still reads as a leftover box. The circle is the
|
|
31
|
+
* silhouette of that target (the same call Spectrum makes for icon-only
|
|
32
|
+
* buttons). It does not meet the tag: the tag has text, the circle does not.
|
|
33
|
+
*
|
|
34
|
+
* ── The weight of the type is hierarchy too ─────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* The four roles do NOT carry the same weight, and that is deliberate. The
|
|
37
|
+
* primary goes in `semibold` because it is the only thing on the screen that
|
|
38
|
+
* has to be seen from far away; the rest in `medium`. Putting all four at the
|
|
39
|
+
* same weight forces color to do the whole job of hierarchy by itself — and
|
|
40
|
+
* then someone glancing at the screen out of the corner of their eye, or who
|
|
41
|
+
* cannot tell the gold from the red apart, sees four identical buttons.
|
|
42
|
+
*
|
|
43
|
+
* ── `surface`: the prop wins, the context is the default ────────────
|
|
44
|
+
*
|
|
45
|
+
* The same piece lives on both surfaces and does NOT behave the same: on the
|
|
46
|
+
* canvas, «ghost» is painted with the navigation blue (7.48:1); on the chrome
|
|
47
|
+
* that blue gives 1.4:1 and the lightened one has to be used. The band that is
|
|
48
|
+
* dark (`AppFrame`'s bar, a `Record.Rail surface="chrome"`) provides its surface
|
|
49
|
+
* through `SurfaceProvider`, so a button written inside it takes the right
|
|
50
|
+
* palette without remembering to say so — which is how a navy band ends up with
|
|
51
|
+
* canvas-colored buttons. The explicit prop still wins, so a one-off decision
|
|
52
|
+
* stays auditable by reading the JSX.
|
|
53
|
+
*
|
|
54
|
+
* ── What this file does NOT do ──────────────────────────────────────
|
|
55
|
+
*
|
|
56
|
+
* It does not know what an invoice is, it does not call anything, it has no
|
|
57
|
+
* state and it does not write a single value by hand: every height, every
|
|
58
|
+
* radius and every color comes from a token. If it needed any of those things,
|
|
59
|
+
* it would not be a primitive.
|
|
60
|
+
* ==================================================================== */
|
|
61
|
+
import { useSurface } from "../../foundations/surface.js";
|
|
62
|
+
import { Tooltip, TooltipTrigger, useInTooltipTrigger, } from "../overlay/Tooltip.js";
|
|
63
|
+
/** The height comes from `--size-control-*`; the padding and the type follow.
|
|
64
|
+
*
|
|
65
|
+
* sm a row, the header of a block
|
|
66
|
+
* md the working size — the action bar of a record
|
|
67
|
+
* lg a SINGLE, prominent action: a dialog's CTA, an empty state
|
|
68
|
+
*
|
|
69
|
+
* **Every button in the same row measures the same.** `lg` is not «the
|
|
70
|
+
* primary»: it is for when the button is alone. Mixing heights in a bar looks
|
|
71
|
+
* like an alignment mistake, not like hierarchy — and hierarchy is already
|
|
72
|
+
* done by the color and the weight, which work without changing the size of
|
|
73
|
+
* anything.
|
|
74
|
+
*
|
|
75
|
+
* The padding grows FASTER than the height (3 → 4 → 6). A tall, narrow button
|
|
76
|
+
* reads as a stretched icon; what gives it presence is the air on the sides of
|
|
77
|
+
* the label, not the height alone. */
|
|
78
|
+
const SIZE = {
|
|
79
|
+
sm: "h-(--size-control-sm) px-3 gap-1.5 text-meta",
|
|
80
|
+
md: "h-(--size-control-md) px-4 gap-2 text-body",
|
|
81
|
+
lg: "h-(--size-control-lg) px-6 gap-2 text-lead",
|
|
82
|
+
};
|
|
83
|
+
/** The weight per role. See the header note: it is not aesthetics, it is half
|
|
84
|
+
* of the hierarchy — the half that keeps working out of the corner of your
|
|
85
|
+
* eye. */
|
|
86
|
+
const WEIGHT = {
|
|
87
|
+
primary: "font-semibold",
|
|
88
|
+
secondary: "font-medium",
|
|
89
|
+
ghost: "font-medium",
|
|
90
|
+
danger: "font-semibold",
|
|
91
|
+
};
|
|
92
|
+
/** Each role on its two surfaces. The table exists so that adding a third
|
|
93
|
+
* surface is one column here and not an `if` scattered across the tree — and
|
|
94
|
+
* so that the day a combination is missing, the compiler says so. */
|
|
95
|
+
const ROLE = {
|
|
96
|
+
primary: {
|
|
97
|
+
// `btn-accent` is the brand gradient, use 3 of 4. Barely a glint of metal:
|
|
98
|
+
// a strong gradient turns a tool into a landing page.
|
|
99
|
+
canvas: "btn-accent border border-transparent hover:brightness-[1.04] active:brightness-95",
|
|
100
|
+
chrome: "btn-accent border border-transparent hover:brightness-[1.04] active:brightness-95",
|
|
101
|
+
},
|
|
102
|
+
secondary: {
|
|
103
|
+
// Quiet on purpose. The blue belongs to `ghost` (go) and the gold to
|
|
104
|
+
// `primary` (do). A secondary painted in the action color is a second
|
|
105
|
+
// primary, which is the defect this file exists to stop.
|
|
106
|
+
canvas: "border border-line-strong bg-ink/6 text-ink hover:bg-ink/10",
|
|
107
|
+
chrome: "border border-chrome-muted/30 bg-chrome-raised text-chrome-ink hover:bg-chrome-ink/10",
|
|
108
|
+
},
|
|
109
|
+
ghost: {
|
|
110
|
+
canvas: "border border-transparent text-primary hover:bg-primary/8",
|
|
111
|
+
chrome: "border border-transparent text-chrome-link hover:bg-chrome-ink/8",
|
|
112
|
+
},
|
|
113
|
+
danger: {
|
|
114
|
+
canvas: "border border-transparent bg-danger text-surface hover:bg-danger-hover",
|
|
115
|
+
chrome: "border border-transparent bg-danger text-surface hover:bg-danger-hover",
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
/** The icon accompanies the label and never replaces it.
|
|
119
|
+
*
|
|
120
|
+
* Size `sm` (12px) on the small button and `md` (16px) on the medium one, with
|
|
121
|
+
* the stroke that belongs to each — see the iconography scale: what stays
|
|
122
|
+
* constant is the optical weight, not the number. */
|
|
123
|
+
/** The look of a button, without the button.
|
|
124
|
+
*
|
|
125
|
+
* Context: it exists because there are things that LOOK like a button and are
|
|
126
|
+
* not — a menu trigger is the case. Without this, each one copies the classes
|
|
127
|
+
* and by the third copy they no longer match: that is exactly what happened
|
|
128
|
+
* with «+ Crear», which was the only primary button in the product with a
|
|
129
|
+
* different height. */
|
|
130
|
+
export function buttonClasses(role = "secondary", size = "md", surface = "canvas") {
|
|
131
|
+
return `inline-flex shrink-0 items-center justify-center rounded-control whitespace-nowrap transition-[background-color,border-color,filter,color] duration-(--duration-instant) ease-(--ease-out) disabled:pointer-events-none disabled:opacity-45 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary ${SIZE[size]} ${WEIGHT[role]} ${ROLE[role][surface]}`;
|
|
132
|
+
}
|
|
133
|
+
const ICON = {
|
|
134
|
+
sm: { size: "size-(--size-icon-sm)", stroke: 2 },
|
|
135
|
+
md: { size: "size-(--size-icon-md)", stroke: 1.75 },
|
|
136
|
+
lg: { size: "size-(--size-icon-lg)", stroke: 1.5 },
|
|
137
|
+
};
|
|
138
|
+
/** `aria-haspopup` + `aria-expanded`, only when the button opens something. Spread AFTER the
|
|
139
|
+
* native props, and only when it applies, so it never erases what a React Aria trigger
|
|
140
|
+
* already wrote. */
|
|
141
|
+
function popupAttrs(expanded, haspopup) {
|
|
142
|
+
if (expanded === undefined && haspopup === undefined)
|
|
143
|
+
return {};
|
|
144
|
+
return {
|
|
145
|
+
"aria-haspopup": haspopup ?? "menu",
|
|
146
|
+
...(expanded === undefined ? {} : { "aria-expanded": expanded }),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export function Button({ children, role = "secondary", size = "md", surface: surfaceProp, icon: Icon, disabled = false, title, type = "button", expanded, haspopup, ref, ...native }) {
|
|
150
|
+
const surface = useSurface(surfaceProp);
|
|
151
|
+
const ic = ICON[size];
|
|
152
|
+
return (_jsxs("button", { ...native, ref: ref, type: type, title: title, disabled: disabled, ...popupAttrs(expanded, haspopup), "data-emphasis": role, className: buttonClasses(role, size, surface), children: [Icon === undefined ? null : (_jsx(Icon, { className: `${ic.size} shrink-0`, strokeWidth: ic.stroke, "aria-hidden": true })), children] }));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The same button, but it is a real link.
|
|
156
|
+
*
|
|
157
|
+
* Context: it exists because a `<button>` that navigates breaks middle-click,
|
|
158
|
+
* «open in a new tab» and «save as» — and downloading a file is exactly that.
|
|
159
|
+
* The look is shared entirely with `Button`: two different silhouettes for
|
|
160
|
+
* «download» and «synthesize» in the same bar is the problem this file exists
|
|
161
|
+
* in order not to have.
|
|
162
|
+
*
|
|
163
|
+
* It does not carry `inline-block`: a flex item gets blockified anyway, and with
|
|
164
|
+
* that class the `<a>`'s box measured its padding with zero content and the text
|
|
165
|
+
* was painted on top of its neighbor.
|
|
166
|
+
*/
|
|
167
|
+
export function ButtonLink({ children, href, role = "secondary", size = "md", surface: surfaceProp, icon: Icon, download, title, ref, ...native }) {
|
|
168
|
+
const surface = useSurface(surfaceProp);
|
|
169
|
+
const ic = ICON[size];
|
|
170
|
+
return (_jsxs("a", { ...native, ref: ref, "data-emphasis": role, href: href, title: title, ...(download === undefined ? {} : { download }), className: `${buttonClasses(role, size, surface)} no-underline`, children: [Icon === undefined ? null : (_jsx(Icon, { className: `${ic.size} shrink-0`, strokeWidth: ic.stroke, "aria-hidden": true })), children] }));
|
|
171
|
+
}
|
|
172
|
+
/** Outside the component because the button and the link share it: the same
|
|
173
|
+
* icon has to look the same whether it leads to an action or to a file. */
|
|
174
|
+
const TONE = {
|
|
175
|
+
normal: {
|
|
176
|
+
canvas: "text-muted hover:bg-ink/6 hover:text-ink",
|
|
177
|
+
chrome: "text-chrome-muted hover:bg-chrome-ink/10 hover:text-chrome-ink",
|
|
178
|
+
},
|
|
179
|
+
action: {
|
|
180
|
+
canvas: "text-primary hover:bg-primary/8",
|
|
181
|
+
chrome: "text-chrome-link hover:bg-chrome-link/12",
|
|
182
|
+
},
|
|
183
|
+
danger: {
|
|
184
|
+
canvas: "text-danger hover:bg-danger/8",
|
|
185
|
+
chrome: "text-chrome-bad hover:bg-chrome-bad/12",
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
const BOX = "inline-flex shrink-0 items-center justify-center rounded-pill transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary";
|
|
189
|
+
const SQUARE = {
|
|
190
|
+
sm: "size-(--size-control-sm)",
|
|
191
|
+
md: "size-(--size-control-md)",
|
|
192
|
+
lg: "size-(--size-control-lg)",
|
|
193
|
+
};
|
|
194
|
+
/** The label of an icon, shown.
|
|
195
|
+
*
|
|
196
|
+
* Context: the native `title` was the only way to learn what an icon does, and it is the
|
|
197
|
+
* browser's tooltip — late, unstyled, never on keyboard focus. So an icon target draws the
|
|
198
|
+
* system's `Tooltip` with its `label` instead, on hover AND on focus, and drops the `title`
|
|
199
|
+
* so two tooltips never show at once. `false` brings the `title` back, for the rare icon
|
|
200
|
+
* whose name is already written next to it. Inside a `TooltipTrigger` the consumer's
|
|
201
|
+
* tooltip wins and this one steps aside. */
|
|
202
|
+
function Labelled({ label, tooltip, children, }) {
|
|
203
|
+
const wrapped = useInTooltipTrigger();
|
|
204
|
+
if (wrapped)
|
|
205
|
+
return children(undefined);
|
|
206
|
+
if (!tooltip)
|
|
207
|
+
return children(label);
|
|
208
|
+
return (_jsxs(TooltipTrigger, { children: [children(undefined), _jsx(Tooltip, { children: label })] }));
|
|
209
|
+
}
|
|
210
|
+
/** Only an icon, and that is why the label is mandatory even though it is not
|
|
211
|
+
* drawn.
|
|
212
|
+
*
|
|
213
|
+
* It exists because «download» was a grey 11 px `↓` character with no box: the
|
|
214
|
+
* most frequent action of a row was the hardest one to hit. Here it has its
|
|
215
|
+
* full control height and a background on hover — the target is the same as
|
|
216
|
+
* that of a button with text, only without the text. */
|
|
217
|
+
export function IconButton({ icon: Icon, label, size = "sm", surface: surfaceProp, tone = "normal", disabled = false, type = "button", expanded, haspopup, tooltip = true, ref, ...native }) {
|
|
218
|
+
const surface = useSurface(surfaceProp);
|
|
219
|
+
const ic = ICON[size];
|
|
220
|
+
return (_jsx(Labelled, { label: label, tooltip: tooltip, children: (title) => (_jsx("button", { ...native, ref: ref, type: type, title: title, "aria-label": label, disabled: disabled, ...popupAttrs(expanded, haspopup), className: `${BOX} disabled:pointer-events-none disabled:opacity-45 ${SQUARE[size]} ${TONE[tone][surface]}`, children: _jsx(Icon, { className: ic.size, strokeWidth: ic.stroke, "aria-hidden": true }) })) }));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The same icon, but it is a real link.
|
|
224
|
+
*
|
|
225
|
+
* Context: it exists for the same reason as `ButtonLink` — downloading a file or
|
|
226
|
+
* going to a view is NOT pressing a button. With a `<button>` you lose
|
|
227
|
+
* middle-click, «copy the address» and the browser's `download`, which is the
|
|
228
|
+
* only thing that keeps a 150 MB CSV from going through the tab's memory.
|
|
229
|
+
*/
|
|
230
|
+
export function IconLink({ icon: Icon, label, href, download, size = "sm", surface: surfaceProp, tone = "action", tooltip = true, ref, ...native }) {
|
|
231
|
+
const surface = useSurface(surfaceProp);
|
|
232
|
+
const ic = ICON[size];
|
|
233
|
+
return (_jsx(Labelled, { label: label, tooltip: tooltip, children: (title) => (_jsx("a", { ...native, ref: ref, href: href, ...(download === undefined ? {} : { download }), title: title, "aria-label": label, className: `${BOX} ${SQUARE[size]} ${TONE[tone][surface]}`, children: _jsx(Icon, { className: ic.size, strokeWidth: ic.stroke, "aria-hidden": true }) })) }));
|
|
234
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Button, buttonClasses, ButtonLink, type ButtonLinkProps, type ButtonPopup, type ButtonProps, type ButtonRole, type ButtonSize, IconButton, type IconButtonProps, IconLink, type IconLinkProps, type IconTone, } from "./Button.js";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { LucideIcon } from "lucide-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* A thumbnail — the photo of a product, the logo of a customer — or, when there is none, its
|
|
5
|
+
* icon on a quiet tile. One size per context so a column of them lines up.
|
|
6
|
+
*
|
|
7
|
+
* Context: a list of forty products where only the name tells them apart makes people read
|
|
8
|
+
* every row; the picture is recognised without reading. The tile without a photo keeps the
|
|
9
|
+
* same size and place, so the rows that have one and the rows that do not still align.
|
|
10
|
+
*/
|
|
11
|
+
export declare function Thumb({ src, alt, icon: Icon, size, }: {
|
|
12
|
+
/** The image. Without it, `icon` on a tile; without both, an empty tile. */
|
|
13
|
+
src?: string;
|
|
14
|
+
/** What the image shows, for whoever cannot see it. Required with `src`. */
|
|
15
|
+
alt?: string;
|
|
16
|
+
icon?: LucideIcon;
|
|
17
|
+
/** `sm` for a dense row (24 px), `md` for a table (32 px), `lg` for a card (48 px). */
|
|
18
|
+
size?: "sm" | "md" | "lg";
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
|
+
/**
|
|
21
|
+
* A cell with hierarchy: what the row IS on top, what tells it apart below — a product and its
|
|
22
|
+
* description, a customer and its NIT, an account and its code — with an optional picture.
|
|
23
|
+
*
|
|
24
|
+
* Context: a table cell used to hold one string. An ERP row is rarely one thing: a product line
|
|
25
|
+
* is «Soporte mensual» AND «plan estándar · SKU SOP-01», and putting them in two columns spends
|
|
26
|
+
* width on a column nobody sorts by, while squeezing them into one string loses the hierarchy.
|
|
27
|
+
* Two lines, the first in ink and the second small and quiet, read as one thing with a detail —
|
|
28
|
+
* the way the eye already reads a line of an invoice.
|
|
29
|
+
*
|
|
30
|
+
* It works in any cell: `DataTable` (a column's `cell`), `Table`/`Td`, the rows of a list, a
|
|
31
|
+
* card. The second line wraps or truncates (`wrap`); the first always truncates, with the full
|
|
32
|
+
* text on hover.
|
|
33
|
+
*/
|
|
34
|
+
export declare function CellStack({ primary, secondary, media, wrap, }: {
|
|
35
|
+
/** What the row is — the name. In ink, one line. */
|
|
36
|
+
primary: ReactNode;
|
|
37
|
+
/** What tells it apart — a description, a code, a reference. Small and quiet. */
|
|
38
|
+
secondary?: ReactNode;
|
|
39
|
+
/** A picture or icon on the left: `{ src, alt }` or `{ icon }`. */
|
|
40
|
+
media?: {
|
|
41
|
+
src?: string;
|
|
42
|
+
alt?: string;
|
|
43
|
+
icon?: LucideIcon;
|
|
44
|
+
};
|
|
45
|
+
/** Let the second line wrap (a description) instead of truncating (a code). */
|
|
46
|
+
wrap?: boolean;
|
|
47
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* A thumbnail — the photo of a product, the logo of a customer — or, when there is none, its
|
|
4
|
+
* icon on a quiet tile. One size per context so a column of them lines up.
|
|
5
|
+
*
|
|
6
|
+
* Context: a list of forty products where only the name tells them apart makes people read
|
|
7
|
+
* every row; the picture is recognised without reading. The tile without a photo keeps the
|
|
8
|
+
* same size and place, so the rows that have one and the rows that do not still align.
|
|
9
|
+
*/
|
|
10
|
+
export function Thumb({ src, alt, icon: Icon, size = "md", }) {
|
|
11
|
+
const box = { sm: "size-6", md: "size-8", lg: "size-12" }[size];
|
|
12
|
+
if (src !== undefined) {
|
|
13
|
+
return (_jsx("img", { src: src, alt: alt ?? "", loading: "lazy", className: `${box} shrink-0 rounded-control bg-ink/5 object-cover` }));
|
|
14
|
+
}
|
|
15
|
+
return (_jsx("span", { "aria-hidden": true, className: `${box} flex shrink-0 items-center justify-center rounded-control bg-ink/5 text-muted`, children: Icon === undefined ? null : (_jsx(Icon, { className: size === "lg" ? "size-(--size-icon-lg)" : "size-(--size-icon-sm)", strokeWidth: 1.75 })) }));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A cell with hierarchy: what the row IS on top, what tells it apart below — a product and its
|
|
19
|
+
* description, a customer and its NIT, an account and its code — with an optional picture.
|
|
20
|
+
*
|
|
21
|
+
* Context: a table cell used to hold one string. An ERP row is rarely one thing: a product line
|
|
22
|
+
* is «Soporte mensual» AND «plan estándar · SKU SOP-01», and putting them in two columns spends
|
|
23
|
+
* width on a column nobody sorts by, while squeezing them into one string loses the hierarchy.
|
|
24
|
+
* Two lines, the first in ink and the second small and quiet, read as one thing with a detail —
|
|
25
|
+
* the way the eye already reads a line of an invoice.
|
|
26
|
+
*
|
|
27
|
+
* It works in any cell: `DataTable` (a column's `cell`), `Table`/`Td`, the rows of a list, a
|
|
28
|
+
* card. The second line wraps or truncates (`wrap`); the first always truncates, with the full
|
|
29
|
+
* text on hover.
|
|
30
|
+
*/
|
|
31
|
+
export function CellStack({ primary, secondary, media, wrap = false, }) {
|
|
32
|
+
return (_jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [media === undefined ? null : (_jsx(Thumb, { size: "md", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate text-meta font-medium text-ink", ...(typeof primary === "string" ? { title: primary } : {}), children: primary }), secondary === undefined ? null : (_jsx("span", { className: `text-micro text-muted ${wrap ? "line-clamp-2" : "truncate"}`, ...(typeof secondary === "string" ? { title: secondary } : {}), children: secondary }))] })] }));
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CellStack, Thumb } from "./CellStack.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ChoiceOption } from "./options.js";
|
|
2
|
+
import { type FormMode } from "../field/mode.js";
|
|
3
|
+
export type ComboBoxProps = {
|
|
4
|
+
/** The chosen option's `value`, or `null` when nothing is chosen. */
|
|
5
|
+
value: string | null;
|
|
6
|
+
onChange?: (next: string | null) => void;
|
|
7
|
+
options: readonly ChoiceOption[];
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
id?: string;
|
|
11
|
+
describedBy?: string;
|
|
12
|
+
invalid?: boolean;
|
|
13
|
+
/** The name of the control when it is NOT inside a `Field` — a cell of an editable table.
|
|
14
|
+
* Inside a field, the field's label names it. */
|
|
15
|
+
"aria-label"?: string;
|
|
16
|
+
/** Forces the mode. By default it inherits the form's: in read mode it draws the label. */
|
|
17
|
+
mode?: FormMode;
|
|
18
|
+
};
|
|
19
|
+
export declare function ComboBox({ mode, ...props }: ComboBoxProps): import("react").JSX.Element;
|