@duskmoon-dev/el-markdown-input 1.1.2 → 1.1.3

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.
@@ -496,6 +496,31 @@ var elementStyles = css`
496
496
  gap: 0.5rem;
497
497
  }
498
498
 
499
+ .status-bar-start,
500
+ .status-bar-end {
501
+ display: flex;
502
+ align-items: center;
503
+ gap: 0.5rem;
504
+ }
505
+
506
+ /* Allow slotted light-DOM children to inherit font and alignment */
507
+ .status-bar ::slotted(*),
508
+ .status-bar-start ::slotted(*),
509
+ .status-bar-end ::slotted(*) {
510
+ font-size: inherit;
511
+ font-family: inherit;
512
+ vertical-align: middle;
513
+ }
514
+
515
+ /* When slot="bottom" is used, the slotted element fills the bar */
516
+ .status-bar > slot[name="bottom"]::slotted(*) {
517
+ display: flex;
518
+ align-items: center;
519
+ justify-content: space-between;
520
+ flex: 1;
521
+ gap: 0.5rem;
522
+ }
523
+
499
524
  .attach-btn {
500
525
  display: inline-flex;
501
526
  align-items: center;
@@ -530,7 +555,6 @@ var elementStyles = css`
530
555
  }
531
556
 
532
557
  .status-bar-count {
533
- margin-left: auto;
534
558
  white-space: nowrap;
535
559
  }
536
560
 
@@ -659,6 +683,52 @@ var elementStyles = css`
659
683
  white-space: nowrap;
660
684
  }
661
685
 
686
+ /* ── Attached files (local form mode) ────────────────────────────── */
687
+
688
+ .upload-attached-row {
689
+ display: flex;
690
+ align-items: center;
691
+ gap: 0.5rem;
692
+ padding: 0.375rem 0.75rem;
693
+ border-top: 1px solid var(--md-border);
694
+ font-size: 0.75rem;
695
+ color: var(--md-text-muted);
696
+ }
697
+
698
+ .upload-attached-size {
699
+ white-space: nowrap;
700
+ color: var(--md-text-muted);
701
+ }
702
+
703
+ .upload-remove-btn {
704
+ display: inline-flex;
705
+ align-items: center;
706
+ justify-content: center;
707
+ width: 1.25rem;
708
+ height: 1.25rem;
709
+ margin-left: auto;
710
+ border: none;
711
+ background: transparent;
712
+ color: var(--md-text-muted);
713
+ font-size: 0.85rem;
714
+ line-height: 1;
715
+ cursor: pointer;
716
+ border-radius: 50%;
717
+ transition:
718
+ color 150ms ease,
719
+ background 150ms ease;
720
+ }
721
+
722
+ .upload-remove-btn:hover {
723
+ color: var(--md-color-error);
724
+ background: var(--md-bg-hover);
725
+ }
726
+
727
+ .upload-remove-btn:focus-visible {
728
+ outline: 2px solid var(--md-accent);
729
+ outline-offset: 1px;
730
+ }
731
+
662
732
  /* ── Resizable editor ──────────────────────────────────────────────── */
663
733
  /* resize attribute mirrors the CSS resize property: vertical | horizontal | both */
664
734
  :host([resize='vertical']) .editor {
@@ -1003,6 +1073,13 @@ var coreMarkdownStyles = markdownBodyCSS.replace(/@layer\s+components\s*\{/, "")
1003
1073
  var markdownBodySheet = css2`
1004
1074
  ${coreMarkdownStyles}
