@flood-md/react 0.2.10 → 0.2.11

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 (49) hide show
  1. package/README.md +239 -1
  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 +1 -1
  15. package/dist/data-table.js +44 -9
  16. package/dist/date-picker.js +2 -2
  17. package/dist/date-range-picker.js +2 -2
  18. package/dist/date-time-picker.js +2 -2
  19. package/dist/date-time-range-picker.js +2 -2
  20. package/dist/disclosure.d.ts +2 -1
  21. package/dist/disclosure.js +4 -4
  22. package/dist/filter-condition.d.ts +3 -0
  23. package/dist/filter-condition.js +21 -0
  24. package/dist/filter-group.d.ts +2 -0
  25. package/dist/filter-group.js +62 -0
  26. package/dist/foundations.css +752 -76
  27. package/dist/index.d.ts +399 -3
  28. package/dist/index.js +61 -7
  29. package/dist/menu.js +5 -4
  30. package/dist/menubar.d.ts +2 -0
  31. package/dist/menubar.js +85 -0
  32. package/dist/navigation-group.d.ts +3 -0
  33. package/dist/navigation-group.js +13 -0
  34. package/dist/navigation-item.d.ts +3 -0
  35. package/dist/navigation-item.js +68 -0
  36. package/dist/navigation-layout.d.ts +2 -1
  37. package/dist/navigation-layout.js +12 -4
  38. package/dist/navigation-tree.js +51 -7
  39. package/dist/navigation.js +2 -2
  40. package/dist/overflow-actions.d.ts +2 -0
  41. package/dist/overflow-actions.js +75 -0
  42. package/dist/overlay-runtime.d.ts +3 -0
  43. package/dist/overlay-runtime.js +35 -1
  44. package/dist/overlays.js +32 -16
  45. package/dist/select.js +4 -1
  46. package/dist/sidebar.d.ts +2 -0
  47. package/dist/sidebar.js +58 -0
  48. package/dist/toolbar.js +2 -2
  49. package/package.json +2 -2
package/README.md CHANGED
@@ -6,9 +6,192 @@ 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
+ SegmentedControl options can display a `count` alongside the label, for example
10
+ `{value: "pending", label: "Pending", count: 2}`. Use this for a small, mutually
11
+ exclusive choice that updates one collection. The count is read with the visible
12
+ label and does not change the submitted value.
13
+
14
+ The 0.2.11 candidate adds optional DataTable row `primaryAction` for opening an
15
+ in-place detail panel, plus `toolbar` and `inset="roomy"` for a shared table edge.
16
+ The title is a native button and the rest of the row also responds to pointer
17
+ activation. Nested controls keep their own actions.
18
+ Set `numeric: true` on a DataTable column of comparable amounts or quantities
19
+ for logical end alignment and tabular digits. An explicit `align` still applies.
20
+
21
+ The 0.2.11 candidate adds optional compact NavigationTree item `flyout` settings
22
+ for mouse-hover previews and a separate positioning reference. Click, keyboard
23
+ and touch activation still work. Popover `focusOnOpen` defaults to true; use false
24
+ for a pointer preview that preserves the current keyboard focus.
25
+
26
+ In the 0.2.11 candidate, `NavigationItem.trailingAction` adds a named sibling
27
+ control to an expanded row. `NavigationTree` items accept the same slot.
28
+ Enable `branchGuides` on a tree or Sidebar group for visual depth cues.
29
+ Choose `Sidebar.headerLayout="inline"` to put the identity and collapse button
30
+ on one line while expanded. The stacked layout keeps the original toggle position.
31
+
32
+ CommandPalette (0.2.11 candidate) provides modal search over pages or commands.
33
+ It shares the CommandList item contract and supports grouped results, disabled
34
+ items, loading and two densities. The host supplies actions and any shortcut.
35
+
36
+ Tooltip trigger props are HTMLElement-safe: render the existing Link or Button
37
+ directly and forward its ref, hover/focus handlers and aria-describedby. Tooltip
38
+ does not add button type, popup or navigation semantics. Raw button triggers
39
+ inside forms keep an explicit type="button". Popover/Menu retain button trigger
40
+ contracts. When one control owns both Tooltip and Popover, compose both refs,
41
+ handlers and descriptions, and disable the tooltip while the interactive layer
42
+ is open so Escape and focus remain owned by that layer.
43
+
44
+ IconButton iconSize (0.2.11 candidate) sizes its inner IconSlot independently of
45
+ the interactive size. It defaults to size; shrinking a supporting glyph must
46
+ not shrink its target or bypass IconSlot with a custom SVG width. Existing glyph
47
+ roles at a 16px root are 2xs=14px, xs/sm=16px and md/lg=20px.
48
+
49
+ This package documents the **0.2.11** source API. Keep Flood package versions aligned and follow the guide matching
10
50
  your installed declarations; source changes do not modify an existing npm release.
