@flood-md/react 0.2.4 → 0.2.5

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.
@@ -51,6 +51,9 @@ export function registerLayer(document, layer) {
51
51
  }
52
52
  else if (path.includes(layer.panel) || path.includes(layer.anchor()))
53
53
  return;
54
+ // A modal backdrop must not move focus to the document after close cleanup.
55
+ if (layer.modal)
56
+ event.preventDefault();
54
57
  (layer.outside ?? layer.close)();
55
58
  };
56
59
  const focus = (event) => {
@@ -68,24 +71,42 @@ export function registerLayer(document, layer) {
68
71
  }
69
72
  const locks = new WeakMap();
70
73
  export function lockScroll(document) {
71
- const style = document.documentElement.style;
74
+ const root = document.documentElement, style = root.style;
72
75
  let lock = locks.get(document);
73
76
  if (!lock) {
74
- const reserve = (document.defaultView?.innerWidth ?? 0) - document.documentElement.clientWidth > 0;
75
- lock = { count: 0, overflow: style.getPropertyValue("overflow"), overflowPriority: style.getPropertyPriority("overflow"), gutter: style.getPropertyValue("scrollbar-gutter"), gutterPriority: style.getPropertyPriority("scrollbar-gutter") };
77
+ const view = document.defaultView;
78
+ const computed = getComputedStyle(root);
79
+ const rect = root.getBoundingClientRect();
80
+ // Chromium can include a stable thin gutter in clientWidth while excluding it
81
+ // from the root's actual layout box. Preserve that box, not just clientWidth.
82
+ const gutter = Math.max(0, (view?.innerWidth ?? root.clientWidth) - Math.min(root.clientWidth, rect.width));
83
+ const leftGutter = Math.min(gutter, Math.max(0, rect.left));
84
+ const rightGutter = gutter - leftGutter;
85
+ const paddingLeft = parseFloat(computed.paddingLeft) || 0;
86
+ const paddingRight = parseFloat(computed.paddingRight) || 0;
87
+ lock = { count: 0, saved: ["overflow", "scrollbar-gutter", "padding-left", "padding-right"].map(key => [key, style.getPropertyValue(key), style.getPropertyPriority(key)]) };
76
88
  locks.set(document, lock);
89
+ // A stable root gutter clips top-layer painting even when its rect fills the viewport.
90
+ // Replace the gutter with normal-flow padding while keeping fixed modal edges flush.
91
+ style.setProperty("scrollbar-gutter", "auto", "important");
77
92
  style.setProperty("overflow", "hidden", "important");
78
- if (reserve)
79
- style.setProperty("scrollbar-gutter", "stable", "important");
93
+ if (leftGutter)
94
+ style.setProperty("padding-left", `${paddingLeft + leftGutter}px`, "important");
95
+ if (rightGutter)
96
+ style.setProperty("padding-right", `${paddingRight + rightGutter}px`, "important");
80
97
  }
81
98
  lock.count++;
82
- return () => { if (--lock.count !== 0)
83
- return; for (const [key, value, priority] of [["overflow", lock.overflow, lock.overflowPriority], ["scrollbar-gutter", lock.gutter, lock.gutterPriority]]) {
84
- if (value)
85
- style.setProperty(key, value, priority);
86
- else
87
- style.removeProperty(key);
88
- } locks.delete(document); };
99
+ return () => {
100
+ if (--lock.count !== 0)
101
+ return;
102
+ for (const [key, value, priority] of lock.saved) {
103
+ if (value)
104
+ style.setProperty(key, value, priority);
105
+ else
106
+ style.removeProperty(key);
107
+ }
108
+ locks.delete(document);
109
+ };
89
110
  }
90
111
  export function positionLayer(anchor, panel, side, align) {
91
112
  const view = panel.ownerDocument.defaultView;
@@ -3,5 +3,11 @@ type AnchoredProps = (TooltipProps | PopoverProps) & {
3
3
  kind: "tooltip" | "popover";
4
4
  };
5
5
  export declare const AnchoredOverlay: import("react").ForwardRefExoticComponent<AnchoredProps & import("react").RefAttributes<HTMLDivElement>>;
6
- export declare const ModalOverlay: import("react").ForwardRefExoticComponent<DialogProps & import("react").RefAttributes<HTMLDialogElement>>;
6
+ interface ModalOverlayProps extends DialogProps {
7
+ showCloseButton?: boolean;
8
+ role?: "dialog" | "alertdialog";
9
+ scrollBody?: boolean;
10
+ footer?: import("react").ReactNode;
11
+ }
12
+ export declare const ModalOverlay: import("react").ForwardRefExoticComponent<ModalOverlayProps & import("react").RefAttributes<HTMLDialogElement>>;
7
13
  export {};
package/dist/overlays.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
3
- import { IconButton } from "./index.js";
3
+ import { IconButton, ScrollArea } from "./index.js";
4
4
  import { positionLayer, registerLayer, returnFocus, lockScroll, tabbables } from "./overlay-runtime.js";
5
5
  import { mergeDescriptions } from "./field-state.js";
6
6
  const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
@@ -113,7 +113,7 @@ export const AnchoredOverlay = forwardRef(function AnchoredOverlay(allProps, for
113
113
  leave();
114
114
  } }, children: kind === "tooltip" ? tip.content : _jsxs(_Fragment, { children: [_jsxs("div", { className: "flood-overlay-header", children: [_jsx("h2", { id: `${id}-title`, className: "flood-overlay-title", children: pop.title }), _jsx(OverlayClose, { label: pop.closeLabel ?? "Close", onClick: close })] }), open && _jsx("div", { className: "flood-overlay-body", children: pop.children })] }) })] });