1005
1075
  `;
1076
+ function formatFileSize(bytes) {
1077
+ if (bytes < 1024)
1078
+ return `${bytes} B`;
1079
+ if (bytes < 1024 * 1024)
1080
+ return `${(bytes / 1024).toFixed(1)} KB`;
1081
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1082
+ }
1006
1083
 
1007
1084
  class ElDmMarkdownInput extends BaseElement {
1008
1085
  static formAssociated = true;
@@ -1049,6 +1126,7 @@ class ElDmMarkdownInput extends BaseElement {
1049
1126
  #lastRenderedSource = null;
1050
1127
  #katexCssInjected = false;
1051
1128
  #uploadIdCounter = 0;
1129
+ #attachedFiles = [];
1052
1130
  constructor() {
1053
1131
  super();
1054
1132
  this.#internals = this.attachInternals();
@@ -1181,10 +1259,20 @@ class ElDmMarkdownInput extends BaseElement {
1181
1259
  ></div>
1182
1260
 
1183
1261
  <div class="status-bar">
1184
- <button class="attach-btn" type="button" aria-label="Attach files" ${disabled || readonly ? "disabled" : ""}>
1185
- &#128206; Attach files
1186
- </button>
1187
- <span class="status-bar-count" aria-live="polite"></span>
1262
+ <slot name="bottom">
1263
+ <div class="status-bar-start">
1264
+ <slot name="bottom-start">
1265
+ <button class="attach-btn" type="button" aria-label="Attach files" ${disabled || readonly ? "disabled" : ""}>
1266
+ &#128206; Attach files
1267
+ </button>
1268
+ </slot>
1269
+ </div>
1270
+ <div class="status-bar-end">
1271
+ <slot name="bottom-end">
1272
+ <span class="status-bar-count" aria-live="polite"></span>
1273
+ </slot>
1274
+ </div>
1275
+ </slot>
1188
1276
  <input
1189
1277
  type="file"
1190
1278
  class="file-input"
@@ -1471,15 +1559,28 @@ class ElDmMarkdownInput extends BaseElement {
1471
1559
  }, ms);
1472
1560
  }
1473
1561
  #syncFormValue() {
1474
- this.#internals?.setFormValue(this.#textarea?.value ?? "");
1562
+ const text = this.#textarea?.value ?? "";
1563
+ if (this.#attachedFiles.length === 0) {
1564
+ this.#internals?.setFormValue(text);
1565
+ return;
1566
+ }
1567
+ const name = this.name || "markdown";
1568
+ const fd = new FormData;
1569
+ fd.append(name, text);
1570
+ for (const f of this.#attachedFiles) {
1571
+ fd.append(`${name}_files`, f, f.name);
1572
+ }
1573
+ this.#internals?.setFormValue(fd);
1475
1574
  }
1476
1575
  #startUpload(file) {
1477
1576
  this.emit("upload-start", { file });
1478
1577
  const id = `upload-${++this.#uploadIdCounter}`;
1479
1578
  const uploadUrl = this.uploadUrl;
1480
1579
  if (!uploadUrl) {
1481
- this.emit("upload-error", { file, error: "no upload-url set" });
1482
- this.#showUploadError(file, "no upload-url set");
1580
+ this.#attachedFiles.push(file);
1581
+ this.#addAttachedRow(file, this.#attachedFiles.length - 1, id);
1582
+ this.#syncFormValue();
1583
+ this.emit("upload-done", { file, url: "", markdown: "" });
1483
1584
  return;
1484
1585
  }
1485
1586
  this.#addProgressRow(id, file.name);
@@ -1534,6 +1635,22 @@ class ElDmMarkdownInput extends BaseElement {
1534
1635
  this.#uploadList.appendChild(row);
1535
1636
  setTimeout(() => row.remove(), 4000);
1536
1637
  }
1638
+ #addAttachedRow(file, index, id) {
1639
+ if (!this.#uploadList)
1640
+ return;
1641
+ const row = document.createElement("div");
1642
+ row.className = "upload-attached-row";
1643
+ row.id = id;
1644
+ row.innerHTML = `
1645
+ <span class="upload-filename">${escapeHtmlStr(file.name)}</span>
1646
+ <span class="upload-attached-size">${formatFileSize(file.size)}</span>
1647
+ <button type="button" class="upload-remove-btn" data-attach-index="${index}" aria-label="Remove ${escapeHtmlStr(file.name)}">&#215;</button>
1648
+ `;
1649
+ row.querySelector(".upload-remove-btn").addEventListener("click", () => {
1650
+ this.removeFile(index);
1651
+ });
1652
+ this.#uploadList.appendChild(row);
1653
+ }
1537
1654
  #handleAutocompleteInput() {
1538
1655
  const ta = this.#textarea;
1539
1656
  if (!ta)
@@ -1739,6 +1856,25 @@ class ElDmMarkdownInput extends BaseElement {
1739
1856
  this.#acSelectedIndex = list.length > 0 ? 0 : -1;
1740
1857
  this.#updateDropdown();
1741
1858
  }
1859
+ getFiles() {
1860
+ return [...this.#attachedFiles];
1861
+ }
1862
+ removeFile(index) {
1863
+ if (index < 0 || index >= this.#attachedFiles.length)
1864
+ return;
1865
+ this.#attachedFiles.splice(index, 1);
1866
+ this.#rebuildAttachedRows();
1867
+ this.#syncFormValue();
1868
+ }
1869
+ #rebuildAttachedRows() {
1870
+ if (!this.#uploadList)
1871
+ return;
1872
+ this.#uploadList.querySelectorAll(".upload-attached-row").forEach((r) => r.remove());
1873
+ this.#attachedFiles.forEach((file, i) => {
1874
+ const id = `upload-${++this.#uploadIdCounter}`;
1875
+ this.#addAttachedRow(file, i, id);
1876
+ });
1877
+ }
1742
1878
  }
1743
1879
  function escapeHtmlStr(s) {
1744
1880
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -1749,5 +1885,5 @@ if (!customElements.get("el-dm-markdown-input")) {
1749
1885
  customElements.define("el-dm-markdown-input", ElDmMarkdownInput);
1750
1886
  }
1751
1887
 
1752
- //# debugId=DF019CF83D1B933264756E2164756E21
1888
+ //# debugId=D5D4120E3A9113D764756E2164756E21
1753
1889
  //# sourceMappingURL=register.js.map