11
51
 
52
+ NavigationItem (0.2.11 candidate) is the shared small row for destinations,
53
+ commands and disclosures. Supply label and either href for a native Link or
54
+ onAction for a Button command. disclosure supplies expanded, onExpandedChange,
55
+ label and optional controls/disabled. With disclosure alone, the full primary
56
+ row toggles the branch; with href or onAction, disclosure is a separate sibling
57
+ IconButton. The row shares its visual surface across both controls, without
58
+ nested interactive elements or making a link click toggle expansion. Current,
59
+ ancestorCurrent, focus and disclosure expansion are separate states. icon,
60
+ reserveIcon, trailing and description are optional anatomy, not required filler.
61
+ Compact rows keep their accessible label and optional Tooltip; NavigationTree
62
+ owns any branch hierarchy and compact Popover, not NavigationItem itself.
63
+ Raw compact NavigationItem cannot combine a separate primary destination/action
64
+ and disclosure: use the NavigationTree branch composition, which retains the
65
+ parent destination alongside children in its flyout. trailing is noninteractive
66
+ metadata; independent controls belong in their own sibling action anatomy.
67
+
68
+ NavigationGroup (0.2.11 candidate) composes related children with optional label,
69
+ hideLabel, headingAction and separator. collapsible uses the existing Collapsible;
70
+ expanded/onExpandedChange or defaultExpanded control only the group disclosure.
71
+ defaultExpanded is true. A collapsible group requires a nonempty label; hideLabel
72
+ omits the visual title while preserving the toggle name. collapseDisabled disables
73
+ only that disclosure, not arbitrary child destinations or heading actions.
74
+ Compact presentation keeps children available and preserves the accepted expanded
75
+ choice for return to expanded navigation. Heading actions remain available on the
76
+ compact axis. Collapsible headerAction supplies a sibling action in its own header,
77
+ so a group command need not be nested in the disclosure button or moved elsewhere.
78
+
79
+ Sidebar (0.2.11 candidate) composes grouped NavigationTree destinations and a
80
+ bounded ScrollArea between fixed header/footer slots. NavigationGroup owns each
81
+ section's optional presentation; NavigationTree composes NavigationItem rows.
82
+ Supply label
83
+ and choose variant="quiet" (default) for restrained inactive navigation and
84
+ rectangular current rows, or standard for the stronger pill recipe. This is a
85
+ scoped component recipe using existing roles, not a global theme change.
86
+ Supply groups `{id,label,items}` and currentValue; item values must be globally unique. Set a
87
+ height on the host. Expanded width defaults to 16rem; collapsed rail is 4.5rem (5rem for coarse pointers).
88
+ collapsed/defaultCollapsed/onCollapsedChange are independent of the route and
89
+ expandedValues/defaultExpandedValues/onExpandedValuesChange. Controlled refusal
90
+ preserves accepted state. side is logical start/end; size matches NavigationTree.
91
+ Header/footer accept ReactNode or `({collapsed}) => ReactNode`; adapt their content
92
+ explicitly. collapsible=false omits the toggle. Localize collapseLabel,
93
+ expandLabel and expandItemLabel; group/item labels supply the navigation names.
94
+ The component does not add preference storage, routing, a mobile drawer or shortcuts.
95
+ Groups can set hideLabel to omit redundant visible section titles while retaining
96
+ nav names. headingAction, separator and collapsible/expanded group configuration
97
+ are independent of branch expandedValues. headerSeparator/footerSeparator are
98
+ independent opt-in shell choices and default false. Header/footer content must
99
+ follow the mode contract: common icon/text tracks when expanded and one full-rail
100
+ axis for the actual brand, toggle, destinations and account target when compact.
101
+ currentKind defaults to page; location identifies a current global
102
+ module. Compose a flat compact Sidebar as a GlobalRail and another Sidebar as
103
+ its ContextBar, keeping route, module, expansion and visibility in the host.
104
+
105
+ Sidebar places its overlay scrollbar inside the existing outer edge spacing.
106
+ Header, body and footer keep the same tracks without an additional reserved
107
+ column, whether navigation overflows or not.
108
+ NavigationTree rows share NavigationItem's space-3 start inset, icon/text tracks
109
+ and deliberate columns for actual trailing anatomy. Iconless descendants begin at the parent text track; missing
110
+ peer icons reserve a slot. Header content should use the same icon size and gap.
111
+
112
+ NavigationTree rows use space-1 between peers and before an expanded branch;
113
+ group separation remains owned by Sidebar.
114
+
115
+ NavigationTree compact (0.2.11 candidate) exposes an icon rail: leaf destinations
116
+ remain links, disabled items remain unavailable, branches use Popover with full
117
+ navigation. flyoutSide controls its physical placement; Sidebar resolves its own
118
+ side and RTL direction. Branch selection closes the flyout and preserves current
119
+ destination/expanded state independently. trailing composes meaningful metadata
120
+ such as Badge in expanded rows. Flood tooltips identify compact destinations on
121
+ hover and focus while preserving the actual link/button; an open branch flyout
122
+ suppresses its hint. Missing icons use a neutral fallback glyph. Current ancestor styling does not
123
+ claim aria-current on a non-current parent.
124
+
125
+ DataTable emptyContent (0.2.11 candidate) accepts a composed EmptyState and
126
+ applicable actions inside the existing full-width body cell. It renders only
127
+ when state="ready" has no visible rows; loading/error retain Spinner/Alert and
128
+ retry ownership. Omitting it preserves labels.empty. Keep header/sort context
129
+ and distinguish an empty collection from no matching results in host content.
130
+ Reset filters in the host and return focus to a persistent query control.
131
+
132
+ CollectionViewSwitcher (0.2.11 candidate) composes a hidden-label Select and
133
+ applicable view commands. Supply views `{id,name,disabled?}`, controlled value
134
+ and onValueChange. Optional actions/onAction use the OverflowActions contract:
135
+ one command renders Button directly, multiple commands adapt through overflow.
136
+ The host owns saved queries, persistence, naming, confirmation and recovery.
137
+ Keep it separate from CollectionToolbar; selectRef gives host dialogs a stable
138
+ focus destination. controlSize defaults to sm; disabled blocks choice/commands.
139
+ Localize label, actionsLabel, moreLabel, view names and supplied action labels.
140
+ Its multi-action rail uses OverflowActions containerVariant="plain" to match
141
+ the adjacent field height. Toolbar variant="plain" removes its material and
142
+ outer inset; contained remains the default for standalone rails. Button variant
143
+ and toolbar containerVariant remain independent in OverflowActions.
144
+
145
+ CollectionToolbar (0.2.11 candidate) composes a named search field, applied Tag
146
+ summaries, reserved Clear filters action and end-aligned Filters Popover. Supply
147
+ controlled searchValue/onSearchValueChange; appliedFilters and onRemoveFilter
148
+ describe accepted conditions. filterContent supplies the host's staged editor.
149
+ The host owns Apply/Cancel, query execution, validation and saved views. Omit
150
+ filterContent for search/summary-only use. onClear is optional; canClear describes
151
+ whether the host has anything to reset, including slot-owned quickFilters.
152
+ Clearing and tag removal focus Filters (or search without a panel). The root is
153
+ a fieldset with ordinary Tab navigation, not an action-only roving toolbar.
154
+ Use summaryPrefix for meaningful all/any context, localized labels for controls,
155
+ and keep saved-view management outside this query row. Narrow containers move
156
+ summaries below search/actions; no material surface is added.
157
+
158
+ FilterGroup (0.2.11 candidate) composes FilterCondition rows and SegmentedControl
159
+ for a flat finite-choice draft. Supply label, properties, controlled value
160
+ `{match:"all"|"any", conditions:[{id,property,operator,values}]}` and onValueChange.
161
+ Rows keep stable IDs. All/any appears at two conditions; an empty group offers
162
+ Add condition without extra explanatory chrome. New rows use an available
163
+ enabled property and empty values. Duplicate properties are prevented for new
164
+ choices by default; allowDuplicateProperties and maxConditions can change that
165
+ policy. Existing accepted rows are preserved when constraints change.
166
+ Accepted additions/removals restore useful focus; refused proposals retain state
167
+ and focus. The host owns application, validation, persistence and query semantics.
168
+ controlSize defaults to sm; labels and conditionLabels localize group/row text.
169
+
170
+ FilterCondition (0.2.11 candidate) composes Fieldset, Field, Grid, two Selects,
171
+ MultiSelect and an optional removal IconButton. Supply a named label, properties
172
+ with finite options, controlled value `{property, operator: "is" | "is-not", values}`
173
+ and onValueChange. Property changes propose an empty values array; operator
174
+ changes preserve values. Empty values remain a draft for host validation.
175
+ No query execution or Apply/Cancel state is built in. controlSize defaults to sm;
176
+ labels localizes names/operator text and hideLabel defaults to true for a named
177
+ row. disabled blocks all interaction; onRemove is optional. A wide host gets one
178
+ row with a larger value track; container queries reflow narrow rows. Hosts own
179
+ duplicate-property policy and focus after removal.
180
+ MultiSelect shows its placeholder only while no values are selected. With chips
181
+ and an empty query, the editor reserves only its real action/caret budget;
182
+ typing expands the search area. A needless placeholder row must not enlarge the
183
+ control, while actual chip overflow can still wrap without clipping values.
184
+
185
+ Toolbar supports shape="rounded" (default) or "pill". OverflowActions uses
186
+ shape="pill" by default and forwards that capability to Toolbar; choose rounded
187
+ for the earlier geometry. A horizontal pill toolbar stays on one scrollable rail.
188
+ Read-only text controls retain keyboard focus and copying while preventing edits
189
+ and picker activation; they are not disabled controls.
190
+
191
+ Tabs uses `variant="contained"` by default. Choose `variant="line"` for page
192
+ sections with a selection marker along the edge next to the panel. Both variants
193
+ support horizontal and vertical orientation and the same keyboard behavior.
194
+
12
195
  Version 0.2.10 adds Fieldset legendSize="label" | "section"
