@danxbot/ui 3.9.1 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,141 @@
1
+ import { type Ref } from "react";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ /**
4
+ * The caller's refusal of a value, with the reason that will be shown.
5
+ *
6
+ * ONE OBJECT RATHER THAN TWO LOOSE RETURNS — `false` plus a separate message
7
+ * prop, say — because those two can disagree: a refusal with no sentence beside
8
+ * it is the mute rejection this whole component exists to prevent, and a
9
+ * sentence with nothing refusing it is a lie. This library already settled that
10
+ * shape three times over: `StatusOption.refusal`, `KanbanLane.refusal` and
11
+ * `SettingRow.readOnly: { reason }`.
12
+ *
13
+ * Written for the person who typed it — "a demand with this name already
14
+ * exists", not "invalid". It is the only thing they will see.
15
+ */
16
+ export interface InlineEditRefusal {
17
+ refusal: string;
18
+ }
19
+ /**
20
+ * What `onCommit` may report. Returning nothing accepts the value.
21
+ *
22
+ * A REFUSAL IS NOT AN EXCEPTION, and keeping the two apart is the point: a
23
+ * refusal is the caller's rule saying no (shown as its own sentence, the field
24
+ * stays open for a correction), while a thrown error or a rejected promise is
25
+ * the save FAILING (shown as `failureMessage`, the same text kept, retryable).
26
+ * Folding them together would put a stack trace's message in front of an
27
+ * end user, or bury a real outage as a validation note.
28
+ */
29
+ export type InlineEditOutcome = void | InlineEditRefusal;
30
+ export interface InlineEditProps {
31
+ /**
32
+ * The committed value. Controlled — this component never holds the truth,
33
+ * it only holds the draft while somebody is typing one.
34
+ */
35
+ value: string;
36
+ /**
37
+ * Commit a new value. Called ONLY when the text actually differs from
38
+ * `value`; see the module comment.
39
+ *
40
+ * May return a promise, in which case the field stays open and pending until
41
+ * it settles. Resolve/return `{ refusal }` to reject the value with a reason;
42
+ * throw or reject to report that the save itself failed.
43
+ *
44
+ * A SERVER'S 422 IS A REFUSAL, NOT A FAILURE — catch it and resolve to
45
+ * `{ refusal: <what the server said> }`. A duplicate name is the caller's
46
+ * rule being applied by the only party who can check it, and it reads to the
47
+ * person exactly like the local one; a rejected promise means the write did
48
+ * not happen for a reason nobody can act on by retyping.
49
+ */
50
+ onCommit: (value: string) => InlineEditOutcome | Promise<InlineEditOutcome>;
51
+ /**
52
+ * The refusals that are knowable WITHOUT a request. Return the reason, or
53
+ * `null` to accept.
54
+ *
55
+ * Runs after the unchanged check and before `onCommit`, so a value that fails
56
+ * here never reaches the network — which is the whole point of it being
57
+ * separate: emptiness and length are decidable locally, uniqueness is not,
58
+ * and a client that guesses at the second one drifts from the server.
59
+ *
60
+ * A BARE STRING RATHER THAN `{ refusal }`, unlike `onCommit`'s return, and
61
+ * the difference is not an inconsistency: `onCommit` has to say WHICH of
62
+ * accepted-or-refused happened, so the reason needs a name to sit under.
63
+ * Everything this function returns is the reason, which is the same position
64
+ * `StatusOption.refusal` and `KanbanLane.refusal` are already a bare string
65
+ * in. An empty string throws, for the reason those do.
66
+ *
67
+ * It is shown through exactly the same channel a refusal from `onCommit` is —
68
+ * the person is not told where in the stack their name was turned down.
69
+ */
70
+ validate?: (value: string) => string | null;
71
+ /**
72
+ * The accessible name, in BOTH modes. REQUIRED — see the module comment on
73
+ * why a control that renames itself mid-interaction is the defect this
74
+ * prevents.
75
+ *
76
+ * Name the FIELD ("Demand title"), not the action ("Edit") and not the
77
+ * current value: it is read in a list of controls where the value is
78
+ * announced separately.
79
+ */
80
+ label: string;
81
+ /**
82
+ * What the display shows in place of a blank value, and the field's
83
+ * placeholder.
84
+ *
85
+ * A WORD, not an em-dash, and not nothing: a display whose entire content is
86
+ * punctuation has no name worth announcing, and one whose content is empty
87
+ * collapses to a few pixels — a control that cannot be hit. See
88
+ * `scripts/probe-card-header.mjs` for the zero-width family this belongs to.
89
+ */
90
+ placeholder?: string;
91
+ /** Box ramp. Matches `Input`'s names; see the recipe on why not its padding. */
92
+ size?: "sm" | "md" | "lg";
93
+ /**
94
+ * Edit as a `<textarea>` that grows with its content.
95
+ *
96
+ * THE COMMIT KEY MOVES, because Enter has to insert a newline. Enter is a
97
+ * newline, Cmd/Ctrl+Enter commits, Escape still cancels and blur still
98
+ * commits. Nothing else changes.
99
+ */
100
+ multiline?: boolean;
101
+ /**
102
+ * This value cannot be edited at all, AND WHY — a different fact from a
103
+ * value being refused, and it belongs on a different prop. `{ reason }`
104
+ * matches `SettingRow.readOnly`; there is no boolean form, because a control
105
+ * that refuses in silence is indistinguishable from one that is broken.
106
+ *
107
+ * The display stays focusable and `aria-disabled`, carrying the reason as
108
+ * its description and in a tooltip, so it can be asked.
109
+ */
110
+ readOnly?: {
111
+ reason: string;
112
+ };
113
+ /**
114
+ * What is shown when the commit REJECTS. Deliberately not the error's own
115
+ * message: that text is written for a log, and this one is read by whoever
116
+ * just typed a name.
117
+ *
118
+ * @default "Could not save. Your text is still here — press Enter to try again."
119
+ */
120
+ failureMessage?: string;
121
+ /**
122
+ * The rejected error, handed on so it can be logged or reported. Without
123
+ * this the exception is swallowed entirely, which is the silent failure this
124
+ * library treats as a defect rather than as tidiness.
125
+ */
126
+ onError?: (error: unknown) => void;
127
+ /**
128
+ * Render the DISPLAY as a different element. See the module comment: the
129
+ * rendered element is given `role="button"`, which replaces its own role.
130
+ */
131
+ render?: useRender.RenderProp;
132
+ /**
133
+ * Applied to BOTH forms, and there is only one of these on purpose. Anything
134
+ * that changes one box's geometry has to change the other's, or the control
135
+ * jumps on click — a second `fieldClassName` would make that jump one prop
136
+ * away at all times.
137
+ */
138
+ className?: string;
139
+ ref?: Ref<HTMLElement>;
140
+ }
141
+ export declare function InlineEdit({ value, onCommit, validate, label, placeholder, size, multiline, readOnly, failureMessage, onError, render, className, ref, }: InlineEditProps): import("react").JSX.Element;
@@ -21,6 +21,7 @@ export { Skeleton, SkeletonText, LoadingOverlay, type SkeletonProps, type Skelet
21
21
  export { ErrorBoundary } from "./components/ErrorBoundary";
22
22
  export { Field, FieldSet, type FieldProps, type FieldSetProps } from "./components/Field";
23
23
  export { Input, Textarea, type InputProps, type TextareaProps } from "./components/Input";
24
+ export { InlineEdit, type InlineEditProps, type InlineEditRefusal, type InlineEditOutcome, } from "./components/InlineEdit";
24
25
  export { SecretField, type SecretFieldProps, type SecretValue } from "./components/SecretField";
25
26
  export { SettingRow, SettingGroup, type SettingRowProps, type SettingGroupProps, type SettingRowControlRenderProps, } from "./components/SettingRow";
26
27
  export { ConnectionCard, type ConnectionCardProps, type ConnectionHealth, } from "./components/ConnectionCard";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danxbot/ui",
3
- "version": "3.9.1",
3
+ "version": "3.10.0",
4
4
  "type": "module",
5
5
  "description": "Danxbot — a domain-agnostic React design system with motion as a first-class primitive.",
6
6
  "license": "MIT",
@@ -73,7 +73,7 @@
73
73
  "gates": "npm run shoot && npm run a11y && node scripts/shoot-overlays.mjs && npm run probe",
74
74
  "a11y": "node scripts/a11y-scan.mjs",
75
75
  "shoot": "node scripts/shoot.mjs",
76
- "probe": "node scripts/probe-scale.mjs && node scripts/probe-switch.mjs && node scripts/probe-slider.mjs && node scripts/probe-panel.mjs && node scripts/probe-popover-width.mjs && node scripts/probe-api.mjs && node scripts/probe-api-live.mjs && node scripts/probe-tabs-overflow.mjs && node scripts/probe-stat-row.mjs && node scripts/probe-packaging.mjs && node scripts/probe-containment.mjs && node scripts/probe-shell.mjs && node scripts/probe-card-header.mjs",
76
+ "probe": "node scripts/probe-scale.mjs && node scripts/probe-switch.mjs && node scripts/probe-slider.mjs && node scripts/probe-panel.mjs && node scripts/probe-popover-width.mjs && node scripts/probe-api.mjs && node scripts/probe-api-live.mjs && node scripts/probe-tabs-overflow.mjs && node scripts/probe-stat-row.mjs && node scripts/probe-packaging.mjs && node scripts/probe-containment.mjs && node scripts/probe-shell.mjs && node scripts/probe-card-header.mjs && node scripts/probe-inline-edit.mjs",
77
77
  "sync": "node scripts/sync.mjs",
78
78
  "visual": "node scripts/visual-docker.mjs",
79
79
  "visual:update": "node scripts/visual-docker.mjs --update-snapshots",