@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
|
@@ -1,19 +1,63 @@
|
|
|
1
1
|
import { schema } from "../schema.js";
|
|
2
2
|
import { orderedIds } from "../model.js";
|
|
3
|
-
import { DEFAULT_WIDTH, MAX_WIDTH, MIN_WIDTH, addColumn, addRow, deleteColumn, deleteRow, moveColumn, moveRow, renameColumn, selectTableCell, setCellText, setColumnKind, setColumnWidth, sortBy } from "../table.js";
|
|
3
|
+
import { DEFAULT_WIDTH, MAX_WIDTH, MIN_WIDTH, addColumn, addRow, deleteColumn, deleteRow, moveColumn, moveRow, renameColumn, selectTableCell, setCellText, setColumnKind, setColumnWidth, setColumnWidths, clearColumnWidths, sortBy } from "../table.js";
|
|
4
4
|
import { COLUMN_KINDS } from "../rich/csv.js";
|
|
5
5
|
import { KIND_ICONS, KIND_LABELS, columnKinds } from "../table-kinds.js";
|
|
6
|
-
import { openPageMenu } from "../chrome.js";
|
|
6
|
+
import { openPageMenu, reanchorPageMenu } from "../chrome.js";
|
|
7
7
|
import { loadChartPart } from "../items.js";
|
|
8
8
|
import { selectLine } from "../table-select.js";
|
|
9
9
|
import { TABLE_CSS } from "../table-style.js";
|
|
10
10
|
import { cellViews } from "../table-views.js";
|
|
11
11
|
import { asStep } from "../blocksel.js";
|
|
12
|
+
import { announce } from "../announce.js";
|
|
13
|
+
import { registerUnsavedState } from "../../lib/unsaved-state.js";
|
|
12
14
|
|
|
13
15
|
const N = schema.nodes;
|
|
14
16
|
|
|
15
17
|
const columns = (table) => orderedIds(table.attrs.cols);
|
|
16
18
|
|
|
19
|
+
const UNAVAILABLE = "That part of the table is no longer available.";
|
|
20
|
+
const GEAR = '<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"><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></svg>';
|
|
21
|
+
const ACTIONS_CSS = `
|
|
22
|
+
.page-editor .page-table-actions { position: absolute; z-index: 2; bottom: 100%; inset-inline-end: 0; inset-inline-start: auto; width: 24px; height: 20px; padding: 0; display: grid; place-items: center; border: 1px solid var(--page-chrome-line-rest); border-bottom: 0; border-radius: 6px 6px 0 0; background: var(--page-chrome-bg-moved); color: var(--page-chrome-ink-moved); font: inherit; cursor: pointer; opacity: 1; }
|
|
23
|
+
.page-editor .page-table-actions:hover { background: var(--page-pop-hover); color: var(--page-ink-soft); }
|
|
24
|
+
@media (max-width: 560px) {
|
|
25
|
+
.page-editor .page-table-actions { position: absolute; }
|
|
26
|
+
.page-editor .page-table-actions::after { content: ""; position: absolute; inset: -12px; }
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const headerId = (tableId, colId) => `${tableId}-${colId}`;
|
|
31
|
+
const columnLabel = (col, index) => col.name || `Column ${index + 1}`;
|
|
32
|
+
const rowLabel = (row, index) => (row.childCount ? row.child(0).textContent.trim() : "") || `Row ${index + 1}`;
|
|
33
|
+
|
|
34
|
+
const tableById = (doc, id) => {
|
|
35
|
+
let found = null;
|
|
36
|
+
doc.forEach((node, offset) => { if (!found && node.type === N.table && node.attrs.id === id) found = { table: node, pos: offset }; });
|
|
37
|
+
return found;
|
|
38
|
+
};
|
|
39
|
+
const rowIndexOf = (table, rowId) => (rowId == null ? 0 : [...table.content.content].findIndex((row) => row.attrs.id === rowId));
|
|
40
|
+
const colIndexOf = (table, colId) => (colId == null ? 0 : columns(table).indexOf(colId));
|
|
41
|
+
|
|
42
|
+
// A table command names its table, row and column when it is offered and finds them
|
|
43
|
+
// again here, so a peer's edit between the two can never hand it another one.
|
|
44
|
+
const selectOn = (view, target, kind) => {
|
|
45
|
+
const found = tableById(view.state.doc, target.tableId);
|
|
46
|
+
if (!found) return false;
|
|
47
|
+
const index = kind === "row" ? rowIndexOf(found.table, target.rowId) : colIndexOf(found.table, target.colId);
|
|
48
|
+
return index < 0 ? false : selectLine(view, found.pos, kind, index);
|
|
49
|
+
};
|
|
50
|
+
const runOn = (view, target, command) => {
|
|
51
|
+
const found = tableById(view.state.doc, target.tableId);
|
|
52
|
+
if (!found) return false;
|
|
53
|
+
const row = rowIndexOf(found.table, target.rowId);
|
|
54
|
+
const col = colIndexOf(found.table, target.colId);
|
|
55
|
+
if (row < 0 || col < 0) return false;
|
|
56
|
+
selectTableCell(view, found.pos, row, col);
|
|
57
|
+
if (command(view.state, null) !== true) return false;
|
|
58
|
+
return command(view.state, view.dispatch);
|
|
59
|
+
};
|
|
60
|
+
|
|
17
61
|
const FONT = ["fontFamily", "fontSize", "fontWeight", "fontStyle", "letterSpacing"];
|
|
18
62
|
const textWidth = (el) => {
|
|
19
63
|
const doc = el.ownerDocument;
|
|
@@ -44,7 +88,10 @@ export class TableView {
|
|
|
44
88
|
this.view = view;
|
|
45
89
|
this.getPos = getPos;
|
|
46
90
|
this.preview = null;
|
|
47
|
-
this.
|
|
91
|
+
this.renaming = null;
|
|
92
|
+
this.renamingUnsaved = null;
|
|
93
|
+
this.menuAnchor = null;
|
|
94
|
+
this.destroyed = false;
|
|
48
95
|
const doc = view.dom.ownerDocument;
|
|
49
96
|
this.dom = doc.createElement("div");
|
|
50
97
|
this.dom.className = "page-table";
|
|
@@ -54,6 +101,7 @@ export class TableView {
|
|
|
54
101
|
const scroll = doc.createElement("div");
|
|
55
102
|
scroll.className = "page-table-scroll";
|
|
56
103
|
const table = doc.createElement("table");
|
|
104
|
+
table.setAttribute("aria-label", "Table");
|
|
57
105
|
this.table = table;
|
|
58
106
|
this.colgroup = doc.createElement("colgroup");
|
|
59
107
|
this.header = doc.createElement("thead");
|
|
@@ -69,13 +117,180 @@ export class TableView {
|
|
|
69
117
|
addColumnButton.className = "page-table-add-col";
|
|
70
118
|
addColumnButton.setAttribute("aria-label", "Add a column");
|
|
71
119
|
addColumnButton.title = "Add a column";
|
|
72
|
-
|
|
120
|
+
const actions = doc.createElement("button");
|
|
121
|
+
actions.type = "button";
|
|
122
|
+
actions.className = "page-table-actions";
|
|
123
|
+
actions.innerHTML = GEAR;
|
|
124
|
+
actions.dataset.control = "actions";
|
|
125
|
+
actions.setAttribute("aria-haspopup", "menu");
|
|
126
|
+
actions.setAttribute("aria-expanded", "false");
|
|
127
|
+
actions.setAttribute("aria-label", "Table actions");
|
|
128
|
+
actions.title = "Table actions";
|
|
129
|
+
this.actionsButton = actions;
|
|
130
|
+
actions.addEventListener("click", (event) => {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
this.menuAnchor = actions;
|
|
133
|
+
this.openActions();
|
|
134
|
+
});
|
|
135
|
+
actions.addEventListener("keydown", (event) => {
|
|
136
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
this.menuAnchor = actions;
|
|
139
|
+
this.openActions();
|
|
140
|
+
});
|
|
141
|
+
for (const control of [addRowButton, addColumnButton, actions]) {
|
|
73
142
|
control.contentEditable = "false";
|
|
74
143
|
control.addEventListener("mousedown", (event) => event.preventDefault());
|
|
75
144
|
}
|
|
76
|
-
this.dom.append(scroll, addRowButton, addColumnButton);
|
|
145
|
+
this.dom.append(scroll, addRowButton, addColumnButton, actions);
|
|
77
146
|
this.render();
|
|
78
147
|
}
|
|
148
|
+
// The control a focus descriptor names, or null when it is gone.
|
|
149
|
+
control(focus) {
|
|
150
|
+
if (!focus || (focus.tableId && focus.tableId !== this.node.attrs.id)) return null;
|
|
151
|
+
const all = [...this.dom.querySelectorAll(`[data-control="${focus.control}"]`)];
|
|
152
|
+
return all.find((el) => (focus.colId == null || el.dataset.col === focus.colId)
|
|
153
|
+
&& (focus.rowId == null || el.dataset.row === focus.rowId)) ?? null;
|
|
154
|
+
}
|
|
155
|
+
controls(control) {
|
|
156
|
+
return [...this.dom.querySelectorAll(`[data-control="${control}"]`)].filter((el) => !el.hidden && !el.closest("[hidden]"));
|
|
157
|
+
}
|
|
158
|
+
// Focus stays on the same control; when a peer removed it, the control that took
|
|
159
|
+
// its place in the column or row, and otherwise the table's own exit.
|
|
160
|
+
focusControl(focus) {
|
|
161
|
+
if (!focus || (focus.tableId && focus.tableId !== this.node.attrs.id)) return null;
|
|
162
|
+
const exact = this.control(focus);
|
|
163
|
+
const peers = exact ? [] : this.controls(focus.control);
|
|
164
|
+
const near = peers.length ? peers[Math.min(focus.index ?? 0, peers.length - 1)] : null;
|
|
165
|
+
const target = exact ?? near ?? this.actionsButton;
|
|
166
|
+
target?.focus?.();
|
|
167
|
+
return target;
|
|
168
|
+
}
|
|
169
|
+
describe(element) {
|
|
170
|
+
if (!element || !this.dom.contains(element)) return null;
|
|
171
|
+
const control = element.dataset?.control;
|
|
172
|
+
if (!control) return null;
|
|
173
|
+
return {
|
|
174
|
+
tableId: this.node.attrs.id,
|
|
175
|
+
rowId: element.dataset.row ?? null,
|
|
176
|
+
colId: element.dataset.col ?? null,
|
|
177
|
+
control,
|
|
178
|
+
index: this.controls(control).indexOf(element),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// Cell views are updated after this one, so a row control a peer removed only
|
|
182
|
+
// finds its replacement once the whole render is done.
|
|
183
|
+
afterUpdate(focus) {
|
|
184
|
+
queueMicrotask(() => {
|
|
185
|
+
if (this.destroyed || this.view.isDestroyed) return;
|
|
186
|
+
const active = this.dom.ownerDocument.activeElement;
|
|
187
|
+
if (active && this.dom.contains(active)) return;
|
|
188
|
+
this.focusControl(focus);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
act(target, command, said) {
|
|
192
|
+
const done = runOn(this.view, target, command);
|
|
193
|
+
announce(this.view, done ? said : UNAVAILABLE);
|
|
194
|
+
return done;
|
|
195
|
+
}
|
|
196
|
+
// What a menu opened now acts on: the caret's cell of this table, by id.
|
|
197
|
+
cellTarget(table = this.node) {
|
|
198
|
+
const pos = this.getPos();
|
|
199
|
+
const { $head } = this.view.state.selection;
|
|
200
|
+
let row = 0, cell = 0;
|
|
201
|
+
for (let depth = $head.depth; depth > 0; depth--) {
|
|
202
|
+
if ($head.node(depth).type !== N.table) continue;
|
|
203
|
+
if (pos != null && $head.before(depth) === pos) {
|
|
204
|
+
row = depth < $head.depth ? $head.index(depth) : 0;
|
|
205
|
+
cell = depth + 1 < $head.depth ? $head.index(depth + 1) : 0;
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
const rows = [...table.content.content];
|
|
210
|
+
return { tableId: table.attrs.id, rowId: rows[row]?.attrs.id ?? null, colId: columns(table)[cell] ?? null };
|
|
211
|
+
}
|
|
212
|
+
openActions() {
|
|
213
|
+
const target = this.cellTarget();
|
|
214
|
+
const table = tableById(this.view.state.doc, target.tableId)?.table;
|
|
215
|
+
if (!table) return false;
|
|
216
|
+
return this.actions(table, target);
|
|
217
|
+
}
|
|
218
|
+
actions(table, target) {
|
|
219
|
+
const ids = columns(table);
|
|
220
|
+
const index = Math.max(0, colIndexOf(table, target.colId));
|
|
221
|
+
const row = Math.max(0, rowIndexOf(table, target.rowId));
|
|
222
|
+
const col = table.attrs.cols[ids[index]] ?? {};
|
|
223
|
+
const sort = table.attrs.sort;
|
|
224
|
+
const at = (command, said) => () => this.act(target, command, said);
|
|
225
|
+
const width = this.widthOfColumn(ids[index]);
|
|
226
|
+
return openPageMenu(this.view, this.actionsButton, [
|
|
227
|
+
{ label: "Insert row above", icon: "+", run: at(addRow(false), "Row inserted") },
|
|
228
|
+
{ label: "Insert row below", icon: "+", run: at(addRow(true), "Row inserted") },
|
|
229
|
+
...(row > 0 ? [{ label: "Move row up", icon: "↑", run: at(moveRow(-1), "Row moved") }] : []),
|
|
230
|
+
...(row < table.childCount - 1 ? [{ label: "Move row down", icon: "↓", run: at(moveRow(1), "Row moved") }] : []),
|
|
231
|
+
{ label: "Delete row", icon: "×", run: at(deleteRow, "Row deleted") },
|
|
232
|
+
{ label: "Insert column left", icon: "+", run: at(addColumn(false), "Column inserted") },
|
|
233
|
+
{ label: "Insert column right", icon: "+", run: at(addColumn(true), "Column inserted") },
|
|
234
|
+
...(index > 0 ? [{ label: "Move column left", icon: "←", run: at(moveColumn(-1), "Column moved") }] : []),
|
|
235
|
+
...(index < ids.length - 1 ? [{ label: "Move column right", icon: "→", run: at(moveColumn(1), "Column moved") }] : []),
|
|
236
|
+
...(ids.length > 1 ? [{ label: "Delete column", icon: "×", run: at(deleteColumn, "Column deleted") }] : []),
|
|
237
|
+
{ label: "Sort ascending", icon: "↑", checked: sort?.col === ids[index] && sort.dir === "asc", run: at(sortBy(ids[index], "asc"), "Sorted ascending") },
|
|
238
|
+
{ label: "Sort descending", icon: "↓", checked: sort?.col === ids[index] && sort.dir === "desc", run: at(sortBy(ids[index], "desc"), "Sorted descending") },
|
|
239
|
+
{ label: "Chart this table", icon: "📊", run: () => { const at = this.getPos(); return loadChartPart(this.view).then(({ insertChartAfterTable }) => insertChartAfterTable(this.view, at)); } },
|
|
240
|
+
{ label: "Rename column", focus: false, run: () => this.beginRename(ids[index]) },
|
|
241
|
+
{ label: "Column width ▸", icon: "↔", hint: col.width ? `${col.width}px` : "Auto", focus: false, run: () => openPageMenu(this.view, this.actionsButton, [
|
|
242
|
+
{ label: "Wider", icon: "+", run: at(this.widthCommand(ids[index], width + 40), "Column width changed") },
|
|
243
|
+
{ label: "Narrower", icon: "−", run: at(this.widthCommand(ids[index], width - 40), "Column width changed") },
|
|
244
|
+
{ label: "Fit to content", icon: "↔", run: () => this.fit(ids[index]) },
|
|
245
|
+
...(this.isSized() ? [{ label: "Reset widths", icon: "×", run: at(clearColumnWidths(ids[index]), "Column widths reset") }] : []),
|
|
246
|
+
]) },
|
|
247
|
+
], { persist: true });
|
|
248
|
+
}
|
|
249
|
+
// The name field for a column. A peer's edit does not take it away while it has
|
|
250
|
+
// the focus, and a cancelled one writes nothing. A name typed here is unsaved work
|
|
251
|
+
// until Enter or a blur commits it, so the exit asks the registry by its name and
|
|
252
|
+
// refuses while the field holds a name the table does not.
|
|
253
|
+
beginRename(colId) {
|
|
254
|
+
const control = this.control({ control: "column", colId });
|
|
255
|
+
const col = this.node.attrs.cols[colId];
|
|
256
|
+
if (!control || !col) return false;
|
|
257
|
+
const doc = this.dom.ownerDocument;
|
|
258
|
+
const tableId = this.node.attrs.id;
|
|
259
|
+
const input = doc.createElement("input");
|
|
260
|
+
input.type = "text";
|
|
261
|
+
input.className = "page-table-rename";
|
|
262
|
+
input.dataset.control = "rename";
|
|
263
|
+
input.dataset.col = colId;
|
|
264
|
+
input.value = col.name ?? "";
|
|
265
|
+
input.setAttribute("aria-label", "Column name");
|
|
266
|
+
const unsaved = registerUnsavedState({
|
|
267
|
+
id: "table-rename",
|
|
268
|
+
isPending: () => this.renaming === input && input.value.trim() !== (col.name ?? ""),
|
|
269
|
+
});
|
|
270
|
+
this.renamingUnsaved = unsaved;
|
|
271
|
+
const finish = (save) => {
|
|
272
|
+
if (this.renaming !== input) return;
|
|
273
|
+
this.renaming = null;
|
|
274
|
+
this.renamingUnsaved = null;
|
|
275
|
+
unsaved.dispose();
|
|
276
|
+
const value = input.value.trim();
|
|
277
|
+
if (save && value) this.act({ tableId, colId }, renameColumn(colId, value), "Column renamed");
|
|
278
|
+
if (input.isConnected) input.replaceWith(control);
|
|
279
|
+
this.focusControl({ tableId, control: "column", colId });
|
|
280
|
+
};
|
|
281
|
+
input.addEventListener("keydown", (event) => {
|
|
282
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
283
|
+
if (event.key === "Enter") { event.preventDefault(); finish(true); }
|
|
284
|
+
if (event.key === "Escape") { event.preventDefault(); finish(false); }
|
|
285
|
+
event.stopPropagation();
|
|
286
|
+
});
|
|
287
|
+
input.addEventListener("blur", () => finish(true));
|
|
288
|
+
this.renaming = input;
|
|
289
|
+
control.replaceWith(input);
|
|
290
|
+
input.focus();
|
|
291
|
+
input.select();
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
79
294
|
append(what) {
|
|
80
295
|
const pos = this.getPos();
|
|
81
296
|
if (pos == null) return;
|
|
@@ -85,6 +300,7 @@ export class TableView {
|
|
|
85
300
|
this.view.focus();
|
|
86
301
|
}
|
|
87
302
|
widths() {
|
|
303
|
+
this.frozen = null;
|
|
88
304
|
const sized = columns(this.node).some((id) => this.node.attrs.cols[id].width);
|
|
89
305
|
const cols = columns(this.node).map((id) => {
|
|
90
306
|
const col = this.dom.ownerDocument.createElement("col");
|
|
@@ -101,17 +317,17 @@ export class TableView {
|
|
|
101
317
|
const doc = this.dom.ownerDocument;
|
|
102
318
|
const handle = doc.createElement("span");
|
|
103
319
|
handle.className = "page-table-resize";
|
|
320
|
+
handle.dataset.control = "resize";
|
|
104
321
|
handle.dataset.col = colId;
|
|
105
322
|
handle.setAttribute("role", "separator");
|
|
106
323
|
handle.setAttribute("aria-orientation", "vertical");
|
|
107
|
-
handle.setAttribute("aria-label", `Resize column ${col
|
|
324
|
+
handle.setAttribute("aria-label", `Resize column ${columnLabel(col, index)}`);
|
|
108
325
|
handle.setAttribute("aria-valuemin", String(MIN_WIDTH));
|
|
109
326
|
handle.setAttribute("aria-valuemax", String(MAX_WIDTH));
|
|
110
327
|
handle.setAttribute("aria-valuenow", String(this.widthOfColumn(colId)));
|
|
111
328
|
handle.tabIndex = 0;
|
|
112
|
-
handle.title = "Drag or use the arrow keys to resize, double-click to
|
|
329
|
+
handle.title = "Drag or use the arrow keys to resize, double-click to reset the table to the page width";
|
|
113
330
|
handle.addEventListener("mousedown", (event) => event.preventDefault());
|
|
114
|
-
handle.addEventListener("focus", () => { this.focusCol = colId; });
|
|
115
331
|
handle.addEventListener("blur", () => this.commitWidth(colId));
|
|
116
332
|
handle.addEventListener("keydown", (event) => this.resizeKey(event, colId));
|
|
117
333
|
handle.addEventListener("keyup", (event) => {
|
|
@@ -120,28 +336,63 @@ export class TableView {
|
|
|
120
336
|
handle.addEventListener("pointerdown", (event) => {
|
|
121
337
|
event.preventDefault();
|
|
122
338
|
const start = event.clientX;
|
|
123
|
-
const from = this.
|
|
339
|
+
const from = this.widthOfColumn(colId);
|
|
340
|
+
this.freeze();
|
|
124
341
|
const rtl = this.dom.ownerDocument.defaultView.getComputedStyle?.(this.dom).direction === "rtl";
|
|
125
342
|
let width = from;
|
|
126
343
|
const move = (e) => {
|
|
127
344
|
width = widthOf(from + (rtl ? start - e.clientX : e.clientX - start)) ?? from;
|
|
128
|
-
|
|
129
|
-
if (col) { this.table.classList.add("is-sized"); col.style.width = `${width}px`; }
|
|
345
|
+
this.showWidth(colId, width);
|
|
130
346
|
};
|
|
131
347
|
const up = () => {
|
|
132
348
|
doc.removeEventListener("pointermove", move);
|
|
133
349
|
doc.removeEventListener("pointerup", up);
|
|
134
|
-
if (width !== from) this.
|
|
350
|
+
if (width !== from) this.sizeColumn(colId, width);
|
|
135
351
|
else this.widths();
|
|
136
352
|
};
|
|
137
353
|
doc.addEventListener("pointermove", move);
|
|
138
354
|
doc.addEventListener("pointerup", up);
|
|
139
355
|
});
|
|
140
|
-
handle.addEventListener("dblclick", (event) => { event.preventDefault(); this.
|
|
356
|
+
handle.addEventListener("dblclick", (event) => { event.preventDefault(); this.command(colId, clearColumnWidths(colId)); });
|
|
141
357
|
return handle;
|
|
142
358
|
}
|
|
359
|
+
isSized() {
|
|
360
|
+
return columns(this.node).some((id) => this.node.attrs.cols[id].width);
|
|
361
|
+
}
|
|
362
|
+
// A table with no widths fills the page, so its columns are measured as drawn.
|
|
363
|
+
drawnWidths() {
|
|
364
|
+
const widths = {};
|
|
365
|
+
for (const th of this.header.querySelectorAll("th")) widths[th.dataset.col] = Math.round(th.getBoundingClientRect().width) || DEFAULT_WIDTH;
|
|
366
|
+
return widths;
|
|
367
|
+
}
|
|
143
368
|
widthOfColumn(colId) {
|
|
144
|
-
|
|
369
|
+
const width = this.node.attrs.cols[colId]?.width;
|
|
370
|
+
if (width) return width;
|
|
371
|
+
return this.isSized() ? DEFAULT_WIDTH : this.drawnWidths()[colId] ?? DEFAULT_WIDTH;
|
|
372
|
+
}
|
|
373
|
+
// Before a preview changes one column of a table that fills the page, every
|
|
374
|
+
// column is pinned at its drawn width, so the others stay where they are.
|
|
375
|
+
// Those widths are kept for the gesture: the table's total follows the
|
|
376
|
+
// previewed column, and the commit saves them rather than measuring again.
|
|
377
|
+
freeze() {
|
|
378
|
+
if (this.isSized() || this.frozen) return;
|
|
379
|
+
this.frozen = this.drawnWidths();
|
|
380
|
+
for (const col of this.colgroup.children) col.style.width = `${this.frozen[col.dataset.col]}px`;
|
|
381
|
+
this.table.style.width = `${Object.values(this.frozen).reduce((sum, w) => sum + w, 0)}px`;
|
|
382
|
+
this.table.classList.add("is-sized");
|
|
383
|
+
}
|
|
384
|
+
showWidth(colId, width) {
|
|
385
|
+
const col = [...this.colgroup.children].find((el) => el.dataset.col === colId);
|
|
386
|
+
if (!col) return;
|
|
387
|
+
this.table.classList.add("is-sized");
|
|
388
|
+
col.style.width = `${width}px`;
|
|
389
|
+
if (this.frozen) this.table.style.width = `${Object.values({ ...this.frozen, [colId]: width }).reduce((sum, w) => sum + w, 0)}px`;
|
|
390
|
+
}
|
|
391
|
+
widthCommand(colId, width) {
|
|
392
|
+
return this.isSized() ? setColumnWidth(colId, width) : setColumnWidths({ ...(this.frozen ?? this.drawnWidths()), [colId]: width }, colId);
|
|
393
|
+
}
|
|
394
|
+
sizeColumn(colId, width) {
|
|
395
|
+
return this.command(colId, this.widthCommand(colId, width));
|
|
145
396
|
}
|
|
146
397
|
handleFor(colId) {
|
|
147
398
|
return [...this.header.querySelectorAll(".page-table-resize")].find((handle) => handle.dataset.col === colId) ?? null;
|
|
@@ -164,9 +415,9 @@ export class TableView {
|
|
|
164
415
|
}
|
|
165
416
|
previewWidth(colId, width) {
|
|
166
417
|
const base = this.preview?.col === colId ? this.preview.base : this.widthOfColumn(colId);
|
|
418
|
+
this.freeze();
|
|
167
419
|
this.preview = { col: colId, width, base };
|
|
168
|
-
|
|
169
|
-
if (col) { this.table.classList.add("is-sized"); col.style.width = `${width}px`; }
|
|
420
|
+
this.showWidth(colId, width);
|
|
170
421
|
const handle = this.handleFor(colId);
|
|
171
422
|
if (handle) handle.setAttribute("aria-valuenow", String(width));
|
|
172
423
|
}
|
|
@@ -185,106 +436,120 @@ export class TableView {
|
|
|
185
436
|
const table = pos == null ? null : this.view.state.doc.nodeAt(pos);
|
|
186
437
|
const col = table?.type === N.table && table.attrs.id === this.node.attrs.id ? table.attrs.cols[colId] : null;
|
|
187
438
|
if (!col) { this.widths(); return false; }
|
|
188
|
-
if ((col.width ??
|
|
189
|
-
const
|
|
439
|
+
if ((col.width ?? preview.base) === preview.width) { this.widths(); return false; }
|
|
440
|
+
const seed = columns(table).some((id) => table.attrs.cols[id].width) ? {} : this.frozen ?? this.drawnWidths();
|
|
441
|
+
const cols = { ...table.attrs.cols };
|
|
442
|
+
for (const [id, width] of Object.entries(seed)) if (cols[id]) cols[id] = { ...cols[id], width };
|
|
443
|
+
cols[colId] = { ...col, width: preview.width };
|
|
190
444
|
this.view.dispatch(asStep(this.view.state.tr.setNodeMarkup(pos, null, { ...table.attrs, cols })));
|
|
191
445
|
return true;
|
|
192
446
|
}
|
|
193
|
-
command(
|
|
194
|
-
const
|
|
195
|
-
if (pos == null) return false;
|
|
196
|
-
selectTableCell(this.view, pos, 0, 0);
|
|
197
|
-
const done = run(this.view.state, this.view.dispatch);
|
|
447
|
+
command(colId, command) {
|
|
448
|
+
const done = runOn(this.view, { tableId: this.node.attrs.id, colId }, command);
|
|
198
449
|
this.view.focus();
|
|
199
450
|
return done;
|
|
200
451
|
}
|
|
201
452
|
fit(colId) {
|
|
202
453
|
let widest = 0;
|
|
203
|
-
for (const cell of this.contentDOM.querySelectorAll("td")) if (cell.dataset.col === colId) widest = Math.max(widest, textWidth(cell.
|
|
454
|
+
for (const cell of this.contentDOM.querySelectorAll("td")) if (cell.dataset.col === colId) widest = Math.max(widest, textWidth(cell.querySelector("span") ?? cell));
|
|
204
455
|
const head = [...this.header.querySelectorAll("th")].find((th) => th.dataset.col === colId)?.querySelector("button");
|
|
205
456
|
widest = Math.max(widest, head?.scrollWidth ?? 0);
|
|
206
|
-
return this.
|
|
457
|
+
return this.sizeColumn(colId, widest + 24);
|
|
207
458
|
}
|
|
208
|
-
render(
|
|
459
|
+
render(focus = null) {
|
|
209
460
|
const doc = this.dom.ownerDocument;
|
|
210
461
|
const row = doc.createElement("tr");
|
|
211
462
|
const effective = columnKinds(this.node);
|
|
463
|
+
const tableId = this.node.attrs.id;
|
|
212
464
|
this.widths();
|
|
213
465
|
for (const [index, colId] of columns(this.node).entries()) {
|
|
214
466
|
const col = this.node.attrs.cols[colId];
|
|
215
467
|
const kind = effective[colId];
|
|
468
|
+
const name = columnLabel(col, index);
|
|
216
469
|
const th = doc.createElement("th");
|
|
217
470
|
th.dataset.col = colId;
|
|
218
471
|
th.dataset.kind = kind;
|
|
472
|
+
th.setAttribute("scope", "col");
|
|
473
|
+
th.id = headerId(tableId, colId);
|
|
219
474
|
if (this.node.attrs.sort?.col === colId) th.setAttribute("aria-sort", this.node.attrs.sort.dir === "asc" ? "ascending" : "descending");
|
|
220
|
-
const control =
|
|
475
|
+
const control = doc.createElement("button");
|
|
476
|
+
control.type = "button";
|
|
477
|
+
control.className = "page-table-col-menu";
|
|
478
|
+
control.textContent = name;
|
|
479
|
+
control.dataset.control = "column";
|
|
480
|
+
control.dataset.col = colId;
|
|
481
|
+
control.dataset.kind = kind;
|
|
482
|
+
control.setAttribute("aria-haspopup", "menu");
|
|
483
|
+
control.setAttribute("aria-expanded", "false");
|
|
484
|
+
control.setAttribute("aria-label", `${name} column actions`);
|
|
485
|
+
control.addEventListener("click", (event) => {
|
|
221
486
|
event.preventDefault();
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
input.type = "text";
|
|
226
|
-
input.value = col.name;
|
|
227
|
-
input.setAttribute("aria-label", "Column name");
|
|
228
|
-
const finish = (save) => {
|
|
229
|
-
if (save && input.value.trim()) run(renameColumn(colId, input.value))();
|
|
230
|
-
else if (input.isConnected) { input.replaceWith(control); control.focus(); }
|
|
231
|
-
};
|
|
232
|
-
input.addEventListener("keydown", (e) => {
|
|
233
|
-
if (e.key === "Enter") { e.preventDefault(); finish(true); }
|
|
234
|
-
if (e.key === "Escape") { e.preventDefault(); finish(false); }
|
|
235
|
-
e.stopPropagation();
|
|
236
|
-
});
|
|
237
|
-
control.replaceWith(input);
|
|
238
|
-
input.focus();
|
|
239
|
-
input.select();
|
|
240
|
-
};
|
|
487
|
+
this.menuAnchor = control;
|
|
488
|
+
const target = { tableId, colId };
|
|
489
|
+
const run = (command, said) => () => this.act(target, command, said);
|
|
241
490
|
openPageMenu(this.view, control, [
|
|
242
|
-
{ label: "Rename", run:
|
|
491
|
+
{ label: "Rename", run: () => this.beginRename(colId), focus: false },
|
|
243
492
|
{ label: "Kind ▸", icon: KIND_ICONS[kind], hint: col.kind ? KIND_LABELS[kind] : `Auto: ${KIND_LABELS[kind]}`, run: () => openPageMenu(this.view, control, [
|
|
244
|
-
{ label: "Auto", icon: col.kind ? "" : KIND_ICONS[kind], hint: col.kind ? "" : KIND_LABELS[kind], checked: !col.kind, run: run(setColumnKind(colId, "auto")) },
|
|
245
|
-
...COLUMN_KINDS.map((option) => ({ label: KIND_LABELS[option], icon: KIND_ICONS[option], checked: col.kind === option, run: run(setColumnKind(colId, option)) })),
|
|
493
|
+
{ label: "Auto", icon: col.kind ? "" : KIND_ICONS[kind], hint: col.kind ? "" : KIND_LABELS[kind], checked: !col.kind, run: run(setColumnKind(colId, "auto"), `Kind ${KIND_LABELS[kind]}`) },
|
|
494
|
+
...COLUMN_KINDS.map((option) => ({ label: KIND_LABELS[option], icon: KIND_ICONS[option], checked: col.kind === option, run: run(setColumnKind(colId, option), `Kind ${KIND_LABELS[option]}`) })),
|
|
246
495
|
]), focus: false },
|
|
247
|
-
{ label: "Sort ascending", icon: "↑", run: run(sortBy(colId, "asc")) },
|
|
248
|
-
{ label: "Sort descending", icon: "↓", run: run(sortBy(colId, "desc")) },
|
|
496
|
+
{ label: "Sort ascending", icon: "↑", run: run(sortBy(colId, "asc"), "Sorted ascending") },
|
|
497
|
+
{ label: "Sort descending", icon: "↓", run: run(sortBy(colId, "desc"), "Sorted descending") },
|
|
249
498
|
{ label: "Width ▸", icon: "↔", hint: col.width ? `${col.width}px` : "Auto", focus: false, run: () => openPageMenu(this.view, control, [
|
|
250
|
-
{ label: "Wider", icon: "+", run: (
|
|
251
|
-
{ label: "Narrower", icon: "−", run: (
|
|
499
|
+
{ label: "Wider", icon: "+", run: run(this.widthCommand(colId, this.widthOfColumn(colId) + 40), "Column width changed") },
|
|
500
|
+
{ label: "Narrower", icon: "−", run: run(this.widthCommand(colId, this.widthOfColumn(colId) - 40), "Column width changed") },
|
|
252
501
|
{ label: "Fit to content", icon: "↔", run: () => this.fit(colId) },
|
|
253
|
-
...(
|
|
502
|
+
...(this.isSized() ? [{ label: "Reset widths", icon: "×", run: run(clearColumnWidths(colId), "Column widths reset") }] : []),
|
|
254
503
|
]) },
|
|
255
504
|
{ label: "Chart this table", icon: "📊", run: () => { const at = this.getPos(); return loadChartPart(this.view).then(({ insertChartAfterTable }) => insertChartAfterTable(this.view, at)); } },
|
|
256
|
-
{ label: "Insert column left", icon: "+", run: run(addColumn(false)) },
|
|
257
|
-
{ label: "Insert column right", icon: "+", run: run(addColumn(true)) },
|
|
258
|
-
...(index > 0 ? [{ label: "Move left", icon: "←", run: run(moveColumn(-1)) }] : []),
|
|
259
|
-
...(index < columns(this.node).length - 1 ? [{ label: "Move right", icon: "→", run: run(moveColumn(1)) }] : []),
|
|
260
|
-
{ label: "Select column", icon: "▦", run: () =>
|
|
261
|
-
{ label: "Delete column", icon: "×", run: run(deleteColumn) },
|
|
262
|
-
]);
|
|
505
|
+
{ label: "Insert column left", icon: "+", run: run(addColumn(false), "Column inserted") },
|
|
506
|
+
{ label: "Insert column right", icon: "+", run: run(addColumn(true), "Column inserted") },
|
|
507
|
+
...(index > 0 ? [{ label: "Move left", icon: "←", run: run(moveColumn(-1), "Column moved") }] : []),
|
|
508
|
+
...(index < columns(this.node).length - 1 ? [{ label: "Move right", icon: "→", run: run(moveColumn(1), "Column moved") }] : []),
|
|
509
|
+
{ label: "Select column", icon: "▦", run: () => selectOn(this.view, target, "col") },
|
|
510
|
+
{ label: "Delete column", icon: "×", run: run(deleteColumn, "Column deleted") },
|
|
511
|
+
], { persist: true });
|
|
263
512
|
});
|
|
264
|
-
control.dataset.kind = kind;
|
|
265
513
|
th.append(control, this.resizer(colId, th, col, index));
|
|
266
514
|
row.append(th);
|
|
267
515
|
}
|
|
516
|
+
const opened = this.menuAnchor;
|
|
268
517
|
this.header.replaceChildren(row);
|
|
269
|
-
if (
|
|
518
|
+
if (opened) {
|
|
519
|
+
const next = this.control({ tableId, control: opened.dataset.control, colId: opened.dataset.col ?? null, rowId: opened.dataset.row ?? null });
|
|
520
|
+
this.menuAnchor = next && next !== opened && reanchorPageMenu(this.view, opened, next) ? next : null;
|
|
521
|
+
}
|
|
522
|
+
if (focus) this.focusControl(focus);
|
|
270
523
|
}
|
|
271
524
|
update(node) {
|
|
272
525
|
if (node.type !== N.table) return false;
|
|
273
|
-
const focused = this.
|
|
526
|
+
const focused = this.describe(this.dom.ownerDocument.activeElement);
|
|
274
527
|
const preview = this.preview;
|
|
528
|
+
const renaming = this.renaming?.isConnected && focused?.control === "rename";
|
|
275
529
|
this.node = node;
|
|
276
530
|
this.dom.dataset.id = node.attrs.id ?? "";
|
|
277
531
|
this.preview = null;
|
|
278
|
-
|
|
279
|
-
if (
|
|
532
|
+
// A peer's edit does not take the name being typed out of the field.
|
|
533
|
+
if (renaming) this.widths();
|
|
534
|
+
else this.render(focused);
|
|
535
|
+
if (preview && node.attrs.cols[preview.col] && (node.attrs.cols[preview.col].width ?? preview.base) === preview.base) {
|
|
280
536
|
this.previewWidth(preview.col, preview.width);
|
|
281
537
|
}
|
|
282
538
|
const effective = columnKinds(node);
|
|
283
539
|
for (const cell of this.contentDOM.querySelectorAll("td[data-col]")) cell.dataset.kind = effective[cell.dataset.col] ?? "text";
|
|
540
|
+
if (focused && !renaming) this.afterUpdate(focused);
|
|
284
541
|
return true;
|
|
285
542
|
}
|
|
286
|
-
stopEvent(event) { return !!event.target.closest?.("thead"); }
|
|
543
|
+
stopEvent(event) { return !!event.target.closest?.("thead, .page-table-actions, .page-table-add-row, .page-table-add-col"); }
|
|
287
544
|
ignoreMutation(mutation) { return !this.contentDOM.contains(mutation.target); }
|
|
545
|
+
destroy() {
|
|
546
|
+
this.destroyed = true;
|
|
547
|
+
this.renaming = null;
|
|
548
|
+
this.renamingUnsaved?.dispose();
|
|
549
|
+
this.renamingUnsaved = null;
|
|
550
|
+
this.menuAnchor = null;
|
|
551
|
+
this.preview = null;
|
|
552
|
+
}
|
|
288
553
|
}
|
|
289
554
|
|
|
290
555
|
|
|
@@ -299,38 +564,63 @@ export class TableCellView {
|
|
|
299
564
|
cellViews.set(this.dom, this);
|
|
300
565
|
this.dom.dataset.col = node.attrs.col;
|
|
301
566
|
this.contentDOM = doc.createElement("span");
|
|
302
|
-
this.dom.append(this.contentDOM);
|
|
303
567
|
const menu = doc.createElement("button");
|
|
304
568
|
menu.type = "button";
|
|
305
569
|
menu.className = "page-table-row-menu";
|
|
306
|
-
menu.
|
|
570
|
+
menu.innerHTML = GEAR;
|
|
571
|
+
menu.dataset.control = "row";
|
|
572
|
+
menu.setAttribute("aria-haspopup", "menu");
|
|
573
|
+
menu.setAttribute("aria-expanded", "false");
|
|
307
574
|
menu.setAttribute("aria-label", "Row actions");
|
|
575
|
+
menu.title = "Row actions";
|
|
308
576
|
menu.contentEditable = "false";
|
|
309
577
|
menu.addEventListener("click", () => {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
const
|
|
578
|
+
const target = this.target();
|
|
579
|
+
if (!target) return;
|
|
580
|
+
const found = tableById(this.view.state.doc, target.tableId);
|
|
581
|
+
if (!found) return;
|
|
582
|
+
const at = (command, said) => () => {
|
|
583
|
+
const done = runOn(this.view, target, command);
|
|
584
|
+
announce(this.view, done ? said : UNAVAILABLE);
|
|
585
|
+
return done;
|
|
586
|
+
};
|
|
587
|
+
const row = Math.max(0, rowIndexOf(found.table, target.rowId));
|
|
588
|
+
const count = found.table.childCount;
|
|
315
589
|
openPageMenu(this.view, menu, [
|
|
316
|
-
{ label: "Insert above", icon: "+", run:
|
|
317
|
-
{ label: "Insert below", icon: "+", run:
|
|
318
|
-
...(row > 0 ? [{ label: "Move up", icon: "↑", run:
|
|
319
|
-
...(row < count - 1 ? [{ label: "Move down", icon: "↓", run:
|
|
320
|
-
{ label: "Select row", icon: "▦", run: () =>
|
|
321
|
-
{ label: "Delete row", icon: "×", run:
|
|
322
|
-
]);
|
|
590
|
+
{ label: "Insert above", icon: "+", run: at(addRow(false), "Row inserted") },
|
|
591
|
+
{ label: "Insert below", icon: "+", run: at(addRow(true), "Row inserted") },
|
|
592
|
+
...(row > 0 ? [{ label: "Move up", icon: "↑", run: at(moveRow(-1), "Row moved") }] : []),
|
|
593
|
+
...(row < count - 1 ? [{ label: "Move down", icon: "↓", run: at(moveRow(1), "Row moved") }] : []),
|
|
594
|
+
{ label: "Select row", icon: "▦", run: () => selectOn(this.view, target, "row") },
|
|
595
|
+
{ label: "Delete row", icon: "×", run: at(deleteRow, "Row deleted") },
|
|
596
|
+
], { persist: true });
|
|
323
597
|
});
|
|
324
598
|
this.menu = menu;
|
|
325
|
-
this.dom.append(menu);
|
|
599
|
+
this.dom.append(menu, this.contentDOM);
|
|
326
600
|
this.update(node);
|
|
327
601
|
}
|
|
602
|
+
// The row and column this cell names when its menu opens; both are looked up
|
|
603
|
+
// again by id when a row of the menu runs.
|
|
604
|
+
target() {
|
|
605
|
+
const pos = this.getPos();
|
|
606
|
+
const $cell = pos == null ? null : this.view.state.doc.resolve(pos);
|
|
607
|
+
const table = $cell?.node(1);
|
|
608
|
+
if (!table || table.type !== N.table) return null;
|
|
609
|
+
return { tableId: table.attrs.id, rowId: $cell.node(2).attrs.id ?? null, colId: this.node.attrs.col ?? null };
|
|
610
|
+
}
|
|
328
611
|
update(node) {
|
|
329
612
|
if (node.type !== N.table_cell) return false;
|
|
330
613
|
this.node = node;
|
|
331
614
|
const $cell = this.view.state.doc.resolve(this.getPos());
|
|
332
615
|
const table = $cell.node(1);
|
|
616
|
+
const row = $cell.node(2);
|
|
617
|
+
const index = $cell.index(1);
|
|
618
|
+
this.dom.dataset.col = node.attrs.col;
|
|
619
|
+
this.dom.setAttribute("headers", headerId(table.attrs.id, node.attrs.col));
|
|
333
620
|
this.dom.dataset.kind = columnKinds(table)[node.attrs.col] ?? "text";
|
|
621
|
+
this.menu.dataset.col = node.attrs.col;
|
|
622
|
+
this.menu.dataset.row = row.attrs.id ?? "";
|
|
623
|
+
this.menu.setAttribute("aria-label", `Row actions: ${rowLabel(row, index)}`);
|
|
334
624
|
this.menu.hidden = $cell.index(2) !== 0;
|
|
335
625
|
if (this.dom.dataset.kind !== "date") this.closeDate();
|
|
336
626
|
return true;
|
|
@@ -377,7 +667,7 @@ export function createFeature() {
|
|
|
377
667
|
table: (node, view, getPos) => new TableView(node, view, getPos),
|
|
378
668
|
table_cell: (node, view, getPos) => new TableCellView(node, view, getPos),
|
|
379
669
|
},
|
|
380
|
-
css: TABLE_CSS,
|
|
670
|
+
css: TABLE_CSS + ACTIONS_CSS,
|
|
381
671
|
dispose() {},
|
|
382
672
|
};
|
|
383
673
|
}
|