@marianmeres/stuic 3.137.0 → 3.138.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.
|
@@ -101,8 +101,18 @@
|
|
|
101
101
|
submitOnModEnter?: boolean;
|
|
102
102
|
/** Clear the value after a successful submit. Default `true`. */
|
|
103
103
|
clearOnSubmit?: boolean;
|
|
104
|
-
/**
|
|
105
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Block submitting an empty/whitespace comment. Default `true`. When blocked,
|
|
106
|
+
* the native inline validation error is surfaced (the submit button stays
|
|
107
|
+
* enabled) instead of disabling the button — a disabled submit makes the whole
|
|
108
|
+
* box feel dead. Set `false` to let empty submits through to `onSubmit`.
|
|
109
|
+
*/
|
|
110
|
+
blockEmptySubmit?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Message rendered by the inline error when an empty submit is blocked (see
|
|
113
|
+
* {@link blockEmptySubmit}). Default `"Please write something before submitting."`.
|
|
114
|
+
*/
|
|
115
|
+
emptyMessage?: string;
|
|
106
116
|
/** Externally-controlled busy state (disables the box + submit). */
|
|
107
117
|
busy?: boolean;
|
|
108
118
|
//
|
|
@@ -171,7 +181,8 @@
|
|
|
171
181
|
showCancel,
|
|
172
182
|
submitOnModEnter = true,
|
|
173
183
|
clearOnSubmit = true,
|
|
174
|
-
|
|
184
|
+
blockEmptySubmit = true,
|
|
185
|
+
emptyMessage = "Please write something before submitting.",
|
|
175
186
|
busy = false,
|
|
176
187
|
//
|
|
177
188
|
labelAfter,
|
|
@@ -207,15 +218,61 @@
|
|
|
207
218
|
const _showSubmit = $derived(showSubmit ?? !!onSubmit);
|
|
208
219
|
const _showCancel = $derived(showCancel ?? !!onCancel);
|
|
209
220
|
const _busy = $derived(disabled || busy || submitting);
|
|
210
|
-
const canSubmit = $derived(!_busy && !(submitDisabledWhenEmpty && isEmpty));
|
|
211
221
|
// Toggling `disabled` on MarkdownEditor remounts its backend, so only feed it
|
|
212
222
|
// the externally-controlled disable (not the transient `submitting`); the
|
|
213
223
|
// footer buttons carry the in-flight state instead.
|
|
214
224
|
const editorDisabled = $derived(disabled || busy);
|
|
215
225
|
|
|
226
|
+
// Empty submits are blocked by surfacing the native inline error (NOT by
|
|
227
|
+
// disabling the button — a disabled submit makes the whole box feel dead).
|
|
228
|
+
// We arm this only for the duration of an explicit submit attempt, so a casual
|
|
229
|
+
// focus/blur of an empty box doesn't nag with an error. Plain (non-reactive)
|
|
230
|
+
// `let`: the validator closure reads it live, and we never want toggling it to
|
|
231
|
+
// recompute `_validate`.
|
|
232
|
+
let emptyCheckArmed = false;
|
|
233
|
+
|
|
234
|
+
// Validation options forwarded to MarkdownEditor. When `blockEmptySubmit` is on
|
|
235
|
+
// we splice an empty-check into the consumer's validator — preserving any custom
|
|
236
|
+
// rule plus the editor's own `required` handling — that fires only while armed.
|
|
237
|
+
const _validate = $derived.by(() => {
|
|
238
|
+
// Consumer disabled validation entirely: pass through untouched. There is no
|
|
239
|
+
// inline error to surface then; `handleSubmit` still refuses empty submits.
|
|
240
|
+
if (validateProp === false) return false;
|
|
241
|
+
if (!blockEmptySubmit) return validateProp;
|
|
242
|
+
const base: Omit<ValidateOptions, "setValidationResult"> =
|
|
243
|
+
typeof validateProp === "object" && validateProp ? validateProp : {};
|
|
244
|
+
const inner = base.customValidator;
|
|
245
|
+
return {
|
|
246
|
+
...base,
|
|
247
|
+
customValidator(
|
|
248
|
+
val: unknown,
|
|
249
|
+
ctx: Record<string, unknown> | undefined,
|
|
250
|
+
el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
|
251
|
+
) {
|
|
252
|
+
if (emptyCheckArmed && (val == null || String(val).trim() === "")) {
|
|
253
|
+
return emptyMessage;
|
|
254
|
+
}
|
|
255
|
+
return inner ? inner(val, ctx, el) || "" : "";
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
|
|
216
260
|
// --- Handlers -------------------------------------------------------------
|
|
217
261
|
async function handleSubmit() {
|
|
218
|
-
if (
|
|
262
|
+
if (_busy) return;
|
|
263
|
+
// Block an empty submit by surfacing the native inline error instead of
|
|
264
|
+
// disabling the button. Arm the empty-check just for this validation pass,
|
|
265
|
+
// then focus the box so the user can start typing.
|
|
266
|
+
if (blockEmptySubmit && isEmpty) {
|
|
267
|
+
emptyCheckArmed = true;
|
|
268
|
+
try {
|
|
269
|
+
editor?.validate();
|
|
270
|
+
} finally {
|
|
271
|
+
emptyCheckArmed = false;
|
|
272
|
+
}
|
|
273
|
+
editor?.focus();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
219
276
|
// Validate before invoking the handler — the submit button bypasses any
|
|
220
277
|
// form-level submit check, so enforce it here (surfaces the inline message).
|
|
221
278
|
const res = editor?.validate();
|
|
@@ -326,7 +383,7 @@
|
|
|
326
383
|
disabled={editorDisabled}
|
|
327
384
|
{placeholder}
|
|
328
385
|
{renderSize}
|
|
329
|
-
validate={
|
|
386
|
+
validate={_validate}
|
|
330
387
|
{toolbar}
|
|
331
388
|
{mobileToolbar}
|
|
332
389
|
{autoSourceOnMobile}
|
|
@@ -376,7 +433,7 @@
|
|
|
376
433
|
type="button"
|
|
377
434
|
intent="primary"
|
|
378
435
|
size={renderSize}
|
|
379
|
-
disabled={
|
|
436
|
+
disabled={_busy}
|
|
380
437
|
spinner={submitting}
|
|
381
438
|
onclick={handleSubmit}
|
|
382
439
|
>
|
|
@@ -79,8 +79,18 @@ export interface Props extends InputWrapClassProps {
|
|
|
79
79
|
submitOnModEnter?: boolean;
|
|
80
80
|
/** Clear the value after a successful submit. Default `true`. */
|
|
81
81
|
clearOnSubmit?: boolean;
|
|
82
|
-
/**
|
|
83
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Block submitting an empty/whitespace comment. Default `true`. When blocked,
|
|
84
|
+
* the native inline validation error is surfaced (the submit button stays
|
|
85
|
+
* enabled) instead of disabling the button — a disabled submit makes the whole
|
|
86
|
+
* box feel dead. Set `false` to let empty submits through to `onSubmit`.
|
|
87
|
+
*/
|
|
88
|
+
blockEmptySubmit?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Message rendered by the inline error when an empty submit is blocked (see
|
|
91
|
+
* {@link blockEmptySubmit}). Default `"Please write something before submitting."`.
|
|
92
|
+
*/
|
|
93
|
+
emptyMessage?: string;
|
|
84
94
|
/** Externally-controlled busy state (disables the box + submit). */
|
|
85
95
|
busy?: boolean;
|
|
86
96
|
labelAfter?: SnippetWithId | THC;
|
|
@@ -82,43 +82,44 @@ Available items: `bold`, `italic`, `heading1`–`heading6`, `link`, `image`,
|
|
|
82
82
|
|
|
83
83
|
## Props
|
|
84
84
|
|
|
85
|
-
| Prop
|
|
86
|
-
|
|
|
87
|
-
| `value`
|
|
88
|
-
| `mode`
|
|
89
|
-
| `input`
|
|
90
|
-
| `name`
|
|
91
|
-
| `label`
|
|
92
|
-
| `description`
|
|
93
|
-
| `placeholder`
|
|
94
|
-
| `renderSize`
|
|
95
|
-
| `required`
|
|
96
|
-
| `disabled`
|
|
97
|
-
| `validate`
|
|
98
|
-
| `toolbar`
|
|
99
|
-
| `mobileToolbar`
|
|
100
|
-
| `autoSourceOnMobile`
|
|
101
|
-
| `mobileQuery`
|
|
102
|
-
| `prompt`
|
|
103
|
-
| `maxHeight`
|
|
104
|
-
| `capToParent`
|
|
105
|
-
| `showModeToggle`
|
|
106
|
-
| `sourceLabel`
|
|
107
|
-
| `previewLabel`
|
|
108
|
-
| `useShortcuts`
|
|
109
|
-
| `onSubmit`
|
|
110
|
-
| `onCancel`
|
|
111
|
-
| `onChange`
|
|
112
|
-
| `submitLabel`
|
|
113
|
-
| `cancelLabel`
|
|
114
|
-
| `showSubmit`
|
|
115
|
-
| `showCancel`
|
|
116
|
-
| `submitOnModEnter`
|
|
117
|
-
| `clearOnSubmit`
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
85
|
+
| Prop | Type | Default | Description |
|
|
86
|
+
| -------------------- | ------------------------------------------ | --------------------------------------------- | --------------------------------------------------------------------------- |
|
|
87
|
+
| `value` | `string` (bindable) | `""` | Comment content (markdown). |
|
|
88
|
+
| `mode` | `"wysiwyg" \| "source"` (bindable) | `"source"` | Active editing surface (opens on raw markdown). |
|
|
89
|
+
| `input` | `HTMLDivElement` (bindable) | — | The editor surface wrapper element. |
|
|
90
|
+
| `name` | `string` | — | Hidden input `name`, for native form submission. |
|
|
91
|
+
| `label` | `THC \| Snippet` | `""` | Field label (via InputWrap). |
|
|
92
|
+
| `description` | `THC \| Snippet` | — | Collapsible hint below the box. |
|
|
93
|
+
| `placeholder` | `string` | — | Editor placeholder. |
|
|
94
|
+
| `renderSize` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Size variant. |
|
|
95
|
+
| `required` | `boolean` | `false` | Mark required + enforce on validation. |
|
|
96
|
+
| `disabled` | `boolean` | `false` | Disable the whole box. |
|
|
97
|
+
| `validate` | `boolean \| ValidateOptions` | — | Enable validation (same contract as `MarkdownEditor`). |
|
|
98
|
+
| `toolbar` | `boolean \| ToolbarItem[]` | `DEFAULT_COMMENT_TOOLBAR` | Formatting toolbar config. |
|
|
99
|
+
| `mobileToolbar` | `boolean \| ToolbarItem[]` | MarkdownEditor default | Toolbar on touch devices. |
|
|
100
|
+
| `autoSourceOnMobile` | `boolean` | `true` | Start in source mode on mobile. |
|
|
101
|
+
| `mobileQuery` | `string` | `(pointer: coarse) …` | Media query defining "mobile". |
|
|
102
|
+
| `prompt` | `PromptFn` | `window.prompt` | URL prompt used by the link/image buttons. |
|
|
103
|
+
| `maxHeight` | `number \| string` | `32rem` | Cap the editing surface height (scrolls inside). |
|
|
104
|
+
| `capToParent` | `boolean` | `true` | Also cap the surface to the parent's available height. |
|
|
105
|
+
| `showModeToggle` | `boolean` | `true` | Show the WYSIWYG/Source toggle. |
|
|
106
|
+
| `sourceLabel` | `string` | `"Source"` | Toggle label while in WYSIWYG mode. |
|
|
107
|
+
| `previewLabel` | `string` | `"Preview"` | Toggle label while in source mode. |
|
|
108
|
+
| `useShortcuts` | `boolean` | `true` | Wire ⌘/Ctrl+B / I / K to bold / italic / link. |
|
|
109
|
+
| `onSubmit` | `(value: string) => void \| Promise<void>` | — | Submit handler. Async → spinner + disabled while pending. |
|
|
110
|
+
| `onCancel` | `() => void` | — | Cancel handler. |
|
|
111
|
+
| `onChange` | `(value: string) => void` | — | Fired on every edit. |
|
|
112
|
+
| `submitLabel` | `string` | `"Comment"` | Submit button label. |
|
|
113
|
+
| `cancelLabel` | `string` | `"Cancel"` | Cancel button label. |
|
|
114
|
+
| `showSubmit` | `boolean` | `!!onSubmit` | Show the submit button. |
|
|
115
|
+
| `showCancel` | `boolean` | `!!onCancel` | Show the cancel button. |
|
|
116
|
+
| `submitOnModEnter` | `boolean` | `true` | Submit on ⌘/Ctrl+Enter. |
|
|
117
|
+
| `clearOnSubmit` | `boolean` | `true` | Clear value after a successful submit. |
|
|
118
|
+
| `blockEmptySubmit` | `boolean` | `true` | Block empty/whitespace submits via the inline error (submit stays enabled). |
|
|
119
|
+
| `emptyMessage` | `string` | `"Please write something before submitting."` | Inline error shown when an empty submit is blocked. |
|
|
120
|
+
| `busy` | `boolean` | `false` | External busy state (disables box + submit). |
|
|
121
|
+
| `avatar` | `THC \| Snippet` | — | Optional gutter to the left of the box. |
|
|
122
|
+
| `footer` | `Snippet` | — | Extra footer content, left of the buttons. |
|
|
122
123
|
|
|
123
124
|
Plus the standard `InputWrap` layout props (`labelAfter`, `below`, `labelLeft`,
|
|
124
125
|
`labelLeftWidth`, `labelLeftBreakpoint`) and `class*` props (`classInput` →
|