@flood-md/react 0.2.10 → 0.2.12

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.
Files changed (57) hide show
  1. package/README.md +275 -5
  2. package/dist/app-shell.d.ts +3 -0
  3. package/dist/app-shell.js +66 -0
  4. package/dist/bulk-action-bar.d.ts +3 -0
  5. package/dist/bulk-action-bar.js +17 -0
  6. package/dist/collection-toolbar.d.ts +3 -0
  7. package/dist/collection-toolbar.js +26 -0
  8. package/dist/collection-view-switcher.d.ts +3 -0
  9. package/dist/collection-view-switcher.js +19 -0
  10. package/dist/combobox.d.ts +6 -1
  11. package/dist/combobox.js +77 -29
  12. package/dist/command-list.d.ts +5 -1
  13. package/dist/command-list.js +2 -2
  14. package/dist/data-display.js +28 -4
  15. package/dist/data-table-columns.d.ts +25 -0
  16. package/dist/data-table-columns.js +271 -0
  17. package/dist/data-table-expansion.d.ts +21 -0
  18. package/dist/data-table-expansion.js +79 -0
  19. package/dist/data-table.js +140 -12
  20. package/dist/date-picker.js +2 -2
  21. package/dist/date-range-picker.js +2 -2
  22. package/dist/date-time-picker.js +2 -2
  23. package/dist/date-time-range-picker.js +2 -2
  24. package/dist/disclosure.d.ts +2 -1
  25. package/dist/disclosure.js +4 -4
  26. package/dist/editable.js +57 -18
  27. package/dist/filter-condition.d.ts +3 -0
  28. package/dist/filter-condition.js +21 -0
  29. package/dist/filter-group.d.ts +2 -0
  30. package/dist/filter-group.js +62 -0
  31. package/dist/foundations.css +1131 -93
  32. package/dist/index.d.ts +469 -5
  33. package/dist/index.js +72 -9
  34. package/dist/menu.js +5 -4
  35. package/dist/menubar.d.ts +2 -0
  36. package/dist/menubar.js +85 -0
  37. package/dist/navigation-group.d.ts +3 -0
  38. package/dist/navigation-group.js +13 -0
  39. package/dist/navigation-item.d.ts +3 -0
  40. package/dist/navigation-item.js +68 -0
  41. package/dist/navigation-layout.d.ts +2 -1
  42. package/dist/navigation-layout.js +12 -4
  43. package/dist/navigation-tree.js +51 -7
  44. package/dist/navigation.js +2 -2
  45. package/dist/overflow-actions.d.ts +2 -0
  46. package/dist/overflow-actions.js +75 -0
  47. package/dist/overlay-runtime.d.ts +6 -2
  48. package/dist/overlay-runtime.js +41 -5
  49. package/dist/overlays.d.ts +1 -0
  50. package/dist/overlays.js +34 -18
  51. package/dist/select.js +4 -1
  52. package/dist/sidebar.d.ts +2 -0
  53. package/dist/sidebar.js +58 -0
  54. package/dist/sparkline.d.ts +2 -0
  55. package/dist/sparkline.js +65 -0
  56. package/dist/toolbar.js +2 -2
  57. package/package.json +2 -2
package/README.md CHANGED
@@ -6,9 +6,203 @@ HTML semantics and refs, no third-party component library or Tailwind dependency
6
6
  Named imports support tree shaking in optimizing ES-module bundlers. Import the
7
7
  shared stylesheet explicitly; styles are not split by component.
8
8
 
9
- This package documents the **0.2.10** source API. Keep Flood package versions aligned and follow the guide matching
9
+ Sheet is a bottom-aligned Drawer for short tasks, navigation and touch-friendly
10
+ choices. It fits short content, caps long content to the viewport, and keeps
11
+ Drawer dismissal, focus restoration, body scrolling and the optional footer.
12
+
13
+ SegmentedControl options can display a `count` alongside the label, for example
14
+ `{value: "pending", label: "Pending", count: 2}`. Use this for a small, mutually
15
+ exclusive choice that updates one collection. The count is read with the visible
16
+ label and does not change the submitted value.
17
+
18
+ Version 0.2.12 adds opt-in DataTable column visibility. Set
19
+ `showColumnVisibility` for the Columns menu, and supply `hiddenColumnIds` and
20
+ `onHiddenColumnIdsChange` when the host saves preferences. The active sort and
21
+ record-opening columns stay visible. See [DataTable](../../docs/components/data-table.md).
22
+ Pass the Columns control from `toolbar={({ columns }) => …}` into
23
+ `CollectionToolbar.actions` to keep it alongside search and filters.
24
+
25
+ The 0.2.11 candidate adds optional DataTable row `primaryAction` for opening an
26
+ in-place detail panel, plus `toolbar` and `inset="roomy"` for a shared table edge.
27
+ The title is a native button and the rest of the row also responds to pointer
28
+ activation. Nested controls keep their own actions.
29
+ Set `numeric: true` on a DataTable column of comparable amounts or quantities
30
+ for logical end alignment and tabular digits. An explicit `align` still applies.
31
+
32
+ The 0.2.11 candidate adds optional compact NavigationTree item `flyout` settings
33
+ for mouse-hover previews and a separate positioning reference. Click, keyboard
34
+ and touch activation still work. Popover `focusOnOpen` defaults to true; use false
35
+ for a pointer preview that preserves the current keyboard focus.
36
+
37
+ In the 0.2.11 candidate, `NavigationItem.trailingAction` adds a named sibling
38
+ control to an expanded row. `NavigationTree` items accept the same slot.
39
+ Enable `branchGuides` on a tree or Sidebar group for visual depth cues.
40
+ Choose `Sidebar.headerLayout="inline"` to put the identity and collapse button
41
+ on one line while expanded. The stacked layout keeps the original toggle position.
42
+
43
+ CommandPalette (0.2.11 candidate) provides modal search over pages or commands.
44
+ It shares the CommandList item contract and supports grouped results, disabled
45
+ items, loading and two densities. The host supplies actions and any shortcut.
46
+
47
+ Tooltip trigger props are HTMLElement-safe: render the existing Link or Button
48
+ directly and forward its ref, hover/focus handlers and aria-describedby. Tooltip
49
+ does not add button type, popup or navigation semantics. Raw button triggers
50
+ inside forms keep an explicit type="button". Popover/Menu retain button trigger
51
+ contracts. When one control owns both Tooltip and Popover, compose both refs,
52
+ handlers and descriptions, and disable the tooltip while the interactive layer
53
+ is open so Escape and focus remain owned by that layer.
54
+
55
+ IconButton iconSize (0.2.11 candidate) sizes its inner IconSlot independently of
56
+ the interactive size. It defaults to size; shrinking a supporting glyph must
57
+ not shrink its target or bypass IconSlot with a custom SVG width. Existing glyph
58
+ roles at a 16px root are 2xs=14px, xs/sm=16px and md/lg=20px.
59
+
60
+ This package documents the **0.2.12** source API. Keep Flood package versions aligned and follow the guide matching
10
61
  your installed declarations; source changes do not modify an existing npm release.
