@shadow-garden/bapbong-ui 0.4.0 → 0.6.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/index.cjs CHANGED
@@ -21,13 +21,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Dialog: () => Dialog,
24
+ cmToPx: () => cmToPx,
24
25
  createFindDialog: () => createFindDialog,
25
26
  defaultMenus: () => defaultMenus,
26
27
  defaultToolbarGroups: () => defaultToolbarGroups,
28
+ marginPresetPicker: () => marginPresetPicker,
27
29
  mountMenubar: () => mountMenubar,
28
30
  mountToolbar: () => mountToolbar,
29
31
  openCellProperties: () => openCellProperties,
32
+ openMarginsDialog: () => openMarginsDialog,
33
+ openPageSizeDialog: () => openPageSizeDialog,
34
+ orientationPicker: () => orientationPicker,
35
+ pageSizePicker: () => pageSizePicker,
30
36
  promptDialog: () => promptDialog,
37
+ pxToCm: () => pxToCm,
31
38
  showContextMenu: () => showContextMenu,
32
39
  showLinkPanel: () => showLinkPanel,
33
40
  tableGridPicker: () => tableGridPicker
@@ -670,7 +677,9 @@ var STYLE3 = `
670
677
  `;
671
678
  function defaultMenus(commands) {
672
679
  const names = [...commands].map((c) => c.name);
673
- const marks = ["bold", "italic", "underline", "strike"].filter((n) => commands.has(n));
680
+ const marks = ["bold", "italic", "underline", "strike"].filter(
681
+ (n) => commands.has(n)
682
+ );
674
683
  const aligns = names.filter((n) => n.startsWith("align-"));
675
684
  const entries = [
676
685
  ...marks.map((command) => ({ command })),
@@ -709,7 +718,8 @@ function mountMenubar(host, editor, options = {}) {
709
718
  p.title.setAttribute("aria-expanded", "true");
710
719
  openIdx = idx;
711
720
  if (latest) refresh(latest);
712
- if (focusFirst) p.dropdown.querySelector(".bb-menu-item")?.focus();
721
+ if (focusFirst)
722
+ p.dropdown.querySelector(".bb-menu-item")?.focus();
713
723
  };
714
724
  const makeRow = (label, opts) => {
715
725
  const item = document.createElement("button");
@@ -753,7 +763,9 @@ function mountMenubar(host, editor, options = {}) {
753
763
  flyout.setAttribute("role", "menu");
754
764
  if ("widget" in entry) {
755
765
  flyout.classList.add("bb-submenu-widget");
756
- flyout.appendChild(entry.widget(() => close()));
766
+ const build = () => flyout.replaceChildren(entry.widget(() => close()));
767
+ wrap.addEventListener("mouseenter", build);
768
+ wrap.addEventListener("focusin", build);
757
769
  } else {
758
770
  buildEntries(entry.submenu, flyout);
759
771
  }
@@ -762,25 +774,37 @@ function mountMenubar(host, editor, options = {}) {
762
774
  } else if ("command" in entry) {
763
775
  const cmd = editor.commands.get(entry.command);
764
776
  if (!cmd) continue;
765
- const { item, check } = makeRow(entry.label ?? labels[entry.command] ?? entry.command, {});
777
+ const { item, check } = makeRow(
778
+ entry.label ?? labels[entry.command] ?? entry.command,
779
+ {}
780
+ );
766
781
  item.setAttribute("role", "menuitemcheckbox");
767
782
  item.addEventListener("click", () => {
768
- if (latest) editor.commands.get(entry.command)?.run(latest, (tr) => editor.dispatch(tr));
783
+ if (latest)
784
+ editor.commands.get(entry.command)?.run(latest, (tr) => editor.dispatch(tr));
769
785
  close();
770
786
  editor.focus();
771
787
  });
772
788
  checks.push({ el: check, active: (s) => cmd.isActive?.(s) ?? false });
773
- if (cmd.isEnabled) enables.push({ el: item, enabled: (s) => cmd.isEnabled(s) });
789
+ if (cmd.isEnabled)
790
+ enables.push({ el: item, enabled: (s) => cmd.isEnabled(s) });
774
791
  container.appendChild(item);
775
792
  } else {
776
- const { item, check } = makeRow(entry.label, { shortcut: entry.shortcut });
777
- item.setAttribute("role", entry.isActive ? "menuitemcheckbox" : "menuitem");
793
+ const { item, check } = makeRow(entry.label, {
794
+ shortcut: entry.shortcut
795
+ });
796
+ item.setAttribute(
797
+ "role",
798
+ entry.isActive ? "menuitemcheckbox" : "menuitem"
799
+ );
778
800
  item.addEventListener("click", () => {
779
801
  entry.run();
780
802
  close();
781
803
  });
782
- if (entry.isActive) checks.push({ el: check, active: () => entry.isActive() });
783
- if (entry.isEnabled) enables.push({ el: item, enabled: () => entry.isEnabled() });
804
+ if (entry.isActive)
805
+ checks.push({ el: check, active: () => entry.isActive() });
806
+ if (entry.isEnabled)
807
+ enables.push({ el: item, enabled: () => entry.isEnabled() });
784
808
  container.appendChild(item);
785
809
  }
786
810
  }
@@ -800,7 +824,10 @@ function mountMenubar(host, editor, options = {}) {
800
824
  dropdown.setAttribute("role", "menu");
801
825
  dropdown.hidden = true;
802
826
  title.addEventListener("mousedown", (e) => e.preventDefault());
803
- title.addEventListener("click", () => openIdx === idx ? close() : open(idx));
827
+ title.addEventListener(
828
+ "click",
829
+ () => openIdx === idx ? close() : open(idx)
830
+ );
804
831
  title.addEventListener("mouseenter", () => {
805
832
  if (openIdx >= 0 && openIdx !== idx) open(idx);
806
833
  });
@@ -1556,16 +1583,335 @@ function tableGridPicker(options) {
1556
1583
  root.append(cellsEl, label);
1557
1584
  return root;
1558
1585
  }
1586
+
1587
+ // packages/ui/src/lib/page-setup.ts
1588
+ var PX_PER_CM = 96 / 2.54;
1589
+ var pxToCm = (px) => px / PX_PER_CM;
1590
+ var cmToPx = (cm) => Math.round(cm * PX_PER_CM);
1591
+ function fmtCm(cm) {
1592
+ return `${Number(cm.toFixed(3))} cm`;
1593
+ }
1594
+ var STYLE9 = `
1595
+ .bb-ps{display:flex;flex-direction:column;min-width:250px;max-height:min(70vh,460px);overflow-y:auto}
1596
+ .bb-ps-row{display:flex;align-items:center;gap:11px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
1597
+ .bb-ps-row:hover,.bb-ps-row:focus{background:var(--bb-ui-hover,#f1efe8);outline:none}
1598
+ .bb-ps-row[aria-checked="true"]{background:var(--bb-ui-active-bg,#e6f1fb)}
1599
+ .bb-ps-icon{flex:none;width:34px;height:42px;display:block}
1600
+ .bb-ps-text{flex:1 1 auto;min-width:0}
1601
+ .bb-ps-name{font-size:13px;font-weight:600;line-height:1.3}
1602
+ .bb-ps-dims{font-size:11.5px;opacity:.68;line-height:1.45;white-space:nowrap}
1603
+ .bb-ps-grid{display:grid;grid-template-columns:auto auto;gap:0 14px;justify-content:start}
1604
+ .bb-ps-sep{height:1px;margin:4px 6px;background:var(--bb-ui-border,#e3e3e0)}
1605
+ .bb-pd{display:flex;flex-direction:column;gap:14px;min-width:300px}
1606
+ .bb-pd-fields{display:grid;gap:10px 18px}
1607
+ .bb-pd-field{display:flex;align-items:center;gap:8px}
1608
+ .bb-pd-label{flex:0 0 74px;font-size:13px}
1609
+ .bb-pd-input{flex:1 1 auto;min-width:0;width:100%;height:30px;padding:0 8px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px}
1610
+ .bb-pd-input:focus{outline:2px solid var(--bb-ui-active-border,#7fb2ec);outline-offset:-1px}
1611
+ .bb-pd-actions{display:flex;justify-content:flex-end;gap:8px}
1612
+ `;
1613
+ var SVG_NS = "http://www.w3.org/2000/svg";
1614
+ function svgEl(name, attrs) {
1615
+ const el = document.createElementNS(SVG_NS, name);
1616
+ for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, String(v));
1617
+ return el;
1618
+ }
1619
+ function pagePreview(ratio) {
1620
+ const svg = svgEl("svg", { viewBox: "0 0 34 42", class: "bb-ps-icon" });
1621
+ const maxW = 26;
1622
+ const maxH = 36;
1623
+ const w = Math.min(maxW, maxH * ratio);
1624
+ const h = w / ratio;
1625
+ svg.appendChild(
1626
+ svgEl("rect", {
1627
+ x: (34 - w) / 2,
1628
+ y: (42 - h) / 2,
1629
+ width: w,
1630
+ height: h,
1631
+ rx: 1,
1632
+ fill: "var(--bb-ui-bg,#fff)",
1633
+ stroke: "var(--bb-ui-fg,#2c2c2a)",
1634
+ "stroke-opacity": 0.45,
1635
+ "stroke-width": 1
1636
+ })
1637
+ );
1638
+ return svg;
1639
+ }
1640
+ function marginPreview(margin, page) {
1641
+ const svg = pagePreview(page.width / page.height);
1642
+ const outline = svg.firstChild;
1643
+ const px = Number(outline.getAttribute("x"));
1644
+ const py = Number(outline.getAttribute("y"));
1645
+ const pw = Number(outline.getAttribute("width"));
1646
+ const ph = Number(outline.getAttribute("height"));
1647
+ const sx = pw / page.width;
1648
+ const sy = ph / page.height;
1649
+ const cw = Math.max(2, pw - (margin.left + margin.right) * sx);
1650
+ const ch = Math.max(2, ph - (margin.top + margin.bottom) * sy);
1651
+ svg.appendChild(
1652
+ svgEl("rect", {
1653
+ x: px + margin.left * sx,
1654
+ y: py + margin.top * sy,
1655
+ width: cw,
1656
+ height: ch,
1657
+ fill: "var(--bb-ui-active-border,#7fb2ec)",
1658
+ "fill-opacity": 0.28,
1659
+ stroke: "var(--bb-ui-active-fg,#0c447c)",
1660
+ "stroke-opacity": 0.55,
1661
+ "stroke-width": 0.8
1662
+ })
1663
+ );
1664
+ return svg;
1665
+ }
1666
+ function presetRow(icon, name, caption, active, onPick) {
1667
+ const row = document.createElement("button");
1668
+ row.type = "button";
1669
+ row.className = "bb-ps-row";
1670
+ row.setAttribute("role", "menuitemradio");
1671
+ row.setAttribute("aria-checked", String(active));
1672
+ const text = document.createElement("span");
1673
+ text.className = "bb-ps-text";
1674
+ const nameEl = document.createElement("span");
1675
+ nameEl.className = "bb-ps-name";
1676
+ nameEl.textContent = name;
1677
+ text.append(nameEl, caption);
1678
+ row.append(icon, text);
1679
+ row.addEventListener("mousedown", (e) => e.preventDefault());
1680
+ row.addEventListener("click", onPick);
1681
+ return row;
1682
+ }
1683
+ function captionLine(text) {
1684
+ const el = document.createElement("span");
1685
+ el.className = "bb-ps-dims";
1686
+ el.style.display = "block";
1687
+ el.textContent = text;
1688
+ return el;
1689
+ }
1690
+ function customRow(icon, title, description, onPick) {
1691
+ icon.appendChild(
1692
+ svgEl("circle", {
1693
+ cx: 26,
1694
+ cy: 34,
1695
+ r: 5.5,
1696
+ fill: "var(--bb-ui-active-fg,#0c447c)"
1697
+ })
1698
+ );
1699
+ const plus = svgEl("path", {
1700
+ d: "M26 31.2v5.6M23.2 34h5.6",
1701
+ stroke: "var(--bb-ui-bg,#fff)",
1702
+ "stroke-width": 1.4,
1703
+ "stroke-linecap": "round"
1704
+ });
1705
+ icon.appendChild(plus);
1706
+ return presetRow(icon, title, captionLine(description), false, onPick);
1707
+ }
1708
+ function pageSizePicker(options) {
1709
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1710
+ const root = document.createElement("div");
1711
+ root.className = "bb-ps";
1712
+ root.setAttribute("role", "menu");
1713
+ for (const item of options.items) {
1714
+ root.appendChild(
1715
+ presetRow(
1716
+ pagePreview(item.px[0] / item.px[1]),
1717
+ item.label,
1718
+ captionLine(`${fmtCm(item.cm[0])} x ${fmtCm(item.cm[1])}`),
1719
+ !!item.active,
1720
+ () => options.onPick(item.key)
1721
+ )
1722
+ );
1723
+ }
1724
+ const sep = document.createElement("div");
1725
+ sep.className = "bb-ps-sep";
1726
+ root.appendChild(sep);
1727
+ root.appendChild(
1728
+ customRow(
1729
+ pagePreview(0.773),
1730
+ "Custom page size",
1731
+ "Define custom page size",
1732
+ options.onCustom
1733
+ )
1734
+ );
1735
+ return root;
1736
+ }
1737
+ function orientationPicker(options) {
1738
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1739
+ const root = document.createElement("div");
1740
+ root.className = "bb-ps";
1741
+ root.setAttribute("role", "menu");
1742
+ const short = Math.min(options.page.width, options.page.height);
1743
+ const long = Math.max(options.page.width, options.page.height);
1744
+ for (const [key, label, ratio] of [
1745
+ ["portrait", "Portrait", short / long],
1746
+ ["landscape", "Landscape", long / short]
1747
+ ]) {
1748
+ root.appendChild(
1749
+ presetRow(
1750
+ pagePreview(ratio),
1751
+ label,
1752
+ captionLine(""),
1753
+ options.active === key,
1754
+ () => options.onPick(key)
1755
+ )
1756
+ );
1757
+ }
1758
+ return root;
1759
+ }
1760
+ function marginPresetPicker(options) {
1761
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1762
+ const root = document.createElement("div");
1763
+ root.className = "bb-ps";
1764
+ root.setAttribute("role", "menu");
1765
+ for (const item of options.items) {
1766
+ const grid = document.createElement("span");
1767
+ grid.className = "bb-ps-dims bb-ps-grid";
1768
+ for (const [label, px] of [
1769
+ ["Top:", item.margin.top],
1770
+ ["Bottom:", item.margin.bottom],
1771
+ ["Left:", item.margin.left],
1772
+ ["Right:", item.margin.right]
1773
+ ]) {
1774
+ const cell = document.createElement("span");
1775
+ cell.textContent = `${label} ${fmtCm(pxToCm(px))}`;
1776
+ grid.appendChild(cell);
1777
+ }
1778
+ root.appendChild(
1779
+ presetRow(
1780
+ marginPreview(item.margin, options.page),
1781
+ item.label,
1782
+ grid,
1783
+ !!item.active,
1784
+ () => options.onPick(item.key)
1785
+ )
1786
+ );
1787
+ }
1788
+ const sep = document.createElement("div");
1789
+ sep.className = "bb-ps-sep";
1790
+ root.appendChild(sep);
1791
+ root.appendChild(
1792
+ customRow(
1793
+ marginPreview({ top: 96, right: 96, bottom: 96, left: 96 }, options.page),
1794
+ "Custom margins",
1795
+ "Define custom margins",
1796
+ options.onCustom
1797
+ )
1798
+ );
1799
+ return root;
1800
+ }
1801
+ function cmField(label, valuePx, min) {
1802
+ const row = document.createElement("label");
1803
+ row.className = "bb-pd-field";
1804
+ const name = document.createElement("span");
1805
+ name.className = "bb-pd-label";
1806
+ name.textContent = label;
1807
+ const input = document.createElement("input");
1808
+ input.className = "bb-pd-input";
1809
+ input.type = "number";
1810
+ input.step = "0.01";
1811
+ input.min = String(Number(pxToCm(min).toFixed(3)));
1812
+ input.value = String(Number(pxToCm(valuePx).toFixed(3)));
1813
+ row.append(name, input);
1814
+ return { row, input };
1815
+ }
1816
+ function dialogActions() {
1817
+ const el = document.createElement("div");
1818
+ el.className = "bb-pd-actions";
1819
+ const cancel = document.createElement("button");
1820
+ cancel.type = "button";
1821
+ cancel.className = "bb-prompt-btn";
1822
+ cancel.textContent = "Cancel";
1823
+ const ok = document.createElement("button");
1824
+ ok.type = "submit";
1825
+ ok.className = "bb-prompt-btn";
1826
+ ok.dataset["primary"] = "true";
1827
+ ok.textContent = "OK";
1828
+ el.append(cancel, ok);
1829
+ return { el, ok, cancel };
1830
+ }
1831
+ function readPx(input, fallbackPx) {
1832
+ const n = Number(input.value);
1833
+ return Number.isFinite(n) && n > 0 ? cmToPx(n) : fallbackPx;
1834
+ }
1835
+ function openPageSizeDialog(options) {
1836
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1837
+ const dialog = new Dialog({ title: "Paper size", modal: true });
1838
+ const form = document.createElement("form");
1839
+ form.className = "bb-pd";
1840
+ const fields = document.createElement("div");
1841
+ fields.className = "bb-pd-fields";
1842
+ const width = cmField("Width:", options.initial.width, 96);
1843
+ const height = cmField("Height:", options.initial.height, 96);
1844
+ fields.append(width.row, height.row);
1845
+ const actions = dialogActions();
1846
+ form.append(fields, actions.el);
1847
+ dialog.setContent(form);
1848
+ dialog.onClose(() => dialog.destroy());
1849
+ actions.cancel.addEventListener("click", () => dialog.close());
1850
+ form.addEventListener("submit", (e) => {
1851
+ e.preventDefault();
1852
+ options.onApply({
1853
+ width: readPx(width.input, options.initial.width),
1854
+ height: readPx(height.input, options.initial.height)
1855
+ });
1856
+ dialog.close();
1857
+ });
1858
+ dialog.open();
1859
+ width.input.focus();
1860
+ width.input.select();
1861
+ }
1862
+ function openMarginsDialog(options) {
1863
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1864
+ const dialog = new Dialog({ title: "Margins", modal: true });
1865
+ const form = document.createElement("form");
1866
+ form.className = "bb-pd";
1867
+ const fields = document.createElement("div");
1868
+ fields.className = "bb-pd-fields";
1869
+ fields.style.gridTemplateColumns = "1fr 1fr";
1870
+ const top = cmField("Top:", options.initial.top, 0);
1871
+ const left = cmField("Left:", options.initial.left, 0);
1872
+ const bottom = cmField("Bottom:", options.initial.bottom, 0);
1873
+ const right = cmField("Right:", options.initial.right, 0);
1874
+ fields.append(top.row, left.row, bottom.row, right.row);
1875
+ const actions = dialogActions();
1876
+ form.append(fields, actions.el);
1877
+ dialog.setContent(form);
1878
+ dialog.onClose(() => dialog.destroy());
1879
+ actions.cancel.addEventListener("click", () => dialog.close());
1880
+ form.addEventListener("submit", (e) => {
1881
+ e.preventDefault();
1882
+ const px = (input, fallback) => {
1883
+ const n = Number(input.value);
1884
+ return Number.isFinite(n) && n >= 0 ? cmToPx(n) : fallback;
1885
+ };
1886
+ options.onApply({
1887
+ top: px(top.input, options.initial.top),
1888
+ right: px(right.input, options.initial.right),
1889
+ bottom: px(bottom.input, options.initial.bottom),
1890
+ left: px(left.input, options.initial.left)
1891
+ });
1892
+ dialog.close();
1893
+ });
1894
+ dialog.open();
1895
+ top.input.focus();
1896
+ top.input.select();
1897
+ }
1559
1898
  // Annotate the CommonJS export names for ESM import in node:
1560
1899
  0 && (module.exports = {
1561
1900
  Dialog,
1901
+ cmToPx,
1562
1902
  createFindDialog,
1563
1903
  defaultMenus,
1564
1904
  defaultToolbarGroups,
1905
+ marginPresetPicker,
1565
1906
  mountMenubar,
1566
1907
  mountToolbar,
1567
1908
  openCellProperties,
1909
+ openMarginsDialog,
1910
+ openPageSizeDialog,
1911
+ orientationPicker,
1912
+ pageSizePicker,
1568
1913
  promptDialog,
1914
+ pxToCm,
1569
1915
  showContextMenu,
1570
1916
  showLinkPanel,
1571
1917
  tableGridPicker
package/dist/index.d.ts CHANGED
@@ -7,4 +7,5 @@ export * from './lib/context-menu.js';
7
7
  export * from './lib/link-panel.js';
8
8
  export * from './lib/cell-properties.js';
9
9
  export * from './lib/table-grid.js';
10
+ export * from './lib/page-setup.js';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACrE,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACrE,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -634,7 +634,9 @@ var STYLE3 = `
634
634
  `;
635
635
  function defaultMenus(commands) {
636
636
  const names = [...commands].map((c) => c.name);
637
- const marks = ["bold", "italic", "underline", "strike"].filter((n) => commands.has(n));
637
+ const marks = ["bold", "italic", "underline", "strike"].filter(
638
+ (n) => commands.has(n)
639
+ );
638
640
  const aligns = names.filter((n) => n.startsWith("align-"));
639
641
  const entries = [
640
642
  ...marks.map((command) => ({ command })),
@@ -673,7 +675,8 @@ function mountMenubar(host, editor, options = {}) {
673
675
  p.title.setAttribute("aria-expanded", "true");
674
676
  openIdx = idx;
675
677
  if (latest) refresh(latest);
676
- if (focusFirst) p.dropdown.querySelector(".bb-menu-item")?.focus();
678
+ if (focusFirst)
679
+ p.dropdown.querySelector(".bb-menu-item")?.focus();
677
680
  };
678
681
  const makeRow = (label, opts) => {
679
682
  const item = document.createElement("button");
@@ -717,7 +720,9 @@ function mountMenubar(host, editor, options = {}) {
717
720
  flyout.setAttribute("role", "menu");
718
721
  if ("widget" in entry) {
719
722
  flyout.classList.add("bb-submenu-widget");
720
- flyout.appendChild(entry.widget(() => close()));
723
+ const build = () => flyout.replaceChildren(entry.widget(() => close()));
724
+ wrap.addEventListener("mouseenter", build);
725
+ wrap.addEventListener("focusin", build);
721
726
  } else {
722
727
  buildEntries(entry.submenu, flyout);
723
728
  }
@@ -726,25 +731,37 @@ function mountMenubar(host, editor, options = {}) {
726
731
  } else if ("command" in entry) {
727
732
  const cmd = editor.commands.get(entry.command);
728
733
  if (!cmd) continue;
729
- const { item, check } = makeRow(entry.label ?? labels[entry.command] ?? entry.command, {});
734
+ const { item, check } = makeRow(
735
+ entry.label ?? labels[entry.command] ?? entry.command,
736
+ {}
737
+ );
730
738
  item.setAttribute("role", "menuitemcheckbox");
731
739
  item.addEventListener("click", () => {
732
- if (latest) editor.commands.get(entry.command)?.run(latest, (tr) => editor.dispatch(tr));
740
+ if (latest)
741
+ editor.commands.get(entry.command)?.run(latest, (tr) => editor.dispatch(tr));
733
742
  close();
734
743
  editor.focus();
735
744
  });
736
745
  checks.push({ el: check, active: (s) => cmd.isActive?.(s) ?? false });
737
- if (cmd.isEnabled) enables.push({ el: item, enabled: (s) => cmd.isEnabled(s) });
746
+ if (cmd.isEnabled)
747
+ enables.push({ el: item, enabled: (s) => cmd.isEnabled(s) });
738
748
  container.appendChild(item);
739
749
  } else {
740
- const { item, check } = makeRow(entry.label, { shortcut: entry.shortcut });
741
- item.setAttribute("role", entry.isActive ? "menuitemcheckbox" : "menuitem");
750
+ const { item, check } = makeRow(entry.label, {
751
+ shortcut: entry.shortcut
752
+ });
753
+ item.setAttribute(
754
+ "role",
755
+ entry.isActive ? "menuitemcheckbox" : "menuitem"
756
+ );
742
757
  item.addEventListener("click", () => {
743
758
  entry.run();
744
759
  close();
745
760
  });
746
- if (entry.isActive) checks.push({ el: check, active: () => entry.isActive() });
747
- if (entry.isEnabled) enables.push({ el: item, enabled: () => entry.isEnabled() });
761
+ if (entry.isActive)
762
+ checks.push({ el: check, active: () => entry.isActive() });
763
+ if (entry.isEnabled)
764
+ enables.push({ el: item, enabled: () => entry.isEnabled() });
748
765
  container.appendChild(item);
749
766
  }
750
767
  }
@@ -764,7 +781,10 @@ function mountMenubar(host, editor, options = {}) {
764
781
  dropdown.setAttribute("role", "menu");
765
782
  dropdown.hidden = true;
766
783
  title.addEventListener("mousedown", (e) => e.preventDefault());
767
- title.addEventListener("click", () => openIdx === idx ? close() : open(idx));
784
+ title.addEventListener(
785
+ "click",
786
+ () => openIdx === idx ? close() : open(idx)
787
+ );
768
788
  title.addEventListener("mouseenter", () => {
769
789
  if (openIdx >= 0 && openIdx !== idx) open(idx);
770
790
  });
@@ -1520,15 +1540,334 @@ function tableGridPicker(options) {
1520
1540
  root.append(cellsEl, label);
1521
1541
  return root;
1522
1542
  }
1543
+
1544
+ // packages/ui/src/lib/page-setup.ts
1545
+ var PX_PER_CM = 96 / 2.54;
1546
+ var pxToCm = (px) => px / PX_PER_CM;
1547
+ var cmToPx = (cm) => Math.round(cm * PX_PER_CM);
1548
+ function fmtCm(cm) {
1549
+ return `${Number(cm.toFixed(3))} cm`;
1550
+ }
1551
+ var STYLE9 = `
1552
+ .bb-ps{display:flex;flex-direction:column;min-width:250px;max-height:min(70vh,460px);overflow-y:auto}
1553
+ .bb-ps-row{display:flex;align-items:center;gap:11px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
1554
+ .bb-ps-row:hover,.bb-ps-row:focus{background:var(--bb-ui-hover,#f1efe8);outline:none}
1555
+ .bb-ps-row[aria-checked="true"]{background:var(--bb-ui-active-bg,#e6f1fb)}
1556
+ .bb-ps-icon{flex:none;width:34px;height:42px;display:block}
1557
+ .bb-ps-text{flex:1 1 auto;min-width:0}
1558
+ .bb-ps-name{font-size:13px;font-weight:600;line-height:1.3}
1559
+ .bb-ps-dims{font-size:11.5px;opacity:.68;line-height:1.45;white-space:nowrap}
1560
+ .bb-ps-grid{display:grid;grid-template-columns:auto auto;gap:0 14px;justify-content:start}
1561
+ .bb-ps-sep{height:1px;margin:4px 6px;background:var(--bb-ui-border,#e3e3e0)}
1562
+ .bb-pd{display:flex;flex-direction:column;gap:14px;min-width:300px}
1563
+ .bb-pd-fields{display:grid;gap:10px 18px}
1564
+ .bb-pd-field{display:flex;align-items:center;gap:8px}
1565
+ .bb-pd-label{flex:0 0 74px;font-size:13px}
1566
+ .bb-pd-input{flex:1 1 auto;min-width:0;width:100%;height:30px;padding:0 8px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px}
1567
+ .bb-pd-input:focus{outline:2px solid var(--bb-ui-active-border,#7fb2ec);outline-offset:-1px}
1568
+ .bb-pd-actions{display:flex;justify-content:flex-end;gap:8px}
1569
+ `;
1570
+ var SVG_NS = "http://www.w3.org/2000/svg";
1571
+ function svgEl(name, attrs) {
1572
+ const el = document.createElementNS(SVG_NS, name);
1573
+ for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, String(v));
1574
+ return el;
1575
+ }
1576
+ function pagePreview(ratio) {
1577
+ const svg = svgEl("svg", { viewBox: "0 0 34 42", class: "bb-ps-icon" });
1578
+ const maxW = 26;
1579
+ const maxH = 36;
1580
+ const w = Math.min(maxW, maxH * ratio);
1581
+ const h = w / ratio;
1582
+ svg.appendChild(
1583
+ svgEl("rect", {
1584
+ x: (34 - w) / 2,
1585
+ y: (42 - h) / 2,
1586
+ width: w,
1587
+ height: h,
1588
+ rx: 1,
1589
+ fill: "var(--bb-ui-bg,#fff)",
1590
+ stroke: "var(--bb-ui-fg,#2c2c2a)",
1591
+ "stroke-opacity": 0.45,
1592
+ "stroke-width": 1
1593
+ })
1594
+ );
1595
+ return svg;
1596
+ }
1597
+ function marginPreview(margin, page) {
1598
+ const svg = pagePreview(page.width / page.height);
1599
+ const outline = svg.firstChild;
1600
+ const px = Number(outline.getAttribute("x"));
1601
+ const py = Number(outline.getAttribute("y"));
1602
+ const pw = Number(outline.getAttribute("width"));
1603
+ const ph = Number(outline.getAttribute("height"));
1604
+ const sx = pw / page.width;
1605
+ const sy = ph / page.height;
1606
+ const cw = Math.max(2, pw - (margin.left + margin.right) * sx);
1607
+ const ch = Math.max(2, ph - (margin.top + margin.bottom) * sy);
1608
+ svg.appendChild(
1609
+ svgEl("rect", {
1610
+ x: px + margin.left * sx,
1611
+ y: py + margin.top * sy,
1612
+ width: cw,
1613
+ height: ch,
1614
+ fill: "var(--bb-ui-active-border,#7fb2ec)",
1615
+ "fill-opacity": 0.28,
1616
+ stroke: "var(--bb-ui-active-fg,#0c447c)",
1617
+ "stroke-opacity": 0.55,
1618
+ "stroke-width": 0.8
1619
+ })
1620
+ );
1621
+ return svg;
1622
+ }
1623
+ function presetRow(icon, name, caption, active, onPick) {
1624
+ const row = document.createElement("button");
1625
+ row.type = "button";
1626
+ row.className = "bb-ps-row";
1627
+ row.setAttribute("role", "menuitemradio");
1628
+ row.setAttribute("aria-checked", String(active));
1629
+ const text = document.createElement("span");
1630
+ text.className = "bb-ps-text";
1631
+ const nameEl = document.createElement("span");
1632
+ nameEl.className = "bb-ps-name";
1633
+ nameEl.textContent = name;
1634
+ text.append(nameEl, caption);
1635
+ row.append(icon, text);
1636
+ row.addEventListener("mousedown", (e) => e.preventDefault());
1637
+ row.addEventListener("click", onPick);
1638
+ return row;
1639
+ }
1640
+ function captionLine(text) {
1641
+ const el = document.createElement("span");
1642
+ el.className = "bb-ps-dims";
1643
+ el.style.display = "block";
1644
+ el.textContent = text;
1645
+ return el;
1646
+ }
1647
+ function customRow(icon, title, description, onPick) {
1648
+ icon.appendChild(
1649
+ svgEl("circle", {
1650
+ cx: 26,
1651
+ cy: 34,
1652
+ r: 5.5,
1653
+ fill: "var(--bb-ui-active-fg,#0c447c)"
1654
+ })
1655
+ );
1656
+ const plus = svgEl("path", {
1657
+ d: "M26 31.2v5.6M23.2 34h5.6",
1658
+ stroke: "var(--bb-ui-bg,#fff)",
1659
+ "stroke-width": 1.4,
1660
+ "stroke-linecap": "round"
1661
+ });
1662
+ icon.appendChild(plus);
1663
+ return presetRow(icon, title, captionLine(description), false, onPick);
1664
+ }
1665
+ function pageSizePicker(options) {
1666
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1667
+ const root = document.createElement("div");
1668
+ root.className = "bb-ps";
1669
+ root.setAttribute("role", "menu");
1670
+ for (const item of options.items) {
1671
+ root.appendChild(
1672
+ presetRow(
1673
+ pagePreview(item.px[0] / item.px[1]),
1674
+ item.label,
1675
+ captionLine(`${fmtCm(item.cm[0])} x ${fmtCm(item.cm[1])}`),
1676
+ !!item.active,
1677
+ () => options.onPick(item.key)
1678
+ )
1679
+ );
1680
+ }
1681
+ const sep = document.createElement("div");
1682
+ sep.className = "bb-ps-sep";
1683
+ root.appendChild(sep);
1684
+ root.appendChild(
1685
+ customRow(
1686
+ pagePreview(0.773),
1687
+ "Custom page size",
1688
+ "Define custom page size",
1689
+ options.onCustom
1690
+ )
1691
+ );
1692
+ return root;
1693
+ }
1694
+ function orientationPicker(options) {
1695
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1696
+ const root = document.createElement("div");
1697
+ root.className = "bb-ps";
1698
+ root.setAttribute("role", "menu");
1699
+ const short = Math.min(options.page.width, options.page.height);
1700
+ const long = Math.max(options.page.width, options.page.height);
1701
+ for (const [key, label, ratio] of [
1702
+ ["portrait", "Portrait", short / long],
1703
+ ["landscape", "Landscape", long / short]
1704
+ ]) {
1705
+ root.appendChild(
1706
+ presetRow(
1707
+ pagePreview(ratio),
1708
+ label,
1709
+ captionLine(""),
1710
+ options.active === key,
1711
+ () => options.onPick(key)
1712
+ )
1713
+ );
1714
+ }
1715
+ return root;
1716
+ }
1717
+ function marginPresetPicker(options) {
1718
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1719
+ const root = document.createElement("div");
1720
+ root.className = "bb-ps";
1721
+ root.setAttribute("role", "menu");
1722
+ for (const item of options.items) {
1723
+ const grid = document.createElement("span");
1724
+ grid.className = "bb-ps-dims bb-ps-grid";
1725
+ for (const [label, px] of [
1726
+ ["Top:", item.margin.top],
1727
+ ["Bottom:", item.margin.bottom],
1728
+ ["Left:", item.margin.left],
1729
+ ["Right:", item.margin.right]
1730
+ ]) {
1731
+ const cell = document.createElement("span");
1732
+ cell.textContent = `${label} ${fmtCm(pxToCm(px))}`;
1733
+ grid.appendChild(cell);
1734
+ }
1735
+ root.appendChild(
1736
+ presetRow(
1737
+ marginPreview(item.margin, options.page),
1738
+ item.label,
1739
+ grid,
1740
+ !!item.active,
1741
+ () => options.onPick(item.key)
1742
+ )
1743
+ );
1744
+ }
1745
+ const sep = document.createElement("div");
1746
+ sep.className = "bb-ps-sep";
1747
+ root.appendChild(sep);
1748
+ root.appendChild(
1749
+ customRow(
1750
+ marginPreview({ top: 96, right: 96, bottom: 96, left: 96 }, options.page),
1751
+ "Custom margins",
1752
+ "Define custom margins",
1753
+ options.onCustom
1754
+ )
1755
+ );
1756
+ return root;
1757
+ }
1758
+ function cmField(label, valuePx, min) {
1759
+ const row = document.createElement("label");
1760
+ row.className = "bb-pd-field";
1761
+ const name = document.createElement("span");
1762
+ name.className = "bb-pd-label";
1763
+ name.textContent = label;
1764
+ const input = document.createElement("input");
1765
+ input.className = "bb-pd-input";
1766
+ input.type = "number";
1767
+ input.step = "0.01";
1768
+ input.min = String(Number(pxToCm(min).toFixed(3)));
1769
+ input.value = String(Number(pxToCm(valuePx).toFixed(3)));
1770
+ row.append(name, input);
1771
+ return { row, input };
1772
+ }
1773
+ function dialogActions() {
1774
+ const el = document.createElement("div");
1775
+ el.className = "bb-pd-actions";
1776
+ const cancel = document.createElement("button");
1777
+ cancel.type = "button";
1778
+ cancel.className = "bb-prompt-btn";
1779
+ cancel.textContent = "Cancel";
1780
+ const ok = document.createElement("button");
1781
+ ok.type = "submit";
1782
+ ok.className = "bb-prompt-btn";
1783
+ ok.dataset["primary"] = "true";
1784
+ ok.textContent = "OK";
1785
+ el.append(cancel, ok);
1786
+ return { el, ok, cancel };
1787
+ }
1788
+ function readPx(input, fallbackPx) {
1789
+ const n = Number(input.value);
1790
+ return Number.isFinite(n) && n > 0 ? cmToPx(n) : fallbackPx;
1791
+ }
1792
+ function openPageSizeDialog(options) {
1793
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1794
+ const dialog = new Dialog({ title: "Paper size", modal: true });
1795
+ const form = document.createElement("form");
1796
+ form.className = "bb-pd";
1797
+ const fields = document.createElement("div");
1798
+ fields.className = "bb-pd-fields";
1799
+ const width = cmField("Width:", options.initial.width, 96);
1800
+ const height = cmField("Height:", options.initial.height, 96);
1801
+ fields.append(width.row, height.row);
1802
+ const actions = dialogActions();
1803
+ form.append(fields, actions.el);
1804
+ dialog.setContent(form);
1805
+ dialog.onClose(() => dialog.destroy());
1806
+ actions.cancel.addEventListener("click", () => dialog.close());
1807
+ form.addEventListener("submit", (e) => {
1808
+ e.preventDefault();
1809
+ options.onApply({
1810
+ width: readPx(width.input, options.initial.width),
1811
+ height: readPx(height.input, options.initial.height)
1812
+ });
1813
+ dialog.close();
1814
+ });
1815
+ dialog.open();
1816
+ width.input.focus();
1817
+ width.input.select();
1818
+ }
1819
+ function openMarginsDialog(options) {
1820
+ injectStyle("bb-ui-pagesetup-styles", STYLE9);
1821
+ const dialog = new Dialog({ title: "Margins", modal: true });
1822
+ const form = document.createElement("form");
1823
+ form.className = "bb-pd";
1824
+ const fields = document.createElement("div");
1825
+ fields.className = "bb-pd-fields";
1826
+ fields.style.gridTemplateColumns = "1fr 1fr";
1827
+ const top = cmField("Top:", options.initial.top, 0);
1828
+ const left = cmField("Left:", options.initial.left, 0);
1829
+ const bottom = cmField("Bottom:", options.initial.bottom, 0);
1830
+ const right = cmField("Right:", options.initial.right, 0);
1831
+ fields.append(top.row, left.row, bottom.row, right.row);
1832
+ const actions = dialogActions();
1833
+ form.append(fields, actions.el);
1834
+ dialog.setContent(form);
1835
+ dialog.onClose(() => dialog.destroy());
1836
+ actions.cancel.addEventListener("click", () => dialog.close());
1837
+ form.addEventListener("submit", (e) => {
1838
+ e.preventDefault();
1839
+ const px = (input, fallback) => {
1840
+ const n = Number(input.value);
1841
+ return Number.isFinite(n) && n >= 0 ? cmToPx(n) : fallback;
1842
+ };
1843
+ options.onApply({
1844
+ top: px(top.input, options.initial.top),
1845
+ right: px(right.input, options.initial.right),
1846
+ bottom: px(bottom.input, options.initial.bottom),
1847
+ left: px(left.input, options.initial.left)
1848
+ });
1849
+ dialog.close();
1850
+ });
1851
+ dialog.open();
1852
+ top.input.focus();
1853
+ top.input.select();
1854
+ }
1523
1855
  export {
1524
1856
  Dialog,
1857
+ cmToPx,
1525
1858
  createFindDialog,
1526
1859
  defaultMenus,
1527
1860
  defaultToolbarGroups,
1861
+ marginPresetPicker,
1528
1862
  mountMenubar,
1529
1863
  mountToolbar,
1530
1864
  openCellProperties,
1865
+ openMarginsDialog,
1866
+ openPageSizeDialog,
1867
+ orientationPicker,
1868
+ pageSizePicker,
1531
1869
  promptDialog,
1870
+ pxToCm,
1532
1871
  showContextMenu,
1533
1872
  showLinkPanel,
1534
1873
  tableGridPicker
@@ -1 +1 @@
1
- {"version":3,"file":"menubar.d.ts","sourceRoot":"","sources":["../../src/lib/menubar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,KAAK,YAAY,EAAmC,MAAM,eAAe,CAAC;AAEnF,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,yBAAyB;AACzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AACD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,IAAI,KAAK,WAAW,CAAC;CAC5C;AACD,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,CAAC;AAE9F,uDAAuD;AACvD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;+EAE2E;IAC3E,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAmDD;mDACmD;AACnD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAUlE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,cAAmB,GAC3B,aAAa,CA0Lf"}
1
+ {"version":3,"file":"menubar.d.ts","sourceRoot":"","sources":["../../src/lib/menubar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EACL,KAAK,YAAY,EAGlB,MAAM,eAAe,CAAC;AAEvB,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,yBAAyB;AACzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AACD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,IAAI,KAAK,WAAW,CAAC;CAC5C;AACD,MAAM,MAAM,SAAS,GACjB,WAAW,GACX,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,WAAW,CAAC;AAEhB,uDAAuD;AACvD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;+EAE2E;IAC3E,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAmDD;mDACmD;AACnD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAYlE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAiOf"}
@@ -0,0 +1,92 @@
1
+ /** px → cm. */
2
+ export declare const pxToCm: (px: number) => number;
3
+ /** cm → px (rounded — the model is integer px). */
4
+ export declare const cmToPx: (cm: number) => number;
5
+ export interface PaperChoice {
6
+ /** Stable key echoed back to `onPick` (e.g. a PaperSize name). */
7
+ key: string;
8
+ label: string;
9
+ /** Nominal portrait size in cm — the caption. Exact values, not derived
10
+ * from px (rounding would print A4 as 21.006 cm). */
11
+ cm: readonly [number, number];
12
+ /** Portrait size in px — drives the preview icon's aspect ratio. */
13
+ px: readonly [number, number];
14
+ active?: boolean;
15
+ }
16
+ export interface MarginChoice {
17
+ key: string;
18
+ label: string;
19
+ /** Margins in px; shown in cm and drawn as the preview's content box. */
20
+ margin: PageMargin;
21
+ active?: boolean;
22
+ }
23
+ export interface PageMargin {
24
+ top: number;
25
+ right: number;
26
+ bottom: number;
27
+ left: number;
28
+ }
29
+ export interface PageSizePickerOptions {
30
+ items: readonly PaperChoice[];
31
+ /** Called with the chosen item's `key`. */
32
+ onPick: (key: string) => void;
33
+ /** Called for the trailing "Custom page size" row. */
34
+ onCustom: () => void;
35
+ }
36
+ /**
37
+ * Layout ▸ Page size flyout: one row per paper preset (preview, name, size in
38
+ * cm) plus a custom row. Returns a detached element for a menu widget slot.
39
+ */
40
+ export declare function pageSizePicker(options: PageSizePickerOptions): HTMLElement;
41
+ export interface OrientationPickerOptions {
42
+ /** Current page size in px — the previews are drawn at its aspect ratio. */
43
+ page: {
44
+ width: number;
45
+ height: number;
46
+ };
47
+ active: 'portrait' | 'landscape';
48
+ onPick: (orientation: 'portrait' | 'landscape') => void;
49
+ }
50
+ /**
51
+ * Layout ▸ Orientation flyout. Same row shape as the other two pickers, so all
52
+ * three entries of the Layout menu read as one family.
53
+ */
54
+ export declare function orientationPicker(options: OrientationPickerOptions): HTMLElement;
55
+ export interface MarginPickerOptions {
56
+ items: readonly MarginChoice[];
57
+ /** Page size in px — the previews draw each preset's content box to scale. */
58
+ page: {
59
+ width: number;
60
+ height: number;
61
+ };
62
+ onPick: (key: string) => void;
63
+ onCustom: () => void;
64
+ }
65
+ /**
66
+ * Layout ▸ Margins flyout: one row per preset (a preview showing the content
67
+ * box to scale, the name, and the four edges in cm) plus a custom row.
68
+ */
69
+ export declare function marginPresetPicker(options: MarginPickerOptions): HTMLElement;
70
+ export interface PageSizeDialogOptions {
71
+ /** Current size in px. */
72
+ initial: {
73
+ width: number;
74
+ height: number;
75
+ };
76
+ /** Applied size in px. */
77
+ onApply: (size: {
78
+ width: number;
79
+ height: number;
80
+ }) => void;
81
+ }
82
+ /** Layout ▸ Page size ▸ Custom page size — width/height in cm. */
83
+ export declare function openPageSizeDialog(options: PageSizeDialogOptions): void;
84
+ export interface MarginsDialogOptions {
85
+ /** Current margins in px. */
86
+ initial: PageMargin;
87
+ /** Applied margins in px. */
88
+ onApply: (margin: PageMargin) => void;
89
+ }
90
+ /** Layout ▸ Margins ▸ Custom margins — the four edges in cm. */
91
+ export declare function openMarginsDialog(options: MarginsDialogOptions): void;
92
+ //# sourceMappingURL=page-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-setup.d.ts","sourceRoot":"","sources":["../../src/lib/page-setup.ts"],"names":[],"mappings":"AAkBA,eAAe;AACf,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,KAAG,MAAwB,CAAC;AAC7D,mDAAmD;AACnD,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,KAAG,MAAoC,CAAC;AAOzE,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd;0DACsD;IACtD,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,oEAAoE;IACpE,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAqJD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,WAAW,CA8B1E;AAED,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;IACjC,MAAM,EAAE,CAAC,WAAW,EAAE,UAAU,GAAG,WAAW,KAAK,IAAI,CAAC;CACzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,WAAW,CA0Bb;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,8EAA8E;IAC9E,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,WAAW,CA0C5E;AAkDD,MAAM,WAAW,qBAAqB;IACpC,0BAA0B;IAC1B,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,0BAA0B;IAC1B,OAAO,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC5D;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CA2BvE;AAED,MAAM,WAAW,oBAAoB;IACnC,6BAA6B;IAC7B,OAAO,EAAE,UAAU,CAAC;IACpB,6BAA6B;IAC7B,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CACvC;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAuCrE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shadow-garden/bapbong-ui",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "bapbong — framework-agnostic editor UI (toolbar/menubar) that mounts into a host element and binds to editor.commands",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -56,6 +56,6 @@
56
56
  }
57
57
  },
58
58
  "dependencies": {
59
- "@shadow-garden/bapbong-contracts": "^0.4.0"
59
+ "@shadow-garden/bapbong-contracts": "^0.5.0"
60
60
  }
61
61
  }