@bizapps/design-system-testing 1.7.2 → 1.8.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.
package/dist/editor.d.cts CHANGED
@@ -164,6 +164,36 @@ interface InsertImageDialogProps {
164
164
  */
165
165
  declare function InsertImageDialog({ open, onOpenChange, labels, url, onUrlChange, alt, onAltChange, file, onFileChange, uploading, onApply, onCancel, removable, onRemove, maxSizeBytes, accept, }: InsertImageDialogProps): react.JSX.Element;
166
166
 
167
+ interface LinkBubbleMenuLabels {
168
+ open: string;
169
+ edit: string;
170
+ remove: string;
171
+ }
172
+ interface LinkBubbleMenuProps {
173
+ editor: Editor | null;
174
+ labels: LinkBubbleMenuLabels;
175
+ /** Opens the consumer's link dialog, pre-filled with the current href. */
176
+ onEdit: () => void;
177
+ /**
178
+ * Follows the link. Left to the consumer for the cases this package cannot
179
+ * judge — routing instead of navigating, or confirming first.
180
+ *
181
+ * Defaults to opening a new tab, which is also what Ctrl/Cmd-click does, so
182
+ * the author never loses unsaved work just by checking a URL.
183
+ */
184
+ onOpen?: (href: string) => void;
185
+ }
186
+ /**
187
+ * A floating bar that appears when the caret sits inside a link, offering to
188
+ * open, edit or unlink it.
189
+ *
190
+ * It exists because an editor cannot let a plain click navigate: clicking is how
191
+ * you place the caret to fix a typo, so `openOnClick` stays false and this gives
192
+ * the author a deliberate way out to the URL. Ctrl/Cmd-click is wired separately,
193
+ * on the editor itself, for people who expect the shortcut.
194
+ */
195
+ declare function LinkBubbleMenu({ editor, labels, onEdit, onOpen }: LinkBubbleMenuProps): react.JSX.Element | null;
196
+
167
197
  /**
168
198
  * Whatever `useEditor` accepts in its `extensions` array.
169
199
  *
@@ -190,6 +220,8 @@ interface RichTextEditorLabels {
190
220
  bullet: string;
191
221
  ordered: string;
192
222
  };
223
+ /** The floating bar shown while the caret sits inside a link. */
224
+ link: LinkBubbleMenuLabels;
193
225
  table: TableMenuLabels;
194
226
  highlight: string;
195
227
  }
@@ -217,6 +249,14 @@ interface UseRichTextEditorOptions {
217
249
  after?: string;
218
250
  items: ToolbarItem[];
219
251
  }[];
252
+ /**
253
+ * Replaces a built-in entry, keyed by its id.
254
+ *
255
+ * For when a product needs a richer version of something the preset already
256
+ * provides — swapping the single highlight button for a palette of shades,
257
+ * say. Replacing rather than adding keeps the toolbar from carrying both.
258
+ */
259
+ replaceToolbar?: Record<string, ToolbarItem>;
220
260
  /** Merged on top of the built-in content styles. */
221
261
  contentClassName?: string;
222
262
  /** Allows data: URIs in images. Off by default: uploads belong in storage. */
@@ -233,6 +273,14 @@ interface UseRichTextEditorOptions {
233
273
  }