11
62
 
63
+ NavigationItem (0.2.11 candidate) is the shared small row for destinations,
64
+ commands and disclosures. Supply label and either href for a native Link or
65
+ onAction for a Button command. disclosure supplies expanded, onExpandedChange,
66
+ label and optional controls/disabled. With disclosure alone, the full primary
67
+ row toggles the branch; with href or onAction, disclosure is a separate sibling
68
+ IconButton. The row shares its visual surface across both controls, without
69
+ nested interactive elements or making a link click toggle expansion. Current,
70
+ ancestorCurrent, focus and disclosure expansion are separate states. icon,
71
+ reserveIcon, trailing and description are optional anatomy, not required filler.
72
+ Compact rows keep their accessible label and optional Tooltip; NavigationTree
73
+ owns any branch hierarchy and compact Popover, not NavigationItem itself.
74
+ Raw compact NavigationItem cannot combine a separate primary destination/action
75
+ and disclosure: use the NavigationTree branch composition, which retains the
76
+ parent destination alongside children in its flyout. trailing is noninteractive
77
+ metadata; independent controls belong in their own sibling action anatomy.
78
+
79
+ NavigationGroup (0.2.11 candidate) composes related children with optional label,
80
+ hideLabel, headingAction and separator. collapsible uses the existing Collapsible;
81
+ expanded/onExpandedChange or defaultExpanded control only the group disclosure.
82
+ defaultExpanded is true. A collapsible group requires a nonempty label; hideLabel
83
+ omits the visual title while preserving the toggle name. collapseDisabled disables
84
+ only that disclosure, not arbitrary child destinations or heading actions.
85
+ Compact presentation keeps children available and preserves the accepted expanded
86
+ choice for return to expanded navigation. Heading actions remain available on the
87
+ compact axis. Collapsible headerAction supplies a sibling action in its own header,
88
+ so a group command need not be nested in the disclosure button or moved elsewhere.
89
+
90
+ Sidebar (0.2.11 candidate) composes grouped NavigationTree destinations and a
91
+ bounded ScrollArea between fixed header/footer slots. NavigationGroup owns each
92
+ section's optional presentation; NavigationTree composes NavigationItem rows.
93
+ Supply label
94
+ and choose variant="quiet" (default) for restrained inactive navigation and
95
+ rectangular current rows, or standard for the stronger pill recipe. This is a
96
+ scoped component recipe using existing roles, not a global theme change.
97
+ Supply groups `{id,label,items}` and currentValue; item values must be globally unique. Set a
98
+ height on the host. Expanded width defaults to 16rem; collapsed rail is 4.5rem (5rem for coarse pointers).
99
+ collapsed/defaultCollapsed/onCollapsedChange are independent of the route and
100
+ expandedValues/defaultExpandedValues/onExpandedValuesChange. Controlled refusal
101
+ preserves accepted state. side is logical start/end; size matches NavigationTree.
102
+ Header/footer accept ReactNode or `({collapsed}) => ReactNode`; adapt their content
103
+ explicitly. collapsible=false omits the toggle. Localize collapseLabel,
104
+ expandLabel and expandItemLabel; group/item labels supply the navigation names.
105
+ The component does not add preference storage, routing, a mobile drawer or shortcuts.
106
+ Groups can set hideLabel to omit redundant visible section titles while retaining
107
+ nav names. headingAction, separator and collapsible/expanded group configuration
108
+ are independent of branch expandedValues. headerSeparator/footerSeparator are
109
+ independent opt-in shell choices and default false. Header/footer content must
110
+ follow the mode contract: common icon/text tracks when expanded and one full-rail
111
+ axis for the actual brand, toggle, destinations and account target when compact.
112
+ currentKind defaults to page; location identifies a current global
113
+ module. Compose a flat compact Sidebar as a GlobalRail and another Sidebar as
114
+ its ContextBar, keeping route, module, expansion and visibility in the host.
115
+
116
+ Sidebar places its overlay scrollbar inside the existing outer edge spacing.
117
+ Header, body and footer keep the same tracks without an additional reserved
118
+ column, whether navigation overflows or not.
119
+ NavigationTree rows share NavigationItem's space-3 start inset, icon/text tracks
120
+ and deliberate columns for actual trailing anatomy. Iconless descendants begin at the parent text track; missing
121
+ peer icons reserve a slot. Header content should use the same icon size and gap.
122
+
123
+ NavigationTree rows use space-1 between peers and before an expanded branch;
124
+ group separation remains owned by Sidebar.
125
+
126
+ NavigationTree compact (0.2.11 candidate) exposes an icon rail: leaf destinations
127
+ remain links, disabled items remain unavailable, branches use Popover with full
128
+ navigation. flyoutSide controls its physical placement; Sidebar resolves its own
129
+ side and RTL direction. Branch selection closes the flyout and preserves current
130
+ destination/expanded state independently. trailing composes meaningful metadata
131
+ such as Badge in expanded rows. Flood tooltips identify compact destinations on
132
+ hover and focus while preserving the actual link/button; an open branch flyout
133
+ suppresses its hint. Missing icons use a neutral fallback glyph. Current ancestor styling does not
134
+ claim aria-current on a non-current parent.
135
+
136
+ DataTable emptyContent (0.2.11 candidate) accepts a composed EmptyState and
137
+ applicable actions inside the existing full-width body cell. It renders only
138
+ when state="ready" has no visible rows; loading/error retain Spinner/Alert and
139
+ retry ownership. Omitting it preserves labels.empty. Keep header/sort context
140
+ and distinguish an empty collection from no matching results in host content.
141
+ Reset filters in the host and return focus to a persistent query control.
142
+
143
+ CollectionViewSwitcher (0.2.11 candidate) composes a hidden-label Select and
144
+ applicable view commands. Supply views `{id,name,disabled?}`, controlled value
145
+ and onValueChange. Optional actions/onAction use the OverflowActions contract:
146
+ one command renders Button directly, multiple commands adapt through overflow.
147
+ The host owns saved queries, persistence, naming, confirmation and recovery.
148
+ Keep it separate from CollectionToolbar; selectRef gives host dialogs a stable
149
+ focus destination. controlSize defaults to sm; disabled blocks choice/commands.
150
+ Localize label, actionsLabel, moreLabel, view names and supplied action labels.
151
+ Its multi-action rail uses OverflowActions containerVariant="plain" to match
152
+ the adjacent field height. Toolbar variant="plain" removes its material and
153
+ outer inset; contained remains the default for standalone rails. Button variant
154
+ and toolbar containerVariant remain independent in OverflowActions.
155
+
156
+ CollectionToolbar (0.2.11 candidate) composes a named search field, applied Tag
157
+ summaries, reserved Clear filters action and end-aligned Filters Popover. Supply
158
+ controlled searchValue/onSearchValueChange; appliedFilters and onRemoveFilter
159
+ describe accepted conditions. filterContent supplies the host's staged editor.
160
+ The host owns Apply/Cancel, query execution, validation and saved views. Omit
161
+ filterContent for search/summary-only use. onClear is optional; canClear describes
162
+ whether the host has anything to reset, including slot-owned quickFilters.
163
+ Clearing and tag removal focus Filters (or search without a panel). The root is
164
+ a fieldset with ordinary Tab navigation, not an action-only roving toolbar.
165
+ Use summaryPrefix for meaningful all/any context, localized labels for controls,
166
+ and keep saved-view management outside this query row. Narrow containers move
167
+ summaries below search/actions; no material surface is added.
168
+
169
+ FilterGroup (0.2.11 candidate) composes FilterCondition rows and SegmentedControl
170
+ for a flat finite-choice draft. Supply label, properties, controlled value
171
+ `{match:"all"|"any", conditions:[{id,property,operator,values}]}` and onValueChange.
172
+ Rows keep stable IDs. All/any appears at two conditions; an empty group offers
173
+ Add condition without extra explanatory chrome. New rows use an available
174
+ enabled property and empty values. Duplicate properties are prevented for new
175
+ choices by default; allowDuplicateProperties and maxConditions can change that
176
+ policy. Existing accepted rows are preserved when constraints change.
177
+ Accepted additions/removals restore useful focus; refused proposals retain state
178
+ and focus. The host owns application, validation, persistence and query semantics.
179
+ controlSize defaults to sm; labels and conditionLabels localize group/row text.
180
+
181
+ FilterCondition (0.2.11 candidate) composes Fieldset, Field, Grid, two Selects,
182
+ MultiSelect and an optional removal IconButton. Supply a named label, properties
183
+ with finite options, controlled value `{property, operator: "is" | "is-not", values}`
184
+ and onValueChange. Property changes propose an empty values array; operator
185
+ changes preserve values. Empty values remain a draft for host validation.
186
+ No query execution or Apply/Cancel state is built in. controlSize defaults to sm;
187
+ labels localizes names/operator text and hideLabel defaults to true for a named
188
+ row. disabled blocks all interaction; onRemove is optional. A wide host gets one
189
+ row with a larger value track; container queries reflow narrow rows. Hosts own
190
+ duplicate-property policy and focus after removal.
191
+ MultiSelect shows its placeholder only while no values are selected. With chips
192
+ and an empty query, the editor reserves only its real action/caret budget;
193
+ typing expands the search area. A needless placeholder row must not enlarge the
194
+ control, while actual chip overflow can still wrap without clipping values.
195
+
196
+ Toolbar supports shape="rounded" (default) or "pill". OverflowActions uses
197
+ shape="pill" by default and forwards that capability to Toolbar; choose rounded
198
+ for the earlier geometry. A horizontal pill toolbar stays on one scrollable rail.
199
+ Read-only text controls retain keyboard focus and copying while preventing edits
200
+ and picker activation; they are not disabled controls.
201
+
202
+ Tabs uses `variant="contained"` by default. Choose `variant="line"` for page
203
+ sections with a selection marker along the edge next to the panel. Both variants
204
+ support horizontal and vertical orientation and the same keyboard behavior.
205
+
12
206
  Version 0.2.10 adds Fieldset legendSize="label" | "section"
