@seekalfred/primitives 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-input.js","names":[],"sources":["../../src/mention-input/mention-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n type ReactNode,\n type UIEvent,\n} from \"react\";\n\nimport { Avatar } from \"../avatar/avatar.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\nimport { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\n\nexport type MentionKind = \"person\" | \"channel\";\n\nexport interface MentionPerson {\n id: string;\n name: string;\n /** One short line under the name, such as a job title. */\n role?: string;\n /** Portrait URL. Initials show when it is missing. */\n avatar?: string;\n}\n\nexport interface MentionChannel {\n id: string;\n name: string;\n description?: string;\n members?: number;\n}\n\n/** A mention inside `text`. `start` and `end` are character offsets; the text between them is `@label` or `#label`. */\nexport interface Mention {\n kind: MentionKind;\n id: string;\n label: string;\n start: number;\n end: number;\n}\n\nexport interface MentionValue {\n text: string;\n mentions: Mention[];\n}\n\nexport interface MentionInputHandle {\n focus: () => void;\n /** Inserts text at the caret, as if typed. */\n insert: (text: string) => void;\n /** Types a trigger at the caret so the suggestions open, adding a space first when needed. */\n openSuggestions: (kind: MentionKind) => void;\n clear: () => void;\n textarea: HTMLTextAreaElement | null;\n}\n\n/**\n * MentionInput: a text area with `@` people and `#` channel mentions. Typing a\n * trigger opens a list of suggestions at the caret; choosing one turns the\n * trigger and what was typed after it into a mention token. A token is atomic:\n * the caret steps over it, a click lands on its nearest edge, and one Backspace\n * removes all of it (and one undo brings it back). The value is plain text plus\n * a `mentions` array of offsets, ready to store or to notify from.\n *\n * It is a real `<textarea>`, not a contenteditable. The tokens are drawn by a\n * mirror layer under the textarea's own transparent text, which shares every\n * metric with it, so each token sits exactly under its characters. That keeps\n * native editing, selection, spellcheck, IME and undo, at a cost: a token can\n * be tinted but not laid out (no avatar or padding inside the text), and a line\n * wrap inside a long token follows the text's own wrap.\n *\n * The list is the component's own listbox, as the textarea is the combobox\n * (`aria-activedescendant`), so React Aria's ComboBox, which owns a one-line\n * input, does not fit.\n */\nexport interface MentionInputProps {\n /** What the field is for, and its accessible name. */\n label: string;\n /** Render the label for screen readers only. */\n labelHidden?: boolean;\n value?: MentionValue;\n defaultValue?: MentionValue;\n onValueChange?: (value: MentionValue) => void;\n /** People offered after `@`. Leave it out to turn person mentions off. */\n people?: MentionPerson[];\n /** Channels offered after `#`. Leave it out to turn channel mentions off. */\n channels?: MentionChannel[];\n /** Called when a suggestion becomes a mention. */\n onMentionAdd?: (mention: Mention) => void;\n /** With `submitOnEnter`, Enter submits and Shift+Enter adds a line. */\n onSubmit?: (value: MentionValue) => void;\n submitOnEnter?: boolean;\n placeholder?: string;\n /** @default 1 */\n minRows?: number;\n /** The field grows to this many rows, then scrolls. @default 8 */\n maxRows?: number;\n /** Where suggestions open. `auto` flips above when there is no room below. @default \"auto\" */\n placement?: \"auto\" | \"top\" | \"bottom\";\n /** @default 6 */\n maxSuggestions?: number;\n /** Helper text under the field. A string, because the copy is animated word by word. */\n description?: string;\n /** Error text under the field. It also marks the field invalid. */\n error?: string;\n disabled?: boolean;\n name?: string;\n id?: string;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\ntype Suggestion =\n | { kind: \"person\"; item: MentionPerson }\n | { kind: \"channel\"; item: MentionChannel };\ntype Trigger = { kind: MentionKind; start: number; query: string };\ninterface Anchor {\n x: number;\n y: number;\n above: boolean;\n}\ninterface View {\n suggestions: Suggestion[];\n trigger: Trigger | null;\n anchor: Anchor;\n activeIndex: number;\n}\n\nconst EMPTY: MentionValue = { text: \"\", mentions: [] };\nconst SYMBOL: Record<MentionKind, string> = { person: \"@\", channel: \"#\" };\n/** The list's width, in px: the same as `w-68`, which the list's class sets. */\nconst POPOVER_WIDTH = 272;\n\nexport const mentionText = (kind: MentionKind, label: string) => `${SYMBOL[kind]}${label}`;\n\n/** Turns a value into storage-friendly text, `<@id>` for people and `<#id>` for channels by default. */\nexport function serializeMentions(\n value: MentionValue,\n format: (mention: Mention) => string = (mention) => `<${SYMBOL[mention.kind]}${mention.id}>`,\n) {\n let out = \"\";\n let at = 0;\n for (const mention of [...value.mentions].sort((a, b) => a.start - b.start)) {\n out += value.text.slice(at, mention.start) + format(mention);\n at = mention.end;\n }\n return out + value.text.slice(at);\n}\n\n/** Moves mentions along with an edit and drops any the edit touched. `hint` is the caret before the edit, which settles repeated characters. */\nfunction reconcile(previous: MentionValue, text: string, hint: number): Mention[] {\n const a = previous.text;\n let prefix = 0;\n const maxPrefix = Math.min(a.length, text.length, Math.max(0, hint));\n while (prefix < maxPrefix && a[prefix] === text[prefix]) prefix++;\n let suffix = 0;\n while (\n suffix < a.length - prefix &&\n suffix < text.length - prefix &&\n a[a.length - 1 - suffix] === text[text.length - 1 - suffix]\n ) {\n suffix++;\n }\n const oldEnd = a.length - suffix;\n const delta = text.length - a.length;\n return previous.mentions\n .flatMap((mention) => {\n if (mention.end <= prefix) return [mention];\n if (mention.start >= oldEnd) {\n return [{ ...mention, start: mention.start + delta, end: mention.end + delta }];\n }\n return [];\n })\n .filter((mention) => text.slice(mention.start, mention.end) === mentionText(mention.kind, mention.label));\n}\n\nfunction findTrigger(\n text: string,\n caret: number,\n mentions: Mention[],\n kinds: Record<MentionKind, boolean>,\n): Trigger | null {\n for (let index = caret - 1; index >= 0 && index >= caret - 48; index--) {\n const char = text[index];\n if (char === \"\\n\") return null;\n if (char !== \"@\" && char !== \"#\") continue;\n const kind: MentionKind = char === \"@\" ? \"person\" : \"channel\";\n if (!kinds[kind]) return null;\n if (index > 0 && !/[\\s([{\"']/.test(text[index - 1]!)) return null;\n if (mentions.some((mention) => index >= mention.start && index < mention.end)) return null;\n const query = text.slice(index + 1, caret);\n const shape = kind === \"person\" ? /^[^\\s@#]*( [^\\s@#]*)?$/ : /^[^\\s@#]*$/;\n return shape.test(query) ? { kind, start: index, query } : null;\n }\n return null;\n}\n\nfunction rank(label: string, extra: string, query: string) {\n const name = label.toLowerCase();\n if (!query) return 1;\n if (name.startsWith(query)) return 4;\n if (name.split(/[\\s\\-_.]+/).some((word) => word.startsWith(query))) return 3;\n if (name.includes(query)) return 2;\n return extra.toLowerCase().includes(query) ? 1 : 0;\n}\n\nfunction initials(name: string) {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase())\n .join(\"\");\n}\n\n/** The matched part of a label reads in full contrast; the rest stays quiet. */\nfunction Highlight({ text, query }: { text: string; query: string }) {\n const at = query ? text.toLowerCase().indexOf(query.toLowerCase()) : -1;\n if (at < 0) return <>{text}</>;\n return (\n <>\n {text.slice(0, at)}\n <mark className=\"bg-transparent text-foreground\">{text.slice(at, at + query.length)}</mark>\n {text.slice(at + query.length)}\n </>\n );\n}\n\n/** The textarea and the mirror share these, so every character of one sits under the same character of the other. */\nconst textMetrics =\n \"box-border m-0 w-full border-0 px-3.5 py-2.25 font-sans text-base leading-6 break-words whitespace-pre-wrap [tab-size:4]\";\n\nconst shellClass = cnAll(\n fieldSurfaceVariants(),\n \"relative overflow-hidden\",\n \"[&:where(:hover:not([data-disabled]))]:border-border-strong\",\n // Focus turns the box's own border dark; no separate ring.\n \"has-[textarea:focus]:not-data-[invalid]:border-foreground\",\n \"forced-colors:has-[textarea:focus]:outline-solid forced-colors:has-[textarea:focus]:outline-(length:--focus-ring-width) forced-colors:has-[textarea:focus]:outline-offset-(--focus-ring-offset)\",\n // The height follows the text, so it is set from script and eased once the first one is in.\n \"data-[ready]:motion-safe:transition-[height,border-color,background-color] data-[ready]:motion-safe:duration-(--duration-smooth) data-[ready]:motion-safe:ease-smooth\",\n);\n\nconst popoverClass = cnAll(\n \"absolute z-30 w-68 max-w-full overflow-hidden\",\n \"rounded-panel border-(length:--border-width) border-border bg-card p-1 text-card-foreground shadow-floating\",\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // Grows from the caret edge, like Select's list: offset toward it and a little small.\n \"[--menu-x:0px] [--menu-y:-6px] origin-top-left\",\n \"data-[above]:[--menu-y:6px] data-[above]:origin-bottom-left data-[above]:-translate-y-full\",\n \"motion-safe:animate-select-popover-in motion-reduce:animate-select-fade-in\",\n \"data-[leaving]:pointer-events-none data-[leaving]:motion-safe:animate-select-popover-out data-[leaving]:motion-reduce:animate-select-fade-out\",\n);\n\nexport const MentionInput = forwardRef<MentionInputHandle, MentionInputProps>(function MentionInput(\n {\n label,\n labelHidden = false,\n value: valueProp,\n defaultValue,\n onValueChange,\n people,\n channels,\n onMentionAdd,\n onSubmit,\n submitOnEnter = false,\n placeholder,\n minRows = 1,\n maxRows = 8,\n placement = \"auto\",\n maxSuggestions = 6,\n description,\n error,\n disabled,\n name,\n id,\n className,\n },\n ref,\n) {\n const uid = useId();\n const listId = `${uid}-list`;\n const [inner, setInner] = useState<MentionValue>(defaultValue ?? EMPTY);\n const value = valueProp ?? inner;\n const live = useRef(value);\n useIsomorphicLayoutEffect(() => {\n live.current = value;\n });\n\n const rootRef = useRef<HTMLDivElement>(null);\n const fieldRef = useRef<HTMLDivElement>(null);\n const areaRef = useRef<HTMLTextAreaElement>(null);\n const backdropRef = useRef<HTMLDivElement>(null);\n const lastCaret = useRef(0);\n const pending = useRef<Mention | null>(null);\n\n const [caret, setCaret] = useState<number | null>(null);\n const [focused, setFocused] = useState(false);\n const [dismissed, setDismissed] = useState<number | null>(null);\n const [active, setActive] = useState(0);\n\n const commit = useCallback(\n (next: MentionValue) => {\n if (valueProp === undefined) setInner(next);\n onValueChange?.(next);\n },\n [onValueChange, valueProp],\n );\n\n const kinds = useMemo(\n () => ({ person: Boolean(people?.length), channel: Boolean(channels?.length) }),\n [channels, people],\n );\n const trigger = useMemo(\n () => (caret === null ? null : findTrigger(value.text, caret, value.mentions, kinds)),\n [caret, kinds, value.mentions, value.text],\n );\n\n const suggestions = useMemo<Suggestion[]>(() => {\n if (!trigger) return [];\n const query = trigger.query.toLowerCase().trim();\n const scored: { entry: Suggestion; score: number }[] =\n trigger.kind === \"person\"\n ? (people ?? []).map((item) => ({\n entry: { kind: \"person\" as const, item },\n score: rank(item.name, item.role ?? \"\", query),\n }))\n : (channels ?? []).map((item) => ({\n entry: { kind: \"channel\" as const, item },\n score: rank(item.name, item.description ?? \"\", query),\n }));\n return scored\n .filter((entry) => entry.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxSuggestions)\n .map((entry) => entry.entry);\n }, [channels, maxSuggestions, people, trigger]);\n\n // A sentence that simply continues after an unmatched name closes the list instead of showing an empty state.\n const open =\n focused &&\n !disabled &&\n Boolean(trigger) &&\n trigger?.start !== dismissed &&\n (suggestions.length > 0 || !trigger?.query.includes(\" \"));\n const activeIndex = Math.min(active, Math.max(0, suggestions.length - 1));\n const queryKey = trigger ? `${trigger.start}:${trigger.query}` : \"\";\n const [lastQueryKey, setLastQueryKey] = useState(queryKey);\n if (lastQueryKey !== queryKey) {\n setLastQueryKey(queryKey);\n setActive(0);\n }\n if (dismissed !== null && trigger?.start !== dismissed) setDismissed(null);\n\n /* Selection: the caret never rests inside a token. Arrow keys hop over it; a click lands on the nearest edge. */\n const onSelect = () => {\n const area = areaRef.current;\n if (!area) return;\n let start = area.selectionStart;\n let end = area.selectionEnd;\n const collapsed = start === end;\n for (const mention of live.current.mentions) {\n if (collapsed && start > mention.start && start < mention.end) {\n const stepped = Math.abs(start - lastCaret.current) === 1;\n const forward = start > lastCaret.current;\n start = end = stepped\n ? forward\n ? mention.end\n : mention.start\n : start - mention.start < mention.end - start\n ? mention.start\n : mention.end;\n } else if (!collapsed) {\n if (start > mention.start && start < mention.end) start = mention.start;\n if (end > mention.start && end < mention.end) end = mention.end;\n }\n }\n if (start !== area.selectionStart || end !== area.selectionEnd) {\n area.setSelectionRange(start, end, area.selectionDirection);\n }\n lastCaret.current = area.selectionStart;\n setCaret(collapsed ? start : null);\n };\n\n const onInput = (event: ChangeEvent<HTMLTextAreaElement>) => {\n const text = event.target.value;\n const mentions = reconcile(live.current, text, lastCaret.current);\n const added = pending.current;\n pending.current = null;\n if (added && text.slice(added.start, added.end) === mentionText(added.kind, added.label)) {\n mentions.push(added);\n mentions.sort((a, b) => a.start - b.start);\n }\n const next = { text, mentions };\n live.current = next;\n commit(next);\n if (added) onMentionAdd?.(added);\n lastCaret.current = event.target.selectionStart;\n setCaret(event.target.selectionStart === event.target.selectionEnd ? event.target.selectionStart : null);\n };\n\n /** Types through the browser so the change joins the native undo stack; falls back to a direct edit. */\n const typeText = useCallback(\n (text: string, from?: number, to?: number) => {\n const area = areaRef.current;\n if (!area) return;\n area.focus({ preventScroll: true });\n if (from !== undefined) area.setSelectionRange(from, to ?? from);\n lastCaret.current = area.selectionStart;\n const typed = typeof document.execCommand === \"function\" && document.execCommand(\"insertText\", false, text);\n if (typed) return;\n const start = area.selectionStart;\n const end = area.selectionEnd;\n const current = live.current;\n const nextText = current.text.slice(0, start) + text + current.text.slice(end);\n const mentions = reconcile(current, nextText, start);\n const added = pending.current;\n pending.current = null;\n if (added) mentions.push(added);\n mentions.sort((a, b) => a.start - b.start);\n const next = { text: nextText, mentions };\n live.current = next;\n commit(next);\n if (added) onMentionAdd?.(added);\n requestAnimationFrame(() => {\n area.setSelectionRange(start + text.length, start + text.length);\n setCaret(start + text.length);\n });\n },\n [commit, onMentionAdd],\n );\n\n const choose = (suggestion: Suggestion | undefined) => {\n if (!suggestion || !trigger || caret === null) return;\n const label = suggestion.item.name;\n const token = mentionText(suggestion.kind, label);\n pending.current = {\n kind: suggestion.kind,\n id: suggestion.item.id,\n label,\n start: trigger.start,\n end: trigger.start + token.length,\n };\n const after = value.text[caret];\n typeText(after === undefined || !/\\s/.test(after) ? `${token} ` : token, trigger.start, caret);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n const area = event.currentTarget;\n lastCaret.current = area.selectionStart;\n if (open) {\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n if (suggestions.length) {\n setActive((activeIndex + (event.key === \"ArrowDown\" ? 1 : -1) + suggestions.length) % suggestions.length);\n }\n return;\n }\n if ((event.key === \"Enter\" || event.key === \"Tab\") && suggestions.length && !event.shiftKey) {\n event.preventDefault();\n choose(suggestions[activeIndex]);\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setDismissed(trigger?.start ?? null);\n return;\n }\n }\n const start = area.selectionStart;\n const end = area.selectionEnd;\n if (start === end && !event.metaKey && (event.key === \"Backspace\" || event.key === \"Delete\")) {\n // Select the whole token and let the native delete run, so one keystroke removes it and undo restores it.\n const hit = live.current.mentions.find((mention) =>\n event.key === \"Backspace\"\n ? start > mention.start && start <= mention.end\n : start >= mention.start && start < mention.end,\n );\n if (hit) area.setSelectionRange(hit.start, hit.end);\n return;\n }\n if (event.key === \"Enter\" && submitOnEnter && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n if (live.current.text.trim()) onSubmit?.(live.current);\n }\n };\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => areaRef.current?.focus(),\n insert: (text) => typeText(text),\n openSuggestions: (kind) => {\n const area = areaRef.current;\n if (!area) return;\n const focusedNow = document.activeElement === area;\n const at = focusedNow ? area.selectionStart : live.current.text.length;\n const before = live.current.text[at - 1];\n setDismissed(null);\n typeText(\n `${before && !/\\s/.test(before) ? \" \" : \"\"}${SYMBOL[kind]}`,\n at,\n focusedNow ? area.selectionEnd : at,\n );\n },\n clear: () => {\n commit(EMPTY);\n live.current = EMPTY;\n setCaret(0);\n },\n get textarea() {\n return areaRef.current;\n },\n }),\n [commit, typeText],\n );\n\n /*\n * Autosize: the mirror's height is the content height. The field follows it within the row limits. The height is\n * written straight to the DOM (no state, no re-render), and the first one lands without easing.\n */\n useIsomorphicLayoutEffect(() => {\n const backdrop = backdropRef.current;\n const field = fieldRef.current;\n if (!backdrop || !field) return;\n const fit = () => {\n const style = getComputedStyle(backdrop);\n const line = parseFloat(style.lineHeight) || 24;\n const pad = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);\n const border = parseFloat(getComputedStyle(field).borderTopWidth) * 2;\n const min = line * minRows + pad;\n const max = line * Math.max(minRows, maxRows) + pad;\n field.style.height = `${Math.min(max, Math.max(min, backdrop.offsetHeight)) + border}px`;\n // Easing starts only after the first height is in, so the field never grows on mount.\n if (!field.hasAttribute(\"data-ready\")) requestAnimationFrame(() => field.setAttribute(\"data-ready\", \"\"));\n };\n fit();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(fit);\n observer.observe(backdrop);\n return () => observer.disconnect();\n }, [maxRows, minRows]);\n\n /* The list hangs from the trigger character, measured from a marker in the mirrored text. */\n const [anchor, setAnchor] = useState<Anchor | null>(null);\n const measureAnchor = useCallback(() => {\n const marker = backdropRef.current?.querySelector<HTMLElement>(\"[data-anchor]\");\n const root = rootRef.current;\n const field = fieldRef.current;\n const area = areaRef.current;\n if (!marker || !root || !field || !area) {\n setAnchor(null);\n return;\n }\n const line = parseFloat(getComputedStyle(marker.parentElement ?? marker).lineHeight) || 24;\n const x = Math.max(0, Math.min(field.offsetLeft + marker.offsetLeft - 10, root.offsetWidth - POPOVER_WIDTH));\n const top = field.offsetTop + marker.offsetTop - area.scrollTop;\n const box = root.getBoundingClientRect();\n const roomBelow = window.innerHeight - (box.top + top + line);\n const above = placement === \"top\" || (placement === \"auto\" && roomBelow < 300 && box.top + top > roomBelow);\n const y = above ? top - 6 : top + line + 6;\n setAnchor((current) =>\n current && current.x === x && current.y === y && current.above === above ? current : { x, y, above },\n );\n }, [placement]);\n useIsomorphicLayoutEffect(() => {\n if (open) measureAnchor();\n }, [measureAnchor, open, trigger?.start, value.text]);\n // The list mounts one commit after `open`, once the anchor is measured.\n const shown = open && Boolean(anchor);\n\n const onScroll = (event: UIEvent<HTMLTextAreaElement>) => {\n const backdrop = backdropRef.current;\n if (backdrop) backdrop.style.transform = `translateY(${-event.currentTarget.scrollTop}px)`;\n measureAnchor();\n };\n\n /*\n * The list stays mounted while it leaves, showing what it showed: `view` is the live content while it is shown, and\n * the last content it had after that.\n */\n const current: View | null =\n shown && anchor ? { suggestions, trigger, anchor, activeIndex } : null;\n const [frozen, setFrozen] = useState<View | null>(current);\n if (\n current &&\n (frozen?.suggestions !== current.suggestions ||\n frozen.trigger !== current.trigger ||\n frozen.anchor !== current.anchor ||\n frozen.activeIndex !== current.activeIndex)\n ) {\n setFrozen(current);\n }\n const view = current ?? frozen;\n const [rendered, setRendered] = useState(shown);\n const leaving = rendered && !shown;\n if (shown && !rendered) setRendered(true);\n const unmount = useCallback(() => setRendered(false), []);\n useExitBackstop(leaving, unmount);\n // Reduced motion removes it at once: an animation that is switched off never ends.\n useIsomorphicLayoutEffect(() => {\n if (leaving && prefersReducedMotion()) unmount();\n }, [leaving, unmount]);\n\n /* The highlight glides between rows. Written to the DOM: it follows the row the keys or the pointer chose. */\n const listRef = useRef<HTMLUListElement>(null);\n const highlightRef = useRef<HTMLLIElement>(null);\n useIsomorphicLayoutEffect(() => {\n const row = listRef.current?.querySelector<HTMLElement>(`[data-index=\"${view?.activeIndex ?? 0}\"]`);\n const highlight = highlightRef.current;\n if (!row || !highlight || !shown) return;\n highlight.style.height = `${row.offsetHeight}px`;\n highlight.style.transform = `translateY(${row.offsetTop}px)`;\n // The first placement lands at once; later ones glide.\n if (!highlight.hasAttribute(\"data-ready\")) requestAnimationFrame(() => highlight.setAttribute(\"data-ready\", \"\"));\n }, [shown, view?.activeIndex, view?.suggestions]);\n\n /* Mirror: the same text as the textarea, with tokens drawn in place and a marker at the open trigger. */\n const mirror = useMemo(() => {\n const parts: ReactNode[] = [];\n const markAt = open && trigger ? trigger.start : -1;\n let at = 0;\n const pushText = (from: number, to: number) => {\n if (markAt >= from && markAt < to) {\n parts.push(\n value.text.slice(from, markAt),\n <span key=\"anchor\" data-anchor=\"\" />,\n value.text.slice(markAt, to),\n );\n } else parts.push(value.text.slice(from, to));\n };\n for (const mention of value.mentions) {\n pushText(at, mention.start);\n parts.push(\n <span\n key={`${mention.start}-${mention.id}`}\n data-slot=\"mention-token\"\n data-kind={mention.kind}\n // A token is tinted exactly under its own glyphs: no horizontal padding or margin, so every character stays where\n // the textarea put it, and the space between two tokens shows as a gap instead of being painted over.\n className=\"box-decoration-clone rounded-2xs bg-border-subtle py-px text-foreground\"\n >\n {value.text.slice(mention.start, mention.end)}\n </span>,\n );\n at = mention.end;\n }\n pushText(at, value.text.length);\n return parts;\n }, [open, trigger, value.mentions, value.text]);\n\n const field = useFieldWiring({\n enabled: true,\n id,\n describedBy: undefined,\n invalid: undefined,\n description,\n error,\n });\n\n const optionId = (index: number) => `${uid}-option-${index}`;\n const noun = trigger?.kind === \"person\" ? [\"person\", \"people\"] : [\"channel\", \"channels\"];\n const countLabel = open\n ? suggestions.length\n ? `${suggestions.length} ${suggestions.length === 1 ? noun[0] : noun[1]}`\n : \"No matches\"\n : \"\";\n\n const control = (\n <div ref={rootRef} className=\"relative\">\n <div\n ref={fieldRef}\n data-slot=\"mention-input-field\"\n data-disabled={disabled || undefined}\n data-invalid={error ? \"\" : undefined}\n data-focused={focused || undefined}\n className={shellClass}\n >\n <div\n ref={backdropRef}\n aria-hidden=\"true\"\n data-slot=\"mention-input-mirror\"\n className={cn(textMetrics, \"pointer-events-none absolute top-0 left-0 text-foreground will-change-transform\")}\n >\n {mirror}\n {\"\"}\n </div>\n <textarea\n ref={areaRef}\n {...field.controlProps}\n name={name}\n data-slot=\"mention-input\"\n value={value.text}\n placeholder={placeholder}\n disabled={disabled}\n rows={minRows}\n spellCheck\n aria-label={labelHidden ? label : undefined}\n aria-autocomplete=\"list\"\n aria-controls={open ? listId : undefined}\n aria-activedescendant={open && suggestions.length ? optionId(activeIndex) : undefined}\n onChange={onInput}\n onSelect={onSelect}\n onKeyDown={onKeyDown}\n onScroll={onScroll}\n onFocus={() => setFocused(true)}\n onBlur={() => {\n setFocused(false);\n setCaret(null);\n }}\n className={cn(\n textMetrics,\n \"absolute inset-0 block h-full resize-none overflow-y-auto bg-transparent text-transparent caret-foreground outline-none [scrollbar-width:none] placeholder:text-muted-foreground selection:bg-primary/25 selection:text-transparent disabled:cursor-not-allowed [&::-webkit-scrollbar]:hidden\",\n )}\n />\n </div>\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {countLabel}\n </span>\n {rendered && view ? (\n <div\n data-slot=\"mention-input-popover\"\n data-above={view.anchor.above || undefined}\n data-leaving={leaving || undefined}\n style={{ left: view.anchor.x, top: view.anchor.y }}\n onMouseDown={(event) => event.preventDefault()}\n onAnimationEnd={(event) => {\n if (leaving && event.target === event.currentTarget) unmount();\n }}\n className={popoverClass}\n >\n {view.suggestions.length ? (\n <ul\n ref={listRef}\n id={listId}\n role=\"listbox\"\n aria-label={view.trigger?.kind === \"person\" ? \"People\" : \"Channels\"}\n className=\"relative m-0 grid list-none p-0\"\n >\n <li\n ref={highlightRef}\n aria-hidden=\"true\"\n data-slot=\"mention-input-highlight\"\n className=\"pointer-events-none absolute inset-x-0 top-0 rounded-control bg-muted data-[ready]:motion-safe:transition-[transform,height] data-[ready]:motion-safe:duration-(--duration-quick) data-[ready]:motion-safe:ease-settle\"\n />\n {view.suggestions.map((suggestion, index) => (\n <li\n key={`${suggestion.kind}-${suggestion.item.id}`}\n id={optionId(index)}\n role=\"option\"\n aria-selected={index === view.activeIndex}\n data-index={index}\n data-slot=\"mention-input-option\"\n className=\"group/option relative flex min-w-0 cursor-pointer items-center gap-2.5 rounded-control py-1.5 ps-2 pe-2.5\"\n onPointerMove={() => {\n if (index !== activeIndex) setActive(index);\n }}\n onClick={() => choose(suggestion)}\n >\n {suggestion.kind === \"person\" ? (\n <Avatar\n size=\"md\"\n alt=\"\"\n src={suggestion.item.avatar}\n fallback={initials(suggestion.item.name)}\n aria-hidden=\"true\"\n />\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"grid size-8 flex-none place-items-center text-text-secondary\"\n >\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n className=\"size-4\"\n >\n <path d=\"M6.5 2.5 5 13.5M11 2.5 9.5 13.5M2.5 6h11M2.5 10h11\" />\n </svg>\n </span>\n )}\n <span className=\"grid min-w-0 flex-1\">\n <span className=\"truncate font-medium text-text-secondary group-aria-selected/option:text-foreground\">\n <Highlight text={suggestion.item.name} query={view.trigger?.query.trim() ?? \"\"} />\n </span>\n {suggestion.kind === \"person\"\n ? suggestion.item.role && (\n <span className=\"truncate text-xs text-muted-foreground\">{suggestion.item.role}</span>\n )\n : suggestion.item.description && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {suggestion.item.description}\n </span>\n )}\n </span>\n {suggestion.kind === \"channel\" && suggestion.item.members !== undefined ? (\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n {suggestion.item.members}\n </span>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"m-0 px-2.5 py-2.5 text-sm text-text-secondary\">\n No {view.trigger?.kind === \"person\" ? \"people\" : \"channels\"} match “{view.trigger?.query}”\n </p>\n )}\n </div>\n ) : null}\n </div>\n );\n\n return (\n <div\n className={cn(\"w-full min-w-0 font-sans\", className)}\n data-slot=\"mention-input-root\"\n >\n <FieldShell\n label={labelHidden ? null : label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={error}\n errorId={field.errorId}\n >\n {control}\n </FieldShell>\n </div>\n );\n});\n\nMentionInput.displayName = \"MentionInput\";\n"],"mappings":"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"}
|
package/dist/menu/menu.d.ts
CHANGED
|
@@ -79,6 +79,8 @@ declare const Menu$1: import("react").ForwardRefExoticComponent<MenuProps$1 & im
|
|
|
79
79
|
interface MenuItemProps$1 extends Omit<MenuItemProps, "className" | "style"> {
|
|
80
80
|
/** Extra classes. Merged last, so they win conflicts with ours. */
|
|
81
81
|
className?: string;
|
|
82
|
+
/** Marks a destructive action: red text, and the highlight tints red while it is on this row. */
|
|
83
|
+
destructive?: boolean;
|
|
82
84
|
}
|
|
83
85
|
declare const MenuItem$1: import("react").ForwardRefExoticComponent<MenuItemProps$1 & import("react").RefAttributes<HTMLDivElement>>;
|
|
84
86
|
/**
|
package/dist/menu/menu.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { mergeRefs } from "../lib/merge-refs.js";
|
|
4
|
+
import { useMenuHighlight } from "../lib/menu-highlight.js";
|
|
3
5
|
import { cva } from "class-variance-authority";
|
|
4
6
|
import { forwardRef } from "react";
|
|
5
7
|
import { Menu, MenuItem, MenuTrigger, Popover, Separator } from "react-aria-components";
|
|
@@ -38,17 +40,17 @@ import { jsx } from "react/jsx-runtime";
|
|
|
38
40
|
*/
|
|
39
41
|
const menuPopoverVariants = cva([
|
|
40
42
|
"min-w-32 overflow-y-auto",
|
|
41
|
-
"rounded-
|
|
43
|
+
"rounded-panel border-(length:--border-width) border-border p-1.5",
|
|
42
44
|
"bg-card text-card-foreground shadow-floating",
|
|
43
|
-
"box-border font-sans text-sm leading-(--leading-normal)"
|
|
45
|
+
"box-border font-sans text-sm leading-(--leading-normal)",
|
|
46
|
+
"menu-motion"
|
|
44
47
|
]);
|
|
45
|
-
const menuVariants = cva(["flex flex-col outline-none"]);
|
|
48
|
+
const menuVariants = cva(["flex flex-col outline-none menu-highlight"]);
|
|
46
49
|
const menuItemVariants = cva([
|
|
47
|
-
"relative flex cursor-pointer select-none items-center gap-2",
|
|
48
|
-
"rounded-
|
|
50
|
+
"relative flex cursor-pointer select-none items-center gap-2.5",
|
|
51
|
+
"min-h-(--density-control-h-sm) rounded-control px-3 py-1.5",
|
|
49
52
|
"box-border text-sm leading-(--leading-normal) text-card-foreground",
|
|
50
|
-
"outline-none",
|
|
51
|
-
"data-[focused]:bg-muted",
|
|
53
|
+
"outline-none menu-item-motion",
|
|
52
54
|
"data-[focus-visible]:outline-solid",
|
|
53
55
|
"data-[focus-visible]:outline-(length:--focus-ring-width)",
|
|
54
56
|
"data-[focus-visible]:outline-offset-(--focus-ring-offset)",
|
|
@@ -57,15 +59,23 @@ const menuItemVariants = cva([
|
|
|
57
59
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
58
60
|
"[&_svg:not([class*='size-'])]:size-(--icon-sm)"
|
|
59
61
|
]);
|
|
60
|
-
const menuSeparatorVariants = cva(["-mx-1 my-1 h-px bg-border border-none"]);
|
|
62
|
+
const menuSeparatorVariants = cva(["-mx-1.5 my-1 h-px bg-border border-none"]);
|
|
63
|
+
/** The edge the grow animation starts from, taken from the placement's alignment. */
|
|
64
|
+
const ORIGIN = {
|
|
65
|
+
start: "[--menu-ox:left]",
|
|
66
|
+
end: "[--menu-ox:right]",
|
|
67
|
+
center: "[--menu-ox:center]"
|
|
68
|
+
};
|
|
61
69
|
const Menu$1 = forwardRef(function Menu$2({ children, className, placement = "bottom start", offset = 4, ...props }, ref) {
|
|
70
|
+
const bind = useMenuHighlight();
|
|
71
|
+
const align = placement?.endsWith("end") ? "end" : placement?.endsWith("start") ? "start" : "center";
|
|
62
72
|
return /* @__PURE__ */ jsx(Popover, {
|
|
63
73
|
placement,
|
|
64
74
|
offset,
|
|
65
75
|
"data-slot": "menu-popover",
|
|
66
|
-
className: cn(menuPopoverVariants(), className),
|
|
76
|
+
className: cn(menuPopoverVariants() + " " + ORIGIN[align], className),
|
|
67
77
|
children: /* @__PURE__ */ jsx(Menu, {
|
|
68
|
-
ref,
|
|
78
|
+
ref: mergeRefs(ref, bind),
|
|
69
79
|
...props,
|
|
70
80
|
"data-slot": "menu",
|
|
71
81
|
className: menuVariants(),
|
|
@@ -73,12 +83,13 @@ const Menu$1 = forwardRef(function Menu$2({ children, className, placement = "bo
|
|
|
73
83
|
})
|
|
74
84
|
});
|
|
75
85
|
});
|
|
76
|
-
const MenuItem$1 = forwardRef(function MenuItem$2({ className, ...props }, ref) {
|
|
86
|
+
const MenuItem$1 = forwardRef(function MenuItem$2({ className, destructive, ...props }, ref) {
|
|
77
87
|
return /* @__PURE__ */ jsx(MenuItem, {
|
|
78
88
|
ref,
|
|
79
89
|
...props,
|
|
80
90
|
"data-slot": "menu-item",
|
|
81
|
-
|
|
91
|
+
"data-destructive": destructive ? "" : void 0,
|
|
92
|
+
className: cn(menuItemVariants() + (destructive ? " text-destructive-strong" : ""), className)
|
|
82
93
|
});
|
|
83
94
|
});
|
|
84
95
|
/**
|
package/dist/menu/menu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu.js","names":["Menu","AriaPopover","AriaMenu","MenuItem","AriaMenuItem","AriaSeparator"],"sources":["../../src/menu/menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover as AriaPopover,\n Separator as AriaSeparator,\n type MenuItemProps as AriaMenuItemProps,\n type MenuProps as AriaMenuProps,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Menu, ported from the legacy shadcn/Radix `dropdown-menu` onto React\n * Aria's Menu collection.\n *\n * FIFTEEN EXPORTS BECOME FOUR, and the census is unambiguous about it. Ten\n * files import the legacy component but only EIGHT render anything --\n * `header.tsx` and `PollQuizCreator.tsx` import parts and use none. Across\n * those eight: `DropdownMenuItem` 42, `DropdownMenu` 13,\n * `DropdownMenuTrigger` 13, `DropdownMenuContent` 13,\n * `DropdownMenuSeparator` 2.\n *\n * Everything else is ZERO: `Group`, `CheckboxItem`, `RadioGroup`,\n * `RadioItem`, `Label`, `Shortcut`, `Sub`, `SubTrigger`, `SubContent`,\n * `Portal`. Nine exports, no call sites. React Aria has `MenuSection`,\n * `Header` and selection modes ready if any of them turns out to be\n * wanted -- they are one component each, added the day something needs one.\n *\n * `variant=\"destructive\"` is also never passed, which resolves a real\n * problem rather than dodging one: `destructive-strong` on a `muted` hover\n * fill measures 4.29:1 against a 4.5 floor. The legacy has the same defect\n * unmeasured and worse (its `destructive/10` composite gives 4.01:1). With\n * no call site there is nothing to ship and nothing to weaken the contract\n * for.\n *\n * The surface is `card`, not a `popover` role: in the legacy theme\n * `--popover` is byte-identical to `--card` in both themes. Item hover is\n * `muted`, not `accent`: `--accent` is byte-identical to `--muted`. Two\n * more of the migration plan's proposed roles that already exist.\n *\n * Every string in the cva blocks below is a STATIC literal.\n */\nexport const menuPopoverVariants = cva([\n // min-w-32 is 8rem, the legacy min-w-[8rem] -- but as a spacing-scale\n // step rather than a bracket value that check:hardcoding rejects.\n \"min-w-32 overflow-y-auto\",\n \"rounded-
|
|
1
|
+
{"version":3,"file":"menu.js","names":["Menu","AriaPopover","AriaMenu","MenuItem","AriaMenuItem","AriaSeparator"],"sources":["../../src/menu/menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover as AriaPopover,\n Separator as AriaSeparator,\n type MenuItemProps as AriaMenuItemProps,\n type MenuProps as AriaMenuProps,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { useMenuHighlight } from \"../lib/menu-highlight.js\";\n\n/**\n * Menu, ported from the legacy shadcn/Radix `dropdown-menu` onto React\n * Aria's Menu collection.\n *\n * FIFTEEN EXPORTS BECOME FOUR, and the census is unambiguous about it. Ten\n * files import the legacy component but only EIGHT render anything --\n * `header.tsx` and `PollQuizCreator.tsx` import parts and use none. Across\n * those eight: `DropdownMenuItem` 42, `DropdownMenu` 13,\n * `DropdownMenuTrigger` 13, `DropdownMenuContent` 13,\n * `DropdownMenuSeparator` 2.\n *\n * Everything else is ZERO: `Group`, `CheckboxItem`, `RadioGroup`,\n * `RadioItem`, `Label`, `Shortcut`, `Sub`, `SubTrigger`, `SubContent`,\n * `Portal`. Nine exports, no call sites. React Aria has `MenuSection`,\n * `Header` and selection modes ready if any of them turns out to be\n * wanted -- they are one component each, added the day something needs one.\n *\n * `variant=\"destructive\"` is also never passed, which resolves a real\n * problem rather than dodging one: `destructive-strong` on a `muted` hover\n * fill measures 4.29:1 against a 4.5 floor. The legacy has the same defect\n * unmeasured and worse (its `destructive/10` composite gives 4.01:1). With\n * no call site there is nothing to ship and nothing to weaken the contract\n * for.\n *\n * The surface is `card`, not a `popover` role: in the legacy theme\n * `--popover` is byte-identical to `--card` in both themes. Item hover is\n * `muted`, not `accent`: `--accent` is byte-identical to `--muted`. Two\n * more of the migration plan's proposed roles that already exist.\n *\n * Every string in the cva blocks below is a STATIC literal.\n */\nexport const menuPopoverVariants = cva([\n // min-w-32 is 8rem, the legacy min-w-[8rem] -- but as a spacing-scale\n // step rather than a bracket value that check:hardcoding rejects.\n \"min-w-32 overflow-y-auto\",\n // A 26px panel with a hairline border and a soft shadow. It grows from its\n // trigger on open and leaves faster (menu-motion).\n \"rounded-panel border-(length:--border-width) border-border p-1.5\",\n \"bg-card text-card-foreground shadow-floating\",\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n \"menu-motion\",\n]);\n\n// menu-highlight: one highlight glides between the rows (see useMenuHighlight).\nexport const menuVariants = cva([\"flex flex-col outline-none menu-highlight\"]);\n\nexport const menuItemVariants = cva([\n \"relative flex cursor-pointer select-none items-center gap-2.5\",\n \"min-h-(--density-control-h-sm) rounded-control px-3 py-1.5\",\n \"box-border text-sm leading-(--leading-normal) text-card-foreground\",\n \"outline-none menu-item-motion\",\n\n // The row paints no highlight itself. React Aria drives one focus from both\n // pointer and keyboard (data-focused, NOT :hover), and the menu draws a\n // single highlight that glides to whichever row has it (menu-highlight), so\n // the row under the mouse and the row the arrow keys reached look the same.\n\n // The focus ring goes on the ITEM, and it is an outline rather than a\n // background alone: a highlight made only of colour disappears under\n // forced-colors, and axe has no rule that would notice.\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n]);\n\nexport const menuSeparatorVariants = cva([\"-mx-1.5 my-1 h-px bg-border border-none\"]);\n\n/** The edge the grow animation starts from, taken from the placement's alignment. */\nconst ORIGIN = {\n start: \"[--menu-ox:left]\",\n end: \"[--menu-ox:right]\",\n center: \"[--menu-ox:center]\",\n} as const;\n\n/**\n * A dropdown menu.\n *\n * @example\n * <MenuTrigger>\n * <Button>Actions</Button>\n * <Menu onAction={(key) => run(key)}>\n * <MenuItem id=\"edit\">Edit</MenuItem>\n * <MenuSeparator />\n * <MenuItem id=\"delete\">Delete</MenuItem>\n * </Menu>\n * </MenuTrigger>\n */\nexport interface MenuProps\n extends Omit<AriaMenuProps<object>, \"className\" | \"style\" | \"children\" | \"items\"> {\n /** `MenuItem` and `MenuSeparator` children. */\n children?: ReactNode;\n /** Extra classes for the popover. Merged last, so they win conflicts. */\n className?: string;\n /**\n * Where the menu opens, as React Aria's single `placement` string.\n *\n * The legacy split this across `side`, `align`, `sideOffset` and\n * `alignOffset`. Its census reads `align=\"end\"` 10 and `align=\"center\"`\n * 2, always with the default bottom side -- so `\"bottom end\"` covers ten\n * of the twelve.\n *\n * @default \"bottom start\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /** Gap between the trigger and the menu. @default 4 */\n offset?: number;\n}\n\nexport const Menu = forwardRef<HTMLDivElement, MenuProps>(function Menu(\n { children, className, placement = \"bottom start\", offset = 4, ...props },\n ref,\n) {\n // The menu mounts only while open, so the highlight binds from a callback ref.\n const bind = useMenuHighlight();\n const align = placement?.endsWith(\"end\")\n ? \"end\"\n : placement?.endsWith(\"start\")\n ? \"start\"\n : \"center\";\n return (\n <AriaPopover\n placement={placement}\n offset={offset}\n data-slot=\"menu-popover\"\n className={cn(menuPopoverVariants() + \" \" + ORIGIN[align], className)}\n >\n <AriaMenu\n // The ref goes on the MENU, not the POPOVER above. The popover here\n // is purely a positioner -- same role as an overlay wrapper -- and\n // the props spread and children already land on AriaMenu.\n ref={mergeRefs(ref, bind)}\n {...props}\n data-slot=\"menu\"\n className={menuVariants()}\n >\n {children}\n </AriaMenu>\n </AriaPopover>\n );\n});\n\n/**\n * One menu row.\n *\n * @example\n * <MenuItem id=\"edit\">Edit</MenuItem>\n * <MenuItem href=\"/settings\">Settings</MenuItem>\n */\nexport interface MenuItemProps extends Omit<AriaMenuItemProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /** Marks a destructive action: red text, and the highlight tints red while it is on this row. */\n destructive?: boolean;\n}\n\nexport const MenuItem = forwardRef<HTMLDivElement, MenuItemProps>(function MenuItem(\n { className, destructive, ...props },\n ref,\n) {\n return (\n <AriaMenuItem\n ref={ref}\n {...props}\n data-slot=\"menu-item\"\n data-destructive={destructive ? \"\" : undefined}\n className={cn(menuItemVariants() + (destructive ? \" text-destructive-strong\" : \"\"), className)}\n />\n );\n});\n\n/**\n * A divider between groups of items.\n *\n * A real collection child: React Aria's `Separator` is a `SeparatorNode`,\n * so unlike an arbitrary element it is legal inside a Menu. Inside one it\n * renders `<div role=\"separator\">` rather than the `<hr>` it uses\n * standalone, because `MenuInner` supplies `elementType: 'div'` -- an\n * `<hr>` is not valid inside a `role=\"menu\"` container.\n */\nexport const MenuSeparator = forwardRef<HTMLElement, { className?: string }>(\n function MenuSeparator({ className }, ref) {\n return (\n <AriaSeparator\n ref={ref}\n data-slot=\"menu-separator\"\n className={cn(menuSeparatorVariants(), className)}\n />\n );\n },\n);\n\n/**\n * Pairs a trigger with a menu and owns the open state.\n *\n * Re-exported from React Aria unchanged. It takes the trigger element\n * directly as its first child, which is what `asChild` was doing on all 13\n * legacy call sites.\n */\nexport { MenuTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAa,sBAAsB,IAAI;CAGrC;CAGA;CACA;CACA;CACA;AACF,CAAC;AAGD,MAAa,eAAe,IAAI,CAAC,2CAA2C,CAAC;AAE7E,MAAa,mBAAmB,IAAI;CAClC;CACA;CACA;CACA;CAUA;CACA;CACA;CACA;CAEA;CACA;CACA;AACF,CAAC;AAED,MAAa,wBAAwB,IAAI,CAAC,yCAAyC,CAAC;;AAGpF,MAAM,SAAS;CACb,OAAO;CACP,KAAK;CACL,QAAQ;AACV;AAoCA,MAAaA,SAAO,WAAsC,SAASA,OACjE,EAAE,UAAU,WAAW,YAAY,gBAAgB,SAAS,GAAG,GAAG,SAClE,KACA;CAEA,MAAM,OAAO,iBAAiB;CAC9B,MAAM,QAAQ,WAAW,SAAS,KAAK,IACnC,QACA,WAAW,SAAS,OAAO,IACzB,UACA;CACN,OACE,oBAACC,SAAD;EACa;EACH;EACR,aAAU;EACV,WAAW,GAAG,oBAAoB,IAAI,MAAM,OAAO,QAAQ,SAAS;EAEpE,UAAA,oBAACC,MAAD;GAIE,KAAK,UAAU,KAAK,IAAI;GACxB,GAAI;GACJ,aAAU;GACV,WAAW,aAAa;GAEvB;EACO,CAAA;CACC,CAAA;AAEjB,CAAC;AAgBD,MAAaC,aAAW,WAA0C,SAASA,WACzE,EAAE,WAAW,aAAa,GAAG,SAC7B,KACA;CACA,OACE,oBAACC,UAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,cAAc,KAAK,KAAA;EACrC,WAAW,GAAG,iBAAiB,KAAK,cAAc,6BAA6B,KAAK,SAAS;CAC9F,CAAA;AAEL,CAAC;;;;;;;;;;AAWD,MAAa,gBAAgB,WAC3B,SAAS,cAAc,EAAE,aAAa,KAAK;CACzC,OACE,oBAACC,WAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,sBAAsB,GAAG,SAAS;CACjD,CAAA;AAEL,CACF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { FocusEvent } from "react";
|
|
2
|
+
//#region src/money-input/money-input.d.ts
|
|
3
|
+
/** What `onValueChange` hands over beside the minor-unit amount. */
|
|
4
|
+
export interface MoneyInputDetails {
|
|
5
|
+
currency: string;
|
|
6
|
+
/** The amount in major units, such as 12.5 for $12.50. */
|
|
7
|
+
major: number | null;
|
|
8
|
+
/** The amount as a full currency string, such as "$12.50". */
|
|
9
|
+
formatted: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* MoneyInput: a currency amount. The value is in minor units, so $12.50 is 1250
|
|
13
|
+
* and 500 yen is 500. Digits group as they are typed without moving the caret,
|
|
14
|
+
* the cents that are still missing wait as muted ghosts so the amount never
|
|
15
|
+
* changes width when it settles, a long amount scales down to fit instead of
|
|
16
|
+
* scrolling, and the quick-add buttons and the arrow keys roll the digits. A
|
|
17
|
+
* currency menu switches the currency (pass one code to hide it); the symbol
|
|
18
|
+
* swaps in place and the digits are kept. Amounts and the currency come from
|
|
19
|
+
* `Intl.NumberFormat`, so the symbol, its side, the grouping and the number of
|
|
20
|
+
* decimals follow the currency and the locale.
|
|
21
|
+
*
|
|
22
|
+
* `min` is checked when the field loses focus; `max` is enforced as you type: a
|
|
23
|
+
* digit that would pass it is refused and the amount strains toward it. Wording
|
|
24
|
+
* for the range comes from the currency, and `error` replaces it.
|
|
25
|
+
*/
|
|
26
|
+
export interface MoneyInputProps {
|
|
27
|
+
/** What the amount is, and the field's accessible name. */
|
|
28
|
+
label: string;
|
|
29
|
+
/** Render the label for screen readers only. */
|
|
30
|
+
labelHidden?: boolean;
|
|
31
|
+
/** Amount in minor units (cents for USD, yen for JPY). `null` is empty. */
|
|
32
|
+
value?: number | null;
|
|
33
|
+
defaultValue?: number | null;
|
|
34
|
+
onValueChange?: (value: number | null, details: MoneyInputDetails) => void;
|
|
35
|
+
/** ISO 4217 code, such as "USD". */
|
|
36
|
+
currency?: string;
|
|
37
|
+
defaultCurrency?: string;
|
|
38
|
+
onCurrencyChange?: (currency: string) => void;
|
|
39
|
+
/** Codes offered by the currency menu. Pass one code to hide the menu. */
|
|
40
|
+
currencies?: string[];
|
|
41
|
+
/** Lowest amount in minor units. Checked when the field loses focus. */
|
|
42
|
+
min?: number;
|
|
43
|
+
/** Highest amount in minor units. Typing past it is refused with a nudge. */
|
|
44
|
+
max?: number;
|
|
45
|
+
/** Quick-add buttons in major units. Pass an empty array to hide them. */
|
|
46
|
+
quickAdd?: number[];
|
|
47
|
+
/** Arrow keys move by this many minor units. Defaults to one major unit; Shift moves ten times as far. */
|
|
48
|
+
step?: number;
|
|
49
|
+
/** Formatting locale. Fixed by default so server and client render the same digits. */
|
|
50
|
+
locale?: string;
|
|
51
|
+
/** Helper text under the field. A string, because the copy is animated word by word. */
|
|
52
|
+
description?: string;
|
|
53
|
+
/** Replaces the built-in range message. */
|
|
54
|
+
error?: string;
|
|
55
|
+
disabled?: boolean;
|
|
56
|
+
/** Adds a hidden input carrying the amount in minor units. */
|
|
57
|
+
name?: string;
|
|
58
|
+
id?: string;
|
|
59
|
+
/** Extra classes for the wrapper. Merged last. */
|
|
60
|
+
className?: string;
|
|
61
|
+
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
|
|
62
|
+
}
|
|
63
|
+
export declare const MoneyInput: import("react").ForwardRefExoticComponent<MoneyInputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
64
|
+
//#endregion
|
|
65
|
+
//# sourceMappingURL=money-input.d.ts.map
|