234
274
  interface UseRichTextEditorResult {
235
275
  editor: Editor | null;
276
+ /**
277
+ * The floating link bar, ready to render beside `<RichTextEditor />`.
278
+ *
279
+ * Returned as an element rather than left to the consumer because it is easy
280
+ * to forget, and an editor without it gives the author no way to reach or
281
+ * remove a link they have already inserted.
282
+ */
283
+ linkMenu: ReactNode;
236
284
  /** Ready for `<RichTextEditor items={...} />`. */
237
285
  toolbar: ToolbarItem[];
238
286
  /** True when the caret has no selection, so a new link needs its own text. */
@@ -254,7 +302,7 @@ interface UseRichTextEditorResult {
254
302
  * What stays with the consumer is what only it can know: the marks its document
255
303
  * contract allows, and the copy. Both arrive as options.
256
304
  */
257
- declare function useRichTextEditor({ labels, onUpdate, content, onInsertLink, onInsertImage, extraExtensions, extraToolbar, contentClassName, allowBase64Images, editorProps, }: UseRichTextEditorOptions): UseRichTextEditorResult;
305
+ declare function useRichTextEditor({ labels, onUpdate, content, onInsertLink, onInsertImage, extraExtensions, extraToolbar, replaceToolbar, contentClassName, allowBase64Images, editorProps, }: UseRichTextEditorOptions): UseRichTextEditorResult;
258
306
 
259
307
  /**
260
308
  * One entry in the palette.
@@ -363,36 +411,6 @@ interface InsertLinkDialogProps {
363
411
  */
364
412
  declare function InsertLinkDialog({ open, onOpenChange, labels, url, onUrlChange, text, onTextChange, needsText, onApply, onCancel, }: InsertLinkDialogProps): react.JSX.Element;
365
413
 
366
- interface LinkBubbleMenuLabels {
367
- open: string;
368
- edit: string;
369
- remove: string;
370
- }
371
- interface LinkBubbleMenuProps {
372
- editor: Editor | null;
373
- labels: LinkBubbleMenuLabels;
374
- /** Opens the consumer's link dialog, pre-filled with the current href. */
375
- onEdit: () => void;
376
- /**
377
- * Follows the link. Left to the consumer for the cases this package cannot
378
- * judge — routing instead of navigating, or confirming first.
379
- *
380
- * Defaults to opening a new tab, which is also what Ctrl/Cmd-click does, so
381
- * the author never loses unsaved work just by checking a URL.
382
- */
383
- onOpen?: (href: string) => void;
384
- }
385
- /**
386
- * A floating bar that appears when the caret sits inside a link, offering to
387
- * open, edit or unlink it.
388
- *
389
- * It exists because an editor cannot let a plain click navigate: clicking is how
390
- * you place the caret to fix a typo, so `openOnClick` stays false and this gives
391
- * the author a deliberate way out to the URL. Ctrl/Cmd-click is wired separately,
392
- * on the editor itself, for people who expect the shortcut.
393
- */
394
- declare function LinkBubbleMenu({ editor, labels, onEdit, onOpen }: LinkBubbleMenuProps): react.JSX.Element | null;
395
-
396
414
  interface ResizableImageOptions {
397
415
  /** Already translated: names the resize handle for assistive tech. */
398
416
  resizeLabel: string;
package/dist/editor.d.ts CHANGED
@@ -164,6 +164,36 @@ interface InsertImageDialogProps {
164
164
  */
165
165
  declare function InsertImageDialog({ open, onOpenChange, labels, url, onUrlChange, alt, onAltChange, file, onFileChange, uploading, onApply, onCancel, removable, onRemove, maxSizeBytes, accept, }: InsertImageDialogProps): react.JSX.Element;
166
166
 
167
+ interface LinkBubbleMenuLabels {
168
+ open: string;
169
+ edit: string;
170
+ remove: string;
171
+ }
172
+ interface LinkBubbleMenuProps {
173
+ editor: Editor | null;
174
+ labels: LinkBubbleMenuLabels;
175
+ /** Opens the consumer's link dialog, pre-filled with the current href. */
176
+ onEdit: () => void;
177
+ /**
178
+ * Follows the link. Left to the consumer for the cases this package cannot
179
+ * judge — routing instead of navigating, or confirming first.
180
+ *
181
+ * Defaults to opening a new tab, which is also what Ctrl/Cmd-click does, so
182
+ * the author never loses unsaved work just by checking a URL.
183
+ */
184
+ onOpen?: (href: string) => void;
185
+ }
186
+ /**
187
+ * A floating bar that appears when the caret sits inside a link, offering to
188
+ * open, edit or unlink it.
189
+ *
190
+ * It exists because an editor cannot let a plain click navigate: clicking is how
191
+ * you place the caret to fix a typo, so `openOnClick` stays false and this gives
192
+ * the author a deliberate way out to the URL. Ctrl/Cmd-click is wired separately,
193
+ * on the editor itself, for people who expect the shortcut.
194
+ */
195
+ declare function LinkBubbleMenu({ editor, labels, onEdit, onOpen }: LinkBubbleMenuProps): react.JSX.Element | null;
196
+
167
197
  /**
168
198
  * Whatever `useEditor` accepts in its `extensions` array.
169
199
  *
@@ -190,6 +220,8 @@ interface RichTextEditorLabels {
190
220
  bullet: string;
191
221
  ordered: string;
192
222
  };
223
+ /** The floating bar shown while the caret sits inside a link. */
224
+ link: LinkBubbleMenuLabels;
193
225
  table: TableMenuLabels;
194
226
  highlight: string;
195
227
  }
@@ -217,6 +249,14 @@ interface UseRichTextEditorOptions {
217
249
  after?: string;
218
250
  items: ToolbarItem[];
219
251
  }[];
252
+ /**
253
+ * Replaces a built-in entry, keyed by its id.
254
+ *
255
+ * For when a product needs a richer version of something the preset already
256
+ * provides — swapping the single highlight button for a palette of shades,
257
+ * say. Replacing rather than adding keeps the toolbar from carrying both.
258
+ */
259
+ replaceToolbar?: Record<string, ToolbarItem>;
220
260
  /** Merged on top of the built-in content styles. */
221
261
  contentClassName?: string;
222
262
  /** Allows data: URIs in images. Off by default: uploads belong in storage. */
@@ -233,6 +273,14 @@ interface UseRichTextEditorOptions {
233
273
  }
234
274
  interface UseRichTextEditorResult {
235
275
  editor: Editor | null;
276
+ /**
277
+ * The floating link bar, ready to render beside `<RichTextEditor />`.
278
+ *
279
+ * Returned as an element rather than left to the consumer because it is easy
280
+ * to forget, and an editor without it gives the author no way to reach or
281
+ * remove a link they have already inserted.
282
+ */
283
+ linkMenu: ReactNode;
236
284
  /** Ready for `<RichTextEditor items={...} />`. */
237
285
  toolbar: ToolbarItem[];
238
286
  /** True when the caret has no selection, so a new link needs its own text. */
@@ -254,7 +302,7 @@ interface UseRichTextEditorResult {
254
302
  * What stays with the consumer is what only it can know: the marks its document
255
303
  * contract allows, and the copy. Both arrive as options.
256
304
  */
257
- declare function useRichTextEditor({ labels, onUpdate, content, onInsertLink, onInsertImage, extraExtensions, extraToolbar, contentClassName, allowBase64Images, editorProps, }: UseRichTextEditorOptions): UseRichTextEditorResult;
305
+ declare function useRichTextEditor({ labels, onUpdate, content, onInsertLink, onInsertImage, extraExtensions, extraToolbar, replaceToolbar, contentClassName, allowBase64Images, editorProps, }: UseRichTextEditorOptions): UseRichTextEditorResult;
258
306
 
259
307
  /**
260
308
  * One entry in the palette.
@@ -363,36 +411,6 @@ interface InsertLinkDialogProps {
363
411
  */
364
412
  declare function InsertLinkDialog({ open, onOpenChange, labels, url, onUrlChange, text, onTextChange, needsText, onApply, onCancel, }: InsertLinkDialogProps): react.JSX.Element;
365
413
 
366
- interface LinkBubbleMenuLabels {
367
- open: string;
368
- edit: string;
369
- remove: string;
370
- }
371
- interface LinkBubbleMenuProps {
372
- editor: Editor | null;
373
- labels: LinkBubbleMenuLabels;
374
- /** Opens the consumer's link dialog, pre-filled with the current href. */
375
- onEdit: () => void;
376
- /**
377
- * Follows the link. Left to the consumer for the cases this package cannot
378
- * judge — routing instead of navigating, or confirming first.
379
- *
380
- * Defaults to opening a new tab, which is also what Ctrl/Cmd-click does, so
381
- * the author never loses unsaved work just by checking a URL.
382
- */
383
- onOpen?: (href: string) => void;
384
- }
385
- /**
386
- * A floating bar that appears when the caret sits inside a link, offering to
387
- * open, edit or unlink it.
388
- *
389
- * It exists because an editor cannot let a plain click navigate: clicking is how
390
- * you place the caret to fix a typo, so `openOnClick` stays false and this gives
391
- * the author a deliberate way out to the URL. Ctrl/Cmd-click is wired separately,
392
- * on the editor itself, for people who expect the shortcut.
393
- */
394
- declare function LinkBubbleMenu({ editor, labels, onEdit, onOpen }: LinkBubbleMenuProps): react.JSX.Element | null;
395
-
396
414
  interface ResizableImageOptions {
397
415
  /** Already translated: names the resize handle for assistive tech. */
398
416
  resizeLabel: string;
package/dist/editor.js CHANGED
@@ -974,6 +974,78 @@ function createResizableImage(options) {
974
974
  }
975
975
  });
976
976
  }
977
+ function LinkActions({ editor, labels, onEdit, onOpen }) {
978
+ const href = useEditorState({
979
+ editor,
980
+ selector: ({ editor: current }) => current.getAttributes("link").href ?? ""
981
+ });
982
+ return /* @__PURE__ */ jsxs(
983
+ "div",
984
+ {
985
+ "data-slot": "link-bubble-menu",
986
+ className: "bg-popover text-popover-foreground flex items-center gap-1 rounded-md border p-1 shadow-md",
987
+ children: [
988
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground max-w-[16rem] truncate px-2 text-xs", children: href }),
989
+ /* @__PURE__ */ jsx(
990
+ Button,
991
+ {
992
+ type: "button",
993
+ size: "sm",
994
+ variant: "ghost",
995
+ title: labels.open,
996
+ "aria-label": labels.open,
997
+ disabled: !href,
998
+ onClick: () => {
999
+ if (!href) return;
1000
+ if (onOpen) {
1001
+ onOpen(href);
1002
+ return;
1003
+ }
1004
+ window.open(href, "_blank", "noopener,noreferrer");
1005
+ },
1006
+ children: /* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" })
1007
+ }
1008
+ ),
1009
+ /* @__PURE__ */ jsx(
1010
+ Button,
1011
+ {
1012
+ type: "button",
1013
+ size: "sm",
1014
+ variant: "ghost",
1015
+ title: labels.edit,
1016
+ "aria-label": labels.edit,
1017
+ onClick: onEdit,
1018
+ children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
1019
+ }
1020
+ ),
1021
+ /* @__PURE__ */ jsx(
1022
+ Button,
1023
+ {
1024
+ type: "button",
1025
+ size: "sm",
1026
+ variant: "ghost",
1027
+ className: "text-destructive",
1028
+ title: labels.remove,
1029
+ "aria-label": labels.remove,
1030
+ onClick: () => editor.chain().focus().extendMarkRange("link").unsetLink().run(),
1031
+ children: /* @__PURE__ */ jsx(Unlink, { className: "h-4 w-4" })
1032
+ }
1033
+ )
1034
+ ]
1035
+ }
1036
+ );
1037
+ }
1038
+ function LinkBubbleMenu({ editor, labels, onEdit, onOpen }) {
1039
+ const shouldShow = useCallback(
1040
+ // Only while the caret is actually in a link — otherwise this bar would
1041
+ // compete with any other bubble menu the consumer adds.
1042
+ ({ editor: current }) => current.isActive("link"),
1043
+ []
1044
+ );
1045
+ const options = useMemo(() => ({ placement: "bottom", offset: 8 }), []);
1046
+ if (!editor) return null;
1047
+ return /* @__PURE__ */ jsx(BubbleMenu, { editor, shouldShow, options, children: /* @__PURE__ */ jsx(LinkActions, { editor, labels, onEdit, onOpen }) });
1048
+ }
977
1049
  function ListMenu({ editor, triggerLabel, icon, options }) {
978
1050
  const active = options.find((option) => editor?.isActive(option.node));
979
1051
  return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
@@ -1026,6 +1098,7 @@ function useRichTextEditor({
1026
1098
  onInsertImage,
1027
1099
  extraExtensions = [],
1028
1100
  extraToolbar = [],
1101
+ replaceToolbar,
1029
1102
  contentClassName,
1030
1103
  allowBase64Images = false,
1031
1104
  editorProps
@@ -1221,7 +1294,9 @@ function useRichTextEditor({
1221
1294
  )
1222
1295
  }
1223
1296
  ];
1224
- let items = builtIn;
1297
+ let items = replaceToolbar ? builtIn.map(
1298
+ (item) => item.kind !== "separator" && replaceToolbar[item.id] ? replaceToolbar[item.id] : item
1299
+ ) : builtIn;
1225
1300
  const appended = [];
1226
1301
  for (const group of extraToolbar) {
1227
1302
  if (!group.after) {
@@ -1238,8 +1313,9 @@ function useRichTextEditor({
1238
1313
  items = [...items.slice(0, at + 1), ...group.items, ...items.slice(at + 1)];
1239
1314
  }
1240
1315
  return [...items, ...appended];
1241
- }, [labels, extraToolbar, onInsertImage, openLink]);
1242
- return { editor, toolbar, linkNeedsText, currentLinkHref, applyLink };
1316
+ }, [labels, extraToolbar, replaceToolbar, onInsertImage, openLink]);
1317
+ const linkMenu = /* @__PURE__ */ jsx(LinkBubbleMenu, { editor, labels: labels.link, onEdit: openLink });
1318
+ return { editor, toolbar, linkMenu, linkNeedsText, currentLinkHref, applyLink };
1243
1319
  }
1244
1320
  function ColorMenu({ editor, labels, icon, swatches }) {
1245
1321
  const active = swatches.find((swatch) => editor?.isActive(swatch.mark));
@@ -1379,78 +1455,6 @@ function InsertLinkDialog({
1379
1455
  ] })
1380
1456
  ] }) });