115
115
  });
116
- export const ModalOverlay = forwardRef(function ModalOverlay({ title, description, trigger, children, open: controlled, defaultOpen = false, onOpenChange, disabled = false, closeLabel = "Close", closeOnOutsideClick = true, initialFocusRef, id: explicitId, className, onKeyDown, "aria-describedby": describedBy, ...props }, forwardedRef) {
116
+ export const ModalOverlay = forwardRef(function ModalOverlay({ showCloseButton = true, scrollBody = false, footer, title, description, trigger, children, open: controlled, defaultOpen = false, onOpenChange, disabled = false, closeLabel = "Close", closeOnOutsideClick = true, initialFocusRef, id: explicitId, className, onKeyDown, "aria-describedby": describedBy, ...props }, forwardedRef) {
117
117
  const [local, setLocal] = useState(defaultOpen), open = controlled ?? local;
118
118
  const dialog = useRef(null), anchor = useRef(null), generated = useId(), id = explicitId ?? `flood-dialog-${generated}`;
119
119
  const request = (next) => { if (controlled === undefined)
@@ -138,6 +138,8 @@ export const ModalOverlay = forwardRef(function ModalOverlay({ title, descriptio
138
138
  if (typeof element.showModal !== "function")
139
139
  throw new Error("Flood Dialog requires native showModal");
140
140
  const previous = document.activeElement;
141
+ if (scrollBody)
142
+ element.dataset.drawerDirection = getComputedStyle(element).direction;
141
143
  element.showModal();
142
144
  const unlock = lockScroll(document);
143
145
  const unregister = registerLayer(document, { panel: element, anchor: () => anchor.current, modal: true, close: () => current.current.request(false), outside: () => { if (current.current.closeOnOutsideClick)
@@ -175,5 +177,5 @@ export const ModalOverlay = forwardRef(function ModalOverlay({ title, descriptio
175
177
  event.preventDefault();
176
178
  first.focus();
177
179
  }
178
- }, children: [_jsxs("div", { className: "flood-overlay-header", children: [_jsx("h2", { id: `${id}-title`, className: "flood-overlay-title", children: title }), _jsx(OverlayClose, { label: closeLabel, onClick: () => request(false) })] }), description && _jsx("p", { id: `${id}-description`, className: "flood-overlay-description", children: description }), open && _jsx("div", { className: "flood-overlay-body", children: children })] })] });
180
+ }, children: [_jsxs("div", { className: "flood-overlay-header", children: [_jsx("h2", { id: `${id}-title`, className: "flood-overlay-title", children: title }), showCloseButton && _jsx(OverlayClose, { label: closeLabel, onClick: () => request(false) })] }), description && _jsx("p", { id: `${id}-description`, className: "flood-overlay-description", children: description }), open && (scrollBody ? _jsx("div", { className: "flood-drawer__body", children: _jsx(ScrollArea, { className: "flood-overlay-body", style: { height: "100%" }, children: children }) }) : _jsx("div", { className: "flood-overlay-body", children: children })), open && footer && _jsx("div", { className: "flood-drawer__footer", children: footer })] })] });
179
181
  });
