@sincpro/web-ui 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +157 -2
- package/dist/components/assist/AssistMark.d.ts +64 -0
- package/dist/components/assist/AssistMark.js +108 -0
- package/dist/components/assist/AssistPending.d.ts +24 -0
- package/dist/components/assist/AssistPending.js +26 -0
- package/dist/components/assist/AssistSuggestion.d.ts +42 -0
- package/dist/components/assist/AssistSuggestion.js +55 -0
- package/dist/components/assist/ChangeReview.d.ts +56 -0
- package/dist/components/assist/ChangeReview.js +121 -0
- package/dist/components/assist/assist.d.ts +77 -0
- package/dist/components/assist/assist.js +87 -0
- package/dist/components/assist/index.d.ts +5 -0
- package/dist/components/assist/index.js +7 -0
- package/dist/components/chatter/Chatter.d.ts +161 -0
- package/dist/components/chatter/Chatter.js +232 -0
- package/dist/components/chatter/Messages.d.ts +66 -0
- package/dist/components/chatter/Messages.js +43 -0
- package/dist/components/chatter/Timeline.d.ts +21 -0
- package/dist/components/chatter/Timeline.js +30 -0
- package/dist/components/chatter/index.d.ts +3 -0
- package/dist/components/chatter/index.js +4 -0
- package/dist/components/form/Form.d.ts +52 -0
- package/dist/components/form/Form.js +49 -0
- package/dist/components/form/Sheet.d.ts +43 -0
- package/dist/components/form/Sheet.js +83 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.js +4 -0
- package/dist/components/frame/Chart.d.ts +13 -0
- package/dist/components/frame/Chart.js +24 -0
- package/dist/components/frame/FrameView.d.ts +33 -0
- package/dist/components/frame/FrameView.js +125 -0
- package/dist/components/frame/index.d.ts +3 -0
- package/dist/components/frame/index.js +3 -0
- package/dist/components/frame/model.d.ts +43 -0
- package/dist/components/frame/model.js +101 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.js +11 -0
- package/dist/components/lines/LineItems.d.ts +74 -0
- package/dist/components/lines/LineItems.js +229 -0
- package/dist/components/lines/index.d.ts +1 -0
- package/dist/components/lines/index.js +2 -0
- package/dist/components/lines/lines.d.ts +23 -0
- package/dist/components/lines/lines.js +26 -0
- package/dist/components/listing/ColumnsMenu.d.ts +20 -0
- package/dist/components/listing/ColumnsMenu.js +46 -0
- package/dist/components/listing/GroupingLevels.d.ts +35 -0
- package/dist/components/listing/GroupingLevels.js +45 -0
- package/dist/components/listing/Listing.d.ts +27 -0
- package/dist/components/listing/Listing.js +86 -0
- package/dist/components/listing/ListingBar.d.ts +73 -0
- package/dist/components/listing/ListingBar.js +105 -0
- package/dist/components/listing/Pagination.d.ts +36 -0
- package/dist/components/listing/Pagination.js +69 -0
- package/dist/components/listing/RecordCard.d.ts +33 -0
- package/dist/components/listing/RecordCard.js +22 -0
- package/dist/components/listing/RecordLink.d.ts +7 -0
- package/dist/components/listing/RecordLink.js +22 -0
- package/dist/components/listing/Row.d.ts +106 -0
- package/dist/components/listing/Row.js +122 -0
- package/dist/components/listing/SortMenu.d.ts +62 -0
- package/dist/components/listing/SortMenu.js +105 -0
- package/dist/components/listing/ViewSwitcher.d.ts +24 -0
- package/dist/components/listing/ViewSwitcher.js +16 -0
- package/dist/components/listing/columns.d.ts +47 -0
- package/dist/components/listing/columns.js +72 -0
- package/dist/components/listing/grouping.d.ts +35 -0
- package/dist/components/listing/grouping.js +47 -0
- package/dist/components/listing/index.d.ts +17 -0
- package/dist/components/listing/index.js +20 -0
- package/dist/components/listing/modelColumns.d.ts +50 -0
- package/dist/components/listing/modelColumns.js +103 -0
- package/dist/components/listing/pages.d.ts +78 -0
- package/dist/components/listing/pages.js +135 -0
- package/dist/components/listing/selection.d.ts +21 -0
- package/dist/components/listing/selection.js +43 -0
- package/dist/components/listing/set.d.ts +48 -0
- package/dist/components/listing/set.js +24 -0
- package/dist/components/listing/tree.d.ts +28 -0
- package/dist/components/listing/tree.js +23 -0
- package/dist/components/navigation/CommandPalette.d.ts +40 -0
- package/dist/components/navigation/CommandPalette.js +114 -0
- package/dist/components/navigation/NavigationTree.d.ts +31 -0
- package/dist/components/navigation/NavigationTree.js +79 -0
- package/dist/components/navigation/commands.d.ts +30 -0
- package/dist/components/navigation/commands.js +30 -0
- package/dist/components/navigation/index.d.ts +3 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/record/Breadcrumb.d.ts +42 -0
- package/dist/components/record/Breadcrumb.js +98 -0
- package/dist/components/record/DirtyActions.d.ts +26 -0
- package/dist/components/record/DirtyActions.js +22 -0
- package/dist/components/record/Pager.d.ts +18 -0
- package/dist/components/record/Pager.js +33 -0
- package/dist/components/record/RecordHeader.d.ts +71 -0
- package/dist/components/record/RecordHeader.js +72 -0
- package/dist/components/record/RecordSkeleton.d.ts +3 -0
- package/dist/components/record/RecordSkeleton.js +21 -0
- package/dist/components/record/SmartButtons.d.ts +35 -0
- package/dist/components/record/SmartButtons.js +38 -0
- package/dist/components/record/StatusBar.d.ts +51 -0
- package/dist/components/record/StatusBar.js +168 -0
- package/dist/components/record/ThumbBar.d.ts +36 -0
- package/dist/components/record/ThumbBar.js +86 -0
- package/dist/components/record/index.d.ts +6 -0
- package/dist/components/record/index.js +8 -0
- package/dist/components/search/CriteriaBuilder.d.ts +58 -0
- package/dist/components/search/CriteriaBuilder.js +278 -0
- package/dist/components/search/CriteriaChips.d.ts +22 -0
- package/dist/components/search/CriteriaChips.js +64 -0
- package/dist/components/search/FilterBuilder.d.ts +142 -0
- package/dist/components/search/FilterBuilder.js +421 -0
- package/dist/components/search/JoinWord.d.ts +8 -0
- package/dist/components/search/JoinWord.js +47 -0
- package/dist/components/search/JsonEditor.d.ts +19 -0
- package/dist/components/search/JsonEditor.js +78 -0
- package/dist/components/search/SearchBar.d.ts +78 -0
- package/dist/components/search/SearchBar.js +387 -0
- package/dist/components/search/criteria.d.ts +116 -0
- package/dist/components/search/criteria.js +159 -0
- package/dist/components/search/datePeriods.d.ts +122 -0
- package/dist/components/search/datePeriods.js +134 -0
- package/dist/components/search/dateRanges.d.ts +54 -0
- package/dist/components/search/dateRanges.js +89 -0
- package/dist/components/search/index.d.ts +9 -0
- package/dist/components/search/index.js +20 -0
- package/dist/components/search/model.d.ts +54 -0
- package/dist/components/search/model.js +37 -0
- package/dist/components/search/query.d.ts +166 -0
- package/dist/components/search/query.js +436 -0
- package/dist/components/shell/AppFrame.d.ts +14 -0
- package/dist/components/shell/AppFrame.js +80 -0
- package/dist/components/shell/TopBar.d.ts +73 -0
- package/dist/components/shell/TopBar.js +54 -0
- package/dist/components/shell/headerSlot.d.ts +17 -0
- package/dist/components/shell/headerSlot.js +21 -0
- package/dist/components/shell/index.d.ts +2 -0
- package/dist/components/shell/index.js +3 -0
- package/dist/foundations/aria.d.ts +14 -0
- package/dist/foundations/aria.js +18 -0
- package/dist/foundations/calendar.d.ts +28 -0
- package/dist/foundations/calendar.js +183 -0
- package/dist/foundations/copy.d.ts +619 -0
- package/dist/foundations/copy.es.d.ts +2 -0
- package/dist/foundations/copy.es.js +377 -0
- package/dist/foundations/copy.js +472 -0
- package/dist/foundations/density.d.ts +16 -0
- package/dist/foundations/density.js +21 -0
- package/dist/foundations/grouping.d.ts +281 -0
- package/dist/foundations/grouping.js +0 -0
- package/dist/foundations/index.d.ts +14 -0
- package/dist/foundations/index.js +30 -0
- package/dist/foundations/layout.d.ts +9 -0
- package/dist/foundations/layout.js +9 -0
- package/dist/foundations/pivot.d.ts +222 -0
- package/dist/foundations/pivot.js +198 -0
- package/dist/foundations/pivotSheet.d.ts +61 -0
- package/dist/foundations/pivotSheet.js +0 -0
- package/dist/foundations/slots.d.ts +67 -0
- package/dist/foundations/slots.js +63 -0
- package/dist/foundations/sticky.d.ts +53 -0
- package/dist/foundations/sticky.js +150 -0
- package/dist/foundations/surface.d.ts +9 -0
- package/dist/foundations/surface.js +26 -0
- package/dist/foundations/theme.d.ts +42 -0
- package/dist/foundations/theme.js +88 -0
- package/dist/foundations/types.d.ts +23 -0
- package/dist/foundations/types.js +50 -0
- package/dist/foundations/variants.d.ts +19 -0
- package/dist/foundations/variants.js +48 -0
- package/dist/foundations/xlsx.d.ts +86 -0
- package/dist/foundations/xlsx.js +0 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/primitives/block/Block.d.ts +66 -0
- package/dist/primitives/block/Block.js +34 -0
- package/dist/primitives/block/Line.d.ts +18 -0
- package/dist/primitives/block/Line.js +45 -0
- package/dist/primitives/block/SectionGrid.d.ts +30 -0
- package/dist/primitives/block/SectionGrid.js +29 -0
- package/dist/primitives/block/Steps.d.ts +76 -0
- package/dist/primitives/block/Steps.js +43 -0
- package/dist/primitives/block/index.d.ts +4 -0
- package/dist/primitives/block/index.js +7 -0
- package/dist/primitives/button/Button.d.ts +137 -0
- package/dist/primitives/button/Button.js +234 -0
- package/dist/primitives/button/index.d.ts +1 -0
- package/dist/primitives/button/index.js +2 -0
- package/dist/primitives/cell/CellStack.d.ts +47 -0
- package/dist/primitives/cell/CellStack.js +33 -0
- package/dist/primitives/cell/index.d.ts +1 -0
- package/dist/primitives/cell/index.js +3 -0
- package/dist/primitives/choice/ComboBox.d.ts +16 -0
- package/dist/primitives/choice/ComboBox.js +52 -0
- package/dist/primitives/choice/RadioGroup.d.ts +18 -0
- package/dist/primitives/choice/RadioGroup.js +50 -0
- package/dist/primitives/choice/SearchField.d.ts +24 -0
- package/dist/primitives/choice/SearchField.js +27 -0
- package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
- package/dist/primitives/choice/SegmentedControl.js +69 -0
- package/dist/primitives/choice/Slider.d.ts +16 -0
- package/dist/primitives/choice/Slider.js +46 -0
- package/dist/primitives/choice/Switch.d.ts +14 -0
- package/dist/primitives/choice/Switch.js +65 -0
- package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
- package/dist/primitives/choice/ThemeSwitcher.js +25 -0
- package/dist/primitives/choice/index.d.ts +8 -0
- package/dist/primitives/choice/index.js +11 -0
- package/dist/primitives/choice/options.d.ts +28 -0
- package/dist/primitives/choice/options.js +6 -0
- package/dist/primitives/date/Calendar.d.ts +31 -0
- package/dist/primitives/date/Calendar.js +110 -0
- package/dist/primitives/date/DatePicker.d.ts +34 -0
- package/dist/primitives/date/DatePicker.js +99 -0
- package/dist/primitives/date/index.d.ts +2 -0
- package/dist/primitives/date/index.js +4 -0
- package/dist/primitives/dialog/Dialog.d.ts +118 -0
- package/dist/primitives/dialog/Dialog.js +121 -0
- package/dist/primitives/dialog/index.d.ts +1 -0
- package/dist/primitives/dialog/index.js +2 -0
- package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
- package/dist/primitives/disclosure/Disclosure.js +52 -0
- package/dist/primitives/disclosure/index.d.ts +1 -0
- package/dist/primitives/disclosure/index.js +2 -0
- package/dist/primitives/field/Checks.d.ts +57 -0
- package/dist/primitives/field/Checks.js +72 -0
- package/dist/primitives/field/DropZone.d.ts +42 -0
- package/dist/primitives/field/DropZone.js +52 -0
- package/dist/primitives/field/Field.d.ts +166 -0
- package/dist/primitives/field/Field.js +240 -0
- package/dist/primitives/field/Suggest.d.ts +48 -0
- package/dist/primitives/field/Suggest.js +43 -0
- package/dist/primitives/field/aria.d.ts +61 -0
- package/dist/primitives/field/aria.js +70 -0
- package/dist/primitives/field/context.d.ts +30 -0
- package/dist/primitives/field/context.js +13 -0
- package/dist/primitives/field/index.d.ts +7 -0
- package/dist/primitives/field/index.js +11 -0
- package/dist/primitives/field/layout.d.ts +24 -0
- package/dist/primitives/field/layout.js +10 -0
- package/dist/primitives/field/mode.d.ts +9 -0
- package/dist/primitives/field/mode.js +13 -0
- package/dist/primitives/hint/Hint.d.ts +31 -0
- package/dist/primitives/hint/Hint.js +42 -0
- package/dist/primitives/hint/index.d.ts +1 -0
- package/dist/primitives/hint/index.js +1 -0
- package/dist/primitives/index.d.ts +19 -0
- package/dist/primitives/index.js +20 -0
- package/dist/primitives/menu/Menu.d.ts +100 -0
- package/dist/primitives/menu/Menu.js +59 -0
- package/dist/primitives/menu/index.d.ts +1 -0
- package/dist/primitives/menu/index.js +2 -0
- package/dist/primitives/notice/Callout.d.ts +49 -0
- package/dist/primitives/notice/Callout.js +28 -0
- package/dist/primitives/notice/Notice.d.ts +45 -0
- package/dist/primitives/notice/Notice.js +61 -0
- package/dist/primitives/notice/Toast.d.ts +55 -0
- package/dist/primitives/notice/Toast.js +142 -0
- package/dist/primitives/notice/index.d.ts +3 -0
- package/dist/primitives/notice/index.js +5 -0
- package/dist/primitives/overlay/Popover.d.ts +6 -0
- package/dist/primitives/overlay/Popover.js +27 -0
- package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
- package/dist/primitives/overlay/PopoverTrigger.js +38 -0
- package/dist/primitives/overlay/Tooltip.d.ts +38 -0
- package/dist/primitives/overlay/Tooltip.js +74 -0
- package/dist/primitives/overlay/index.d.ts +3 -0
- package/dist/primitives/overlay/index.js +5 -0
- package/dist/primitives/picker/Picker.d.ts +48 -0
- package/dist/primitives/picker/Picker.js +150 -0
- package/dist/primitives/picker/index.d.ts +1 -0
- package/dist/primitives/picker/index.js +2 -0
- package/dist/primitives/surface/Peek.d.ts +60 -0
- package/dist/primitives/surface/Peek.js +71 -0
- package/dist/primitives/surface/index.d.ts +1 -0
- package/dist/primitives/surface/index.js +2 -0
- package/dist/primitives/table/CellEditor.d.ts +14 -0
- package/dist/primitives/table/CellEditor.js +83 -0
- package/dist/primitives/table/DataTable.d.ts +135 -0
- package/dist/primitives/table/DataTable.js +244 -0
- package/dist/primitives/table/EditableTable.d.ts +96 -0
- package/dist/primitives/table/EditableTable.js +423 -0
- package/dist/primitives/table/PivotTable.d.ts +14 -0
- package/dist/primitives/table/PivotTable.js +0 -0
- package/dist/primitives/table/ReportTable.d.ts +38 -0
- package/dist/primitives/table/ReportTable.js +106 -0
- package/dist/primitives/table/RowActions.d.ts +16 -0
- package/dist/primitives/table/RowActions.js +28 -0
- package/dist/primitives/table/SheetTable.d.ts +70 -0
- package/dist/primitives/table/SheetTable.js +197 -0
- package/dist/primitives/table/Table.d.ts +106 -0
- package/dist/primitives/table/Table.js +116 -0
- package/dist/primitives/table/index.d.ts +7 -0
- package/dist/primitives/table/index.js +9 -0
- package/dist/primitives/tabs/Tabs.d.ts +91 -0
- package/dist/primitives/tabs/Tabs.js +56 -0
- package/dist/primitives/tabs/index.d.ts +1 -0
- package/dist/primitives/tabs/index.js +1 -0
- package/dist/primitives/tag/Tag.d.ts +36 -0
- package/dist/primitives/tag/Tag.js +68 -0
- package/dist/primitives/tag/Tags.d.ts +57 -0
- package/dist/primitives/tag/Tags.js +104 -0
- package/dist/primitives/tag/TagsField.d.ts +32 -0
- package/dist/primitives/tag/TagsField.js +182 -0
- package/dist/primitives/tag/choices.d.ts +38 -0
- package/dist/primitives/tag/choices.js +26 -0
- package/dist/primitives/tag/index.d.ts +4 -0
- package/dist/primitives/tag/index.js +8 -0
- package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
- package/dist/primitives/toolbar/Toolbar.js +36 -0
- package/dist/primitives/toolbar/index.d.ts +1 -0
- package/dist/primitives/toolbar/index.js +2 -0
- package/dist/primitives/value/Copyable.d.ts +37 -0
- package/dist/primitives/value/Copyable.js +50 -0
- package/dist/primitives/value/Fecha.d.ts +12 -0
- package/dist/primitives/value/Fecha.js +80 -0
- package/dist/primitives/value/Meter.d.ts +29 -0
- package/dist/primitives/value/Meter.js +46 -0
- package/dist/primitives/value/Metric.d.ts +42 -0
- package/dist/primitives/value/Metric.js +38 -0
- package/dist/primitives/value/Person.d.ts +90 -0
- package/dist/primitives/value/Person.js +208 -0
- package/dist/primitives/value/Relation.d.ts +69 -0
- package/dist/primitives/value/Relation.js +57 -0
- package/dist/primitives/value/Totals.d.ts +26 -0
- package/dist/primitives/value/Totals.js +4 -0
- package/dist/primitives/value/Value.d.ts +77 -0
- package/dist/primitives/value/Value.js +119 -0
- package/dist/primitives/value/index.d.ts +8 -0
- package/dist/primitives/value/index.js +9 -0
- package/dist/tokens.css +1025 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +3 -0
- package/dist/views/list/List.d.ts +113 -0
- package/dist/views/list/List.js +97 -0
- package/dist/views/list/ListView.d.ts +6 -0
- package/dist/views/list/ListView.js +269 -0
- package/dist/views/list/columns.d.ts +18 -0
- package/dist/views/list/columns.js +27 -0
- package/dist/views/list/derive.d.ts +76 -0
- package/dist/views/list/derive.js +181 -0
- package/dist/views/list/index.d.ts +2 -0
- package/dist/views/list/index.js +4 -0
- package/dist/views/list/presentations.d.ts +39 -0
- package/dist/views/list/presentations.js +103 -0
- package/dist/views/list/types.d.ts +272 -0
- package/dist/views/list/types.js +1 -0
- package/dist/views/list/useNarrowList.d.ts +17 -0
- package/dist/views/list/useNarrowList.js +32 -0
- package/dist/views/record/Record.d.ts +126 -0
- package/dist/views/record/Record.js +141 -0
- package/dist/views/record/index.d.ts +1 -0
- package/dist/views/record/index.js +2 -0
- package/package.json +109 -4
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type FormMode } from "./mode.js";
|
|
2
|
+
/**
|
|
3
|
+
* A text with suggestions: the open field that proposes.
|
|
4
|
+
*
|
|
5
|
+
* Context: there were two text controls and neither served the most common case of an ERP
|
|
6
|
+
* form. `Select` is a CLOSED list —what is not there cannot be written— and `TextInput` is
|
|
7
|
+
* free but does not help. The unit of a product, the bank of a payment, the city of a
|
|
8
|
+
* customer: fields where values already exist and they have to be offered **without
|
|
9
|
+
* forbidding a new one**.
|
|
10
|
+
*
|
|
11
|
+
* What not having it costs shows in the data: without suggestions «Bs.», «BOB» and
|
|
12
|
+
* «bolivianos» end up in the same column, and then somebody writes a `CASE` to unify them.
|
|
13
|
+
*
|
|
14
|
+
* ── Why it is not a `<datalist>` ────────────────────────────────────
|
|
15
|
+
*
|
|
16
|
+
* Because every browser draws it its own way and none follows the tokens: in the middle of a
|
|
17
|
+
* form with the system's type, radius and colors, an operating-system dropdown appears. It is
|
|
18
|
+
* React Aria's `ComboBox` with `allowsCustomValue` instead: the list floats on the system's
|
|
19
|
+
* `Popover`, the keyboard is a combobox's (arrows move, Enter picks, Escape closes), and
|
|
20
|
+
* whatever is typed stays typed. The filter is React Aria's `contains` in the copy's locale,
|
|
21
|
+
* so «potosi» finds «Potosí».
|
|
22
|
+
*
|
|
23
|
+
* ── What it is NOT ──────────────────────────────────────────────────
|
|
24
|
+
*
|
|
25
|
+
* It is not `ComboBox`, whose value must be one of its options, nor `Picker`, which chooses
|
|
26
|
+
* ONE record among thousands with its own search, metadata rows and dialog. This is a text
|
|
27
|
+
* field that also proposes: the list is short, it is of values and not of records, and what
|
|
28
|
+
* is stored is what was written.
|
|
29
|
+
*/
|
|
30
|
+
export type SuggestProps = {
|
|
31
|
+
id?: string;
|
|
32
|
+
value: string;
|
|
33
|
+
onChange?: (next: string) => void;
|
|
34
|
+
/** What already exists. Short: if they are hundreds, what is needed is a `Picker`. */
|
|
35
|
+
suggestions: readonly string[];
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
describedBy?: string;
|
|
38
|
+
invalid?: boolean;
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
mono?: boolean;
|
|
41
|
+
mode?: FormMode;
|
|
42
|
+
/** How many are shown at once. More than this is a list to be read, and reading a list
|
|
43
|
+
* is what this control exists to avoid. */
|
|
44
|
+
max?: number;
|
|
45
|
+
/** The name of the field when it is not inside a `Field`. */
|
|
46
|
+
"aria-label"?: string;
|
|
47
|
+
};
|
|
48
|
+
export declare function Suggest({ mode, ...props }: SuggestProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AriaLocale } from "../../foundations/aria.js";
|
|
4
|
+
import { FieldGroup, listOptionStyles } from "./aria.js";
|
|
5
|
+
import { useFieldIds } from "./context.js";
|
|
6
|
+
import { ReadValue } from "./Field.js";
|
|
7
|
+
import { useFormMode } from "./mode.js";
|
|
8
|
+
import { Popover } from "../overlay/Popover.js";
|
|
9
|
+
import { ComboBox as AriaComboBox, Input, ListBox, ListBoxItem, useFilter, } from "react-aria-components";
|
|
10
|
+
const MAX = 8;
|
|
11
|
+
export function Suggest({ mode, ...props }) {
|
|
12
|
+
if (useFormMode(mode) === "read")
|
|
13
|
+
return _jsx(ReadValue, { value: props.value, mono: props.mono });
|
|
14
|
+
/* The filter reads the locale from React Aria's context, so the box that provides it has
|
|
15
|
+
to be ABOVE the component that asks for it. */
|
|
16
|
+
return (_jsx(AriaLocale, { children: _jsx(EditableSuggest, { ...props }) }));
|
|
17
|
+
}
|
|
18
|
+
function EditableSuggest({ id: ownId, value, onChange, suggestions, placeholder, describedBy: ownDescribedBy, invalid: ownInvalid, disabled = false, mono = false, max = MAX, "aria-label": ariaLabel, }) {
|
|
19
|
+
const { id, labelledBy, describedBy, invalid } = useFieldIds({
|
|
20
|
+
id: ownId,
|
|
21
|
+
describedBy: ownDescribedBy,
|
|
22
|
+
invalid: ownInvalid,
|
|
23
|
+
});
|
|
24
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
25
|
+
/* Filtered by what is typed, hiding the one that ALREADY is exactly the value: offering
|
|
26
|
+
what someone just finished typing is a suggestion that suggests nothing. The list is
|
|
27
|
+
filtered here and not by React Aria because of that rule and of `max`. */
|
|
28
|
+
const needle = value.trim();
|
|
29
|
+
/* «Exactly» ignores case but NOT accents: «potosi» still offers «Potosí», which is the
|
|
30
|
+
spelling the person was reaching for. */
|
|
31
|
+
const exact = needle.toLocaleLowerCase();
|
|
32
|
+
const shown = suggestions
|
|
33
|
+
.filter((one) => one.toLocaleLowerCase() !== exact)
|
|
34
|
+
.filter((one) => needle === "" || contains(one, needle))
|
|
35
|
+
.slice(0, max)
|
|
36
|
+
.map((one) => ({ id: one }));
|
|
37
|
+
return (_jsxs(AriaComboBox, { id: id, "aria-labelledby": labelledBy, "aria-label": labelledBy === undefined ? (ariaLabel ?? placeholder) : ariaLabel,
|
|
38
|
+
/* An open field: what is typed is the value, a suggestion only fills it faster. */
|
|
39
|
+
allowsCustomValue: true, inputValue: value, onInputChange: (next) => onChange?.(next), items: shown,
|
|
40
|
+
/* It proposes as soon as the field is entered, like the hand-written one did: the
|
|
41
|
+
suggestions are most useful BEFORE anything is typed. */
|
|
42
|
+
menuTrigger: "focus", isInvalid: invalid, isDisabled: disabled, className: "w-full min-w-0", children: [_jsx(FieldGroup, { children: _jsx(Input, { "aria-describedby": describedBy, placeholder: placeholder, className: `h-full min-w-0 flex-1 bg-transparent px-2.5 text-meta text-ink outline-0 placeholder:text-muted disabled:cursor-not-allowed ${mono ? "font-mono" : ""}` }) }), _jsx(Popover, { placement: "bottom start", className: "w-(--trigger-width)", children: _jsx(ListBox, { className: "max-h-[inherit] overflow-auto p-1 outline-0", children: (item) => (_jsx(ListBoxItem, { id: item.id, textValue: item.id, className: listOptionStyles, children: _jsx("span", { className: `min-w-0 truncate ${mono ? "font-mono" : ""}`, children: item.id }) })) }) })] }));
|
|
43
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { composeClasses } from "../../foundations/variants.js";
|
|
2
|
+
import type { Ref } from "react";
|
|
3
|
+
import { type ButtonProps as AriaButtonProps, type GroupProps } from "react-aria-components";
|
|
4
|
+
/**
|
|
5
|
+
* The pieces every React Aria control of the system is built from — the box a date field,
|
|
6
|
+
* a combobox or a number field draws, and the small button inside it.
|
|
7
|
+
*
|
|
8
|
+
* Context: they look exactly like `controlClasses()` — same height, border, radius and
|
|
9
|
+
* focus — because a form mixes both kinds of control in one column, and a date field one
|
|
10
|
+
* pixel taller than the text input above it is the first thing an eye catches.
|
|
11
|
+
*/
|
|
12
|
+
export declare const fieldGroupStyles: import("tailwind-variants/config").TVReturnType<{
|
|
13
|
+
isFocusWithin: {
|
|
14
|
+
false: string;
|
|
15
|
+
true: string;
|
|
16
|
+
};
|
|
17
|
+
isInvalid: {
|
|
18
|
+
true: string;
|
|
19
|
+
};
|
|
20
|
+
isDisabled: {
|
|
21
|
+
true: string;
|
|
22
|
+
};
|
|
23
|
+
}, undefined, "group flex h-(--size-control-md) w-full min-w-0 items-center overflow-hidden rounded-control border bg-surface text-meta text-ink transition-colors duration-(--duration-instant) forced-colors:bg-[Field]", {
|
|
24
|
+
isFocusVisible: {
|
|
25
|
+
false: string;
|
|
26
|
+
true: string;
|
|
27
|
+
};
|
|
28
|
+
}, undefined, import("tailwind-variants/config").TVReturnType<{
|
|
29
|
+
isFocusVisible: {
|
|
30
|
+
false: string;
|
|
31
|
+
true: string;
|
|
32
|
+
};
|
|
33
|
+
}, undefined, "outline-primary outline-offset-2 forced-colors:outline-[Highlight]", undefined, undefined, undefined>>;
|
|
34
|
+
/** The box of a composite control: a field and its buttons, framed as one input. The `ref`
|
|
35
|
+
* reaches the box (React 19: a plain prop) — a control that floats a list as wide as the
|
|
36
|
+
* box, like `TagsField`, measures it. */
|
|
37
|
+
export declare function FieldGroup({ ref, ...props }: GroupProps & {
|
|
38
|
+
ref?: Ref<HTMLDivElement>;
|
|
39
|
+
}): import("react").JSX.Element;
|
|
40
|
+
/** The button inside a field: the calendar of a date picker, the arrow of a combobox. */
|
|
41
|
+
export declare function FieldButton(props: AriaButtonProps): import("react").JSX.Element;
|
|
42
|
+
/**
|
|
43
|
+
* One row of a floating list — the options of a `Select`, the suggestions of a `Suggest`.
|
|
44
|
+
*
|
|
45
|
+
* Context: the same row `ComboBox` draws (control height, the `primary` tint on the row the
|
|
46
|
+
* keyboard is on), so every list that drops from a field reads as one kind of thing whatever
|
|
47
|
+
* control opened it.
|
|
48
|
+
*/
|
|
49
|
+
export declare const listOptionStyles: import("tailwind-variants/config").TVReturnType<{
|
|
50
|
+
isFocused: {
|
|
51
|
+
true: string;
|
|
52
|
+
};
|
|
53
|
+
isPressed: {
|
|
54
|
+
true: string;
|
|
55
|
+
};
|
|
56
|
+
isDisabled: {
|
|
57
|
+
true: string;
|
|
58
|
+
};
|
|
59
|
+
}, undefined, "group flex min-h-(--size-control-md) cursor-default items-center gap-2 rounded-control px-2 py-1 text-meta text-ink outline-0 select-none forced-color-adjust-none", undefined, undefined, undefined>;
|
|
60
|
+
/** For a control that only adds to the system's classes. */
|
|
61
|
+
export { composeClasses };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { composeClasses, focusRing, tv } from "../../foundations/variants.js";
|
|
4
|
+
import { Button as AriaButton, composeRenderProps, Group, } from "react-aria-components";
|
|
5
|
+
/**
|
|
6
|
+
* The pieces every React Aria control of the system is built from — the box a date field,
|
|
7
|
+
* a combobox or a number field draws, and the small button inside it.
|
|
8
|
+
*
|
|
9
|
+
* Context: they look exactly like `controlClasses()` — same height, border, radius and
|
|
10
|
+
* focus — because a form mixes both kinds of control in one column, and a date field one
|
|
11
|
+
* pixel taller than the text input above it is the first thing an eye catches.
|
|
12
|
+
*/
|
|
13
|
+
export const fieldGroupStyles = tv({
|
|
14
|
+
extend: focusRing,
|
|
15
|
+
base: "group flex h-(--size-control-md) w-full min-w-0 items-center overflow-hidden rounded-control border bg-surface text-meta text-ink transition-colors duration-(--duration-instant) forced-colors:bg-[Field]",
|
|
16
|
+
variants: {
|
|
17
|
+
isFocusWithin: {
|
|
18
|
+
false: "border-line-strong forced-colors:border-[ButtonBorder]",
|
|
19
|
+
true: "border-primary forced-colors:border-[Highlight]",
|
|
20
|
+
},
|
|
21
|
+
isInvalid: {
|
|
22
|
+
true: "border-danger forced-colors:border-[Mark]",
|
|
23
|
+
},
|
|
24
|
+
isDisabled: {
|
|
25
|
+
true: "cursor-not-allowed bg-ground text-muted forced-colors:border-[GrayText]",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
/** The box of a composite control: a field and its buttons, framed as one input. The `ref`
|
|
30
|
+
* reaches the box (React 19: a plain prop) — a control that floats a list as wide as the
|
|
31
|
+
* box, like `TagsField`, measures it. */
|
|
32
|
+
export function FieldGroup({ ref, ...props }) {
|
|
33
|
+
return (_jsx(Group, { ref: ref, ...props, className: composeRenderProps(props.className, (className, state) => fieldGroupStyles({ ...state, className })) }));
|
|
34
|
+
}
|
|
35
|
+
const fieldButton = tv({
|
|
36
|
+
extend: focusRing,
|
|
37
|
+
base: "inline-flex shrink-0 items-center justify-center rounded-mark p-1 text-muted transition-colors duration-(--duration-instant) hover:bg-ink/6 hover:text-ink pressed:bg-ink/10",
|
|
38
|
+
variants: {
|
|
39
|
+
isDisabled: {
|
|
40
|
+
true: "text-line-strong hover:bg-transparent forced-colors:text-[GrayText]",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
/** The button inside a field: the calendar of a date picker, the arrow of a combobox. */
|
|
45
|
+
export function FieldButton(props) {
|
|
46
|
+
return (_jsx(AriaButton, { ...props, className: composeRenderProps(props.className, (className, state) => fieldButton({ ...state, className })) }));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One row of a floating list — the options of a `Select`, the suggestions of a `Suggest`.
|
|
50
|
+
*
|
|
51
|
+
* Context: the same row `ComboBox` draws (control height, the `primary` tint on the row the
|
|
52
|
+
* keyboard is on), so every list that drops from a field reads as one kind of thing whatever
|
|
53
|
+
* control opened it.
|
|
54
|
+
*/
|
|
55
|
+
export const listOptionStyles = tv({
|
|
56
|
+
base: "group flex min-h-(--size-control-md) cursor-default items-center gap-2 rounded-control px-2 py-1 text-meta text-ink outline-0 select-none forced-color-adjust-none",
|
|
57
|
+
variants: {
|
|
58
|
+
isFocused: {
|
|
59
|
+
true: "bg-primary/8 forced-colors:bg-[Highlight] forced-colors:text-[HighlightText]",
|
|
60
|
+
},
|
|
61
|
+
isPressed: {
|
|
62
|
+
true: "bg-primary/12",
|
|
63
|
+
},
|
|
64
|
+
isDisabled: {
|
|
65
|
+
true: "text-muted opacity-45 forced-colors:text-[GrayText]",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
/** For a control that only adds to the system's classes. */
|
|
70
|
+
export { composeClasses };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a `Field` hands to the control inside it: the id its label points at, the ids that
|
|
3
|
+
* describe it (hint, error), and whether it is in error.
|
|
4
|
+
*
|
|
5
|
+
* Context: `Field` used to hand these through a render prop — `{(aria) => <TextInput
|
|
6
|
+
* id={aria.id} … />}` — on every field of every form. Correct, and three lines of wiring
|
|
7
|
+
* per field that anyone could get wrong (forget `describedBy` and the error is never read
|
|
8
|
+
* aloud). Now a control written directly inside a `Field` reads them from here; the render
|
|
9
|
+
* prop still works, and an explicit prop on the control always wins.
|
|
10
|
+
*/
|
|
11
|
+
export type FieldIds = {
|
|
12
|
+
id: string;
|
|
13
|
+
/** The id of the field's label. A native input is named by `<label for>`; a React Aria
|
|
14
|
+
* control — a date picker, a combobox — is a group of elements, and is named by this. */
|
|
15
|
+
labelId: string;
|
|
16
|
+
describedBy: string | undefined;
|
|
17
|
+
invalid: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare const FieldIdsContext: import("react").Context<FieldIds | null>;
|
|
20
|
+
/** The ids of the enclosing `Field`, resolved against the control's own props. */
|
|
21
|
+
export declare function useFieldIds(own: {
|
|
22
|
+
id?: string;
|
|
23
|
+
describedBy?: string;
|
|
24
|
+
invalid?: boolean;
|
|
25
|
+
}): {
|
|
26
|
+
id: string | undefined;
|
|
27
|
+
labelledBy: string | undefined;
|
|
28
|
+
describedBy: string | undefined;
|
|
29
|
+
invalid: boolean;
|
|
30
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
export const FieldIdsContext = createContext(null);
|
|
4
|
+
/** The ids of the enclosing `Field`, resolved against the control's own props. */
|
|
5
|
+
export function useFieldIds(own) {
|
|
6
|
+
const field = useContext(FieldIdsContext);
|
|
7
|
+
return {
|
|
8
|
+
id: own.id ?? field?.id,
|
|
9
|
+
labelledBy: field?.labelId,
|
|
10
|
+
describedBy: own.describedBy ?? field?.describedBy,
|
|
11
|
+
invalid: own.invalid ?? field?.invalid ?? false,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Checkbox, type CheckItem, CheckList } from "./Checks.js";
|
|
2
|
+
export { type FieldIds, useFieldIds } from "./context.js";
|
|
3
|
+
export { DropZone, type DropZoneProps } from "./DropZone.js";
|
|
4
|
+
export { controlClasses, DataField, Field, NumberInput, ReadValue, Select, type SelectOption, TextArea, TextInput, } from "./Field.js";
|
|
5
|
+
export { type FieldLayout, FieldLayoutProvider, useSheetLayout, } from "./layout.js";
|
|
6
|
+
export { type FormMode, FormModeProvider, useFormMode, } from "./mode.js";
|
|
7
|
+
export { Suggest, type SuggestProps } from "./Suggest.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* THE CONTROLS a form is written with.
|
|
2
|
+
What ARRANGES them —the sheet, the section, the footer— lives in `components/form`: a
|
|
3
|
+
control does not know a form exists, and is used the same way inside a cell, a filter or
|
|
4
|
+
a dialog. */
|
|
5
|
+
export { Checkbox, CheckList } from "./Checks.js";
|
|
6
|
+
export { useFieldIds } from "./context.js";
|
|
7
|
+
export { DropZone } from "./DropZone.js";
|
|
8
|
+
export { controlClasses, DataField, Field, NumberInput, ReadValue, Select, TextArea, TextInput, } from "./Field.js";
|
|
9
|
+
export { FieldLayoutProvider, useSheetLayout, } from "./layout.js";
|
|
10
|
+
export { FormModeProvider, useFormMode, } from "./mode.js";
|
|
11
|
+
export { Suggest } from "./Suggest.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* HOW A FIELD PLACES ITS LABEL. Declared once, inherited by every field.
|
|
4
|
+
*
|
|
5
|
+
* stacked the label above. The grouped look of iOS. The default.
|
|
6
|
+
* horizontal label | value, and every value starts on the same vertical — the Odoo
|
|
7
|
+
* form, where twenty fields read as a column and not as a bounce.
|
|
8
|
+
*
|
|
9
|
+
* Context: it lives in `primitives/field` and not next to `Sheet`, which declares it,
|
|
10
|
+
* because the one that READS it is `Field` — and a primitive cannot import from a
|
|
11
|
+
* component. The container sets it, the field obeys, and the type that joins them is in the
|
|
12
|
+
* level both can see.
|
|
13
|
+
*
|
|
14
|
+
* Being a context and not a prop is what keeps a sheet from ending up half aligned: nobody
|
|
15
|
+
* has to remember to pass it to each field.
|
|
16
|
+
*/
|
|
17
|
+
export type FieldLayout = "stacked" | "horizontal";
|
|
18
|
+
/** The name the sheet knows it by. */
|
|
19
|
+
export type SheetLayout = FieldLayout;
|
|
20
|
+
export declare function useSheetLayout(): FieldLayout;
|
|
21
|
+
export declare function FieldLayoutProvider({ value, children, }: {
|
|
22
|
+
value: FieldLayout;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
const LayoutCtx = createContext("stacked");
|
|
5
|
+
export function useSheetLayout() {
|
|
6
|
+
return useContext(LayoutCtx);
|
|
7
|
+
}
|
|
8
|
+
export function FieldLayoutProvider({ value, children, }) {
|
|
9
|
+
return _jsx(LayoutCtx.Provider, { value: value, children: children });
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type FormMode = "edit" | "read";
|
|
3
|
+
export declare function FormModeProvider({ value, children, }: {
|
|
4
|
+
value: FormMode;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
/** The mode of this field. The prop beats the context — a computed field stays
|
|
8
|
+
* in read mode inside a form that is being edited. */
|
|
9
|
+
export declare function useFormMode(explicit?: FormMode): FormMode;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
const Ctx = createContext("edit");
|
|
5
|
+
export function FormModeProvider({ value, children, }) {
|
|
6
|
+
return _jsx(Ctx.Provider, { value: value, children: children });
|
|
7
|
+
}
|
|
8
|
+
/** The mode of this field. The prop beats the context — a computed field stays
|
|
9
|
+
* in read mode inside a form that is being edited. */
|
|
10
|
+
export function useFormMode(explicit) {
|
|
11
|
+
const inherited = useContext(Ctx);
|
|
12
|
+
return explicit ?? inherited;
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A sentence that does not fit on the page, attached to a mark.
|
|
4
|
+
*
|
|
5
|
+
* Context: `title` on a field is a native tooltip and nobody finds it. Odoo
|
|
6
|
+
* puts a `?` next to the label; pressing it is how you read the help without
|
|
7
|
+
* it eating a line. The mark is the affordance; the sentence is the content.
|
|
8
|
+
*
|
|
9
|
+
* ── Why a click and a popover, and not a `Tooltip` ──
|
|
10
|
+
*
|
|
11
|
+
* A `Tooltip` is a LABEL: a few words that show on hover and vanish when the
|
|
12
|
+
* pointer leaves — useless on a tablet, and gone before a sentence is read.
|
|
13
|
+
* This is content, so it opens on press (mouse, touch, Enter or Space) and
|
|
14
|
+
* stays until it is put away: a second press, Escape, or a click outside. It
|
|
15
|
+
* is React Aria's `DialogTrigger` on the system's `Popover` — the same white
|
|
16
|
+
* surface as a calendar or a small filter form — so placement, flipping when
|
|
17
|
+
* there is no room below, focus moving in and back to the `?`, and Escape are
|
|
18
|
+
* React Aria's, not a hand-written listener.
|
|
19
|
+
*
|
|
20
|
+
* The sentence also lives, hidden, next to the mark under `id`: a field points
|
|
21
|
+
* its `aria-describedby` there, and a closed popover is not in the page for a
|
|
22
|
+
* screen reader to read.
|
|
23
|
+
*/
|
|
24
|
+
export declare function Hint({ children, label, id, }: {
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
/** Visible mark. The `?` is the default because it is the one that gets
|
|
27
|
+
* recognized as «there is more to read». */
|
|
28
|
+
label?: ReactNode;
|
|
29
|
+
/** For `aria-describedby` on the field the help belongs to. */
|
|
30
|
+
id?: string;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { tv } from "../../foundations/variants.js";
|
|
4
|
+
import { Popover } from "../overlay/Popover.js";
|
|
5
|
+
import { CircleHelp } from "lucide-react";
|
|
6
|
+
import { useId } from "react";
|
|
7
|
+
import { Button, Dialog, DialogTrigger } from "react-aria-components";
|
|
8
|
+
const mark = tv({
|
|
9
|
+
base: "inline-flex size-4 shrink-0 cursor-help items-center justify-center rounded-pill text-muted outline-0 transition-colors duration-(--duration-instant) hover:bg-ink/8 hover:text-ink aria-expanded:bg-ink/8 aria-expanded:text-ink forced-colors:text-[ButtonText]",
|
|
10
|
+
variants: {
|
|
11
|
+
isFocusVisible: {
|
|
12
|
+
true: "outline-2 outline-offset-1 outline-primary forced-colors:outline-[Highlight]",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* A sentence that does not fit on the page, attached to a mark.
|
|
18
|
+
*
|
|
19
|
+
* Context: `title` on a field is a native tooltip and nobody finds it. Odoo
|
|
20
|
+
* puts a `?` next to the label; pressing it is how you read the help without
|
|
21
|
+
* it eating a line. The mark is the affordance; the sentence is the content.
|
|
22
|
+
*
|
|
23
|
+
* ── Why a click and a popover, and not a `Tooltip` ──
|
|
24
|
+
*
|
|
25
|
+
* A `Tooltip` is a LABEL: a few words that show on hover and vanish when the
|
|
26
|
+
* pointer leaves — useless on a tablet, and gone before a sentence is read.
|
|
27
|
+
* This is content, so it opens on press (mouse, touch, Enter or Space) and
|
|
28
|
+
* stays until it is put away: a second press, Escape, or a click outside. It
|
|
29
|
+
* is React Aria's `DialogTrigger` on the system's `Popover` — the same white
|
|
30
|
+
* surface as a calendar or a small filter form — so placement, flipping when
|
|
31
|
+
* there is no room below, focus moving in and back to the `?`, and Escape are
|
|
32
|
+
* React Aria's, not a hand-written listener.
|
|
33
|
+
*
|
|
34
|
+
* The sentence also lives, hidden, next to the mark under `id`: a field points
|
|
35
|
+
* its `aria-describedby` there, and a closed popover is not in the page for a
|
|
36
|
+
* screen reader to read.
|
|
37
|
+
*/
|
|
38
|
+
export function Hint({ children, label = "?", id, }) {
|
|
39
|
+
const auto = useId();
|
|
40
|
+
const textId = id ?? auto;
|
|
41
|
+
return (_jsxs("span", { className: "inline-flex shrink-0", children: [_jsxs(DialogTrigger, { children: [_jsx(Button, { "aria-label": typeof children === "string" ? children : undefined, className: (state) => mark(state), children: label === "?" ? (_jsx(CircleHelp, { className: "size-3.5", strokeWidth: 2, "aria-hidden": true })) : (label) }), _jsx(Popover, { placement: "bottom", className: "max-w-64 rounded-box", children: _jsx(Dialog, { "aria-labelledby": textId, className: "px-3 py-2.5 text-micro leading-relaxed text-ink outline-0", children: children }) })] }), _jsx("span", { id: textId, hidden: true, children: children })] }));
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Hint } from "./Hint.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Hint } from "./Hint.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * from "./block/index.js";
|
|
2
|
+
export * from "./button/index.js";
|
|
3
|
+
export * from "./cell/index.js";
|
|
4
|
+
export * from "./choice/index.js";
|
|
5
|
+
export * from "./date/index.js";
|
|
6
|
+
export * from "./dialog/index.js";
|
|
7
|
+
export * from "./disclosure/index.js";
|
|
8
|
+
export * from "./field/index.js";
|
|
9
|
+
export * from "./hint/index.js";
|
|
10
|
+
export * from "./menu/index.js";
|
|
11
|
+
export * from "./notice/index.js";
|
|
12
|
+
export * from "./overlay/index.js";
|
|
13
|
+
export * from "./picker/index.js";
|
|
14
|
+
export * from "./surface/index.js";
|
|
15
|
+
export * from "./table/index.js";
|
|
16
|
+
export * from "./tabs/index.js";
|
|
17
|
+
export * from "./tag/index.js";
|
|
18
|
+
export * from "./toolbar/index.js";
|
|
19
|
+
export * from "./value/index.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* PRIMITIVES — a piece that does not know which screen it is on. */
|
|
2
|
+
export * from "./block/index.js";
|
|
3
|
+
export * from "./button/index.js";
|
|
4
|
+
export * from "./cell/index.js";
|
|
5
|
+
export * from "./choice/index.js";
|
|
6
|
+
export * from "./date/index.js";
|
|
7
|
+
export * from "./dialog/index.js";
|
|
8
|
+
export * from "./disclosure/index.js";
|
|
9
|
+
export * from "./field/index.js";
|
|
10
|
+
export * from "./hint/index.js";
|
|
11
|
+
export * from "./menu/index.js";
|
|
12
|
+
export * from "./notice/index.js";
|
|
13
|
+
export * from "./overlay/index.js";
|
|
14
|
+
export * from "./picker/index.js";
|
|
15
|
+
export * from "./surface/index.js";
|
|
16
|
+
export * from "./table/index.js";
|
|
17
|
+
export * from "./tabs/index.js";
|
|
18
|
+
export * from "./tag/index.js";
|
|
19
|
+
export * from "./toolbar/index.js";
|
|
20
|
+
export * from "./value/index.js";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { type ButtonRole, type ButtonSize } from "../button/Button.js";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
export interface MenuOption {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
/** The line below. It exists because a thirty-entry menu without hints forces
|
|
8
|
+
* you to open each one to discover what it does. */
|
|
9
|
+
hint?: string;
|
|
10
|
+
icon?: LucideIcon;
|
|
11
|
+
/** For the menus that CHOOSE (group by, favorites) instead of executing. */
|
|
12
|
+
checked?: boolean;
|
|
13
|
+
/** A hairline ABOVE this option. It separates the three rows of a date —months,
|
|
14
|
+
* quarters, years— which are one group because they combine, but read as
|
|
15
|
+
* three because they answer at three different zooms. */
|
|
16
|
+
divider?: boolean;
|
|
17
|
+
/** Cómo se SACA esta opción cuando se dibuja como ficha, si sacarla no es lo mismo que
|
|
18
|
+
* volver a elegirla. El filtro a medida es el caso: elegirlo abre el constructor, y eso
|
|
19
|
+
* no es lo que tiene que pasar al tocar su ×. */
|
|
20
|
+
onRemove?: () => void;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Visual mark at the end of the label. A string is drawn as a pill
|
|
23
|
+
* —"maqueta", "nuevo"—; anything else is drawn as is, which is how a status
|
|
24
|
+
* `Tag` or an already formatted counter comes in. */
|
|
25
|
+
badge?: ReactNode;
|
|
26
|
+
/** What THIS option does. It exists on top of the menu's `onSelect` because a
|
|
27
|
+
* menu built from a registry —twenty-five operations, each one with its own
|
|
28
|
+
* action— has no reasonable `switch` on the other side. */
|
|
29
|
+
onSelect?: () => void;
|
|
30
|
+
}
|
|
31
|
+
export interface MenuGroup {
|
|
32
|
+
/** The group's label, in the brand voice. Without a label, the group is just
|
|
33
|
+
* a separator — which also serves a purpose. */
|
|
34
|
+
label?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The group folds under its label, and starts folded unless something in it
|
|
37
|
+
* is ticked.
|
|
38
|
+
*
|
|
39
|
+
* It exists for the DATE narrowings. «Fecha de pedido» is not one option, it
|
|
40
|
+
* is eleven —three months, four quarters, three years— and eleven options
|
|
41
|
+
* always open push the rest of the panel off the screen. A date is the only
|
|
42
|
+
* field that brings its own little calendar, so it is the only one that
|
|
43
|
+
* folds; the others are a list and a list does not need a door.
|
|
44
|
+
*/
|
|
45
|
+
collapsible?: boolean;
|
|
46
|
+
options: MenuOption[];
|
|
47
|
+
}
|
|
48
|
+
declare const WIDTH: {
|
|
49
|
+
/** A menu of short verbs hanging off an icon: "duplicate", "delete". */
|
|
50
|
+
readonly sm: "min-w-44 max-w-64";
|
|
51
|
+
readonly md: "min-w-56 max-w-80";
|
|
52
|
+
readonly lg: "min-w-80 max-w-(--container-short)";
|
|
53
|
+
};
|
|
54
|
+
export declare function Menu({ label, groups, onSelect, surface, align, icon: Trigger,
|
|
55
|
+
/** The menu as a control of a list ("filters ▾") carries the chevron; the
|
|
56
|
+
* actions `⋯` does not, because the icon already says there is more. */
|
|
57
|
+
chevron, active, iconOnly, variant, searchPlaceholder, emptyText, width, role, size, title, closeOnSelect, }: {
|
|
58
|
+
label: string;
|
|
59
|
+
groups: readonly MenuGroup[];
|
|
60
|
+
onSelect?: (id: string) => void;
|
|
61
|
+
surface?: "canvas" | "chrome";
|
|
62
|
+
align?: "start" | "end";
|
|
63
|
+
icon?: LucideIcon;
|
|
64
|
+
chevron?: boolean;
|
|
65
|
+
/** Something is chosen inside. A filter menu that doesn't look different when
|
|
66
|
+
* it filters is how someone ends up looking at a trimmed list without
|
|
67
|
+
* knowing it. */
|
|
68
|
+
active?: boolean;
|
|
69
|
+
/** `text` removes the frame and the background: it is the menu of an
|
|
70
|
+
* application BAR. `button` —the default— is the menu of a control. */
|
|
71
|
+
variant?: "button" | "text";
|
|
72
|
+
/** With a search box, the menu filters over label and hint. It only goes in
|
|
73
|
+
* when there are enough entries for searching to gain something — a search
|
|
74
|
+
* box on top of four options is one more control between the hand and what
|
|
75
|
+
* was already visible. */
|
|
76
|
+
searchPlaceholder?: string;
|
|
77
|
+
emptyText?: string;
|
|
78
|
+
/** `md` for a menu of verbs; `lg` when the entries carry a hint that cannot be
|
|
79
|
+
* truncated without losing its reason to exist. */
|
|
80
|
+
width?: keyof typeof WIDTH;
|
|
81
|
+
/** The trigger looks like a BUTTON of that role. It is for the menu that
|
|
82
|
+
* starts something —"+ Create"—, which has to look like the main action and
|
|
83
|
+
* not like a control of the list. Without this, the trigger is the usual
|
|
84
|
+
* small one. */
|
|
85
|
+
role?: ButtonRole;
|
|
86
|
+
size?: ButtonSize;
|
|
87
|
+
/** What explains the menu on hover. It is not drawn. */
|
|
88
|
+
title?: string;
|
|
89
|
+
/** Icon only. `label` becomes the accessible name and the `title`, and stops
|
|
90
|
+
* being drawn — it is the actions `⋯`, where the icon already says there is
|
|
91
|
+
* more.
|
|
92
|
+
*
|
|
93
|
+
* The label does NOT become optional when hidden: a menu without an
|
|
94
|
+
* accessible name is a menu a screen reader announces as "button". */
|
|
95
|
+
iconOnly?: boolean;
|
|
96
|
+
/** Column and filter menus toggle several options in a row. Closing after
|
|
97
|
+
* each click turns three hides into three open-close round trips. */
|
|
98
|
+
closeOnSelect?: boolean;
|
|
99
|
+
}): import("react").JSX.Element;
|
|
100
|
+
export {};
|