@threadlabs/looma 0.26.24 → 0.26.26

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.
@@ -1760,6 +1760,7 @@ var LoomaEditor = defineComponent({
1760
1760
  const linkContextEditing = ref(false);
1761
1761
  const linkHref = ref("");
1762
1762
  const linkText = ref("");
1763
+ const linkTextEditable = ref(false);
1763
1764
  const linkNewTab = ref(true);
1764
1765
  const linkError = ref("");
1765
1766
  const linkMode = ref("url");
@@ -2129,6 +2130,8 @@ var LoomaEditor = defineComponent({
2129
2130
  linkResults.value = [];
2130
2131
  linkNewTab.value = false;
2131
2132
  linkError.value = "";
2133
+ if (linkTextEditable.value && !linkText.value.trim()) linkText.value = target.label;
2134
+ applyLink();
2132
2135
  };
2133
2136
  const moveLinkResultFocus = (event, offset) => {
2134
2137
  const buttons = [...linkFormRoot.value?.querySelectorAll("[data-link-result]") ?? []];
@@ -2157,6 +2160,7 @@ var LoomaEditor = defineComponent({
2157
2160
  to,
2158
2161
  existing
2159
2162
  };
2163
+ linkTextEditable.value = empty && !existing;
2160
2164
  const attrs = existing ? instance.getAttributes("link") : {};
2161
2165
  linkHref.value = typeof attrs.href === "string" ? attrs.href : "";
2162
2166
  linkNewTab.value = attrs.target !== "_self";
@@ -2183,36 +2187,40 @@ var LoomaEditor = defineComponent({
2183
2187
  (linkMode.value === "url" ? linkDestinationInput() : linkFormRoot.value?.querySelector("[data-link-search]"))?.focus();
2184
2188
  });
2185
2189
  };
2186
- const saveLink = () => {
2190
+ const applyLink = (reportError = false) => {
2187
2191
  const instance = editor.value;
2188
2192
  const selection = linkSelection;
2189
- if (!instance || !selection) return;
2193
+ if (!instance || !selection) return false;
2190
2194
  const href = linkMode.value === "target" ? validLinkHref(linkSelectedTarget.value?.href ?? "") : validLinkHref(linkHref.value);
2191
2195
  if (!href) {
2192
- linkError.value = linkMode.value === "target" ? "Choose a destination from the results." : "Enter an http, https, mailto, tel, or relative URL.";
2193
- (linkMode.value === "url" ? linkDestinationInput() : linkFormRoot.value?.querySelector("[data-link-search]"))?.focus();
2194
- return;
2196
+ if (reportError) linkError.value = linkMode.value === "target" ? "Choose a destination from the results." : "Enter an http, https, mailto, tel, or relative URL.";
2197
+ return false;
2195
2198
  }
2196
2199
  if (linkMode.value === "url" && props.linkSearch && !selection.existing && !/^(https?:|mailto:|tel:)/i.test(href)) {
2197
- linkError.value = "Enter a full website, email, or phone URL, or choose a destination.";
2198
- linkDestinationInput()?.focus();
2199
- return;
2200
+ if (reportError) linkError.value = "Enter a full website, email, or phone URL, or choose a destination.";
2201
+ return false;
2200
2202
  }
2201
2203
  const attrs = {
2202
2204
  href: props.linkBaseUrl ? siteRelativeHref(href, props.linkBaseUrl) : href,
2203
2205
  target: linkNewTab.value ? "_blank" : "_self",
2204
2206
  rel: linkNewTab.value ? "noopener noreferrer" : null
2205
2207
  };
2206
- const chain = instance.chain().focus().setTextSelection({
2208
+ const current = instance.state.selection;
2209
+ const chain = instance.chain().setTextSelection({
2207
2210
  from: selection.from,
2208
2211
  to: selection.to
2209
2212
  });
2210
- if (selection.from === selection.to && !selection.existing) {
2213
+ if (linkTextEditable.value) {
2211
2214
  const text = linkText.value.trim();
2212
2215
  if (!text) {
2213
- linkError.value = "Enter link text.";
2214
- return;
2216
+ if (reportError) linkError.value = "Enter link text.";
2217
+ return false;
2215
2218
  }
2219
+ linkSelection = {
2220
+ from: selection.from,
2221
+ to: selection.from + text.length,
2222
+ existing: true
2223
+ };
2216
2224
  chain.insertContent({
2217
2225
  type: "text",
2218
2226
  text,
@@ -2220,24 +2228,17 @@ var LoomaEditor = defineComponent({
2220
2228
  type: "link",
2221
2229
  attrs
2222
2230
  }]
2223
- }).run();
2231
+ }).setTextSelection(selection.from).run();
2224
2232
  } else {
2225
2233
  if (selection.existing && selection.from === selection.to) chain.extendMarkRange("link");
2226
- chain.setLink(attrs).setTextSelection(selection.to).run();
2234
+ chain.setLink(attrs).setTextSelection({
2235
+ from: current.from,
2236
+ to: current.to
2237
+ }).run();
2238
+ selection.existing = true;
2227
2239
  }
2228
- linkOpen.value = false;
2229
- linkContextEditing.value = false;
2230
- };
2231
- const removeLink = () => {
2232
- const instance = editor.value;
2233
- const selection = linkSelection;
2234
- if (!instance || !selection?.existing) return;
2235
- instance.chain().focus().setTextSelection({
2236
- from: selection.from,
2237
- to: selection.to
2238
- }).extendMarkRange("link").unsetLink().setTextSelection(selection.to).run();
2239
- linkOpen.value = false;
2240
- linkContextEditing.value = false;
2240
+ linkError.value = "";
2241
+ return true;
2241
2242
  };
2242
2243
  const removeCurrentLink = () => {
2243
2244
  const instance = editor.value;
@@ -2245,11 +2246,14 @@ var LoomaEditor = defineComponent({
2245
2246
  instance.chain().focus().extendMarkRange("link").unsetLink().run();
2246
2247
  linkContextEditing.value = false;
2247
2248
  };
2248
- const cancelLink = () => {
2249
+ const dismissLink = (restoreFocus = true) => {
2249
2250
  linkOpen.value = false;
2250
2251
  linkContextEditing.value = false;
2251
2252
  const selection = linkSelection;
2252
- if (selection) editor.value?.chain().focus().setTextSelection(selection.to).run();
2253
+ if (restoreFocus && selection) editor.value?.chain().focus().setTextSelection(selection.to).run();
2254
+ dismissedFormattingSelection = editor.value?.state.selection ?? null;
2255
+ linkPopup?.hide();
2256
+ formattingPopup?.hide();
2253
2257
  };
2254
2258
  const renderLinkDestination = (mode) => [h(FormField, { invalid: Boolean(linkError.value) }, {
2255
2259
  label: () => h("label", props.linkSearch ? "Link destination" : "URL"),
@@ -2278,6 +2282,7 @@ var LoomaEditor = defineComponent({
2278
2282
  }
2279
2283
  linkHref.value = value;
2280
2284
  linkError.value = "";
2285
+ applyLink();
2281
2286
  },
2282
2287
  onKeydown: (event) => {
2283
2288
  if (mode === "target" && event.key === "ArrowDown") moveLinkResultFocus(event, 1);
@@ -2351,6 +2356,7 @@ var LoomaEditor = defineComponent({
2351
2356
  checked: linkNewTab.value,
2352
2357
  onChange: (event) => {
2353
2358
  linkNewTab.value = event.detail.checked;
2359
+ applyLink();
2354
2360
  }
2355
2361
  }, () => "Open in new tab")];
2356
2362
  const renderLinkForm = () => h("form", {
@@ -2359,7 +2365,18 @@ var LoomaEditor = defineComponent({
2359
2365
  "aria-label": "Edit link",
2360
2366
  onSubmit: (event) => {
2361
2367
  event.preventDefault();
2362
- saveLink();
2368
+ if (applyLink(true)) dismissLink();
2369
+ },
2370
+ onKeydown: (event) => {
2371
+ if (event.key === "Escape") {
2372
+ event.preventDefault();
2373
+ event.stopPropagation();
2374
+ dismissLink();
2375
+ } else if (event.key === "Enter" && event.target instanceof HTMLInputElement) {
2376
+ event.preventDefault();
2377
+ event.stopPropagation();
2378
+ if (applyLink(true)) dismissLink();
2379
+ }
2363
2380
  }
2364
2381
  }, [h(Stack, { gap: "s" }, () => [
2365
2382
  h(Text, {
@@ -2368,7 +2385,7 @@ var LoomaEditor = defineComponent({
2368
2385
  weight: "semibold"
2369
2386
  }, () => linkSelection?.existing ? "Edit link" : "Add link"),
2370
2387
  h(Stack, { gap: "s" }, () => renderLinkDestination(linkMode.value)),
2371
- linkSelection?.from === linkSelection?.to && !linkSelection?.existing ? h(FormField, {}, {
2388
+ linkTextEditable.value ? h(FormField, {}, {
2372
2389
  label: () => h("label", "Text"),
2373
2390
  default: () => h(Input, {
2374
2391
  size: "sm",
@@ -2376,6 +2393,7 @@ var LoomaEditor = defineComponent({
2376
2393
  onInput: (event) => {
2377
2394
  linkText.value = event.target.value;
2378
2395
  linkError.value = "";
2396
+ applyLink();
2379
2397
  }
2380
2398
  })
2381
2399
  }) : null,
@@ -2383,42 +2401,7 @@ var LoomaEditor = defineComponent({
2383
2401
  size: "sm",
2384
2402
  tone: "danger",
2385
2403
  role: "alert"
2386
- }, () => linkError.value) : null,
2387
- h(Separator),
2388
- h(Cluster, {
2389
- gap: "xs",
2390
- justify: "between",
2391
- wrap: "nowrap"
2392
- }, () => [linkSelection?.existing ? h(Button, {
2393
- type: "button",
2394
- variant: "ghost",
2395
- tone: "danger",
2396
- size: "sm",
2397
- onClick: removeLink
2398
- }, () => "Remove link") : null, validLinkHref(linkHref.value) ? h(Button, {
2399
- as: "a",
2400
- variant: "outline",
2401
- tone: "accent",
2402
- size: "sm",
2403
- href: validLinkHref(linkHref.value),
2404
- target: "_blank",
2405
- rel: "noopener noreferrer"
2406
- }, () => "Open link") : null]),
2407
- h(Cluster, {
2408
- gap: "xs",
2409
- justify: "end",
2410
- wrap: "nowrap"
2411
- }, () => [h(Button, {
2412
- type: "button",
2413
- variant: "outline",
2414
- tone: "accent",
2415
- size: "sm",
2416
- onClick: cancelLink
2417
- }, () => "Cancel"), h(Button, {
2418
- type: "submit",
2419
- variant: "solid",
2420
- size: "sm"
2421
- }, () => "Save link")])
2404
+ }, () => linkError.value) : null
2422
2405
  ])]);
2423
2406
  const renderLinkContext = (instance) => {
2424
2407
  if (linkContextEditing.value) return renderLinkForm();
@@ -2790,6 +2773,7 @@ var LoomaEditor = defineComponent({
2790
2773
  const onDocumentPointerDown = (event) => {
2791
2774
  const path = typeof event.composedPath === "function" ? event.composedPath() : [];
2792
2775
  const inElement = (element) => Boolean(element && (path.includes(element) || event.target instanceof Node && element.contains(event.target)));
2776
+ if ((linkOpen.value || linkContextEditing.value) && !inElement(linkFormRoot.value)) dismissLink(false);
2793
2777
  const inTableUi = inElement(tableToolbarShell.value) || inElement(tableOverlayShell.value) || inElement(tableMenuShell.value) || inElement(mobileToolbarShell.value);
2794
2778
  tableInteractionActive = inTableUi;
2795
2779
  if (inTableUi) setTimeout(() => {