package/dist/select.js CHANGED
@@ -328,6 +328,6 @@ export const SingleSelect = forwardRef(function SingleSelect({ children, value,
328
328
  onKeyDown: (event) => { onKeyDown?.(event); event.stopPropagation(); }, onKeyUp: onKeyUp, onClick: onClick, onInvalid: (event) => { onInvalid?.(event); const prevented = event.defaultPrevented; event.preventDefault(); if (!prevented) {
329
329
  setValidation(event.currentTarget.validationMessage);
330
330
  trigger.current?.focus();
331
- } }, children: children }), open && _jsx("div", { ref: menu, id: menuId, role: "listbox", "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : labelledBy ?? triggerId, popover: "manual", className: "flood-select__menu", onPointerDown: (event) => { if (event.pointerType === "mouse")
332
- event.preventDefault(); }, onClick: (event) => event.preventDefault(), children: _jsxs(ScrollArea, { ref: scrollViewport, orientation: "vertical", tabIndex: -1, className: "flood-select__viewport", children: [!choices.some((choice) => !choice.hidden) && _jsx("div", { className: "flood-select__empty", children: "No options" }), groups.map((group, index) => group.label ? _jsxs("div", { role: "group", "aria-label": group.label, children: [_jsx("div", { className: "flood-select__group", "aria-hidden": "true", children: group.label }), group.items.map(renderOption)] }, index) : _jsx(Fragment, { children: group.items.map(renderOption) }, index))] }) }), validation && _jsx("span", { className: "flood-select__validation", id: validationId, role: "alert", children: validation })] });
331
+ } }, children: children }), open && _jsx("div", { ref: menu, popover: "manual", className: "flood-select__menu", onPointerDown: (event) => { if (event.pointerType === "mouse")
332
+ event.preventDefault(); }, onClick: (event) => event.preventDefault(), children: _jsxs(ScrollArea, { ref: scrollViewport, id: menuId, role: "listbox", "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : labelledBy ?? triggerId, orientation: "vertical", tabIndex: -1, className: "flood-select__viewport", children: [!choices.some((choice) => !choice.hidden) && _jsx("div", { className: "flood-select__empty", children: "No options" }), groups.map((group, index) => group.label ? _jsxs("div", { role: "group", "aria-label": group.label, children: [_jsx("div", { className: "flood-select__group", "aria-hidden": "true", children: group.label }), group.items.map(renderOption)] }, index) : _jsx(Fragment, { children: group.items.map(renderOption) }, index))] }) }), validation && _jsx("span", { className: "flood-select__validation", id: validationId, role: "alert", children: validation })] });
333
333
  });
@@ -0,0 +1,7 @@
1
+ import type { SliderProps, Fieldset } from "./index.js";
2
+ interface Props extends SliderProps {
3
+ fieldsetComponent: typeof Fieldset;
4
+ }
5
+ /** Native range inputs keep keyboard/AT interaction; the shared rail handles pointer selection. */
6
+ export declare const RangeControl: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLFieldSetElement>>;
7
+ export {};
package/dist/slider.js ADDED
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useId, useRef, useState } from "react";
3
+ import { hasFieldContent, mergeDescriptions, resolveFieldStatus } from "./field-state.js";
4
+ /** Native range inputs keep keyboard/AT interaction; the shared rail handles pointer selection. */
5
+ export const RangeControl = forwardRef(function RangeControl({ fieldsetComponent: Group, label, value, defaultValue = [50], onValueChange, min = 0, max = 100, step = 1, thumbLabels, showValue = true, readOnly = false, controlSize = "md", disabled, className, name, onKeyDown, ...props }, ref) {
6
+ if (![min, max, step, max - min, (max - min) / step].every(Number.isFinite) || min >= max || step <= 0)
7
+ throw new Error("Flood Slider requires finite increasing bounds and a positive representable step");
8
+ if (!label.trim())
9
+ throw new Error("Flood Slider requires an accessible label");
10
+ const generatedId = useId();
11
+ const id = props.id ?? `flood-slider-${generatedId}`;
12
+ const hasError = hasFieldContent(props.error);
13
+ const status = resolveFieldStatus(props.status, hasError, props["aria-invalid"]);
14
+ const describedBy = mergeDescriptions(props["aria-describedby"], hasFieldContent(props.hint) && `${id}-hint`, hasError && `${id}-error`, !hasError && hasFieldContent(props.message) && `${id}-message`);
15
+ const snap = (number) => Number(Math.max(min, Math.min(min + Math.floor((max - min) / step + 1e-9) * step, min + Math.round((number - min) / step) * step)).toPrecision(14));
16
+ const normalize = (numbers) => {
17
+ if ((numbers.length !== 1 && numbers.length !== 2) || numbers.some(number => !Number.isFinite(number)) || (numbers.length === 2 && numbers[0] > numbers[1]))
18
+ throw new Error("Flood Slider accepts one value or an ordered pair of finite values");
19
+ return numbers.map(snap);
20
+ };
21
+ const initial = useRef(normalize(defaultValue));
22
+ const [local, setLocal] = useState(initial.current);
23
+ const values = normalize(value ?? local);
24
+ if (thumbLabels && (thumbLabels.length !== values.length || thumbLabels.some(text => !text.trim())))
25
+ throw new Error("Flood Slider needs one nonempty label per thumb");
26
+ const inputs = useRef([]);
27
+ const drag = useRef(null);
28
+ const valuesRef = useRef(values);
29
+ valuesRef.current = values;
30
+ const update = (index, next) => {
31
+ const before = valuesRef.current;
32
+ const number = Math.max(index === 1 ? before[0] : min, Math.min(index === 0 && before.length === 2 ? before[1] : max, snap(next)));
33
+ const result = before.map((old, position) => position === index ? number : old);
34
+ if (result.every((number, position) => number === before[position]))
35
+ return;
36
+ if (value === undefined) {
37
+ valuesRef.current = result;
38
+ setLocal(result);
39
+ }
40
+ onValueChange?.([...result]);
41
+ };
42
+ useEffect(() => {
43
+ const form = inputs.current[0]?.form;
44
+ if (!form || value !== undefined)
45
+ return;
46
+ const reset = (event) => queueMicrotask(() => { if (!event.defaultPrevented)
47
+ setLocal(initial.current); });
48
+ form.addEventListener("reset", reset);
49
+ return () => form.removeEventListener("reset", reset);
50
+ }, [value, props.form]);
51
+ const percent = (number) => 100 * (number - min) / (max - min);
52
+ const pointValue = (node, clientX) => {
53
+ const box = node.getBoundingClientRect();
54
+ const ratio = Math.max(0, Math.min(1, (clientX - box.left) / box.width));
55
+ return min + (node.ownerDocument.defaultView?.getComputedStyle(node).direction === "rtl" ? 1 - ratio : ratio) * (max - min);
56
+ };
57
+ const inert = (node) => disabled || readOnly || Boolean(node.closest("fieldset:disabled"));
58
+ const start = values.length === 1 ? 0 : percent(values[0]);
59
+ const end = percent(values[values.length - 1]);
60
+ return _jsxs(Group, { ...props, id: id, ref: ref, legend: label, name: name, disabled: disabled, className: ["flood-slider", `flood-control--${controlSize}`, className].filter(Boolean).join(" "), "data-readonly": readOnly || undefined, onKeyDown: event => {
61
+ onKeyDown?.(event);
62
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.nativeEvent.isComposing)
63
+ return;
64
+ if (readOnly && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "PageUp", "PageDown"].includes(event.key))
65
+ event.preventDefault();
66
+ const index = inputs.current.indexOf(event.target);
67
+ if (index < 0 || inert(event.currentTarget) || (event.key !== "ArrowLeft" && event.key !== "ArrowRight"))
68
+ return;
69
+ // Native WebKit does not reverse horizontal range keys in RTL.
70
+ const rtl = event.currentTarget.ownerDocument.defaultView?.getComputedStyle(event.currentTarget).direction === "rtl";
71
+ event.preventDefault();
72
+ update(index, values[index] + step * (event.key === (rtl ? "ArrowLeft" : "ArrowRight") ? 1 : -1));
73
+ }, children: [showValue && _jsx("div", { className: "flood-slider__value", "aria-hidden": "true", children: _jsx("bdi", { dir: "ltr", children: values.join(" – ") }) }), _jsxs("div", { className: "flood-slider__rail", style: { "--flood-slider-start": `${start}%`, "--flood-slider-end": `${100 - end}%` }, onPointerDown: event => {
74
+ if (inputs.current.includes(event.target) || inert(event.currentTarget) || event.button !== 0)
75
+ return;
76
+ event.preventDefault();
77
+ const next = pointValue(event.currentTarget, event.clientX);
78
+ const lowerDistance = Math.abs(next - values[0]);
79
+ const upperDistance = values.length === 2 ? Math.abs(next - values[1]) : Infinity;
80
+ const index = upperDistance < lowerDistance || (values.length === 2 && upperDistance === lowerDistance && next >= values[0]) ? 1 : 0;
81
+ drag.current = index;
82
+ inputs.current[index]?.focus();
83
+ event.currentTarget.setPointerCapture(event.pointerId);
84
+ update(index, next);
85
+ }, onPointerMove: event => { if (drag.current !== null && !inert(event.currentTarget))
86
+ update(drag.current, pointValue(event.currentTarget, event.clientX)); }, onPointerUp: () => { drag.current = null; }, onLostPointerCapture: () => { drag.current = null; }, onPointerCancel: () => { drag.current = null; }, children: [_jsx("span", { className: "flood-slider__track", "aria-hidden": "true", children: _jsx("span", { className: "flood-slider__fill" }) }), values.map((number, index) => _jsx("input", { ref: node => { inputs.current[index] = node; }, type: "range", className: "flood-slider__input", name: name, form: props.form, value: number, min: min, max: max, step: step, disabled: disabled, "aria-label": thumbLabels?.[index] ?? (values.length === 1 ? label : `${label}: ${index === 0 ? "minimum" : "maximum"}`), "aria-valuemin": index === 1 ? values[0] : min, "aria-valuemax": index === 0 && values.length === 2 ? values[1] : max, "aria-describedby": describedBy, "aria-readonly": readOnly || undefined, "aria-invalid": status === "error" || undefined, onPointerDown: event => { if (readOnly)
87
+ event.preventDefault(); }, onChange: event => { if (!inert(event.currentTarget))
88
+ update(index, event.currentTarget.valueAsNumber); } }, index))] })] });
89
+ });
@@ -0,0 +1,9 @@
1
+ import type { SearchInputProps, PasswordInputProps, InputGroup, IconButton, IconSlot } from "./index.js";
2
+ type Parts = {
3
+ group: typeof InputGroup;
4
+ action: typeof IconButton;
5
+ icon: typeof IconSlot;
6
+ };
7
+ export declare const SearchField: import("react").ForwardRefExoticComponent<SearchInputProps & Parts & import("react").RefAttributes<HTMLInputElement>>;
8
+ export declare const PasswordField: import("react").ForwardRefExoticComponent<PasswordInputProps & Parts & import("react").RefAttributes<HTMLInputElement>>;
9
+ export {};
@@ -0,0 +1,45 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
3
+ const glyph = (kind, visible = false) => _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: kind === "search" ? _jsxs(_Fragment, { children: [_jsx("circle", { cx: "10.5", cy: "10.5", r: "6.5" }), _jsx("path", { d: "m16 16 4 4" })] }) : kind === "clear" ? _jsx("path", { d: "m6 6 12 12M18 6 6 18" }) : _jsxs(_Fragment, { children: [_jsx("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" }), visible && _jsx("path", { d: "m3 3 18 18" })] }) });
4
+ export const SearchField = forwardRef(function SearchField({ group: Group, action: Action, icon: Icon, clearable = true, clearLabel = "Clear search", controlSize = "md", className, value, defaultValue, onChange, disabled, readOnly, ...props }, ref) {
5
+ const input = useRef(null);
6
+ useImperativeHandle(ref, () => input.current, []);
7
+ const [text, setText] = useState(String(defaultValue ?? ""));
8
+ const current = value === undefined ? text : String(value);
9
+ useEffect(() => {
10
+ const form = input.current?.form;
11
+ if (!form)
12
+ return;
13
+ const reset = (event) => setTimeout(() => { if (!event.defaultPrevented && input.current)
14
+ setText(input.current.value); }, 0);
15
+ form.addEventListener("reset", reset);
16
+ return () => form.removeEventListener("reset", reset);
17
+ }, [props.form]);
18
+ const clear = () => {
19
+ const node = input.current;
20
+ if (!node || readOnly || node.matches(":disabled"))
21
+ return;
22
+ const view = node.ownerDocument.defaultView;
23
+ Object.getOwnPropertyDescriptor(view.HTMLInputElement.prototype, "value").set.call(node, "");
24
+ node.dispatchEvent(new view.Event("input", { bubbles: true }));
25
+ node.focus();
26
+ };
27
+ return _jsx(Group, { ...props, ref: input, type: "search", controlSize: controlSize, className: ["flood-search-input", className].filter(Boolean).join(" "), value: value, defaultValue: defaultValue, disabled: disabled, readOnly: readOnly, onChange: event => { setText(event.currentTarget.value); onChange?.(event); }, start: _jsx(Icon, { size: controlSize, children: glyph("search") }), end: clearable && current.length > 0 && !readOnly ? _jsx(Action, { label: clearLabel, variant: "ghost", size: controlSize, disabled: disabled, onPointerDown: event => event.preventDefault(), onClick: clear, children: glyph("clear") }) : undefined });
28
+ });
29
+ export const PasswordField = forwardRef(function PasswordField({ group: Group, action: Action, icon: _Icon, visible, defaultVisible = false, onVisibleChange, showPasswordLabel = "Show password", hidePasswordLabel = "Hide password", controlSize = "md", className, disabled, ...props }, ref) {
30
+ const input = useRef(null);
31
+ useImperativeHandle(ref, () => input.current, []);
32
+ const [local, setLocal] = useState(defaultVisible);
33
+ const shown = visible ?? local;
34
+ return _jsx(Group, { ...props, ref: input, type: shown ? "text" : "password", disabled: disabled, controlSize: controlSize, className: ["flood-password-input", className].filter(Boolean).join(" "), end: _jsx(Action, { label: shown ? hidePasswordLabel : showPasswordLabel, variant: "ghost", size: controlSize, disabled: disabled, onPointerDown: event => event.preventDefault(), onClick: () => {
35
+ const node = input.current;
36
+ if (!node || node.matches(":disabled"))
37
+ return;
38
+ const start = node.selectionStart, end = node.selectionEnd, direction = node.selectionDirection;
39
+ if (visible === undefined)
40
+ setLocal(!shown);
41
+ onVisibleChange?.(!shown);
42
+ requestAnimationFrame(() => { if (start !== null && end !== null && node.isConnected)
43
+ node.setSelectionRange(start, end, direction ?? undefined); });
44
+ }, children: glyph("eye", shown) }) });
45
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flood-md/react",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "description": "Original Flood components for React and Next.js.",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "react-dom": "^18.3.0 || ^19.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@flood-md/tokens": "0.2.4"
33
+ "@flood-md/tokens": "0.2.5"
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"