13
196
  (default label). Independent section legends use a 1rem/600 role; named fields
14
197
  retain their 13px-equivalent label size with a .25rem inner gap. Dialog task
@@ -755,6 +938,26 @@ See [WAI Menu Button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/) and
755
938
 
756
939
  ### Toolbar and Kbd (0.2.4)
757
940
 
941
+ BulkActionBar (0.2.11 candidate) composes Toolbar and OverflowActions for selected
942
+ records. Supply label, selectionCount, items and onClear; onAction receives the
943
+ command ID. A zero count renders nothing. selectionLabel is the complete
944
+ localized count. The pill rail stays in one row, with two visible commands by
945
+ default; maxVisibleActions accepts 1, 2 or 3 and available width can reduce it
946
+ further. Remaining commands use More. pending disables all commands and clearing.
947
+ counter="label" (default) shows the summary, "number" shows only the number with
948
+ the full accessible label, and "none" removes the counter slot. Label/number
949
+ slots reserve 7rem/3rem respectively, bounded on narrow rails, and use tabular
950
+ digits so changing selection does not move commands. counterWidth customizes
951
+ that reservation for translations or larger counts; long summaries truncate
952
+ visually while retaining their full text/title. A hidden counter frees its slot.
953
+ The host owns accepted changes, captured IDs, confirmation and focus after the
954
+ bar disappears. Positioning is host-owned: use a stable bounded width for a
955
+ floating rail, plus viewport safe-area spacing and measured scroll clearance.
956
+
957
+ OverflowActions also accepts leading/trailing slots and maxVisibleActions. Slot
958
+ width is reserved before measuring commands; use action owners in those slots
959
+ and retain a stable containing width. Do not add form inputs to the Toolbar.
960
+
758
961
  Toolbar groups Button, IconButton, Toggle, native buttons/links and Menu triggers