13
207
  (default label). Independent section legends use a 1rem/600 role; named fields
14
208
  retain their 13px-equivalent label size with a .25rem inner gap. Dialog task
@@ -248,6 +442,10 @@ edge-to-edge content when the host already provides safe padding. The default
248
442
  padding can wrap text and change content height. Native
249
443
  fallback scrollbars retain their platform behavior.
250
444
 
445
+ AppShell content mode uses the same ScrollArea. For a workspace whose layout
446
+ already provides clearance, `scrollContentInset="none"` keeps the content
447
+ width stable when rails appear. The default protects edge-to-edge content.
448
+
251
449
  ```tsx
252
450
  <ScrollArea aria-label="Activity" style={{ maxHeight: "12rem" }}>
253
451
  <Stack gap="4">{/* Long activity content */}</Stack>
@@ -421,8 +619,9 @@ Available in 0.2.3. Import Flood CSS and use FloodRoot for dark foundations.
421
619
 
422
620
  Disclosure corner geometry: Accordion headers have square internal seams. Only
423
621
  the first header has upper shell corners, and only a closed last header has lower
424
- shell corners. An open Collapsible header has square lower corners where its
425
- panel begins. Hover backgrounds follow that same shape without clipping focus.
622
+ shell corners. A standalone Collapsible has an inset, rounded trigger and a
623
+ separate gap before the revealed content. The trigger label and panel share
624
+ an inline edge.
426
625
 
427
626
 
428
627
  ## Tooltip, Popover and Dialog
@@ -621,7 +820,12 @@ assertive uses role=alert. Choose urgency deliberately for dynamically inserted
621
820
  content. Announcements depend on actual assistive technology; initial static
622
821
  markup is not guaranteed to be read aloud. No focus movement, auto-dismiss,
623
822
  timeout or built-in close/action button is supplied. Compose named host actions
624
- inside children when needed. Alert is separate from Dialog and future Toast.
823
+ inside children when needed. A titleless Alert centers its glyph alongside a
824
+ single Inline row. Subtle and ghost actions inherit the feedback tint on
825
+ interactive states. Use a floating ToastRegion for brief operation results.
826
+ ToastRegion defaults to `placement="bottom-end"`. Choose `bottom-start`,
827
+ `top-end` or `top-start` for a different logical corner, or `inline` to
828
+ embed it in a layout. New floating cards enter from their placement edge.
625
829
 
626
830
  See the [WAI Meter pattern](https://www.w3.org/WAI/ARIA/apg/patterns/meter/),
627
831
  [Alert pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alert/) and
@@ -755,11 +959,37 @@ See [WAI Menu Button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/) and
755
959
 
756
960
  ### Toolbar and Kbd (0.2.4)
757
961
 
962
+ BulkActionBar (0.2.11 candidate) composes Toolbar and OverflowActions for selected
963
+ records. Supply label, selectionCount, items and onClear; onAction receives the
964
+ command ID. A zero count renders nothing. selectionLabel is the complete
965
+ localized count. The pill rail stays in one row, with two visible commands by
966
+ default; maxVisibleActions accepts 1, 2 or 3 and available width can reduce it
967
+ further. Remaining commands use More. pending disables all commands and clearing.
968
+ counter="label" (default) shows the summary, "number" shows only the number with
969
+ the full accessible label, and "none" removes the counter slot. Label/number
970
+ slots reserve 7rem/3rem respectively, bounded on narrow rails, and use tabular
971
+ digits so changing selection does not move commands. counterWidth customizes
972
+ that reservation for translations or larger counts; long summaries truncate
973
+ visually while retaining their full text/title. A hidden counter frees its slot.
974
+ The host owns accepted changes, captured IDs, confirmation and focus after the
975
+ bar disappears. Positioning is host-owned: use a stable bounded width for a
976
+ floating rail, plus viewport safe-area spacing and measured scroll clearance.
977
+ For table selection, place the rail at the viewport's bottom center. Set
978
+ `showSelection={false}` on DataTable to keep its count available to assistive
979
+ technology while BulkActionBar displays the visible count.
980
+
981
+ OverflowActions also accepts leading/trailing slots and maxVisibleActions. Slot
982
+ width is reserved before measuring commands; use action owners in those slots
983
+ and retain a stable containing width. Do not add form inputs to the Toolbar.
984
+
758
985
  Toolbar groups Button, IconButton, Toggle, native buttons/links and Menu triggers
759
986
  under one named action panel. After hydration it uses one Tab entry, directional
760
987
  arrows, Home/End and optional looping. Disabled/hidden actions are skipped. Arrows
761
988
  move focus, not activation. Horizontal order follows writing direction. In vertical
762
989
  toolbars open Menu with Enter/Space; horizontal menus retain Up/Down opening.
990
+ A vertical pill centers its actions on a single axis. Use same-size icon controls
991
+ with accessible names for a compact rail. Use a rounded toolbar for a vertical
992
+ group of labeled actions.
763
993
  Keep text inputs and independent selection widgets outside this action-only API.
764
994
  Native child handlers and refs remain attached; Toolbar owns their tabIndex while
765
995
  mounted and restores original attributes on cleanup. Without JavaScript, native
@@ -906,6 +1136,7 @@ relative path, without reading their contents. Single mode replaces its selectio
906
1136
 
907
1137
  AlertDialog (0.2.5) is a concise confirmation, composed from Flood's
908
1138
  native modal layer and Button. Supply title, description, trigger and onConfirm.
1139
+ Line breaks in the description separate distinct thoughts while allowing each line to wrap.
909
1140
  Cancel receives initial focus; Escape cancels; outside clicks do not dismiss.
910
1141
  Tab containment, background inertness, scroll lock and return focus reuse Dialog.
911
1142
  Translate confirmLabel/cancelLabel. onConfirm runs before requesting close, while
@@ -942,7 +1173,8 @@ The CSS build assembles the original component styles into the shared stylesheet
942
1173
 
943
1174
 
944
1175
  Section headingLevel controls native h2–h6 semantics. Its independent headingSize
945
- uses the existing Heading roles (section/page/large/display), defaulting to section.
1176
+ uses the Heading roles (group/section/page/large/display), defaulting to section.
1177
+ Choose group for a compact section inside a Drawer or another constrained panel.
946
1178
  Keep document hierarchy meaningful when changing visual emphasis.
947
1179
 
948
1180
  ## Editable and TagInput · 0.2.7
@@ -962,3 +1194,41 @@ Popover positionReference optionally supplies a connected same-document element
962
1194
  DateRangeField, RangeCalendar and DateRangePicker (0.2.7) share ordered range values and the original Calendar grid. ResizablePanels adds real pointer/keyboard geometry above static Split. ErrorSummary links host validation to native controls. See each generated catalog page and its guide under docs/components/. DateField validationMessage adds an external native constraint after the field's own date validity.
963
1195
 
964
1196
  Menu (0.2.7) supports recursive submenu items and ContextMenu reuses its native layer engine. Context triggers preserve the actual pointer/keyboard source; nested searches stay scoped, Escape unwinds one level and Tab exits through the root. See docs/components/menu.md and context-menu.md.
1197
+
1198
+ ## Collection contracts in 0.2.11
1199
+
1200
+ Autocomplete submits free text, including strings outside its suggestions.
1201
+ Combobox and MultiSelect continue submitting IDs. Search controls accept local
1202
+ filtering (default) or manual filtering with controlled query/value and
1203
+ optionsQuery identifying the result. A mismatched result stays nonselectable.
1204
+ loadState distinguishes ready/loading/error; onRetry is an optional host action.
1205
+ Use selectedOptions to retain a selected record's presentation while results
1206
+ change. Query cancellation and transport stay with the host. Options may have
1207
+ group labels; option IDs remain globally unique.
1208
+
1209
+ OverflowActions takes named command items, an onAction callback, optional priority,
1210
+ size/variant and a localized moreLabel. It preserves source order and moves lower
1211
+ priorities to an existing Menu. It is not a navigation-link container. Menubar
1212
+ coordinates menus with controlled/uncontrolled openValue and roving triggers;
1213
+ child commands reuse Menu. Use native links for destinations. Button/IconButton
1214
+ tone="danger" uses existing semantic colors within FloodRoot, independently of
1215
+ prominence. Tone itself does not confirm a destructive operation.
1216
+
1217
+ DataTable paginationMode="server" renders supplied rows without sorting/slicing.
1218
+ Provide page/onPageChange and nonnegative totalRows; sortable columns also require
1219
+ controlled sort (null when absent) and onSortChange. Reconcile an out-of-range page
1220
+ when a query changes; ready rows must match pageSize/totalRows. Selection defaults
1221
+ to the supplied page in server mode and the full collection in client mode.
1222
+ Server selectionScope="dataset" requires controlled selectedIds/onSelectionChange;
1223
+ the host validates IDs outside the page. Checkboxes only select the current page.
1224
+ The host owns request execution and cancellation, remote query selection and
1225
+ virtualization. DataTable supports optional column visibility, width preferences,
1226
+ logical edge pinning and expandable record details. Use Sparkline for a compact
1227
+ trend cell. See [DataTable](../../docs/components/data-table.md) and
1228
+ [Sparkline](../../docs/components/sparkline.md).
1229
+
1230
+ DatePicker and DateRangePicker pass viewLabels to their calendars. Calendar panels
1231
+ use minimal Popover chrome, with Escape/selection/trigger dismissal; there is no
1232
+ separate hidden close-button API. DateTimePicker datePickerLabels/timePickerLabels
1233
+ forward popup labels plus segmentLabels and validation messages. DateTimeRangePicker
1234
+ exposes corresponding start/end label objects; outer field labels remain distinct.
@@ -0,0 +1,3 @@
1
+ import { type AppShellProps, type AppTopBarProps } from "./index.js";
2
+ export declare const ApplicationTopBar: import("react").ForwardRefExoticComponent<AppTopBarProps & import("react").RefAttributes<HTMLElement>>;
3
+ export declare const ApplicationShell: import("react").ForwardRefExoticComponent<AppShellProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,66 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
3
+ import { AppTopBar, Button, Drawer, Inline, ScrollArea, SkipLink } from "./index.js";
4
+ const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
5
+ export const ApplicationTopBar = /* @__PURE__ */ forwardRef(function ApplicationTopBar({ start, end, bordered, className, ...props }, ref) {
6
+ return _jsxs("header", { ...props, ref: ref, className: ["flood-app-top-bar", className].filter(Boolean).join(" "), "data-bordered": bordered || undefined, children: [start != null && _jsx(Inline, { align: "center", gap: "2", className: "flood-app-top-bar__start", children: start }), end != null && _jsx(Inline, { align: "center", gap: "2", className: "flood-app-top-bar__end", children: end })] });
7
+ });
8
+ export const ApplicationShell = /* @__PURE__ */ forwardRef(function ApplicationShell({ navigation, navigationSide, navigationDrawer, topBar, footer, scroll, mainId, mainAs: Main = "main", mainLabel, mainRef, scrollLabel, scrollOrientation = "both", scrollContentInset = "scrollbar", skipLabel, children, className, ...props }, ref) {
9
+ const generatedId = useId();
10
+ const contentId = mainId ?? `flood-app-${generatedId}`;
11
+ const root = useRef(null);
12
+ const dock = useRef(null);
13
+ const trigger = useRef(null);
14
+ const drawer = useRef(null);
15
+ const focusAfterResize = useRef(false);
16
+ const [narrow, setNarrow] = useState(false);
17
+ const [open, setOpen] = useState(false);
18
+ const below = navigationDrawer?.below;
19
+ const useDrawer = Boolean(navigationDrawer && narrow);
20
+ useImperativeHandle(ref, () => root.current, []);
21
+ useBrowserLayoutEffect(() => {
22
+ const node = root.current;
23
+ if (!node || below === undefined) {
24
+ setNarrow(false);
25
+ setOpen(false);
26
+ return;
27
+ }
28
+ const update = () => {
29
+ const width = node.getBoundingClientRect().width;
30
+ if (!width)
31
+ return;
32
+ const next = width < below;
33
+ if (next === narrow)
34
+ return;
35
+ const active = node.ownerDocument.activeElement;
36
+ focusAfterResize.current = Boolean(next ? dock.current?.contains(active) : trigger.current === active || drawer.current?.contains(active));
37
+ if (!next)
38
+ setOpen(false);
39
+ setNarrow(next);
40
+ };
41
+ update();
42
+ const observer = new ResizeObserver(update);
43
+ observer.observe(node);
44
+ return () => observer.disconnect();
45
+ }, [below, narrow]);
46
+ useBrowserLayoutEffect(() => {
47
+ if (!focusAfterResize.current)
48
+ return;
49
+ focusAfterResize.current = false;
50
+ const target = useDrawer ? trigger.current : root.current?.ownerDocument.getElementById(contentId);
51
+ target?.focus({ preventScroll: true });
52
+ }, [contentId, useDrawer]);
53
+ const navigationSlot = !useDrawer && navigation != null && navigation !== false
54
+ ? _jsx("div", { ref: dock, className: "flood-app-shell__navigation", children: navigation })
55
+ : null;
56
+ const navigationTrigger = useDrawer && navigationDrawer ? _jsx(Drawer, { ref: drawer, title: navigationDrawer.title, side: navigationSide, size: navigationDrawer.size ?? "sm", open: open, onOpenChange: setOpen, trigger: ({ ref: drawerTrigger, ...buttonProps }) => _jsx(Button, { ...buttonProps, ref: node => {
57
+ trigger.current = node;
58
+ drawerTrigger(node);
59
+ }, variant: "subtle", size: "sm", children: navigationDrawer.triggerLabel ?? "Navigation" }), children: open && navigationDrawer.renderContent(() => setOpen(false)) }) : null;
60
+ const chrome = typeof topBar === "function"
61
+ ? topBar(navigationTrigger)
62
+ : _jsxs(_Fragment, { children: [navigationTrigger && _jsx(AppTopBar, { start: navigationTrigger }), topBar] });
63
+ return _jsxs("div", { ...props, ref: root, className: ["flood-app-shell", className].filter(Boolean).join(" "), "data-scroll": scroll, children: [_jsx(SkipLink, { href: `#${contentId}`, children: skipLabel }), navigationSide === "start" && navigationSlot, _jsxs("div", { className: "flood-app-shell__workspace", children: [chrome, _jsx(Main, { ref: mainRef, id: contentId, "aria-label": mainLabel, tabIndex: -1, className: "flood-app-shell__main", children: scroll === "content"
64
+ ? _jsx(ScrollArea, { orientation: scrollOrientation, contentInset: scrollContentInset, "aria-label": scrollLabel, className: "flood-app-shell__scroll", children: children })
65
+ : children }), footer] }), navigationSide === "end" && navigationSlot] });
66
+ });
@@ -0,0 +1,3 @@
1
+ import { type BulkActionBarProps } from "./index.js";
2
+ /** Selection owns the count; the host owns accepted record updates and focus after removal. */
3
+ export declare const SelectionActionBar: import("react").ForwardRefExoticComponent<BulkActionBarProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,17 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { IconButton, OverflowActions, Text, VisuallyHidden } from "./index.js";
4
+ /** Selection owns the count; the host owns accepted record updates and focus after removal. */
5
+ export const SelectionActionBar = /* @__PURE__ */ forwardRef(function SelectionActionBar({ label, selectionCount, selectionLabel = `${selectionCount} selected`, items, onAction, onClear, counter = "label", counterWidth, clearLabel = "Clear selection", moreLabel = "More", size = "sm", maxVisibleActions = 2, disabled = false, pending = false, className, ...props }, ref) {
6
+ if (!Number.isSafeInteger(selectionCount) || selectionCount < 0)
7
+ throw new Error("Flood BulkActionBar selectionCount must be a nonnegative safe integer");
8
+ if (!selectionLabel.trim() || !clearLabel.trim())
9
+ throw new Error("Flood BulkActionBar requires named selection and clear action");
10
+ if (![1, 2, 3].includes(maxVisibleActions))
11
+ throw new Error("Flood BulkActionBar keeps one to three commands visible");
12
+ if (!["label", "number", "none"].includes(counter))
13
+ throw new Error("Flood BulkActionBar counter must be label, number or none");
14
+ if (selectionCount === 0)
15
+ return null;
16
+ return _jsx(OverflowActions, { ...props, ref: ref, label: label, items: items, onAction: onAction, moreLabel: moreLabel, size: size, variant: "subtle", shape: "pill", maxVisibleActions: maxVisibleActions, disabled: disabled || pending, "aria-busy": pending || undefined, className: ["flood-bulk-action-bar", className].filter(Boolean).join(" "), leading: counter === "none" ? undefined : _jsx(Text, { size: "compact", className: "flood-bulk-action-bar__selection", "data-counter": counter, style: counterWidth === undefined ? undefined : { inlineSize: counterWidth }, title: selectionLabel, children: counter === "number" ? _jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: selectionCount }), _jsx(VisuallyHidden, { children: selectionLabel })] }) : selectionLabel }), trailing: _jsx(IconButton, { label: clearLabel, size: size, variant: "subtle", disabled: disabled || pending, onClick: onClear, children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "m6 6 12 12M18 6 6 18" }) }) }) });
17
+ });
@@ -0,0 +1,3 @@
1
+ import { type CollectionToolbarProps } from './index.js';
2
+ /** Query chrome only: the host supplies filter drafts, accepted query and saved views. */
3
+ export declare const CollectionQueryBar: import("react").ForwardRefExoticComponent<CollectionToolbarProps & import("react").RefAttributes<HTMLFieldSetElement>>;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId, useImperativeHandle, useRef } from 'react';
3
+ import { Button, Field, Fieldset, IconSlot, Inline, Popover, SearchInput, Tag, Text } from './index.js';
4
+ /** Query chrome only: the host supplies filter drafts, accepted query and saved views. */
5
+ export const CollectionQueryBar = /* @__PURE__ */ forwardRef(function CollectionQueryBar({ label, searchValue, onSearchValueChange, searchLabel = 'Search', searchPlaceholder = 'Search…', clearSearchLabel = 'Clear search', appliedFilters = [], onRemoveFilter, appliedFiltersLabel = 'Applied filters', summaryPrefix, quickFilters, actions, filterContent, filtersLabel = 'Filters', filterTitle = filtersLabel, filtersOpen, onFiltersOpenChange, filterPanelRef, filterPanelClassName, onClear, canClear = Boolean(searchValue || appliedFilters.length), clearLabel = 'Clear filters', controlSize = 'sm', disabled = false, hideLabel = true, className, ...props }, forwardedRef) {
6
+ const root = useRef(null);
7
+ useImperativeHandle(forwardedRef, () => root.current, []);
8
+ const generated = useId(), triggerId = `flood-collection-filters-${generated}`, searchId = `flood-collection-search-${generated}`;
9
+ if (![label, searchLabel, clearSearchLabel, appliedFiltersLabel, filtersLabel, filterTitle, clearLabel].every(text => text.trim()) ||
10
+ new Set(appliedFilters.map(filter => filter.id)).size !== appliedFilters.length || appliedFilters.some(filter => !filter.id.trim() || !filter.label.trim()))
11
+ throw new Error('Flood CollectionToolbar requires named controls and unique applied filter IDs');
12
+ const hasPanel = filterContent !== undefined && filterContent !== null && filterContent !== false;
13
+ const hasSummary = Boolean(quickFilters) || appliedFilters.length > 0;
14
+ function focusPersistent() {
15
+ const document = root.current?.ownerDocument;
16
+ document?.getElementById(hasPanel ? triggerId : searchId)?.focus({ preventScroll: true });
17
+ }
18
+ return _jsx(Fieldset, { ...props, ref: root, legend: label, hideLabel: hideLabel, disabled: disabled, className: ['flood-collection-toolbar', className].filter(Boolean).join(' '), children: _jsxs(Inline, { gap: "3", align: "center", className: "flood-collection-toolbar__row", children: [_jsx("div", { className: "flood-collection-toolbar__search", children: _jsx(Field, { id: searchId, label: searchLabel, hideLabel: true, children: field => _jsx(SearchInput, { ...field, controlSize: controlSize, value: searchValue, placeholder: searchPlaceholder, clearLabel: clearSearchLabel, disabled: disabled, onChange: event => { if (!disabled)
19
+ onSearchValueChange(event.currentTarget.value); } }) }) }), hasSummary && _jsxs(Inline, { gap: "2", align: "center", role: "group", "aria-label": appliedFiltersLabel, className: "flood-collection-toolbar__summaries", children: [quickFilters, summaryPrefix && _jsx(Text, { size: "compact", tone: "subtle", children: summaryPrefix }), appliedFilters.map(filter => _jsx(Tag, { size: controlSize === 'xl' ? 'lg' : 'md', disabled: disabled || filter.disabled, removeLabel: filter.removeLabel ?? `Remove ${filter.label}`, onRemove: onRemoveFilter ? () => {
20
+ if (disabled || filter.disabled)
21
+ return;
22
+ focusPersistent();
23
+ onRemoveFilter(filter.id);
24
+ } : undefined, children: filter.label }, filter.id))] }), (onClear || hasPanel || actions) && _jsxs(Inline, { gap: "2", align: "center", className: "flood-collection-toolbar__commands", children: [onClear && _jsx(Button, { size: controlSize, variant: "outline", className: "flood-collection-toolbar__clear", "data-visible": canClear, disabled: disabled || !canClear, "aria-hidden": !canClear || undefined, onClick: () => { if (disabled || !canClear)
25
+ return; focusPersistent(); onClear(); }, children: clearLabel }), hasPanel && _jsx(Popover, { title: filterTitle, ref: filterPanelRef, className: filterPanelClassName, open: filtersOpen, onOpenChange: onFiltersOpenChange, disabled: disabled, align: "end", trigger: trigger => _jsxs(Button, { ...trigger, id: triggerId, variant: "outline", size: controlSize, children: [_jsx(IconSlot, { size: controlSize, children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "M4 6h16M7 12h10m-7 6h4" }) }) }), filtersLabel] }), children: filterContent }), actions] })] }) });
26
+ });
@@ -0,0 +1,3 @@
1
+ import { type CollectionViewSwitcherProps } from './index.js';
2
+ /** Presentation of accepted definitions; the host owns their queries and persistence. */
3
+ export declare const CollectionViewSelector: import("react").ForwardRefExoticComponent<CollectionViewSwitcherProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { Button, Field, IconSlot, Inline, OverflowActions, Select } from './index.js';
4
+ /** Presentation of accepted definitions; the host owns their queries and persistence. */
5
+ export const CollectionViewSelector = /* @__PURE__ */ forwardRef(function CollectionViewSelector({ label, views, value, onValueChange, actions = [], onAction, actionsLabel = `${label} actions`, moreLabel = 'View actions', controlSize = 'sm', disabled = false, selectRef, className, ...props }, ref) {
6
+ if (!label.trim() || !actionsLabel.trim() || !moreLabel.trim() || new Set(views.map(view => view.id)).size !== views.length ||
7
+ views.some(view => !view.id.trim() || !view.name.trim()) || new Set(actions.map(action => action.id)).size !== actions.length ||
8
+ actions.some(action => !action.id.trim() || !action.label.trim()))
9
+ throw new Error('Flood CollectionViewSwitcher requires unique named views and actions');
10
+ const single = actions.length === 1 ? actions[0] : undefined;
11
+ return _jsxs(Inline, { ...props, ref: ref, role: "group", "aria-label": `${label} controls`, gap: "3", align: "center", className: ['flood-collection-view-switcher', className].filter(Boolean).join(' '), children: [_jsx("div", { className: "flood-collection-view-switcher__select", children: _jsx(Field, { label: label, hideLabel: true, children: field => _jsx(Select, { ...field, ref: selectRef, controlSize: controlSize, disabled: disabled, value: value, onChange: event => {
12
+ const next = views.find(view => view.id === event.currentTarget.value);
13
+ if (!disabled && next && !next.disabled)
14
+ onValueChange(next.id);
15
+ }, children: views.map(view => _jsx("option", { value: view.id, disabled: view.disabled, children: view.name }, view.id)) }) }) }), actions.length > 0 && _jsx("div", { className: "flood-collection-view-switcher__actions", children: single ?
16
+ _jsxs(Button, { size: controlSize, variant: "subtle", tone: single.tone, disabled: disabled || single.disabled, onClick: () => { if (!disabled && !single.disabled)
17
+ onAction?.(single.id); }, children: [single.icon && _jsx(IconSlot, { size: controlSize, children: single.icon }), single.label] }) : _jsx(OverflowActions, { label: actionsLabel, moreLabel: moreLabel, size: controlSize, items: actions, containerVariant: "plain", disabled: disabled, onAction: id => { if (!disabled)
18
+ onAction?.(id); } }) })] });
19
+ });
@@ -1,9 +1,14 @@
1
- import { type ComboboxProps } from "./index.js";
1
+ import { type ComboboxProps, type ComboboxOption } from "./index.js";
2
2
  interface SearchSelectionProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onValueChange"> {
3
3
  selectedValues?: readonly string[];
4
4
  initialValues: readonly string[];
5
5
  onSelectionChange?: (value: string[]) => void;
6
6
  multiple?: boolean;
7
+ freeText?: boolean;
8
+ textValue?: string;
9
+ initialText?: string;
10
+ onTextChange?: (text: string) => void;
11
+ onOptionSelect?: (option: ComboboxOption) => void;
7
12
  removeItemLabel?: string;
8
13
  }
9
14
  /** Shared search/layer model. Public single and multiple value contracts stay distinct. */