1381
1457
  }
1382
- function LinkActions({ editor, labels, onEdit, onOpen }) {
1383
- const href = useEditorState({
1384
- editor,
1385
- selector: ({ editor: current }) => current.getAttributes("link").href ?? ""
1386
- });
1387
- return /* @__PURE__ */ jsxs(
1388
- "div",
1389
- {
1390
- "data-slot": "link-bubble-menu",
1391
- className: "bg-popover text-popover-foreground flex items-center gap-1 rounded-md border p-1 shadow-md",
1392
- children: [
1393
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground max-w-[16rem] truncate px-2 text-xs", children: href }),
1394
- /* @__PURE__ */ jsx(
1395
- Button,
1396
- {
1397
- type: "button",
1398
- size: "sm",
1399
- variant: "ghost",
1400
- title: labels.open,
1401
- "aria-label": labels.open,
1402
- disabled: !href,
1403
- onClick: () => {
1404
- if (!href) return;
1405
- if (onOpen) {
1406
- onOpen(href);
1407
- return;
1408
- }
1409
- window.open(href, "_blank", "noopener,noreferrer");
1410
- },
1411
- children: /* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" })
1412
- }
1413
- ),
1414
- /* @__PURE__ */ jsx(
1415
- Button,
1416
- {
1417
- type: "button",
1418
- size: "sm",
1419
- variant: "ghost",
1420
- title: labels.edit,
1421
- "aria-label": labels.edit,
1422
- onClick: onEdit,
1423
- children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
1424
- }
1425
- ),
1426
- /* @__PURE__ */ jsx(
1427
- Button,
1428
- {
1429
- type: "button",
1430
- size: "sm",
1431
- variant: "ghost",
1432
- className: "text-destructive",
1433
- title: labels.remove,
1434
- "aria-label": labels.remove,
1435
- onClick: () => editor.chain().focus().extendMarkRange("link").unsetLink().run(),
1436
- children: /* @__PURE__ */ jsx(Unlink, { className: "h-4 w-4" })
1437
- }
1438
- )
1439
- ]
1440
- }
1441
- );
1442
- }
1443
- function LinkBubbleMenu({ editor, labels, onEdit, onOpen }) {
1444
- const shouldShow = useCallback(
1445
- // Only while the caret is actually in a link — otherwise this bar would
1446
- // compete with any other bubble menu the consumer adds.
1447
- ({ editor: current }) => current.isActive("link"),
1448
- []
1449
- );
1450
- const options = useMemo(() => ({ placement: "bottom", offset: 8 }), []);
1451
- if (!editor) return null;
1452
- return /* @__PURE__ */ jsx(BubbleMenu, { editor, shouldShow, options, children: /* @__PURE__ */ jsx(LinkActions, { editor, labels, onEdit, onOpen }) });
1453
- }
1454
1458
 
1455
1459
  export { ColorMenu, EDITOR_CONTENT_STYLE, InsertImageDialog, InsertLinkDialog, LinkBubbleMenu, ListMenu, RichTextEditor, RichTextToolbar, TableMenu, createResizableImage, useRichTextEditor };
1456
1460
  //# sourceMappingURL=editor.js.map