@panphora/clayjs 1.17.0 → 1.18.1
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/all.js +1 -1
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +72474 -62090
- package/dist/m/attrs/{save-freeze-MYNVXREN.js → save-freeze-LWGF6XUN.js} +1 -1
- package/dist/m/c-2GRUM77Q.js +30 -0
- package/dist/m/c-2KIEGNC4.js +1 -0
- package/dist/m/{c-AMSNPOCX.js → c-2KL3BD52.js} +1 -1
- package/dist/m/c-2ZZBC45O.js +1 -0
- package/dist/m/c-372N6H3B.js +1 -0
- package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
- package/dist/m/c-3LDGIBMN.js +1 -0
- package/dist/m/c-3O7OBDCM.js +1 -0
- package/dist/m/c-4ASQPRQL.js +1 -0
- package/dist/m/c-4TXNLX4K.js +6 -0
- package/dist/m/c-4YU3BR4D.js +6 -0
- package/dist/m/c-5734MF2P.js +1 -0
- package/dist/m/c-57XDY6HC.js +1 -0
- package/dist/m/{c-KAU6E43D.js → c-5B4YBZXC.js} +1 -1
- package/dist/m/c-5ONTYIUI.js +1 -0
- package/dist/m/c-5OOVFCJM.js +1 -0
- package/dist/m/{c-3XIRD4NU.js → c-5T6NZHNM.js} +1 -1
- package/dist/m/{c-74MGYWZF.js → c-5ZQV7KC4.js} +1 -1
- package/dist/m/{c-7OLLJPWP.js → c-6DB2OLRJ.js} +1 -1
- package/dist/m/c-6DNRCPNM.js +1 -0
- package/dist/m/c-6UGWFMQC.js +2 -0
- package/dist/m/c-7K4SCQB7.js +1 -0
- package/dist/m/c-7MG5RU63.js +1 -0
- package/dist/m/c-7PRJCA3E.js +1 -0
- package/dist/m/c-A6L3FMUS.js +1 -0
- package/dist/m/c-AMU3BPPR.js +4 -0
- package/dist/m/{c-Y5HBBIQ4.js → c-AMYEFA2E.js} +1 -1
- package/dist/m/c-AQYCG7QW.js +1 -0
- package/dist/m/c-ATKAOH23.js +1 -0
- package/dist/m/c-B5CS55WR.js +1 -0
- package/dist/m/c-B5UJQT7W.js +1 -0
- package/dist/m/c-B7J4K2AE.js +3 -0
- package/dist/m/c-BHZYSHW4.js +1 -0
- package/dist/m/{c-VHM5XQHK.js → c-BIJALSRD.js} +1 -1
- package/dist/m/c-BJ7YQ5QZ.js +1 -0
- package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
- package/dist/m/{c-VE6ZMYMC.js → c-BUAYFEV5.js} +1 -1
- package/dist/m/c-C6IPRG34.js +1 -0
- package/dist/m/{c-KU3ZFADW.js → c-CAS3NND4.js} +1 -1
- package/dist/m/{c-PS5ARVWR.js → c-CCQ25GFY.js} +1 -1
- package/dist/m/c-CJFDLD43.js +1 -0
- package/dist/m/c-COPTL5WB.js +1 -0
- package/dist/m/c-D3E4WJ4M.js +166 -0
- package/dist/m/c-D5IKEKXW.js +7 -0
- package/dist/m/c-DHBRA6DL.js +1 -0
- package/dist/m/c-DKLS74XB.js +72 -0
- package/dist/m/c-DVYGFTSK.js +3 -0
- package/dist/m/c-E3ZXARWA.js +1 -0
- package/dist/m/c-EK63G3CG.js +1 -0
- package/dist/m/c-ERQSNR6K.js +13 -0
- package/dist/m/c-EVYAISP3.js +1 -0
- package/dist/m/c-EXKHPOVC.js +3 -0
- package/dist/m/{c-QJ4OM3G6.js → c-FWKHM4E7.js} +1 -1
- package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
- package/dist/m/c-GNDL3TKB.js +1 -0
- package/dist/m/c-GRWMD6O5.js +1 -0
- package/dist/m/c-GRXHHFPO.js +1 -0
- package/dist/m/c-HG2C77BM.js +4 -0
- package/dist/m/c-HHGJBU4D.js +1 -0
- package/dist/m/c-HMV6P4RX.js +1 -0
- package/dist/m/c-HMYWIHFG.js +1 -0
- package/dist/m/c-HT2DKBV2.js +1 -0
- package/dist/m/c-I2SCCUEW.js +1 -0
- package/dist/m/{c-TJ6WOJL3.js → c-ICJYHABF.js} +2 -2
- package/dist/m/c-IMZCWYHR.js +7 -0
- package/dist/m/c-IVMD3FEM.js +1 -0
- package/dist/m/{c-WDC5GVKU.js → c-IVTRVENZ.js} +1 -1
- package/dist/m/c-JKZJIUIP.js +1 -0
- package/dist/m/{c-UDUUJPFZ.js → c-JNLX3RJB.js} +1 -1
- package/dist/m/c-K3ADYNLU.js +1 -0
- package/dist/m/c-KGHVFRR3.js +1 -0
- package/dist/m/{c-5QWQ7A5L.js → c-KJJPV77R.js} +1 -1
- package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
- package/dist/m/{c-EZU56NQV.js → c-KP64LT2A.js} +2 -2
- package/dist/m/c-KQ7E3PYY.js +1 -0
- package/dist/m/c-L3F5ZLIV.js +1 -0
- package/dist/m/{c-3RUU4TPX.js → c-L42OLXWM.js} +3 -2
- package/dist/m/c-L5VFATT6.js +1 -0
- package/dist/m/c-L7JL5TL3.js +3 -0
- package/dist/m/c-LKSL4AUJ.js +1 -0
- package/dist/m/c-M6FNQCTQ.js +1 -0
- package/dist/m/c-MCOOHCSG.js +1 -0
- package/dist/m/c-MHI3GYKF.js +1 -0
- package/dist/m/c-MWXFLW4J.js +1 -0
- package/dist/m/{c-5RJFTLSF.js → c-NEL7HQW3.js} +1 -1
- package/dist/m/{c-DCXOXBF3.js → c-NQJRVJ56.js} +1 -1
- package/dist/m/c-NWX3MFT4.js +1 -0
- package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
- package/dist/m/c-OKXI52N7.js +1 -0
- package/dist/m/{c-R3L7ZJGB.js → c-OPLUZR3A.js} +1 -1
- package/dist/m/{c-GSFIGKOP.js → c-OTMKN2OO.js} +1 -1
- package/dist/m/{c-6NC5I2TV.js → c-OYRZ7XG2.js} +1 -1
- package/dist/m/{c-EVCQ3BKA.js → c-P3OFYUMA.js} +1 -1
- package/dist/m/c-PLN76GF3.js +6 -0
- package/dist/m/c-PLP4JKFC.js +1 -0
- package/dist/m/c-Q26MME6N.js +1 -0
- package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
- package/dist/m/{c-FRLHSY7E.js → c-QIKT6SKM.js} +1 -1
- package/dist/m/c-QIXT3Q7C.js +28 -0
- package/dist/m/c-QO4HNGCQ.js +1 -0
- package/dist/m/{c-JU3NK25H.js → c-R27DWQKX.js} +1 -1
- package/dist/m/{c-RSMOODDU.js → c-R2XUODVY.js} +1 -1
- package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
- package/dist/m/c-RND3G2S2.js +1 -0
- package/dist/m/{c-Q622NS2Y.js → c-S2U6NJU4.js} +1 -1
- package/dist/m/c-SPZPGD3R.js +1 -0
- package/dist/m/c-SW4YEG2D.js +8 -0
- package/dist/m/c-T637T7SN.js +1 -0
- package/dist/m/c-TJAXGIRV.js +1 -0
- package/dist/m/{c-RYUXUUBX.js → c-TNEM3RNM.js} +1 -1
- package/dist/m/c-TTB5EK4S.js +32 -0
- package/dist/m/c-U7SHNWDH.js +1 -0
- package/dist/m/c-UDHIVLYH.js +20 -0
- package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
- package/dist/m/{c-QICP2YR5.js → c-UORXLSD3.js} +1 -1
- package/dist/m/c-UPD33ZPE.js +1 -0
- package/dist/m/c-URSMSFCB.js +1 -0
- package/dist/m/c-UY4QX42B.js +1 -0
- package/dist/m/c-V44KWI2L.js +4 -0
- package/dist/m/c-VILENZTD.js +1 -0
- package/dist/m/{c-HDDEEL77.js → c-VJYEVRYK.js} +1 -1
- package/dist/m/{c-SMBZG5X4.js → c-VR2BJ4AN.js} +1 -1
- package/dist/m/c-W4JCVLAB.js +1 -0
- package/dist/m/c-W6DQSHXI.js +135 -0
- package/dist/m/c-WBGWGOM7.js +1 -0
- package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
- package/dist/m/c-WGW764WX.js +1 -0
- package/dist/m/c-WMRJKQ2V.js +5 -0
- package/dist/m/c-WNTYKCDG.js +1 -0
- package/dist/m/c-WQWIRWO3.js +72 -0
- package/dist/m/c-X5LNHET5.js +1 -0
- package/dist/m/{c-I2ZATQCD.js → c-X5TSXZYJ.js} +2 -2
- package/dist/m/c-XGH4GS2T.js +1 -0
- package/dist/m/c-Y6F2FORL.js +1 -0
- package/dist/m/c-YBKHUAK7.js +10 -0
- package/dist/m/c-YBSS55KU.js +20 -0
- package/dist/m/c-YCLU4UNT.js +2 -0
- package/dist/m/c-YVZ2L7CE.js +1 -0
- package/dist/m/c-ZK4GIZH2.js +1 -0
- package/dist/m/c-ZRXJD4G6.js +1 -0
- package/dist/m/c-ZWBI4HYK.js +1 -0
- package/dist/m/core/{admin-attrs-QHOKPJ7W.js → admin-attrs-YY7T5FGU.js} +1 -1
- package/dist/m/core/autosave-S6OMNDCK.js +1 -0
- package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
- package/dist/m/core/{host-permission-FNXVEFNE.js → host-permission-6EVJGNPL.js} +1 -1
- package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
- package/dist/m/core/{persist-JFYRHMRJ.js → persist-UKXV5KLA.js} +1 -1
- package/dist/m/core/recovery-DRFZRJED.js +1 -0
- package/dist/m/core/save-LYY7K6U7.js +1 -0
- package/dist/m/core/save-core-MKRPSEEO.js +1 -0
- package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
- package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
- package/dist/m/internals/{index-PVXGPUDA.js → index-TGLQINN3.js} +1 -1
- package/dist/m/loader-3BBNZZGM.js +1 -0
- package/dist/m/manifest-26C55578.js +1 -0
- package/dist/m/manifest-edit-4396A009.js +1 -0
- package/dist/m/manifest.json +2111 -1573
- package/dist/m/page/{editor-NNJA4OKD.js → editor-6LRB3GE4.js} +123 -21
- package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
- package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
- package/dist/m/plugins/{demo-RM5IR6AO.js → demo-OYO5GCAO.js} +1 -1
- package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
- package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
- package/dist/m/plugins/live-AD4H6XSL.js +1 -0
- package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
- package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
- package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
- package/dist/m/plugins/{people-SGPJKANK.js → people-JCHVPF7P.js} +1 -1
- package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
- package/dist/m/plugins/{richclay-NRQDSWX6.js → richclay-KLGETK77.js} +2 -2
- package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
- package/dist/m/plugins/source-TJXOIOYY.js +1 -0
- package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
- package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
- package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
- package/dist/m/plugins/{upload-WLYKK6TL.js → upload-NHGJXZK2.js} +1 -1
- package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
- package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
- package/dist/m/plugins/{wire-4N6GYAF6.js → wire-VBWXEXU2.js} +1 -1
- package/dist/m/reader-core-4W2KYEWQ.js +77 -0
- package/dist/m/route/route-62CHXL5B.js +1 -0
- package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
- package/dist/m/ui/index-IVWN2QC4.js +1 -0
- package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
- package/dist/offline-manifest.json +1367 -930
- package/dist/page/themes/light-1.css +8 -7
- package/dist/server/core.cjs +1440 -288
- package/dist/server/offline-runtime.cjs +63 -63
- package/dist/server/offline-runtime.mjs +63 -63
- package/dist/server/page-compaction-worker.cjs +3710 -0
- package/dist/server/rooms.mjs +5 -0
- package/docs/guide.md +653 -101
- package/entries/sap.js +5 -5
- package/package.json +1 -1
- package/src/core/admin-contenteditable.js +5 -2
- package/src/core/admin-inputs.js +1 -0
- package/src/core/admin-onclick.js +2 -1
- package/src/core/conflict-notice-ui.js +4 -7
- package/src/core/download.js +2 -2
- package/src/core/host-meta.js +38 -0
- package/src/core/is-edit-mode.js +17 -1
- package/src/core/mode-key.js +125 -19
- package/src/core/mode-support.js +5 -0
- package/src/core/mode-transition.js +20 -6
- package/src/core/preserve-authored.js +4 -4
- package/src/core/recovery.js +121 -33
- package/src/core/save-accepted.js +33 -0
- package/src/core/save-core.js +106 -31
- package/src/core/save-job.js +108 -0
- package/src/core/save-prepare.js +53 -0
- package/src/core/save-size-notice.js +3 -1
- package/src/core/save-slices.js +15 -0
- package/src/core/save.js +557 -127
- package/src/core/serialize-steps.js +109 -0
- package/src/core/snapshot.js +170 -39
- package/src/core/source-map.js +83 -38
- package/src/core/stale-host-notice.js +1 -0
- package/src/core/upgrade-run.js +59 -8
- package/src/dom/active-element.js +5 -0
- package/src/lib/assets.js +1 -0
- package/src/lib/slice.js +65 -0
- package/src/lib/uimotion.js +774 -0
- package/src/lib/unsaved-state.js +2 -2
- package/src/live/fast-lane.js +10 -3
- package/src/live/local-lane.js +202 -11
- package/src/live/ops.js +16 -0
- package/src/live/page-compaction.js +162 -0
- package/src/live/server-lane.js +1 -0
- package/src/live/store.js +11 -3
- package/src/loader-logic.js +3 -1
- package/src/loader.js +14 -4
- package/src/page/adopt-boot.js +2 -1
- package/src/page/awareness.js +33 -2
- package/src/page/blocks.js +89 -27
- package/src/page/boot-pack.js +1 -1
- package/src/page/chart-tables.js +21 -3
- package/src/page/chart.js +406 -63
- package/src/page/checklist.js +10 -135
- package/src/page/checkpoint-proof.js +7 -1
- package/src/page/chrome.js +250 -51
- package/src/page/clipboard-shape.js +126 -0
- package/src/page/drag.js +33 -3
- package/src/page/editor.js +136 -25
- package/src/page/export-assets.js +357 -0
- package/src/page/export-html.js +209 -0
- package/src/page/export-markdown.js +361 -0
- package/src/page/features/checklist.js +428 -0
- package/src/page/features/export-html.js +189 -0
- package/src/page/features/export-markdown.js +169 -0
- package/src/page/features/import-markdown.js +349 -0
- package/src/page/features/media.js +61 -5
- package/src/page/features/registry.js +18 -0
- package/src/page/features/table.js +373 -83
- package/src/page/features/title-cover.js +331 -0
- package/src/page/features/title-icon.js +213 -0
- package/src/page/features/toc.js +87 -0
- package/src/page/format-command.js +25 -1
- package/src/page/format-gate.js +68 -18
- package/src/page/format-host.js +17 -0
- package/src/page/format-native-commands.js +21 -5
- package/src/page/image-actions.js +64 -0
- package/src/page/import-markdown.js +262 -0
- package/src/page/items.js +135 -17
- package/src/page/markdown.js +6 -2
- package/src/page/media.js +35 -119
- package/src/page/placeholder.js +14 -1
- package/src/page/projection.js +98 -10
- package/src/page/recover.js +10 -12
- package/src/page/recovery-ui.js +464 -0
- package/src/page/rich/chart.js +63 -7
- package/src/page/save-projection.js +28 -0
- package/src/page/save-states.js +13 -3
- package/src/page/schema.js +19 -3
- package/src/page/table-paste.js +3 -1
- package/src/page/table-style.js +4 -4
- package/src/page/table.js +44 -2
- package/src/page/themes/light-1.css +8 -7
- package/src/page/themes/light-1.source.css +8 -7
- package/src/page/toc-link.js +31 -0
- package/src/page/toc.js +5 -0
- package/src/page/view-hooks.js +38 -12
- package/src/page/view.js +13 -0
- package/src/page/ybind/binding.js +88 -5
- package/src/page/ybind/bridge.js +187 -20
- package/src/page/ybind/containment.js +162 -0
- package/src/page/ybind/doc.js +17 -9
- package/src/page/ybind/file-debt.js +63 -0
- package/src/page/ybind/layout.js +7 -2
- package/src/page/ybind/marks.js +1 -1
- package/src/page/ybind/moves.js +5 -4
- package/src/page/ybind/origins.js +106 -4
- package/src/page/ybind/parts.js +8 -1
- package/src/page/ybind/provider.js +113 -17
- package/src/page/ybind/recovery-proof.js +32 -9
- package/src/page/ybind/recovery-replay.js +558 -15
- package/src/page/ybind/recovery.js +174 -45
- package/src/page/ybind/table.js +67 -11
- package/src/page/ybind/walk-delta.js +258 -0
- package/src/page/ybind/walk.js +54 -29
- package/src/page/ybind/writer.js +1 -1
- package/src/plugins/ai-edit.js +13 -9
- package/src/plugins/indicator-chip.js +91 -0
- package/src/plugins/indicator.js +13 -80
- package/src/plugins/live.js +89 -14
- package/src/plugins/menu/actions.js +196 -0
- package/src/plugins/menu/people.js +279 -0
- package/src/plugins/menu/placement.js +121 -0
- package/src/plugins/menu/preferences.js +9 -0
- package/src/plugins/menu/seo.js +316 -397
- package/src/plugins/menu/style.js +171 -0
- package/src/plugins/menu/variants.js +296 -0
- package/src/plugins/menu.js +186 -183
- package/src/plugins/page-view.js +3 -2
- package/src/plugins/page.js +249 -44
- package/src/plugins/sortable.js +8 -1
- package/src/plugins/source.js +6 -0
- package/src/plugins/upgrade.js +51 -17
- package/src/plugins/upload-ui.js +12 -17
- package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
- package/src/plugins/visitor-drafts/intro-style.js +7 -2
- package/src/plugins/visitor-drafts/intro.js +5 -3
- package/src/plugins/visitor-drafts/owner-chip.js +8 -12
- package/src/plugins/visitor-drafts/prompt.js +11 -9
- package/src/plugins/visitor-drafts/source.js +4 -3
- package/src/plugins/visitor-drafts/store.js +138 -9
- package/src/plugins/visitor-drafts/ui-memory.js +2 -2
- package/src/plugins/visitor-drafts-owner.js +4 -6
- package/src/plugins/visitor-drafts.js +5 -3
- package/src/recovery/store.js +1 -1
- package/src/route/route.js +4 -0
- package/src/server/rooms/index.js +5 -1
- package/src/server/rooms/live-data-rooms.js +302 -50
- package/src/server/rooms/page-compaction-worker.js +12 -0
- package/src/server/rooms/page-compactor.js +92 -0
- package/src/server/rooms/page-room-conformance.js +461 -0
- package/src/source/parse-steps.js +94 -0
- package/src/source/session.js +233 -57
- package/src/sync/editing-sender.js +12 -0
- package/src/sync/live-sync-edit.js +22 -2
- package/src/sync/live-sync.js +150 -23
- package/src/sync/page-relay.js +52 -0
- package/src/sync/presence-store.js +44 -4
- package/src/sync/presence.js +6 -3
- package/src/sync/saved-stream.js +11 -6
- package/src/ui/bevel-controls.js +1 -0
- package/src/ui/bevel-dialog.js +5 -2
- package/src/ui/editor-ui.js +77 -0
- package/src/ui/page-colour.js +281 -0
- package/src/ui/toast.js +16 -7
- package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +9 -9
- package/src/vendor/parse5.vendor.js +2 -2
- package/src/vendor/sap-core.vendor.js +5 -5
- package/dist/m/c-2SPVQSHC.js +0 -1
- package/dist/m/c-336SGU5W.js +0 -1
- package/dist/m/c-3H6BZDNJ.js +0 -6
- package/dist/m/c-4DFEJ2PO.js +0 -3
- package/dist/m/c-56QRZTZH.js +0 -1
- package/dist/m/c-5J4E7O4Y.js +0 -4
- package/dist/m/c-5LKDWQTL.js +0 -19
- package/dist/m/c-5V3MOVJT.js +0 -1
- package/dist/m/c-6AZOGAPE.js +0 -1
- package/dist/m/c-6CRUUXOX.js +0 -5
- package/dist/m/c-6HMB24EV.js +0 -1
- package/dist/m/c-6KEVEQ7L.js +0 -1
- package/dist/m/c-A3E3NIMC.js +0 -1
- package/dist/m/c-BQ6LOBEE.js +0 -1
- package/dist/m/c-BTDI4Y23.js +0 -1
- package/dist/m/c-CJ3A7ZKE.js +0 -1
- package/dist/m/c-CQHPGKX2.js +0 -1
- package/dist/m/c-DFS4XZJV.js +0 -2
- package/dist/m/c-DNENWY4O.js +0 -1
- package/dist/m/c-E4LIR5X5.js +0 -1
- package/dist/m/c-FDNHHZY5.js +0 -1
- package/dist/m/c-FROJUZMV.js +0 -1
- package/dist/m/c-G2JPKCTP.js +0 -1
- package/dist/m/c-GB6TU7WV.js +0 -1
- package/dist/m/c-GFJJEBTU.js +0 -1
- package/dist/m/c-GJAEGGXY.js +0 -1
- package/dist/m/c-GOWBDM6J.js +0 -1
- package/dist/m/c-HFKUVACH.js +0 -1
- package/dist/m/c-HHDFCNHV.js +0 -1
- package/dist/m/c-IEM4NTCS.js +0 -1
- package/dist/m/c-JDCX26UP.js +0 -25
- package/dist/m/c-KGVGROZT.js +0 -1
- package/dist/m/c-KQ6TYJAP.js +0 -135
- package/dist/m/c-LFPHCXY3.js +0 -1
- package/dist/m/c-LRPUO2MX.js +0 -1
- package/dist/m/c-LWLBHLVC.js +0 -3
- package/dist/m/c-M2L47S2X.js +0 -7
- package/dist/m/c-MFP2ZWD4.js +0 -1
- package/dist/m/c-MKSQ4Y57.js +0 -1
- package/dist/m/c-NUFFJVVJ.js +0 -1
- package/dist/m/c-NUHSPT5P.js +0 -1
- package/dist/m/c-O72HVYSA.js +0 -1
- package/dist/m/c-OIBUTDXI.js +0 -1
- package/dist/m/c-OW5QIGG3.js +0 -1
- package/dist/m/c-PGRCK73F.js +0 -1
- package/dist/m/c-Q7NQD65V.js +0 -1
- package/dist/m/c-QH63LQOS.js +0 -1
- package/dist/m/c-R7IWVINQ.js +0 -1
- package/dist/m/c-RM7J7BV2.js +0 -1
- package/dist/m/c-S4ZEH46B.js +0 -9
- package/dist/m/c-SDAXDM2D.js +0 -1
- package/dist/m/c-SMGU7RT3.js +0 -235
- package/dist/m/c-SPIORRYS.js +0 -1
- package/dist/m/c-SWLRT66R.js +0 -1
- package/dist/m/c-TCJTVAFN.js +0 -1
- package/dist/m/c-TQEMVJCS.js +0 -1
- package/dist/m/c-TYLXX4NF.js +0 -21
- package/dist/m/c-U62OB4BT.js +0 -1
- package/dist/m/c-U6YWUAR3.js +0 -8
- package/dist/m/c-UDCSU5G5.js +0 -2
- package/dist/m/c-VI5RMSVY.js +0 -1
- package/dist/m/c-VRZN56XY.js +0 -1
- package/dist/m/c-W5MCHEGA.js +0 -1
- package/dist/m/c-WNYBLIOZ.js +0 -1
- package/dist/m/c-WWXIDH4O.js +0 -28
- package/dist/m/c-XCNTK76U.js +0 -1
- package/dist/m/c-XMS6DPBR.js +0 -1
- package/dist/m/c-ZUCOKOHT.js +0 -1
- package/dist/m/c-ZVPVXTJF.js +0 -4
- package/dist/m/c-ZVRKXKM7.js +0 -1
- package/dist/m/core/autosave-HRYGUWV6.js +0 -1
- package/dist/m/core/conflict-notice-6MROJN2L.js +0 -1
- package/dist/m/core/is-edit-mode-KTWA3O5N.js +0 -1
- package/dist/m/core/recovery-FLTCC2OC.js +0 -1
- package/dist/m/core/save-HQZ4S667.js +0 -1
- package/dist/m/core/save-core-KGQWFUNH.js +0 -1
- package/dist/m/core/snapshot-EGQZNT45.js +0 -1
- package/dist/m/core/unsaved-warning-IDJLD66H.js +0 -1
- package/dist/m/loader-S4I7GKXR.js +0 -1
- package/dist/m/manifest-4C419D67.js +0 -1
- package/dist/m/manifest-edit-B077F288.js +0 -1
- package/dist/m/plugins/ai-edit-QN4PMMNL.js +0 -6
- package/dist/m/plugins/indicator-URDBBOQO.js +0 -1
- package/dist/m/plugins/live-HFWD4OIH.js +0 -1
- package/dist/m/plugins/menu-3HIEQSI6.js +0 -138
- package/dist/m/plugins/page-JOKB6XPR.js +0 -2
- package/dist/m/plugins/page-view-NXOJHNW6.js +0 -1
- package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
- package/dist/m/plugins/sortable-GWT4PWDV.js +0 -1
- package/dist/m/plugins/source-4ZRSAIYM.js +0 -1
- package/dist/m/plugins/undo-VDA5P7FH.js +0 -1
- package/dist/m/plugins/upgrade-W7FG5VFG.js +0 -1
- package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +0 -21
- package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +0 -1
- package/dist/m/reader-core-DXYCURFF.js +0 -68
- package/dist/m/route/route-KOCZSEGN.js +0 -1
- package/dist/m/sync/live-sync-J7VMYTKS.js +0 -1
- package/dist/m/ui/index-3JLVI27O.js +0 -1
- package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
- /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
- /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-NRQDSWX6.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
package/src/page/chart.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { schema } from "./schema.js";
|
|
2
2
|
import { blockJson, keyBetween, newId, orderedIds } from "./model.js";
|
|
3
|
-
import { chartTables } from "./chart-tables.js";
|
|
3
|
+
import { chartTables, tableName } from "./chart-tables.js";
|
|
4
4
|
import { chartModel, renderChartSvg } from "./rich/chart.js";
|
|
5
5
|
import { openPageMenu } from "./chrome.js";
|
|
6
6
|
import { project } from "./projection.js";
|
|
7
7
|
import { columnKinds } from "./table-kinds.js";
|
|
8
8
|
import { cellText } from "./rich/cell.js";
|
|
9
|
+
import { registerUnsavedState } from "../lib/unsaved-state.js";
|
|
10
|
+
import toast from "../ui/toast-lazy.js";
|
|
11
|
+
import { asStep } from "./blocksel.js";
|
|
12
|
+
import { state } from "../vendor/prosemirror.vendor.js";
|
|
13
|
+
|
|
14
|
+
const { TextSelection } = state;
|
|
9
15
|
|
|
10
16
|
const views = new WeakMap();
|
|
17
|
+
// A chart inserted by the slash command or a table action opens its inspector as soon
|
|
18
|
+
// as its node view exists; one that arrives any other way stays closed.
|
|
19
|
+
const openOnMount = new Set();
|
|
11
20
|
|
|
12
21
|
function sourceTable(doc, id) {
|
|
13
22
|
let table = null;
|
|
@@ -25,6 +34,7 @@ function chartAttrs(table, pos, parent) {
|
|
|
25
34
|
if (!colIds.length) return null;
|
|
26
35
|
const kinds = columnKinds(table);
|
|
27
36
|
const series = colIds.filter((id) => id !== colIds[0] && kinds[id] === "number");
|
|
37
|
+
if (!series.length) return null;
|
|
28
38
|
return {
|
|
29
39
|
id: newId(),
|
|
30
40
|
pos,
|
|
@@ -32,7 +42,7 @@ function chartAttrs(table, pos, parent) {
|
|
|
32
42
|
source: table.attrs.id,
|
|
33
43
|
kind: "bar",
|
|
34
44
|
x: colIds[0],
|
|
35
|
-
series
|
|
45
|
+
series,
|
|
36
46
|
title: "",
|
|
37
47
|
highlight: null,
|
|
38
48
|
};
|
|
@@ -45,7 +55,11 @@ export function insertChartAfterTable(view, tablePos) {
|
|
|
45
55
|
const after = tablePos + table.nodeSize;
|
|
46
56
|
const next = doc.maybeChild(doc.resolve(after).index(0));
|
|
47
57
|
const attrs = chartAttrs(table, keyBetween(table.attrs.pos, next?.attrs.pos ?? null), table.attrs.parent);
|
|
48
|
-
if (!attrs)
|
|
58
|
+
if (!attrs) {
|
|
59
|
+
toast("This table has no number column to chart yet.");
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
openOnMount.add(attrs.id);
|
|
49
63
|
view.dispatch(view.state.tr.insert(after, schema.nodes.chart.create(attrs)));
|
|
50
64
|
return true;
|
|
51
65
|
}
|
|
@@ -65,9 +79,14 @@ function placeChart(view, index, table, remove) {
|
|
|
65
79
|
const next = tr.doc.maybeChild(index + 1);
|
|
66
80
|
const pos = replace ? node.attrs.pos : keyBetween(node.attrs.pos, next?.attrs.pos ?? null);
|
|
67
81
|
const attrs = chartAttrs(table, pos, node.attrs.parent ?? null);
|
|
68
|
-
if (!attrs)
|
|
82
|
+
if (!attrs) {
|
|
83
|
+
if (remove) view.dispatch(view.state.tr.delete(remove.from, remove.to));
|
|
84
|
+
toast("This table has no number column to chart yet.");
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
69
87
|
const chart = schema.nodes.chart.create(attrs);
|
|
70
88
|
tr = replace ? tr.replaceWith(at, at + node.nodeSize, chart) : tr.insert(at + node.nodeSize, chart);
|
|
89
|
+
openOnMount.add(attrs.id);
|
|
71
90
|
view.dispatch(tr.scrollIntoView());
|
|
72
91
|
return true;
|
|
73
92
|
}
|
|
@@ -76,49 +95,151 @@ export const tableLabel = (table) => orderedIds(table.attrs.cols).map((id) => ta
|
|
|
76
95
|
|
|
77
96
|
export function chartFromSlash(view, index, remove = null) {
|
|
78
97
|
const tables = chartTables(view.state.doc);
|
|
79
|
-
if (!tables.length)
|
|
98
|
+
if (!tables.length) {
|
|
99
|
+
if (remove) view.dispatch(view.state.tr.delete(remove.from, remove.to));
|
|
100
|
+
toast("Add a table first: a chart plots the numbers in a table on this page.");
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
80
103
|
if (tables.length === 1) return placeChart(view, index, tables[0], remove);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
const doc = view.state.doc;
|
|
105
|
+
const anchor = view.nodeDOM(doc.resolve(view.state.selection.head).before(1)) ?? view.dom;
|
|
106
|
+
return openPageMenu(view, anchor, tables.map((table) => ({
|
|
107
|
+
label: tableName(doc, table.attrs.id) ?? "",
|
|
108
|
+
description: `${table.childCount} ${table.childCount === 1 ? "row" : "rows"} · ${tableLabel(table)}`,
|
|
85
109
|
icon: "▦",
|
|
86
110
|
run: () => placeChart(view, index, table, remove),
|
|
87
111
|
})));
|
|
88
112
|
}
|
|
89
113
|
|
|
114
|
+
const ICON = (body) => `<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
|
|
115
|
+
const TABLE_ICON = ICON('<rect x="2.5" y="3" width="11" height="10" rx="1.5"/><path d="M2.5 6.5h11M6 6.5V13"/>');
|
|
116
|
+
const GEAR_ICON = ICON('<g transform="scale(0.6667)" stroke-width="2.25"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></g>');
|
|
117
|
+
const TRASH_ICON = ICON('<path d="M2.75 4.5h10.5M6.25 4.5V2.75h3.5V4.5M4.25 4.5l.6 8.75h6.3l.6-8.75"/>');
|
|
118
|
+
const CHART_TYPES = [["bar", "Bar"], ["line", "Line"], ["pie", "Pie"]];
|
|
119
|
+
const UNTITLED = "Untitled chart";
|
|
120
|
+
|
|
121
|
+
function tableOffset(doc, id) {
|
|
122
|
+
let pos = null;
|
|
123
|
+
doc.forEach((node, offset) => { if (pos === null && node.type === schema.nodes.table && node.attrs.id === id) pos = offset; });
|
|
124
|
+
return pos;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function documentTables(doc) {
|
|
128
|
+
const tables = [];
|
|
129
|
+
doc.forEach((node) => { if (node.type === schema.nodes.table && orderedIds(node.attrs.cols).length > 1) tables.push(node); });
|
|
130
|
+
return tables.map((node) => ({ id: node.attrs.id, name: tableName(doc, node.attrs.id), rows: node.childCount }));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function defaultColumns(table) {
|
|
134
|
+
const colIds = orderedIds(table.attrs.cols);
|
|
135
|
+
if (!colIds.length) return null;
|
|
136
|
+
const kinds = columnKinds(table);
|
|
137
|
+
const x = colIds.find((id) => kinds[id] !== "number") ?? colIds[0];
|
|
138
|
+
const series = colIds.filter((id) => id !== x && kinds[id] === "number");
|
|
139
|
+
return { x, series: series.length ? series : colIds.filter((id) => id !== x).slice(0, 1) };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function element(doc, tag, className, text = null) {
|
|
143
|
+
const node = doc.createElement(tag);
|
|
144
|
+
if (className) node.className = className;
|
|
145
|
+
if (text !== null) node.textContent = text;
|
|
146
|
+
return node;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function option(doc, value, label, selected = false) {
|
|
150
|
+
const node = element(doc, "option", "", label);
|
|
151
|
+
node.value = value;
|
|
152
|
+
node.selected = selected;
|
|
153
|
+
return node;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function select(doc, field, label) {
|
|
157
|
+
const node = element(doc, "select", "page-chart-select");
|
|
158
|
+
node.dataset.field = field;
|
|
159
|
+
node.setAttribute("aria-label", label);
|
|
160
|
+
return node;
|
|
161
|
+
}
|
|
162
|
+
|
|
90
163
|
export class ChartView {
|
|
91
164
|
constructor(node, view, getPos) {
|
|
92
165
|
this.node = node;
|
|
93
166
|
this.view = view;
|
|
94
167
|
this.getPos = getPos;
|
|
95
168
|
this.source = undefined;
|
|
169
|
+
this.tables = null;
|
|
96
170
|
this.frame = null;
|
|
171
|
+
this.unsaved = null;
|
|
172
|
+
this.titleInput = null;
|
|
97
173
|
const doc = view.dom.ownerDocument;
|
|
174
|
+
this.doc = doc;
|
|
98
175
|
this.dom = doc.createElement("figure");
|
|
99
176
|
this.dom.className = "page-chart";
|
|
100
177
|
this.dom.dataset.id = node.attrs.id ?? "";
|
|
101
178
|
this.dom.dataset.type = "chart";
|
|
102
|
-
this.
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
105
|
-
this.
|
|
106
|
-
this.
|
|
107
|
-
this.
|
|
108
|
-
this.
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
179
|
+
this.head = element(doc, "div", "page-chart-head");
|
|
180
|
+
this.head.contentEditable = "false";
|
|
181
|
+
this.title = element(doc, "span", "page-chart-title");
|
|
182
|
+
this.chip = element(doc, "button", "page-chart-source");
|
|
183
|
+
this.chip.type = "button";
|
|
184
|
+
this.chip.contentEditable = "false";
|
|
185
|
+
this.chip.setAttribute("aria-label", "Go to source table");
|
|
186
|
+
this.chip.innerHTML = TABLE_ICON;
|
|
187
|
+
this.chipName = element(doc, "span", "page-chart-source-name");
|
|
188
|
+
this.chip.append(this.chipName);
|
|
189
|
+
this.chip.addEventListener("click", () => this.goToSource());
|
|
190
|
+
this.edit = element(doc, "button", "page-chart-edit");
|
|
191
|
+
this.edit.type = "button";
|
|
192
|
+
this.edit.contentEditable = "false";
|
|
193
|
+
this.edit.setAttribute("aria-haspopup", "dialog");
|
|
194
|
+
this.edit.setAttribute("aria-expanded", "false");
|
|
195
|
+
this.edit.innerHTML = GEAR_ICON;
|
|
196
|
+
this.edit.append("Edit chart");
|
|
197
|
+
this.edit.addEventListener("click", () => (this.panel.isConnected ? this.closePanel(true) : this.openPanel()));
|
|
198
|
+
this.head.append(this.title, this.chip, this.edit);
|
|
199
|
+
this.plot = element(doc, "div", "page-chart-plot");
|
|
200
|
+
this.broken = element(doc, "div", "page-chart-broken");
|
|
201
|
+
this.broken.hidden = true;
|
|
202
|
+
this.broken.contentEditable = "false";
|
|
203
|
+
this.brokenKey = null;
|
|
204
|
+
this.brokenSelect = null;
|
|
205
|
+
this.broken.addEventListener("click", (event) => this.brokenClick(event));
|
|
206
|
+
this.panel = element(doc, "div", "page-chart-panel");
|
|
207
|
+
this.panel.tabIndex = -1;
|
|
208
|
+
this.panel.contentEditable = "false";
|
|
209
|
+
this.panel.setAttribute("role", "dialog");
|
|
210
|
+
this.panel.setAttribute("aria-label", "Chart settings");
|
|
211
|
+
this.panel.addEventListener("click", (event) => this.panelClick(event));
|
|
212
|
+
this.panel.addEventListener("change", (event) => this.panelChange(event));
|
|
213
|
+
this.panel.addEventListener("mousedown", (event) => { if (event.target.closest?.("button")) event.preventDefault(); });
|
|
214
|
+
this.outside = (event) => {
|
|
215
|
+
if (!this.panel.isConnected || this.dom.contains(event.target)) return;
|
|
216
|
+
this.commitTitle();
|
|
217
|
+
this.closePanel();
|
|
218
|
+
};
|
|
219
|
+
this.escape = (event) => {
|
|
220
|
+
if (!this.panel.isConnected || event.key !== "Escape") return;
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
this.closePanel(true);
|
|
224
|
+
};
|
|
225
|
+
doc.addEventListener("mousedown", this.outside, true);
|
|
226
|
+
doc.addEventListener("keydown", this.escape, true);
|
|
227
|
+
this.dom.append(this.head, this.plot, this.broken);
|
|
113
228
|
if (!views.has(view)) views.set(view, new Set());
|
|
114
229
|
views.get(view).add(this);
|
|
115
230
|
this.refresh();
|
|
231
|
+
if (openOnMount.delete(node.attrs.id)) this.openSoon();
|
|
116
232
|
}
|
|
117
233
|
|
|
118
234
|
refresh() {
|
|
119
|
-
|
|
120
|
-
if (
|
|
235
|
+
this.renderHead();
|
|
236
|
+
if (this.panel.isConnected && this.panelKey() !== this.renderedKey) this.redrawPanel();
|
|
237
|
+
const doc = this.view.state.doc;
|
|
238
|
+
const source = sourceTable(doc, this.node.attrs.source);
|
|
239
|
+
const tables = documentTables(doc).map((table) => `${table.id}:${table.name}:${table.rows}`).join("|");
|
|
240
|
+
if (source === this.source && tables === this.tables) return;
|
|
121
241
|
this.source = source;
|
|
242
|
+
this.tables = tables;
|
|
122
243
|
this.schedule();
|
|
123
244
|
}
|
|
124
245
|
|
|
@@ -133,7 +254,70 @@ export class ChartView {
|
|
|
133
254
|
const attrs = this.node.attrs;
|
|
134
255
|
const chart = chartModel({ id: attrs.id, type: attrs.kind, x: attrs.x, series: attrs.series, title: attrs.title, highlight: attrs.highlight }, tableData(this.source));
|
|
135
256
|
this.plot.innerHTML = renderChartSvg(chart, { width: 640, height: 320, narrow: this.dom.getBoundingClientRect().width < 480 });
|
|
136
|
-
this.
|
|
257
|
+
this.renderBroken(chart);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
renderBroken(chart) {
|
|
261
|
+
const broken = chart.state === "missing" && chart.reason === "table";
|
|
262
|
+
this.plot.hidden = broken;
|
|
263
|
+
this.broken.hidden = !broken;
|
|
264
|
+
if (!broken) {
|
|
265
|
+
if (this.brokenKey !== null) {
|
|
266
|
+
this.broken.textContent = "";
|
|
267
|
+
this.brokenKey = null;
|
|
268
|
+
this.brokenSelect = null;
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const tables = documentTables(this.view.state.doc);
|
|
273
|
+
const key = tables.map((table) => `${table.id}:${table.name}:${table.rows}`).join("|");
|
|
274
|
+
if (this.brokenKey === key) return;
|
|
275
|
+
this.brokenKey = key;
|
|
276
|
+
this.brokenSelect = null;
|
|
277
|
+
const doc = this.doc;
|
|
278
|
+
this.broken.textContent = "";
|
|
279
|
+
this.broken.append(
|
|
280
|
+
element(doc, "div", "page-chart-broken-head", "This chart's table was deleted"),
|
|
281
|
+
element(doc, "p", "page-chart-broken-text", "Pick another table in this document, or delete the chart. Undo brings the table back."),
|
|
282
|
+
);
|
|
283
|
+
const actions = element(doc, "div", "page-chart-broken-actions");
|
|
284
|
+
if (tables.length) {
|
|
285
|
+
const sources = select(doc, "rescue", "Source table");
|
|
286
|
+
for (const table of tables) sources.append(option(doc, table.id, `${table.name} (${table.rows} ${table.rows === 1 ? "row" : "rows"})`));
|
|
287
|
+
this.brokenSelect = sources;
|
|
288
|
+
const use = element(doc, "button", "page-chart-use", "Use this table");
|
|
289
|
+
use.type = "button";
|
|
290
|
+
use.dataset.action = "use";
|
|
291
|
+
actions.append(sources, use);
|
|
292
|
+
}
|
|
293
|
+
const remove = element(doc, "button", "page-chart-delete", "Delete chart");
|
|
294
|
+
remove.type = "button";
|
|
295
|
+
remove.dataset.action = "delete";
|
|
296
|
+
actions.append(remove);
|
|
297
|
+
this.broken.append(actions);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
brokenClick(event) {
|
|
301
|
+
const button = event.target.closest?.("button[data-action]");
|
|
302
|
+
if (!button) return;
|
|
303
|
+
if (button.dataset.action === "delete") this.removeChart();
|
|
304
|
+
else if (button.dataset.action === "use") this.switchSource(this.brokenSelect?.value ?? "");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
openSoon() {
|
|
308
|
+
const win = this.dom.ownerDocument.defaultView;
|
|
309
|
+
const raf = win.requestAnimationFrame?.bind(win) ?? ((fn) => win.setTimeout(fn, 0));
|
|
310
|
+
raf(() => { if (!this.view.isDestroyed && this.dom.isConnected) this.openPanel(); });
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
renderHead() {
|
|
314
|
+
const attrs = this.node.attrs;
|
|
315
|
+
const name = tableName(this.view.state.doc, attrs.source);
|
|
316
|
+
const broken = !sourceTable(this.view.state.doc, attrs.source);
|
|
317
|
+
this.title.textContent = attrs.title || UNTITLED;
|
|
318
|
+
this.title.classList.toggle("is-empty", !attrs.title);
|
|
319
|
+
this.chipName.textContent = broken ? "No table" : name ?? "";
|
|
320
|
+
this.chip.disabled = broken;
|
|
137
321
|
}
|
|
138
322
|
|
|
139
323
|
setAttrs(changes) {
|
|
@@ -144,48 +328,199 @@ export class ChartView {
|
|
|
144
328
|
this.view.dispatch(this.view.state.tr.setNodeMarkup(pos, null, { ...node.attrs, ...changes }));
|
|
145
329
|
}
|
|
146
330
|
|
|
147
|
-
|
|
148
|
-
|
|
331
|
+
row(label, ...children) {
|
|
332
|
+
const row = element(this.doc, "div", "page-chart-row");
|
|
333
|
+
if (label) row.append(element(this.doc, "span", "page-chart-label", label));
|
|
334
|
+
row.append(...children);
|
|
335
|
+
return row;
|
|
149
336
|
}
|
|
150
337
|
|
|
151
|
-
|
|
338
|
+
panelKey() {
|
|
339
|
+
const doc = this.view.state.doc;
|
|
340
|
+
const tables = documentTables(doc).map((table) => `${table.id}:${table.name}:${table.rows}`).join("|");
|
|
341
|
+
return JSON.stringify([this.node.attrs, tables, tableData(sourceTable(doc, this.node.attrs.source))]);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
redrawPanel() {
|
|
345
|
+
const active = this.doc.activeElement;
|
|
346
|
+
const focus = active && this.panel.contains(active) ? active.dataset ?? {} : null;
|
|
347
|
+
const input = this.titleInput;
|
|
348
|
+
const draft = input && input.value !== this.renderedTitle ? { value: input.value, start: input.selectionStart, end: input.selectionEnd } : null;
|
|
349
|
+
this.renderPanel();
|
|
350
|
+
if (draft && this.titleInput) this.titleInput.value = draft.value;
|
|
351
|
+
if (!focus) return;
|
|
352
|
+
const match = [...this.panel.querySelectorAll("button, input, select")].find((control) =>
|
|
353
|
+
["field", "kind", "col", "action"].every((key) => control.dataset[key] === focus[key]));
|
|
354
|
+
if (!match) return this.panel.focus();
|
|
355
|
+
match.focus();
|
|
356
|
+
if (draft && match === this.titleInput) match.setSelectionRange?.(draft.start, draft.end);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
renderPanel() {
|
|
360
|
+
const doc = this.doc;
|
|
361
|
+
this.renderedKey = this.panelKey();
|
|
362
|
+
this.renderedTitle = this.node.attrs.title ?? "";
|
|
152
363
|
const attrs = this.node.attrs;
|
|
153
|
-
const
|
|
154
|
-
const cols =
|
|
155
|
-
const rows =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
])
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
364
|
+
const source = tableData(this.source);
|
|
365
|
+
const cols = source?.cols ?? [];
|
|
366
|
+
const rows = source?.rows ?? [];
|
|
367
|
+
const pie = attrs.kind === "pie";
|
|
368
|
+
this.unsaved?.dispose();
|
|
369
|
+
this.unsaved = null;
|
|
370
|
+
this.titleInput = null;
|
|
371
|
+
this.panel.textContent = "";
|
|
372
|
+
|
|
373
|
+
const types = element(doc, "div", "page-chart-types");
|
|
374
|
+
for (const [kind, label] of CHART_TYPES) {
|
|
375
|
+
const button = element(doc, "button", "page-chart-type", label);
|
|
376
|
+
button.type = "button";
|
|
377
|
+
button.dataset.kind = kind;
|
|
378
|
+
button.setAttribute("aria-pressed", String(attrs.kind === kind));
|
|
379
|
+
types.append(button);
|
|
380
|
+
}
|
|
381
|
+
this.panel.append(this.row("Chart type", types));
|
|
382
|
+
|
|
383
|
+
const sources = select(doc, "source", "Source table");
|
|
384
|
+
for (const table of documentTables(this.view.state.doc)) {
|
|
385
|
+
sources.append(option(doc, table.id, `${table.name} (${table.rows} ${table.rows === 1 ? "row" : "rows"})`, table.id === attrs.source));
|
|
386
|
+
}
|
|
387
|
+
this.panel.append(this.row("Source table", sources));
|
|
388
|
+
|
|
389
|
+
const labels = select(doc, "labels", "Labels column");
|
|
390
|
+
for (const col of cols) labels.append(option(doc, col.id, col.name || col.id, col.id === attrs.x));
|
|
391
|
+
this.panel.append(this.row("Labels column", labels));
|
|
392
|
+
|
|
393
|
+
const values = element(doc, "div", "page-chart-values");
|
|
394
|
+
cols.filter((col) => col.id !== attrs.x).forEach((col, index) => {
|
|
395
|
+
const line = element(doc, "label", "page-chart-value");
|
|
396
|
+
const check = element(doc, "input");
|
|
397
|
+
check.type = "checkbox";
|
|
398
|
+
check.dataset.field = "series";
|
|
399
|
+
check.dataset.col = col.id;
|
|
400
|
+
check.checked = attrs.series.includes(col.id);
|
|
401
|
+
const swatch = element(doc, "span", "page-chart-swatch");
|
|
402
|
+
swatch.style.background = `var(--chart-${index % 6 + 1})`;
|
|
403
|
+
swatch.setAttribute("aria-hidden", "true");
|
|
404
|
+
line.append(check, swatch, element(doc, "span", "page-chart-value-name", col.name || col.id));
|
|
405
|
+
values.append(line);
|
|
406
|
+
});
|
|
407
|
+
const valueRow = this.row(pie ? "Value column" : "Value columns", values);
|
|
408
|
+
if (pie) valueRow.append(element(doc, "span", "page-chart-hint", "Pie charts plot the first value column across rows."));
|
|
409
|
+
this.panel.append(valueRow);
|
|
410
|
+
|
|
411
|
+
const highlight = select(doc, "highlight", "Highlight row");
|
|
412
|
+
highlight.append(option(doc, "", "None", !attrs.highlight));
|
|
413
|
+
for (const row of rows) highlight.append(option(doc, row.id, cellText(row.cells?.[attrs.x]) || row.id, row.id === attrs.highlight));
|
|
414
|
+
this.panel.append(this.row("Highlight row", highlight));
|
|
415
|
+
|
|
416
|
+
const title = element(doc, "input", "page-chart-title-input");
|
|
417
|
+
title.type = "text";
|
|
418
|
+
title.dataset.field = "title";
|
|
419
|
+
title.setAttribute("aria-label", "Chart title");
|
|
420
|
+
title.value = attrs.title ?? "";
|
|
421
|
+
title.addEventListener("keydown", (event) => {
|
|
422
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
423
|
+
if (event.key !== "Enter") return;
|
|
424
|
+
event.preventDefault();
|
|
425
|
+
event.stopPropagation();
|
|
426
|
+
this.commitTitle();
|
|
427
|
+
});
|
|
428
|
+
title.addEventListener("change", () => this.commitTitle());
|
|
429
|
+
title.addEventListener("blur", () => this.commitTitle());
|
|
430
|
+
this.titleInput = title;
|
|
431
|
+
this.panel.append(this.row("Chart title", title));
|
|
432
|
+
|
|
433
|
+
const foot = element(doc, "div", "page-chart-foot");
|
|
434
|
+
const remove = element(doc, "button", "page-chart-remove");
|
|
435
|
+
remove.type = "button";
|
|
436
|
+
remove.dataset.action = "remove";
|
|
437
|
+
remove.innerHTML = TRASH_ICON;
|
|
438
|
+
remove.append("Remove");
|
|
439
|
+
const done = element(doc, "button", "page-chart-done", "Done");
|
|
440
|
+
done.type = "button";
|
|
441
|
+
done.dataset.action = "done";
|
|
442
|
+
foot.append(remove, done);
|
|
443
|
+
this.panel.append(foot);
|
|
444
|
+
|
|
445
|
+
this.unsaved = registerUnsavedState({
|
|
446
|
+
id: "chart-title",
|
|
447
|
+
isPending: () => title.isConnected && title.value !== (this.node.attrs.title ?? ""),
|
|
184
448
|
});
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
openPanel() {
|
|
452
|
+
if (this.panel.isConnected) return;
|
|
453
|
+
this.renderPanel();
|
|
454
|
+
this.dom.append(this.panel);
|
|
455
|
+
this.edit.setAttribute("aria-expanded", "true");
|
|
456
|
+
this.panel.focus();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
closePanel(refocus = false) {
|
|
460
|
+
if (!this.panel.isConnected) return;
|
|
461
|
+
this.unsaved?.dispose();
|
|
462
|
+
this.unsaved = null;
|
|
463
|
+
this.titleInput = null;
|
|
464
|
+
this.panel.remove();
|
|
465
|
+
this.edit.setAttribute("aria-expanded", "false");
|
|
466
|
+
if (refocus) this.edit.focus();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
panelClick(event) {
|
|
470
|
+
const button = event.target.closest?.("button");
|
|
471
|
+
if (!button) return;
|
|
472
|
+
if (button.dataset.action === "done") {
|
|
473
|
+
this.commitTitle();
|
|
474
|
+
this.closePanel(true);
|
|
475
|
+
}
|
|
476
|
+
else if (button.dataset.action === "remove") this.removeChart();
|
|
477
|
+
else if (button.dataset.kind) this.setAttrs({ kind: button.dataset.kind });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
panelChange(event) {
|
|
481
|
+
const control = event.target;
|
|
482
|
+
if (!control?.dataset) return;
|
|
483
|
+
if (control.dataset.field === "source") this.switchSource(control.value);
|
|
484
|
+
else if (control.dataset.field === "labels") this.setAttrs({ x: control.value, series: this.node.attrs.series.filter((id) => id !== control.value) });
|
|
485
|
+
else if (control.dataset.field === "highlight") this.setAttrs({ highlight: control.value || null });
|
|
486
|
+
else if (control.dataset.field === "series") {
|
|
487
|
+
const series = this.node.attrs.series.filter((id) => id !== control.dataset.col);
|
|
488
|
+
this.setAttrs({ series: control.checked ? [...series, control.dataset.col] : series });
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
switchSource(id) {
|
|
493
|
+
const table = sourceTable(this.view.state.doc, id);
|
|
494
|
+
const defaults = table ? defaultColumns(table) : null;
|
|
495
|
+
if (!defaults) return;
|
|
496
|
+
this.setAttrs({ source: id, x: defaults.x, series: defaults.series });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
commitTitle() {
|
|
500
|
+
const input = this.titleInput;
|
|
501
|
+
if (!input || input.value === (this.node.attrs.title ?? "")) return;
|
|
502
|
+
this.setAttrs({ title: input.value });
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
goToSource() {
|
|
506
|
+
const doc = this.view.state.doc;
|
|
507
|
+
const pos = tableOffset(doc, this.node.attrs.source);
|
|
508
|
+
const table = pos === null ? null : doc.nodeAt(pos);
|
|
509
|
+
if (!table?.childCount || !table.child(0).childCount) return;
|
|
510
|
+
const at = pos + 3;
|
|
511
|
+
this.view.dispatch(this.view.state.tr.setSelection(TextSelection.create(doc, at)).scrollIntoView());
|
|
512
|
+
this.view.focus?.();
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
removeChart() {
|
|
516
|
+
const pos = this.getPos();
|
|
517
|
+
if (pos == null) return;
|
|
518
|
+
const node = this.view.state.doc.nodeAt(pos);
|
|
519
|
+
if (node?.type !== schema.nodes.chart) return;
|
|
520
|
+
this.closePanel();
|
|
521
|
+
const tr = this.view.state.tr.delete(pos, pos + node.nodeSize);
|
|
522
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(Math.min(pos, tr.doc.content.size)), -1));
|
|
523
|
+
this.view.dispatch(asStep(tr).scrollIntoView());
|
|
189
524
|
}
|
|
190
525
|
|
|
191
526
|
update(node) {
|
|
@@ -193,13 +528,21 @@ export class ChartView {
|
|
|
193
528
|
const changed = !node.sameMarkup(this.node);
|
|
194
529
|
this.node = node;
|
|
195
530
|
this.dom.dataset.id = node.attrs.id ?? "";
|
|
196
|
-
if (changed) {
|
|
531
|
+
if (changed) {
|
|
532
|
+
this.source = sourceTable(this.view.state.doc, node.attrs.source);
|
|
533
|
+
this.renderHead();
|
|
534
|
+
this.schedule();
|
|
535
|
+
}
|
|
197
536
|
return true;
|
|
198
537
|
}
|
|
199
538
|
|
|
200
|
-
stopEvent(event) { return !!event.target.closest?.(".page-chart-
|
|
539
|
+
stopEvent(event) { return !!event.target.closest?.(".page-chart-head, .page-chart-panel"); }
|
|
201
540
|
|
|
202
541
|
destroy() {
|
|
542
|
+
this.unsaved?.dispose();
|
|
543
|
+
this.unsaved = null;
|
|
544
|
+
this.doc.removeEventListener("mousedown", this.outside, true);
|
|
545
|
+
this.doc.removeEventListener("keydown", this.escape, true);
|
|
203
546
|
views.get(this.view)?.delete(this);
|
|
204
547
|
if (this.frame !== null) {
|
|
205
548
|
const win = this.dom.ownerDocument.defaultView;
|