759
962
  under one named action panel. After hydration it uses one Tab entry, directional
760
963
  arrows, Home/End and optional looping. Disabled/hidden actions are skipped. Arrows
@@ -962,3 +1165,38 @@ Popover positionReference optionally supplies a connected same-document element
962
1165
  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
1166
 
964
1167
  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.
1168
+
1169
+ ## Collection contracts in 0.2.11
1170
+
1171
+ Autocomplete submits free text, including strings outside its suggestions.
1172
+ Combobox and MultiSelect continue submitting IDs. Search controls accept local
1173
+ filtering (default) or manual filtering with controlled query/value and
1174
+ optionsQuery identifying the result. A mismatched result stays nonselectable.
1175
+ loadState distinguishes ready/loading/error; onRetry is an optional host action.
1176
+ Use selectedOptions to retain a selected record's presentation while results
1177
+ change. Query cancellation and transport stay with the host. Options may have
1178
+ group labels; option IDs remain globally unique.
1179
+
1180
+ OverflowActions takes named command items, an onAction callback, optional priority,
1181
+ size/variant and a localized moreLabel. It preserves source order and moves lower
1182
+ priorities to an existing Menu. It is not a navigation-link container. Menubar
1183
+ coordinates menus with controlled/uncontrolled openValue and roving triggers;
1184
+ child commands reuse Menu. Use native links for destinations. Button/IconButton
1185
+ tone="danger" uses existing semantic colors within FloodRoot, independently of
1186
+ prominence. Tone itself does not confirm a destructive operation.
1187
+
1188
+ DataTable paginationMode="server" renders supplied rows without sorting/slicing.
1189
+ Provide page/onPageChange and nonnegative totalRows; sortable columns also require
1190
+ controlled sort (null when absent) and onSortChange. Reconcile an out-of-range page
1191
+ when a query changes; ready rows must match pageSize/totalRows. Selection defaults
1192
+ to the supplied page in server mode and the full collection in client mode.
1193
+ Server selectionScope="dataset" requires controlled selectedIds/onSelectionChange;
1194
+ the host validates IDs outside the page. Checkboxes only select the current page.
1195
+ This API does not fetch, cancel requests, select an entire remote query, virtualize
1196
+ or manage column visibility/resizing/pinning.
1197
+
1198
+ DatePicker and DateRangePicker pass viewLabels to their calendars. Calendar panels
1199
+ use minimal Popover chrome, with Escape/selection/trigger dismissal; there is no
1200
+ separate hidden close-button API. DateTimePicker datePickerLabels/timePickerLabels
1201
+ forward popup labels plus segmentLabels and validation messages. DateTimeRangePicker
1202
+ 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", 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, "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, 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) && _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 })] })] }) });
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. */