@jxsuite/studio 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/data/elements-meta.json +5 -15
- package/data/html-meta.json +51 -6
- package/dist/chunks/{2020-xp8vnwma.js → 2020-g79n01v9.js} +14 -14
- package/dist/chunks/action-group-sp6tna8v.js +118 -0
- package/dist/chunks/adapter-nj7tnjfn.js +13 -0
- package/dist/chunks/cf-account-picker-fyf63pmy.js +495 -0
- package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
- package/dist/chunks/color-area-v17df7b8.js +154 -0
- package/dist/chunks/color-field-anwybnkn.js +208 -0
- package/dist/chunks/color-slider-44ez96fc.js +79 -0
- package/dist/chunks/color-xgg3kfw7.js +35 -0
- package/dist/chunks/combobox-5e7jev23.js +236 -0
- package/dist/chunks/components-2w1f35qs.js +18 -0
- package/dist/chunks/content-source-3wqeas45.js +46 -0
- package/dist/chunks/convert-to-repeater-d0cgnb6d.js +639 -0
- package/dist/chunks/dialog-s890b918.js +85 -0
- package/dist/chunks/editor-3qhbg51x.js +35 -0
- package/dist/chunks/entry-commands-d6g50j7s.js +42 -0
- package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
- package/dist/chunks/file-ops-3btj6st0.js +47 -0
- package/dist/chunks/files-ef8fgs3w.js +72 -0
- package/dist/chunks/format-host-3c33nwr8.js +40 -0
- package/dist/chunks/fs-events-13tj6sex.js +18 -0
- package/dist/chunks/git-panel-4wp2rn9b.js +49 -0
- package/dist/chunks/icons-yy10wk9r.js +402 -0
- package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
- package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
- package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
- package/dist/chunks/iframe-host-jgesd924.js +115 -0
- package/dist/chunks/{index-nj44jpzj.js → index-mdmyx6s2.js} +11 -11
- package/dist/chunks/index-n4j7qxyr.js +109 -0
- package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
- package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
- package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
- package/dist/chunks/layers-1nq5evsh.js +29 -0
- package/dist/chunks/layers-panel-ar2gh90g.js +40 -0
- package/dist/chunks/listbox-6v2ac08j.js +44 -0
- package/dist/chunks/lit-html-m244w9nh.js +17 -0
- package/dist/chunks/media-picker-2qbetq73.js +33 -0
- package/dist/chunks/menu-q99sy9rf.js +285 -0
- package/dist/chunks/menu-rf7n72mf.js +14 -0
- package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
- package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
- package/dist/chunks/{monaco-setup-rws7k4br.js → monaco-setup-8r62fj6y.js} +27 -37
- package/dist/chunks/notify-628t0anh.js +21 -0
- package/dist/chunks/number-field-5cndyft8.js +72 -0
- package/dist/chunks/parse-0zx14y15.js +10 -0
- package/dist/chunks/platform-ts9cg1a4.js +15 -0
- package/dist/chunks/popover-47z3ytva.js +25 -0
- package/dist/chunks/publish-panel-98f7sr3g.js +27 -0
- package/dist/chunks/quick-search-srzz83k2.js +45 -0
- package/dist/chunks/references-n553f0e1.js +26 -0
- package/dist/chunks/right-panel-rrgx2dn3.js +45 -0
- package/dist/chunks/section-registry-5pq32t0q.js +25 -0
- package/dist/chunks/select-2r16ez41.js +49 -0
- package/dist/chunks/split-g7nm1rgb.js +191 -0
- package/dist/chunks/studio-0hqbpbd0.js +62 -0
- package/dist/chunks/studio-18avnhjp.js +2430 -0
- package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
- package/dist/chunks/{studio-hbtp0eh1.js → studio-25hkdrkw.js} +356 -85
- package/dist/chunks/studio-26d7myd1.js +258 -0
- package/dist/chunks/{studio-p8j86wag.js → studio-38emantv.js} +22 -5
- package/dist/chunks/studio-3gxv60tn.js +422 -0
- package/dist/chunks/{studio-8swrjbny.js → studio-3hxkd92t.js} +275 -163
- package/dist/chunks/studio-4m0haseg.js +185 -0
- package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
- package/dist/chunks/{studio-gv8vvshc.js → studio-5523bpzt.js} +11 -11
- package/dist/chunks/{studio-5q69gfag.js → studio-57mf66w2.js} +1753 -2484
- package/dist/chunks/studio-5jy81t3s.js +17463 -0
- package/dist/chunks/studio-5rt7sqa0.js +1168 -0
- package/dist/chunks/studio-5te5tqng.js +81 -0
- package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
- package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
- package/dist/chunks/studio-6xa21ej9.js +28 -0
- package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
- package/dist/chunks/{studio-eenfkt4e.js → studio-79y5sz51.js} +52 -38
- package/dist/chunks/{studio-4q0wznzf.js → studio-7e11jpa9.js} +461 -145
- package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
- package/dist/chunks/studio-7xptrg0j.js +954 -0
- package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
- package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
- package/dist/chunks/studio-8qtrpzr3.js +839 -0
- package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
- package/dist/chunks/studio-9xxz1q69.js +1356 -0
- package/dist/chunks/studio-akbww0j5.js +13615 -0
- package/dist/chunks/studio-arfpe389.js +195 -0
- package/dist/chunks/{studio-2zkz8evn.js → studio-az3zd49t.js} +473 -281
- package/dist/chunks/studio-cpvb2ed1.js +1171 -0
- package/dist/chunks/{studio-9r6j5ev1.js → studio-d0300636.js} +5078 -19
- package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
- package/dist/chunks/studio-d1ja2x9r.js +362 -0
- package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
- package/dist/chunks/{studio-4pqqxbrt.js → studio-em9nw8eh.js} +301 -112
- package/dist/chunks/studio-erdatzvp.js +1190 -0
- package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
- package/dist/chunks/studio-fabxk6r9.js +2811 -0
- package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
- package/dist/chunks/studio-hhvs4sdg.js +195 -0
- package/dist/chunks/{studio-68n67094.js → studio-kcgff4r0.js} +15 -18
- package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
- package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
- package/dist/chunks/studio-pm6hgy7r.js +4763 -0
- package/dist/chunks/studio-q0a3yk4n.js +33 -0
- package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
- package/dist/chunks/studio-qahxvnan.js +111 -0
- package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
- package/dist/chunks/{studio-kjjsjak4.js → studio-rkstz940.js} +3661 -6136
- package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
- package/dist/chunks/studio-rpyah2ew.js +2805 -0
- package/dist/chunks/studio-rs76k67r.js +12 -0
- package/dist/chunks/{studio-yxrkyv8a.js → studio-s6z853aq.js} +12108 -11766
- package/dist/chunks/{studio-sf7zjgv1.js → studio-sgdc5pfc.js} +8 -8
- package/dist/chunks/studio-tb3ev01j.js +763 -0
- package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
- package/dist/chunks/studio-vhv5tgxm.js +51 -0
- package/dist/chunks/{studio-htsf0w6m.js → studio-vz68yn9e.js} +31 -154
- package/dist/chunks/studio-wqpngpsk.js +174 -0
- package/dist/chunks/{studio-eesa1myv.js → studio-x3rty6tr.js} +58 -58
- package/dist/chunks/studio-xsykpvsh.js +8448 -0
- package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
- package/dist/chunks/studio-yxa81f45.js +12 -0
- package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
- package/dist/chunks/tabs-wbp1cdws.js +156 -0
- package/dist/chunks/textfield-abk6qyee.js +73 -0
- package/dist/chunks/toast-780pvfbv.js +27 -0
- package/dist/chunks/toast-host-3sqv46sn.js +116 -0
- package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
- package/dist/chunks/tooltip-eptb614w.js +369 -0
- package/dist/chunks/tree-a73p0pqm.js +367 -0
- package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
- package/dist/chunks/workspace-wrew8zk4.js +69 -0
- package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
- package/dist/iframe-entry.js +1812 -698
- package/dist/manifest.json +7 -0
- package/dist/studio.js +52900 -36573
- package/dist/workers/editor.worker.js +1040 -1085
- package/dist/workers/json.worker.js +1946 -1991
- package/dist/workers/ts.worker.js +4960 -5005
- package/favicon.ico +0 -0
- package/index.html +2 -0
- package/package.json +22 -55
- package/src/about/about-modal.ts +73 -123
- package/src/browse/library-commands.ts +20 -49
- package/src/browse/library-layouts.ts +87 -218
- package/src/browse/library-pane.ts +406 -438
- package/src/canvas/canvas-helpers.ts +0 -49
- package/src/canvas/canvas-live-render.ts +25 -2
- package/src/canvas/canvas-render.ts +666 -231
- package/src/canvas/canvas-surface.ts +0 -1
- package/src/canvas/canvas-utils.ts +310 -117
- package/src/canvas/dialog-path.ts +76 -0
- package/src/canvas/dialog-state.ts +67 -0
- package/src/canvas/diff-marks.ts +252 -0
- package/src/canvas/diff-toolbar.ts +349 -0
- package/src/canvas/diff-view.ts +123 -0
- package/src/canvas/edit-width-drag.ts +187 -0
- package/src/canvas/edit-width.ts +150 -0
- package/src/canvas/iframe-entry.ts +125 -8
- package/src/canvas/iframe-host.ts +271 -15
- package/src/canvas/iframe-interaction.ts +133 -13
- package/src/canvas/iframe-patch.ts +7 -1
- package/src/canvas/iframe-position.ts +5 -6
- package/src/canvas/iframe-protocol.ts +106 -5
- package/src/canvas/iframe-render.ts +373 -5
- package/src/canvas/iframe-slash.ts +2 -2
- package/src/canvas/iframe-subtree.ts +5 -8
- package/src/canvas/path-mapping.ts +25 -3
- package/src/canvas/popover-path.ts +106 -0
- package/src/canvas/popover-state.ts +155 -0
- package/src/canvas/surface-registry.ts +21 -10
- package/src/collab/presence-chips.ts +51 -73
- package/src/commands/app-commands.ts +10 -0
- package/src/commands/budget.ts +12 -0
- package/src/commands/command-args.ts +369 -6
- package/src/commands/defaults.ts +44 -14
- package/src/commands/live-context.ts +11 -5
- package/src/commands/reference.ts +12 -2
- package/src/commands/registry.ts +165 -8
- package/src/content/entry-commands.ts +16 -23
- package/src/content/entry-editor.ts +144 -101
- package/src/editor/canvas-slash-bridge.ts +2 -2
- package/src/editor/context-menu.ts +49 -200
- package/src/editor/convert-to-component.ts +42 -82
- package/src/editor/convert-to-repeater.ts +132 -173
- package/src/editor/inline-edit.ts +11 -0
- package/src/editor/shortcuts.ts +65 -26
- package/src/editor/slash-menu.ts +150 -157
- package/src/files/file-ops.ts +117 -11
- package/src/files/files.ts +949 -664
- package/src/files/json-layout.ts +474 -0
- package/src/files/media-paths.ts +13 -42
- package/src/files/media-usage.ts +41 -131
- package/src/files/serialize-document.ts +8 -1
- package/src/format/convert-file.ts +16 -10
- package/src/format/format-host.ts +50 -1
- package/src/github/github-auth.ts +71 -29
- package/src/github/github-publish.ts +57 -65
- package/src/grid/cell-editors.ts +46 -4
- package/src/grid/cell-popovers.ts +65 -62
- package/src/grid/grid-open.ts +86 -64
- package/src/grid/grid-panel.ts +390 -469
- package/src/grid/redirects-grid.ts +40 -44
- package/src/hosting/document.ts +2 -0
- package/src/hosting/layout.ts +10 -0
- package/src/i18n/i18n-commands.ts +127 -62
- package/src/media/media-pane.ts +83 -133
- package/src/new-project/add-repo-modal.ts +125 -176
- package/src/new-project/import-tab.ts +201 -250
- package/src/new-project/location-fields.ts +101 -165
- package/src/new-project/new-project-modal.ts +376 -367
- package/src/packages/jxsuite-update.ts +76 -18
- package/src/panels/activity-panel.ts +137 -104
- package/src/panels/ai-chat/chat-markdown.ts +15 -13
- package/src/panels/ai-chat/chat-view.ts +307 -472
- package/src/panels/ai-chat/composer.ts +175 -153
- package/src/panels/ai-chat/sessions-view.ts +31 -62
- package/src/panels/ai-panel.ts +210 -185
- package/src/panels/block-action-bar.ts +437 -643
- package/src/panels/bottom-dock.ts +112 -47
- package/src/panels/chat-panel.ts +19 -14
- package/src/panels/data-explorer.ts +141 -102
- package/src/panels/data-grid.ts +183 -150
- package/src/panels/dnd.ts +75 -20
- package/src/panels/editors.ts +26 -19
- package/src/panels/elements-panel.ts +243 -186
- package/src/panels/empty-state.ts +26 -42
- package/src/panels/events-panel.ts +950 -729
- package/src/panels/formula-workspace.ts +434 -207
- package/src/panels/frontmatter-fields.ts +185 -153
- package/src/panels/frontmatter-panel.ts +411 -187
- package/src/panels/git-diff-open.ts +148 -0
- package/src/panels/git-panel.ts +679 -605
- package/src/panels/head-panel.ts +617 -435
- package/src/panels/i18n-panel.ts +153 -133
- package/src/panels/imports-panel.ts +388 -414
- package/src/panels/jump-bar.ts +174 -147
- package/src/panels/layers-panel.ts +785 -671
- package/src/panels/left-panel.ts +166 -94
- package/src/panels/navigator-panels.ts +1 -1
- package/src/panels/pane-context.ts +529 -677
- package/src/panels/pane-grid.ts +265 -241
- package/src/panels/panel-registry.ts +15 -4
- package/src/panels/problems-panel.ts +177 -99
- package/src/panels/properties-panel.ts +1525 -966
- package/src/panels/provenance.ts +31 -113
- package/src/panels/quick-search.ts +217 -172
- package/src/panels/right-panel.ts +135 -169
- package/src/panels/seo-modal.ts +346 -167
- package/src/panels/settings-menu.ts +109 -646
- package/src/panels/settings-pane.ts +102 -86
- package/src/panels/signals-panel.ts +1318 -1172
- package/src/panels/statement-editor.ts +556 -428
- package/src/panels/style-panel.ts +1367 -630
- package/src/panels/style-utils.ts +36 -32
- package/src/panels/stylebook-doc.ts +30 -73
- package/src/panels/stylebook-layers-panel.ts +202 -109
- package/src/panels/stylebook-panel.ts +124 -88
- package/src/panels/tab-strip.ts +385 -287
- package/src/platforms/cloud.ts +347 -86
- package/src/platforms/devserver.ts +13 -0
- package/src/publish/deploy-checklist.ts +124 -78
- package/src/publish/publish-commands.ts +4 -12
- package/src/publish/publish-panel.ts +165 -299
- package/src/services/a11y-report.ts +100 -43
- package/src/services/ai-command-tools.ts +468 -0
- package/src/services/ai-import-tools.ts +50 -12
- package/src/services/ai-models.ts +108 -1
- package/src/services/ai-session-store.ts +6 -0
- package/src/services/ai-system-prompt.ts +110 -18
- package/src/services/ai-tools.ts +28 -143
- package/src/services/ai-write-report.ts +174 -0
- package/src/services/context-manager.ts +12 -1
- package/src/services/document-assistant.ts +50 -2
- package/src/services/idle.ts +16 -14
- package/src/services/live-surfaces.ts +237 -0
- package/src/services/model-uri.ts +75 -0
- package/src/services/notify.ts +63 -6
- package/src/services/popover-report.ts +325 -0
- package/src/services/surface-registry.ts +56 -0
- package/src/services/tool-executor.ts +17 -0
- package/src/settings/contexts-section.ts +301 -299
- package/src/settings/contributed-section.ts +361 -222
- package/src/settings/css-vars-editor.ts +320 -325
- package/src/settings/defs-editor.ts +572 -535
- package/src/settings/dependencies-editor.ts +79 -107
- package/src/settings/extension-commands.ts +379 -0
- package/src/settings/extension-rows.ts +113 -0
- package/src/settings/extensions-section.ts +287 -0
- package/src/settings/general-settings.ts +130 -181
- package/src/settings/head-editor.ts +10 -305
- package/src/settings/locales-section.ts +146 -196
- package/src/settings/preferences-accounts.ts +193 -12
- package/src/settings/preferences-dialog.ts +384 -307
- package/src/settings/project-sections.ts +108 -181
- package/src/settings/schema-field-ui.ts +11 -399
- package/src/settings/section-registry.ts +8 -1
- package/src/settings/settings-document.ts +31 -29
- package/src/shell/tree.ts +42 -87
- package/src/shell.ts +48 -36
- package/src/studio.ts +90 -67
- package/src/style/token-ref.ts +0 -39
- package/src/surfaces/about.json +263 -0
- package/src/surfaces/about.ts +163 -0
- package/src/surfaces/add-repo.json +486 -0
- package/src/surfaces/add-repo.ts +236 -0
- package/src/surfaces/ai-chat.json +2185 -0
- package/src/surfaces/ai-chat.ts +549 -0
- package/src/surfaces/ai-credentials-form.json +332 -0
- package/src/surfaces/ai-credentials-form.ts +151 -0
- package/src/surfaces/ai-managed-connect.json +108 -0
- package/src/surfaces/ai-managed-connect.ts +123 -0
- package/src/surfaces/ai-model-picker.json +114 -0
- package/src/surfaces/ai-model-picker.ts +139 -0
- package/src/surfaces/block-action-bar.json +640 -0
- package/src/surfaces/block-action-bar.ts +368 -0
- package/src/surfaces/bottom-dock.json +154 -0
- package/src/surfaces/bottom-dock.ts +125 -0
- package/src/surfaces/canvas-stage.json +444 -0
- package/src/surfaces/canvas-stage.ts +253 -0
- package/src/surfaces/cf-account-picker.json +300 -0
- package/src/surfaces/cf-account-picker.ts +214 -0
- package/src/surfaces/commandbar.json +984 -0
- package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
- package/src/surfaces/convert-repeater.json +309 -0
- package/src/surfaces/convert-repeater.ts +276 -0
- package/src/surfaces/data-actions.json +157 -0
- package/src/surfaces/data-actions.ts +115 -0
- package/src/surfaces/dialog.json +289 -0
- package/src/surfaces/dialog.ts +343 -0
- package/src/surfaces/diff-toolbar.json +266 -0
- package/src/surfaces/diff-toolbar.ts +139 -0
- package/src/surfaces/doc-header.json +739 -0
- package/src/surfaces/doc-header.ts +223 -0
- package/src/surfaces/empty-state.json +152 -0
- package/src/surfaces/empty-state.ts +173 -0
- package/src/surfaces/entry-editor.json +281 -0
- package/src/surfaces/entry-editor.ts +191 -0
- package/src/surfaces/expression-editor.json +977 -0
- package/src/surfaces/expression-editor.ts +266 -0
- package/src/surfaces/files-panel.json +592 -0
- package/src/surfaces/files-panel.ts +301 -0
- package/src/surfaces/formula-palette.json +364 -0
- package/src/surfaces/formula-palette.ts +340 -0
- package/src/surfaces/git-panel.json +1822 -0
- package/src/surfaces/git-panel.ts +272 -0
- package/src/surfaces/github-auth.json +112 -0
- package/src/surfaces/github-auth.ts +139 -0
- package/src/surfaces/github-publish.json +206 -0
- package/src/surfaces/github-publish.ts +169 -0
- package/src/surfaces/grid-cell.json +218 -0
- package/src/surfaces/grid-cell.ts +120 -0
- package/src/surfaces/grid-open.json +147 -0
- package/src/surfaces/grid-open.ts +87 -0
- package/src/surfaces/grid-panel.json +706 -0
- package/src/surfaces/grid-panel.ts +206 -0
- package/src/surfaces/grid-replace.json +179 -0
- package/src/surfaces/grid-replace.ts +89 -0
- package/src/surfaces/grid-views.json +446 -0
- package/src/surfaces/grid-views.ts +172 -0
- package/src/surfaces/inspector-dock.json +187 -0
- package/src/surfaces/inspector-dock.ts +161 -0
- package/src/surfaces/jump-bar.json +243 -0
- package/src/surfaces/jump-bar.ts +129 -0
- package/src/surfaces/jxsuite-update.json +146 -0
- package/src/surfaces/jxsuite-update.ts +149 -0
- package/src/surfaces/library-pane.json +1509 -0
- package/src/surfaces/library-pane.ts +363 -0
- package/src/surfaces/logic-panel.json +1949 -0
- package/src/surfaces/logic-panel.ts +369 -0
- package/src/surfaces/logic-workspace.json +685 -0
- package/src/surfaces/logic-workspace.ts +238 -0
- package/src/surfaces/media-browser.json +303 -0
- package/src/surfaces/media-browser.ts +145 -0
- package/src/surfaces/media-field.json +163 -0
- package/src/surfaces/media-field.ts +116 -0
- package/src/surfaces/media-pane.json +513 -0
- package/src/surfaces/media-pane.ts +197 -0
- package/src/surfaces/menu.json +232 -0
- package/src/surfaces/menu.ts +375 -0
- package/src/surfaces/navigator-dock.json +113 -0
- package/src/surfaces/navigator-dock.ts +130 -0
- package/src/surfaces/new-project.json +1654 -0
- package/src/surfaces/new-project.ts +411 -0
- package/src/surfaces/palette.json +535 -0
- package/src/surfaces/pane-context.json +1497 -0
- package/src/surfaces/pane-context.ts +513 -0
- package/src/surfaces/pane-grid.json +188 -0
- package/src/surfaces/pane-grid.ts +250 -0
- package/src/surfaces/panel-activity.json +563 -0
- package/src/surfaces/panel-activity.ts +187 -0
- package/src/surfaces/panel-data.json +141 -0
- package/src/surfaces/panel-data.ts +156 -0
- package/src/surfaces/panel-deploy-checklist.json +283 -0
- package/src/surfaces/panel-deploy-checklist.ts +175 -0
- package/src/surfaces/panel-elements.json +367 -0
- package/src/surfaces/panel-elements.ts +175 -0
- package/src/surfaces/panel-i18n.json +569 -0
- package/src/surfaces/panel-i18n.ts +212 -0
- package/src/surfaces/panel-imports.json +780 -0
- package/src/surfaces/panel-imports.ts +209 -0
- package/src/surfaces/panel-outline.json +734 -0
- package/src/surfaces/panel-outline.ts +326 -0
- package/src/surfaces/panel-page.json +1017 -0
- package/src/surfaces/panel-page.ts +240 -0
- package/src/surfaces/panel-problems.json +497 -0
- package/src/surfaces/panel-problems.ts +157 -0
- package/src/surfaces/panel-signals.json +1973 -0
- package/src/surfaces/panel-signals.ts +436 -0
- package/src/surfaces/panel-stylebook-layers.json +317 -0
- package/src/surfaces/panel-stylebook-layers.ts +155 -0
- package/src/surfaces/preferences.json +850 -0
- package/src/surfaces/preferences.ts +332 -0
- package/src/surfaces/progress-modal.json +110 -0
- package/src/surfaces/progress-modal.ts +150 -0
- package/src/surfaces/properties-panel.json +1578 -0
- package/src/surfaces/properties-panel.ts +337 -0
- package/src/surfaces/publish.json +945 -0
- package/src/surfaces/publish.ts +392 -0
- package/src/surfaces/push-plan.json +110 -0
- package/src/surfaces/push-plan.ts +87 -0
- package/src/surfaces/rail.json +255 -0
- package/src/surfaces/rail.ts +206 -0
- package/src/surfaces/reference-field.json +191 -0
- package/src/surfaces/reference-field.ts +168 -0
- package/src/surfaces/schema-builder.json +1011 -0
- package/src/surfaces/schema-builder.ts +181 -0
- package/src/surfaces/schema-form.json +1013 -0
- package/src/surfaces/schema-form.ts +287 -0
- package/src/surfaces/secret-field.json +60 -0
- package/src/surfaces/secret-field.ts +146 -0
- package/src/surfaces/seo.json +932 -0
- package/src/surfaces/seo.ts +334 -0
- package/src/surfaces/settings-contexts.json +538 -0
- package/src/surfaces/settings-contexts.ts +196 -0
- package/src/surfaces/settings-contributed.json +489 -0
- package/src/surfaces/settings-contributed.ts +199 -0
- package/src/surfaces/settings-css-vars.json +820 -0
- package/src/surfaces/settings-css-vars.ts +199 -0
- package/src/surfaces/settings-defs.json +1363 -0
- package/src/surfaces/settings-defs.ts +232 -0
- package/src/surfaces/settings-deploy.json +108 -0
- package/src/surfaces/settings-deploy.ts +135 -0
- package/src/surfaces/settings-extensions.json +486 -0
- package/src/surfaces/settings-extensions.ts +201 -0
- package/src/surfaces/settings-head.json +779 -0
- package/src/surfaces/settings-head.ts +397 -0
- package/src/surfaces/settings-locales.json +420 -0
- package/src/surfaces/settings-locales.ts +137 -0
- package/src/surfaces/settings-overview.json +423 -0
- package/src/surfaces/settings-overview.ts +218 -0
- package/src/surfaces/settings-packages.json +549 -0
- package/src/surfaces/settings-packages.ts +158 -0
- package/src/surfaces/settings-pane.json +147 -0
- package/src/surfaces/settings-pane.ts +150 -0
- package/src/surfaces/settings-rawjson.json +104 -0
- package/src/surfaces/settings-rawjson.ts +107 -0
- package/src/surfaces/shell.json +99 -0
- package/src/surfaces/shell.ts +30 -0
- package/src/surfaces/slash-menu.json +180 -0
- package/src/surfaces/slash-menu.ts +272 -0
- package/src/surfaces/statements.json +607 -0
- package/src/surfaces/statements.ts +238 -0
- package/src/surfaces/statusbar.json +94 -0
- package/src/{panels → surfaces}/statusbar.ts +163 -90
- package/src/surfaces/style-panel.json +1643 -0
- package/src/surfaces/style-panel.ts +338 -0
- package/src/surfaces/stylebook-chrome.json +122 -0
- package/src/surfaces/stylebook-chrome.ts +122 -0
- package/src/surfaces/tab-strip.json +448 -0
- package/src/surfaces/tab-strip.ts +200 -0
- package/src/surfaces/target-line.json +419 -0
- package/src/surfaces/target-line.ts +349 -0
- package/src/surfaces/toasts.json +189 -0
- package/src/surfaces/welcome.json +571 -0
- package/src/surfaces/welcome.ts +338 -0
- package/src/tabs/project-config.ts +71 -15
- package/src/tabs/tab.ts +52 -1
- package/src/types.ts +49 -3
- package/src/ui/ai-credentials-form.ts +104 -100
- package/src/ui/ai-managed-connect.ts +116 -49
- package/src/ui/ai-model-picker.ts +102 -47
- package/src/ui/cf-account-picker.ts +135 -0
- package/src/ui/color-selector.ts +80 -301
- package/src/ui/dynamic-slot.ts +43 -283
- package/src/ui/expression-editor.ts +797 -576
- package/src/ui/field-input.ts +8 -181
- package/src/ui/form-controls.ts +838 -366
- package/src/ui/formula-chips.ts +80 -85
- package/src/ui/kit.ts +33 -0
- package/src/ui/layers.ts +293 -593
- package/src/ui/media-picker.ts +203 -284
- package/src/ui/panel-resize.ts +251 -64
- package/src/ui/popover-surface.ts +153 -0
- package/src/ui/progress-modal.ts +22 -60
- package/src/ui/regions.ts +13 -7
- package/src/ui/schema-form.ts +906 -440
- package/src/ui/surface.ts +122 -0
- package/src/ui/unit-selector.ts +15 -131
- package/src/ui/value-source.ts +14 -0
- package/src/ui/virtual-window.ts +88 -9
- package/src/utils/canvas-media.ts +72 -0
- package/src/utils/edit-display.ts +7 -2
- package/src/utils/element-selectors.ts +69 -0
- package/src/utils/link-target.ts +2 -2
- package/src/utils/studio-utils.ts +0 -10
- package/src/view.ts +15 -0
- package/src/workspace/pane-derive.ts +36 -14
- package/src/workspace/workspace.ts +15 -6
- package/styles/canvas.css +72 -467
- package/styles/forced-colors.css +48 -82
- package/styles/forced-colors.json +41 -0
- package/styles/inspector.css +2 -1301
- package/styles/overlays.css +96 -2823
- package/styles/panels.css +6 -1423
- package/styles/shell-frame.css +272 -0
- package/styles/shell-frame.json +227 -0
- package/styles/shell.css +0 -1532
- package/styles/tokens.css +65 -136
- package/styles/tokens.json +84 -0
- package/dist/chunks/Overlay.dev-723bv0df.js +0 -23
- package/dist/chunks/adapter-jkcyhgtr.js +0 -14
- package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
- package/dist/chunks/components-zmx10t62.js +0 -21
- package/dist/chunks/content-source-t88vb2dr.js +0 -49
- package/dist/chunks/convert-to-repeater-dhw1nrz9.js +0 -268
- package/dist/chunks/editor-cptq5jp1.js +0 -37
- package/dist/chunks/entry-commands-6z6bhxg0.js +0 -46
- package/dist/chunks/extension-sections-3ydgjbdy.js +0 -311
- package/dist/chunks/file-ops-rskmhxyq.js +0 -48
- package/dist/chunks/files-0cratsr0.js +0 -92
- package/dist/chunks/flow-qbfgrpeh.js +0 -634
- package/dist/chunks/focus-trap.esm-ggawf1hy.js +0 -1024
- package/dist/chunks/focus-visible-6eg70z18.js +0 -167
- package/dist/chunks/format-host-tgcmfs4p.js +0 -43
- package/dist/chunks/fs-events-hrrg328g.js +0 -21
- package/dist/chunks/git-panel-0yywyk76.js +0 -58
- package/dist/chunks/index-zqxmqxmh.js +0 -109
- package/dist/chunks/jx-validate-8q2g05x1.js +0 -19
- package/dist/chunks/layers-panel-z3tqe5cd.js +0 -67
- package/dist/chunks/layers-q75tj2mf.js +0 -45
- package/dist/chunks/lit-html-j055mkh8.js +0 -22
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
- package/dist/chunks/notify-yjm79n4s.js +0 -30
- package/dist/chunks/parse-24d0gay3.js +0 -19
- package/dist/chunks/platform-ba1j1gbh.js +0 -16
- package/dist/chunks/publish-panel-54jvhskq.js +0 -28
- package/dist/chunks/quick-search-cgfmjwq5.js +0 -65
- package/dist/chunks/references-3qx8fkyp.js +0 -29
- package/dist/chunks/right-panel-cqp8bc4v.js +0 -46
- package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
- package/dist/chunks/sp-overlay.dev-yztmtrs1.js +0 -18
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
- package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +0 -15
- package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
- package/dist/chunks/studio-0a5qrh01.js +0 -3
- package/dist/chunks/studio-18j3bmcz.js +0 -14
- package/dist/chunks/studio-1hbqnt19.js +0 -1823
- package/dist/chunks/studio-4rzyrtjz.js +0 -426
- package/dist/chunks/studio-5rywrkxz.js +0 -1903
- package/dist/chunks/studio-7afd21nw.js +0 -67
- package/dist/chunks/studio-7nte2hj4.js +0 -613
- package/dist/chunks/studio-8jpzqk5s.js +0 -128
- package/dist/chunks/studio-8q517dfr.js +0 -24
- package/dist/chunks/studio-9c4k4vhv.js +0 -357
- package/dist/chunks/studio-9n4mrmnb.js +0 -184
- package/dist/chunks/studio-a0tswy0w.js +0 -58
- package/dist/chunks/studio-ahjfdy6e.js +0 -387
- package/dist/chunks/studio-ahnzd9rg.js +0 -23
- package/dist/chunks/studio-bw5dz6km.js +0 -608
- package/dist/chunks/studio-cb12yz9w.js +0 -15055
- package/dist/chunks/studio-d1ks410h.js +0 -175
- package/dist/chunks/studio-eqq1w638.js +0 -113
- package/dist/chunks/studio-ex8zyp98.js +0 -1813
- package/dist/chunks/studio-h4rxscfc.js +0 -10
- package/dist/chunks/studio-kyh9avvv.js +0 -28
- package/dist/chunks/studio-kyk2v92a.js +0 -659
- package/dist/chunks/studio-m3hwn7y1.js +0 -120
- package/dist/chunks/studio-mmv72a27.js +0 -4344
- package/dist/chunks/studio-n8b9wqwy.js +0 -291
- package/dist/chunks/studio-pdfjntbk.js +0 -6939
- package/dist/chunks/studio-qq6jym04.js +0 -53
- package/dist/chunks/studio-r1r7afxx.js +0 -1066
- package/dist/chunks/studio-rdx8jcxy.js +0 -83
- package/dist/chunks/studio-rn9ta2mz.js +0 -111
- package/dist/chunks/studio-t7qb9db8.js +0 -665
- package/dist/chunks/studio-tmc5emrw.js +0 -8
- package/dist/chunks/studio-txwkm6p9.js +0 -12
- package/dist/chunks/studio-v38hmvx1.js +0 -390
- package/dist/chunks/studio-vadzgkrz.js +0 -318
- package/dist/chunks/studio-wx5609xd.js +0 -341
- package/dist/chunks/studio-z6nw2ek2.js +0 -18
- package/dist/chunks/studio-zczps70q.js +0 -378
- package/dist/chunks/workspace-xpres40v.js +0 -89
- package/src/panels/activity-bar.ts +0 -251
- package/src/panels/panel-scheduler.ts +0 -219
- package/src/panels/style-inputs.ts +0 -237
- package/src/panels/target-line.ts +0 -274
- package/src/panels/welcome-screen.ts +0 -346
- package/src/ui/button-group.ts +0 -102
- package/src/ui/choice-field.ts +0 -72
- package/src/ui/field-row.ts +0 -249
- package/src/ui/formula-palette.ts +0 -213
- package/src/ui/icons.ts +0 -52
- package/src/ui/jx-theme.ts +0 -130
- package/src/ui/spectrum.ts +0 -340
- package/src/ui/value-selector.ts +0 -174
- package/src/ui/widgets.ts +0 -172
- /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
- /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
- /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
- /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
package/src/grid/grid-panel.ts
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Grid canvas panel — the
|
|
2
|
+
* Grid canvas panel — the FLOW behind the grid surface, drawn on a pane's stage when a tab's
|
|
3
|
+
* canvasMode is "grid".
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* `canvas-render` calls renderGridMode() on entry and detachGridPanel() in its mode-change
|
|
6
|
+
* teardown. The frame is `surfaces/grid-panel.json`, mounted once per entry; a panel-local effect
|
|
7
|
+
* tracks the controller's reactive state and re-PROJECTS it, so the Save badge, row count and
|
|
7
8
|
* loading/error surfaces stay live without renderCanvas involvement. The Tabulator view is created
|
|
8
|
-
* once per tab entry, after the controller has loaded columns
|
|
9
|
-
* data survives in the controller/buffer, so a rebuild on
|
|
9
|
+
* once per tab entry, after the controller has loaded columns and the document has made the host
|
|
10
|
+
* node, and destroyed on detach — all grid data survives in the controller/buffer, so a rebuild on
|
|
11
|
+
* re-entry is cheap.
|
|
12
|
+
*
|
|
13
|
+
* **The markup left.** Everything this module used to render — the toolbar, the saved-view panel,
|
|
14
|
+
* find-and-replace, the "no grid source" sentence — is a document now (`surfaces/grid-panel.json`,
|
|
15
|
+
* `surfaces/grid-views.json`, `surfaces/grid-replace.json`), and what is here decides rather than
|
|
16
|
+
* draws. Two things that used to need saying out loud stopped needing it:
|
|
17
|
+
*
|
|
18
|
+
* - **The engine's host node cannot be destroyed by a toolbar tick.** The lit shell wrapped that div
|
|
19
|
+
* in `guard([source.id])` because a re-render of the surrounding template would otherwise
|
|
20
|
+
* re-commit it and silently take Tabulator with it. A document reconciles in place, so the
|
|
21
|
+
* guarantee is the rendering model's rather than a directive somebody has to remember.
|
|
22
|
+
* - **The panel state that is not reactive still needs a bump.** `localStorage` is not reactive and
|
|
23
|
+
* neither is the engine, so a column drag saved from `grid-view.ts`, a view saved from the
|
|
24
|
+
* palette, and the host node arriving all reach the surface through
|
|
25
|
+
* {@link ActiveGridPanel.bump}.
|
|
10
26
|
*
|
|
11
27
|
* **The panel owns the view state** (plan §12 P7.2). Saved views persist through `grid-layout.ts` —
|
|
12
28
|
* one store, the grid id as its key, so "per collection" needs no code — and this module is what
|
|
13
29
|
* drives them onto the four surfaces they touch: the engine (column order, width and visibility,
|
|
14
30
|
* applied by a REBUILD, which is cheap because the data never leaves the controller), the
|
|
15
|
-
* controller (sort and grouping, which are row order and belong to the data), and the
|
|
31
|
+
* controller (sort and grouping, which are row order and belong to the data), and the surface's own
|
|
16
32
|
* filter box. Applying a view is therefore one function with one order of operations, not five
|
|
17
33
|
* controls that each half-remember what the others did.
|
|
18
34
|
*/
|
|
19
|
-
import { html, render as litRender, nothing } from "lit-html";
|
|
20
|
-
import { guard } from "lit-html/directives/guard.js";
|
|
21
|
-
import { live } from "lit-html/directives/live.js";
|
|
22
|
-
import { ref } from "lit-html/directives/ref.js";
|
|
23
35
|
import { effect, effectScope, reactive } from "../reactivity";
|
|
24
|
-
import {
|
|
36
|
+
import { showConfirmDialog, showPromptDialog } from "../ui/layers";
|
|
25
37
|
import { notify } from "../services/notify";
|
|
26
38
|
import { rectOf } from "../utils/geometry";
|
|
27
39
|
import { activeTab, workspace } from "../workspace/workspace";
|
|
@@ -29,6 +41,9 @@ import { createGridController, getGridController } from "./grid-controller";
|
|
|
29
41
|
import { createCsvFileSource } from "./sources/csv-file-source";
|
|
30
42
|
import { createGridView } from "./grid-view";
|
|
31
43
|
import { parseGridTabId } from "./grid-source";
|
|
44
|
+
import { mountGridPanelSurface } from "../surfaces/grid-panel";
|
|
45
|
+
import { openGridViewsSurface } from "../surfaces/grid-views";
|
|
46
|
+
import { openGridReplaceSurface } from "../surfaces/grid-replace";
|
|
32
47
|
import {
|
|
33
48
|
activeViewModified,
|
|
34
49
|
activeViewName,
|
|
@@ -48,6 +63,13 @@ import type { GridView } from "./grid-view";
|
|
|
48
63
|
import type { Tab } from "../tabs/tab";
|
|
49
64
|
import type { CanvasSurface } from "../canvas/canvas-surface";
|
|
50
65
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
66
|
+
import type {
|
|
67
|
+
GridPanelActions,
|
|
68
|
+
GridPanelSurfaceHandle,
|
|
69
|
+
GridPanelView,
|
|
70
|
+
} from "../surfaces/grid-panel";
|
|
71
|
+
import type { GridViewsSurfaceHandle, GridViewsView } from "../surfaces/grid-views";
|
|
72
|
+
import type { GridReplaceSurfaceHandle } from "../surfaces/grid-replace";
|
|
51
73
|
|
|
52
74
|
interface ActiveGridPanel {
|
|
53
75
|
/** The pane whose stage this grid is drawn on. */
|
|
@@ -56,6 +78,14 @@ interface ActiveGridPanel {
|
|
|
56
78
|
scope: EffectScope;
|
|
57
79
|
view: GridView | null;
|
|
58
80
|
wrap: HTMLElement;
|
|
81
|
+
/** The mounted frame, or null while the mount is still in flight. */
|
|
82
|
+
surface: GridPanelSurfaceHandle | null;
|
|
83
|
+
/** The saved-view panel while it is open. */
|
|
84
|
+
views: GridViewsSurfaceHandle | null;
|
|
85
|
+
/** Where that panel was opened, so a re-projection cannot move it back to the origin. */
|
|
86
|
+
viewsAt: { x: number; y: number };
|
|
87
|
+
/** Find & replace while it is open. */
|
|
88
|
+
replace: GridReplaceSurfaceHandle | null;
|
|
59
89
|
/**
|
|
60
90
|
* Rebuild the engine over the same controller.
|
|
61
91
|
*
|
|
@@ -66,7 +96,7 @@ interface ActiveGridPanel {
|
|
|
66
96
|
remount: () => void;
|
|
67
97
|
/** Apply a whole layout — sort, grouping, columns, filter — in one order of operations. */
|
|
68
98
|
applyLayout: (layout: GridLayout | null) => void;
|
|
69
|
-
/** Re-
|
|
99
|
+
/** Re-project. `localStorage` is not reactive, so a view edit has to say so. */
|
|
70
100
|
bump: () => void;
|
|
71
101
|
}
|
|
72
102
|
|
|
@@ -79,6 +109,15 @@ interface ActiveGridPanel {
|
|
|
79
109
|
*/
|
|
80
110
|
const _active = new Map<string, ActiveGridPanel>();
|
|
81
111
|
|
|
112
|
+
/**
|
|
113
|
+
* The "no grid source" frame in each pane, which has no panel record to hang off.
|
|
114
|
+
*
|
|
115
|
+
* It is still a mounted document, so something has to be able to dispose it — `mountSurface`
|
|
116
|
+
* registers every mount so a saved kit edit can find it again, and a frame nobody disposes stays in
|
|
117
|
+
* that registry after its stage has been emptied.
|
|
118
|
+
*/
|
|
119
|
+
const _missing = new Map<string, GridPanelSurfaceHandle>();
|
|
120
|
+
|
|
82
121
|
/** The grid mounted in a pane, or null. */
|
|
83
122
|
function activeIn(paneId: string): ActiveGridPanel | null {
|
|
84
123
|
return _active.get(paneId) ?? null;
|
|
@@ -86,12 +125,17 @@ function activeIn(paneId: string): ActiveGridPanel | null {
|
|
|
86
125
|
|
|
87
126
|
/** Destroy one pane's grid view/effects (canvas-render teardown + tab switches). */
|
|
88
127
|
export function detachGridPanel(paneId: string) {
|
|
128
|
+
_missing.get(paneId)?.dispose();
|
|
129
|
+
_missing.delete(paneId);
|
|
89
130
|
const panel = _active.get(paneId);
|
|
90
131
|
if (!panel) {
|
|
91
132
|
return;
|
|
92
133
|
}
|
|
134
|
+
panel.views?.close();
|
|
135
|
+
panel.replace?.close();
|
|
93
136
|
panel.view?.destroy();
|
|
94
137
|
panel.scope.stop();
|
|
138
|
+
panel.surface?.dispose();
|
|
95
139
|
_active.delete(paneId);
|
|
96
140
|
}
|
|
97
141
|
|
|
@@ -101,127 +145,7 @@ export function gridPanelMounted(paneId: string, tab: Tab): boolean {
|
|
|
101
145
|
return panel !== null && panel.tabId === tab.id && panel.wrap.isConnected;
|
|
102
146
|
}
|
|
103
147
|
|
|
104
|
-
|
|
105
|
-
const getView = () => panel.view;
|
|
106
|
-
const { state } = controller;
|
|
107
|
-
const gridId = controller.source.id;
|
|
108
|
-
const layout = loadGridLayout(gridId);
|
|
109
|
-
const dirty = controller.buffer.dirtyCount();
|
|
110
|
-
const sourceRef = parseGridTabId(controller.source.id);
|
|
111
|
-
const lossyNote =
|
|
112
|
-
sourceRef?.kind === "collection" || sourceRef?.kind === "pages"
|
|
113
|
-
? html`<span
|
|
114
|
-
class="jx-grid-note"
|
|
115
|
-
title="Saving re-serializes frontmatter; YAML comments and key order are not preserved."
|
|
116
|
-
>
|
|
117
|
-
rewrites frontmatter
|
|
118
|
-
</span>`
|
|
119
|
-
: nothing;
|
|
120
|
-
|
|
121
|
-
return html`
|
|
122
|
-
<div class="jx-grid-toolbar">
|
|
123
|
-
<sp-button
|
|
124
|
-
size="s"
|
|
125
|
-
variant="accent"
|
|
126
|
-
?disabled=${dirty === 0 || state.saving}
|
|
127
|
-
@click=${() => {
|
|
128
|
-
void controller.save();
|
|
129
|
-
}}
|
|
130
|
-
>
|
|
131
|
-
Save${dirty > 0 ? ` (${dirty})` : ""}
|
|
132
|
-
</sp-button>
|
|
133
|
-
<sp-action-button
|
|
134
|
-
size="s"
|
|
135
|
-
quiet
|
|
136
|
-
title="Reload from source"
|
|
137
|
-
?disabled=${state.loading || state.saving}
|
|
138
|
-
@click=${() => {
|
|
139
|
-
void controller.refresh();
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
142
|
-
Refresh
|
|
143
|
-
</sp-action-button>
|
|
144
|
-
<sp-divider size="s" vertical></sp-divider>
|
|
145
|
-
${
|
|
146
|
-
controller.source.capabilities.insert
|
|
147
|
-
? html`<sp-action-button
|
|
148
|
-
size="s"
|
|
149
|
-
quiet
|
|
150
|
-
title="Add a row (saved on Save)"
|
|
151
|
-
@click=${() => controller.addRow()}
|
|
152
|
-
>
|
|
153
|
-
Add Row
|
|
154
|
-
</sp-action-button>`
|
|
155
|
-
: nothing
|
|
156
|
-
}
|
|
157
|
-
${
|
|
158
|
-
controller.source.capabilities.delete
|
|
159
|
-
? html`<sp-action-button
|
|
160
|
-
size="s"
|
|
161
|
-
quiet
|
|
162
|
-
title="Mark selected rows for deletion"
|
|
163
|
-
@click=${() => controller.deleteRows(getView()?.getSelectedRowKeys() ?? [])}
|
|
164
|
-
>
|
|
165
|
-
Delete Rows
|
|
166
|
-
</sp-action-button>`
|
|
167
|
-
: nothing
|
|
168
|
-
}
|
|
169
|
-
<sp-action-button
|
|
170
|
-
size="s"
|
|
171
|
-
quiet
|
|
172
|
-
title="Fill the selected range down from its first row (Ctrl/Cmd-D)"
|
|
173
|
-
@click=${() => getView()?.fillDown()}
|
|
174
|
-
>
|
|
175
|
-
Fill Down
|
|
176
|
-
</sp-action-button>
|
|
177
|
-
<sp-action-button
|
|
178
|
-
size="s"
|
|
179
|
-
quiet
|
|
180
|
-
title="Find & replace across text cells"
|
|
181
|
-
@click=${(e: MouseEvent) => openReplacePopover(controller, e.currentTarget as HTMLElement)}
|
|
182
|
-
>
|
|
183
|
-
Replace
|
|
184
|
-
</sp-action-button>
|
|
185
|
-
<sp-action-button
|
|
186
|
-
size="s"
|
|
187
|
-
quiet
|
|
188
|
-
class="jx-grid-view-button"
|
|
189
|
-
title="Saved views, columns, sort and grouping"
|
|
190
|
-
@click=${(e: MouseEvent) => openViewPopover(controller, panel, e.currentTarget as HTMLElement)}
|
|
191
|
-
>
|
|
192
|
-
${viewButtonLabel(gridId)}
|
|
193
|
-
</sp-action-button>
|
|
194
|
-
<sp-search
|
|
195
|
-
size="s"
|
|
196
|
-
quiet
|
|
197
|
-
placeholder="Filter rows"
|
|
198
|
-
.value=${live(layout?.filter ?? "")}
|
|
199
|
-
@input=${(e: Event) => {
|
|
200
|
-
const term = (e.target as HTMLInputElement).value;
|
|
201
|
-
// The filter is part of the view, so it persists with the rest of it rather than
|
|
202
|
-
// Evaporating on a tab switch and taking a saved view's meaning with it.
|
|
203
|
-
saveGridLayout(gridId, { filter: term });
|
|
204
|
-
getView()?.setSearch(term);
|
|
205
|
-
}}
|
|
206
|
-
@submit=${(e: Event) => e.preventDefault()}
|
|
207
|
-
></sp-search>
|
|
208
|
-
<span class="jx-grid-spacer"></span>
|
|
209
|
-
${groupNoteTpl(controller)} ${lossyNote}
|
|
210
|
-
${controller.source.capabilities.remotePaging ? pagerTpl(controller) : nothing}
|
|
211
|
-
<span class="jx-grid-count">
|
|
212
|
-
${
|
|
213
|
-
state.loading
|
|
214
|
-
? "Loading…"
|
|
215
|
-
: state.error
|
|
216
|
-
? html`<span class="jx-grid-error-text">${state.error}</span>`
|
|
217
|
-
: `${state.total} row${state.total === 1 ? "" : "s"}`
|
|
218
|
-
}
|
|
219
|
-
</span>
|
|
220
|
-
</div>
|
|
221
|
-
`;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ─── Saved views ──────────────────────────────────────────────────────────────
|
|
148
|
+
// ─── Projection ───────────────────────────────────────────────────────────────
|
|
225
149
|
|
|
226
150
|
/**
|
|
227
151
|
* The View button's label: the applied view's name, with a dot when the layout has since drifted.
|
|
@@ -238,206 +162,210 @@ function viewButtonLabel(gridId: string): string {
|
|
|
238
162
|
}
|
|
239
163
|
|
|
240
164
|
/** "Grouped by Status · 3 groups" — the grouping is invisible in the rows, so the toolbar says it. */
|
|
241
|
-
function
|
|
165
|
+
function groupNote(controller: GridController): string {
|
|
242
166
|
const field = controller.state.grouping;
|
|
243
167
|
if (!field) {
|
|
244
|
-
return
|
|
168
|
+
return "";
|
|
245
169
|
}
|
|
246
170
|
const title = controller.state.columns.find((column) => column.field === field)?.title ?? field;
|
|
247
171
|
const count = controller.groups().length;
|
|
248
|
-
return
|
|
249
|
-
|
|
250
|
-
|
|
172
|
+
return `Grouped by ${title} · ${count} group${count === 1 ? "" : "s"}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** The page the reader is on, as "51–100". Only remote-paged sources have one. */
|
|
176
|
+
function pageRange(controller: GridController): string {
|
|
177
|
+
const { state } = controller;
|
|
178
|
+
const limit = state.query.limit ?? 50;
|
|
179
|
+
const offset = state.query.offset ?? 0;
|
|
180
|
+
const from = state.total === 0 ? 0 : offset + 1;
|
|
181
|
+
return `${from}–${Math.min(offset + limit, state.total)}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Everything the frame draws right now, read from the controller, the buffer and the layout. */
|
|
185
|
+
function projectPanel(controller: GridController): GridPanelView {
|
|
186
|
+
const { state } = controller;
|
|
187
|
+
const gridId = controller.source.id;
|
|
188
|
+
const dirty = controller.buffer.dirtyCount();
|
|
189
|
+
const sourceRef = parseGridTabId(gridId);
|
|
190
|
+
const limit = state.query.limit ?? 50;
|
|
191
|
+
const offset = state.query.offset ?? 0;
|
|
192
|
+
const note = groupNote(controller);
|
|
193
|
+
return {
|
|
194
|
+
canDelete: controller.source.capabilities.delete === true,
|
|
195
|
+
canInsert: controller.source.capabilities.insert === true,
|
|
196
|
+
countLabel: `${state.total} row${state.total === 1 ? "" : "s"}`,
|
|
197
|
+
countState: state.loading ? "loading" : state.error ? "error" : "total",
|
|
198
|
+
error: state.error ?? "",
|
|
199
|
+
filter: loadGridLayout(gridId)?.filter ?? "",
|
|
200
|
+
groupNote: note,
|
|
201
|
+
groupState: note === "" ? "hidden" : "shown",
|
|
202
|
+
lossyState:
|
|
203
|
+
sourceRef?.kind === "collection" || sourceRef?.kind === "pages" ? "shown" : "hidden",
|
|
204
|
+
nextDisabled: offset + limit >= state.total || state.loading,
|
|
205
|
+
pageRange: pageRange(controller),
|
|
206
|
+
pagerState: controller.source.capabilities.remotePaging ? "shown" : "hidden",
|
|
207
|
+
prevDisabled: offset === 0 || state.loading,
|
|
208
|
+
refreshDisabled: state.loading || state.saving,
|
|
209
|
+
saveDisabled: dirty === 0 || state.saving,
|
|
210
|
+
saveLabel: dirty > 0 ? `Save (${dirty})` : "Save",
|
|
211
|
+
view: "grid",
|
|
212
|
+
viewLabel: viewButtonLabel(gridId),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─── Saved views ──────────────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
/** What the View panel shows: the saved views, and the four facets one of them is made of. */
|
|
219
|
+
function projectViews(controller: GridController, at: { x: number; y: number }): GridViewsView {
|
|
220
|
+
const gridId = controller.source.id;
|
|
221
|
+
const layout = loadGridLayout(gridId) ?? {};
|
|
222
|
+
const saved = listSavedViews(gridId);
|
|
223
|
+
const activeName = activeViewName(gridId);
|
|
224
|
+
const hidden = new Set(layout.hidden);
|
|
225
|
+
const sort = layout.sort ?? null;
|
|
226
|
+
const { columns } = controller.state;
|
|
227
|
+
const fields = columns.map((column) => ({ label: column.title, value: column.field }));
|
|
228
|
+
return {
|
|
229
|
+
columns: columns.map((column) => ({
|
|
230
|
+
field: column.field,
|
|
231
|
+
title: column.title,
|
|
232
|
+
visible: !hidden.has(column.field),
|
|
233
|
+
})),
|
|
234
|
+
dirOptions: [
|
|
235
|
+
{ label: "Ascending", value: "asc" },
|
|
236
|
+
{ label: "Descending", value: "desc" },
|
|
237
|
+
],
|
|
238
|
+
fieldOptions: [{ label: "Source order", value: "" }, ...fields],
|
|
239
|
+
groupField: controller.state.grouping ?? "",
|
|
240
|
+
groupOptions: [{ label: "Ungrouped", value: "" }, ...fields],
|
|
241
|
+
saveLabel: activeName ? "Save as…" : "Save view…",
|
|
242
|
+
sortDir: sort?.dir ?? "asc",
|
|
243
|
+
sortDirDisabled: sort === null,
|
|
244
|
+
sortField: sort?.field ?? "",
|
|
245
|
+
views: saved.map((view) => ({
|
|
246
|
+
active: view.name === activeName ? "true" : "false",
|
|
247
|
+
current: view.name === activeName ? "true" : "false",
|
|
248
|
+
deleteLabel: `Delete the saved view ${view.name}`,
|
|
249
|
+
key: view.name,
|
|
250
|
+
name: view.name,
|
|
251
|
+
})),
|
|
252
|
+
viewsState: saved.length === 0 ? "empty" : "listed",
|
|
253
|
+
x: at.x,
|
|
254
|
+
y: at.y,
|
|
255
|
+
};
|
|
251
256
|
}
|
|
252
257
|
|
|
253
258
|
/**
|
|
254
|
-
* The View
|
|
259
|
+
* The View panel: saved views, then the four facets one of them is made of.
|
|
255
260
|
*
|
|
256
261
|
* One control rather than four, because the chrome budget is a cap on named things in the toolbar
|
|
257
262
|
* (plan §2, principle 9) and these four are only ever adjusted together. Each edit writes the
|
|
258
263
|
* working layout and applies immediately — there is no Apply button, so there is no state in which
|
|
259
|
-
* the
|
|
264
|
+
* the panel shows something the grid is not already doing.
|
|
260
265
|
*/
|
|
261
|
-
function openViewPopover(
|
|
266
|
+
function openViewPopover(
|
|
267
|
+
controller: GridController,
|
|
268
|
+
panel: ActiveGridPanel,
|
|
269
|
+
anchor: HTMLElement,
|
|
270
|
+
): void {
|
|
271
|
+
/* A second press on the button TOGGLES. The platform does not light-dismiss a popover from its
|
|
272
|
+
own invoker — that is what passing the anchor as the popover's source buys — so without this
|
|
273
|
+
the button stacks a second panel into the same layer slot, and the first one's `toggle` tears
|
|
274
|
+
down the slot the second was just mounted into. */
|
|
275
|
+
if (panel.views?.isOpen()) {
|
|
276
|
+
panel.views.close();
|
|
277
|
+
panel.views = null;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
262
280
|
const rect = rectOf(anchor);
|
|
281
|
+
const at = { x: Math.max(4, rect.left), y: rect.bottom + 4 };
|
|
282
|
+
panel.viewsAt = at;
|
|
263
283
|
const gridId = controller.source.id;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
region: "grid/views",
|
|
269
|
-
});
|
|
270
|
-
const rerender = () => handle.update(body());
|
|
271
|
-
const close = () => handle.dismiss();
|
|
284
|
+
const close = () => {
|
|
285
|
+
panel.views?.close();
|
|
286
|
+
panel.views = null;
|
|
287
|
+
};
|
|
272
288
|
|
|
273
|
-
/** Persist one facet, put it on screen, and refresh both the
|
|
289
|
+
/** Persist one facet, put it on screen, and refresh both the panel and the toolbar. */
|
|
274
290
|
const change = (patch: GridLayout, apply: () => void) => {
|
|
275
291
|
saveGridLayout(gridId, patch);
|
|
276
292
|
apply();
|
|
277
293
|
panel.bump();
|
|
278
|
-
rerender();
|
|
279
294
|
};
|
|
280
295
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const hidden = new Set(layout.hidden);
|
|
286
|
-
const sort = layout.sort ?? null;
|
|
287
|
-
const { columns } = controller.state;
|
|
288
|
-
|
|
289
|
-
const setSort = (spec: GridSortSpec | null) =>
|
|
290
|
-
change({ sort: spec }, () => {
|
|
291
|
-
void controller.setSort(spec);
|
|
292
|
-
});
|
|
296
|
+
const setSort = (spec: GridSortSpec | null) =>
|
|
297
|
+
change({ sort: spec }, () => {
|
|
298
|
+
void controller.setSort(spec);
|
|
299
|
+
});
|
|
293
300
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const confirmed = await showConfirmDialog(
|
|
323
|
-
"Delete View",
|
|
324
|
-
`Delete the saved view "${view.name}"? The grid keeps its current layout.`,
|
|
325
|
-
{ confirmLabel: "Delete", destructive: true },
|
|
326
|
-
);
|
|
327
|
-
if (confirmed && deleteSavedView(gridId, view.name)) {
|
|
328
|
-
panel.bump();
|
|
329
|
-
rerender();
|
|
330
|
-
}
|
|
331
|
-
}}
|
|
332
|
-
>✕</sp-action-button
|
|
333
|
-
>
|
|
334
|
-
</div>`,
|
|
335
|
-
)
|
|
301
|
+
panel.views = openGridViewsSurface(
|
|
302
|
+
projectViews(controller, at),
|
|
303
|
+
{
|
|
304
|
+
applyView: (name) => {
|
|
305
|
+
panel.applyLayout(applySavedView(gridId, name));
|
|
306
|
+
close();
|
|
307
|
+
},
|
|
308
|
+
deleteView: (name) => {
|
|
309
|
+
void confirmDeleteView(gridId, name, panel);
|
|
310
|
+
},
|
|
311
|
+
dismissed: () => {
|
|
312
|
+
panel.views = null;
|
|
313
|
+
},
|
|
314
|
+
reset: () => {
|
|
315
|
+
resetGridLayout(gridId);
|
|
316
|
+
panel.applyLayout(null);
|
|
317
|
+
close();
|
|
318
|
+
},
|
|
319
|
+
saveView: () => {
|
|
320
|
+
void promptSaveView(controller, panel);
|
|
321
|
+
},
|
|
322
|
+
setGroup: (field) => {
|
|
323
|
+
change({ groupBy: field || null }, () => controller.setGrouping(field || null));
|
|
324
|
+
},
|
|
325
|
+
setSortDir: (dir) => {
|
|
326
|
+
const sort = loadGridLayout(gridId)?.sort ?? null;
|
|
327
|
+
if (sort) {
|
|
328
|
+
setSort({ dir: dir === "desc" ? "desc" : "asc", field: sort.field });
|
|
336
329
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
close();
|
|
355
|
-
}}
|
|
356
|
-
>
|
|
357
|
-
Reset
|
|
358
|
-
</sp-button>
|
|
359
|
-
</div>
|
|
360
|
-
|
|
361
|
-
<div class="jx-grid-cell-popover-title">Columns</div>
|
|
362
|
-
${columns.map(
|
|
363
|
-
(column) => html`<label class="jx-grid-view-check">
|
|
364
|
-
<input
|
|
365
|
-
type="checkbox"
|
|
366
|
-
.checked=${!hidden.has(column.field)}
|
|
367
|
-
data-field=${column.field}
|
|
368
|
-
@change=${(e: Event) => {
|
|
369
|
-
const next = new Set(hidden);
|
|
370
|
-
if ((e.target as HTMLInputElement).checked) {
|
|
371
|
-
next.delete(column.field);
|
|
372
|
-
} else {
|
|
373
|
-
next.add(column.field);
|
|
374
|
-
}
|
|
375
|
-
change({ hidden: [...next] }, () => panel.remount());
|
|
376
|
-
}}
|
|
377
|
-
/>
|
|
378
|
-
${column.title}
|
|
379
|
-
</label>`,
|
|
380
|
-
)}
|
|
381
|
-
|
|
382
|
-
<div class="jx-grid-cell-popover-title">Sort</div>
|
|
383
|
-
<select
|
|
384
|
-
class="jx-grid-input jx-grid-sort-field"
|
|
385
|
-
@change=${(e: Event) => {
|
|
386
|
-
const field = (e.target as HTMLSelectElement).value;
|
|
387
|
-
setSort(field === "" ? null : { dir: sort?.dir ?? "asc", field });
|
|
388
|
-
}}
|
|
389
|
-
>
|
|
390
|
-
<option value="" ?selected=${!sort}>Source order</option>
|
|
391
|
-
${columns.map(
|
|
392
|
-
(column) =>
|
|
393
|
-
html`<option value=${column.field} ?selected=${sort?.field === column.field}>
|
|
394
|
-
${column.title}
|
|
395
|
-
</option>`,
|
|
396
|
-
)}
|
|
397
|
-
</select>
|
|
398
|
-
<select
|
|
399
|
-
class="jx-grid-input jx-grid-sort-dir"
|
|
400
|
-
?disabled=${!sort}
|
|
401
|
-
@change=${(e: Event) => {
|
|
402
|
-
const dir = (e.target as HTMLSelectElement).value === "desc" ? "desc" : "asc";
|
|
403
|
-
if (sort) {
|
|
404
|
-
setSort({ dir, field: sort.field });
|
|
405
|
-
}
|
|
406
|
-
}}
|
|
407
|
-
>
|
|
408
|
-
<option value="asc" ?selected=${sort?.dir !== "desc"}>Ascending</option>
|
|
409
|
-
<option value="desc" ?selected=${sort?.dir === "desc"}>Descending</option>
|
|
410
|
-
</select>
|
|
411
|
-
|
|
412
|
-
<div class="jx-grid-cell-popover-title">Group by</div>
|
|
413
|
-
<select
|
|
414
|
-
class="jx-grid-input jx-grid-group-field"
|
|
415
|
-
@change=${(e: Event) => {
|
|
416
|
-
const field = (e.target as HTMLSelectElement).value || null;
|
|
417
|
-
change({ groupBy: field }, () => controller.setGrouping(field));
|
|
418
|
-
}}
|
|
419
|
-
>
|
|
420
|
-
<option value="" ?selected=${!controller.state.grouping}>Ungrouped</option>
|
|
421
|
-
${columns.map(
|
|
422
|
-
(column) =>
|
|
423
|
-
html`<option
|
|
424
|
-
value=${column.field}
|
|
425
|
-
?selected=${controller.state.grouping === column.field}
|
|
426
|
-
>
|
|
427
|
-
${column.title}
|
|
428
|
-
</option>`,
|
|
429
|
-
)}
|
|
430
|
-
</select>
|
|
431
|
-
</div>
|
|
432
|
-
</sp-popover>`;
|
|
433
|
-
}
|
|
330
|
+
},
|
|
331
|
+
setSortField: (field) => {
|
|
332
|
+
const sort = loadGridLayout(gridId)?.sort ?? null;
|
|
333
|
+
setSort(field === "" ? null : { dir: sort?.dir ?? "asc", field });
|
|
334
|
+
},
|
|
335
|
+
toggleColumn: (field, visible) => {
|
|
336
|
+
const hidden = new Set(loadGridLayout(gridId)?.hidden);
|
|
337
|
+
if (visible) {
|
|
338
|
+
hidden.delete(field);
|
|
339
|
+
} else {
|
|
340
|
+
hidden.add(field);
|
|
341
|
+
}
|
|
342
|
+
change({ hidden: [...hidden] }, () => panel.remount());
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
anchor,
|
|
346
|
+
);
|
|
434
347
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
348
|
+
/* The button's label is derived from storage, and storage is ALSO written by the engine — a
|
|
349
|
+
column drag or resize goes straight to `saveGridLayout` from `grid-view.ts`. Refreshing the
|
|
350
|
+
toolbar as the control opens is what stops the drift dot lagging a resize by one interaction,
|
|
351
|
+
and it is what puts the freshly-read projection into the panel that just opened. */
|
|
439
352
|
panel.bump();
|
|
440
|
-
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** Ask before forgetting a named view, and re-project when one is actually gone. */
|
|
356
|
+
async function confirmDeleteView(
|
|
357
|
+
gridId: string,
|
|
358
|
+
name: string,
|
|
359
|
+
panel: ActiveGridPanel,
|
|
360
|
+
): Promise<void> {
|
|
361
|
+
const confirmed = await showConfirmDialog(
|
|
362
|
+
"Delete View",
|
|
363
|
+
`Delete the saved view "${name}"? The grid keeps its current layout.`,
|
|
364
|
+
{ confirmLabel: "Delete", destructive: true },
|
|
365
|
+
);
|
|
366
|
+
if (confirmed && deleteSavedView(gridId, name)) {
|
|
367
|
+
panel.bump();
|
|
368
|
+
}
|
|
441
369
|
}
|
|
442
370
|
|
|
443
371
|
/**
|
|
@@ -450,7 +378,6 @@ function openViewPopover(controller: GridController, panel: ActiveGridPanel, anc
|
|
|
450
378
|
async function promptSaveView(
|
|
451
379
|
controller: GridController,
|
|
452
380
|
panel: ActiveGridPanel,
|
|
453
|
-
rerender: () => void,
|
|
454
381
|
): Promise<string | null> {
|
|
455
382
|
const gridId = controller.source.id;
|
|
456
383
|
const name = await showPromptDialog("Save Grid View", {
|
|
@@ -473,136 +400,50 @@ async function promptSaveView(
|
|
|
473
400
|
return null;
|
|
474
401
|
}
|
|
475
402
|
panel.bump();
|
|
476
|
-
rerender();
|
|
477
403
|
return saved.name;
|
|
478
404
|
}
|
|
479
405
|
|
|
480
|
-
/** Find & replace
|
|
481
|
-
function openReplacePopover(
|
|
482
|
-
const rect = rectOf(anchor);
|
|
483
|
-
let find = "";
|
|
484
|
-
let replace = "";
|
|
485
|
-
const handle = renderPopover(
|
|
486
|
-
html`<sp-popover
|
|
487
|
-
open
|
|
488
|
-
class="jx-grid-replace-popover"
|
|
489
|
-
style="position:fixed;z-index:10000;left:${Math.max(4, rect.left)}px;top:${rect.bottom + 4}px"
|
|
490
|
-
>
|
|
491
|
-
<div class="jx-grid-cell-popover-body">
|
|
492
|
-
<div class="jx-grid-cell-popover-title">Find & Replace</div>
|
|
493
|
-
<input
|
|
494
|
-
class="jx-grid-input"
|
|
495
|
-
placeholder="Find…"
|
|
496
|
-
@input=${(e: Event) => (find = (e.target as HTMLInputElement).value)}
|
|
497
|
-
/>
|
|
498
|
-
<input
|
|
499
|
-
class="jx-grid-input"
|
|
500
|
-
placeholder="Replace with…"
|
|
501
|
-
@input=${(e: Event) => (replace = (e.target as HTMLInputElement).value)}
|
|
502
|
-
/>
|
|
503
|
-
<div class="jx-grid-cell-popover-actions">
|
|
504
|
-
<sp-button
|
|
505
|
-
size="s"
|
|
506
|
-
variant="accent"
|
|
507
|
-
@click=${() => {
|
|
508
|
-
const changed = controller.replaceAll(find, replace);
|
|
509
|
-
if (changed === 0) {
|
|
510
|
-
notify.info("No matches.", { key: "grid.replaceAll" });
|
|
511
|
-
} else {
|
|
512
|
-
notify.success(
|
|
513
|
-
`Replaced in ${changed} cell${changed === 1 ? "" : "s"} — save to apply.`,
|
|
514
|
-
{ action: "file.save", key: "grid.replaceAll" },
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
if (changed > 0) {
|
|
518
|
-
handle.dismiss();
|
|
519
|
-
}
|
|
520
|
-
}}
|
|
521
|
-
>
|
|
522
|
-
Replace All
|
|
523
|
-
</sp-button>
|
|
524
|
-
<sp-button size="s" variant="secondary" @click=${() => handle.dismiss()}>
|
|
525
|
-
Cancel
|
|
526
|
-
</sp-button>
|
|
527
|
-
</div>
|
|
528
|
-
</div>
|
|
529
|
-
</sp-popover>`,
|
|
530
|
-
{ dismissOnOutsideClick: true },
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/** Prev/Next pager for remote-paged sources (connector tables). */
|
|
535
|
-
function pagerTpl(controller: GridController) {
|
|
536
|
-
const { state } = controller;
|
|
537
|
-
const limit = state.query.limit ?? 50;
|
|
538
|
-
const offset = state.query.offset ?? 0;
|
|
539
|
-
const from = state.total === 0 ? 0 : offset + 1;
|
|
540
|
-
const to = Math.min(offset + limit, state.total);
|
|
541
|
-
return html`
|
|
542
|
-
<sp-action-button
|
|
543
|
-
size="s"
|
|
544
|
-
quiet
|
|
545
|
-
title="Previous page"
|
|
546
|
-
?disabled=${offset === 0 || state.loading}
|
|
547
|
-
@click=${() => {
|
|
548
|
-
void controller.setQuery({
|
|
549
|
-
...state.query,
|
|
550
|
-
limit,
|
|
551
|
-
offset: Math.max(0, offset - limit),
|
|
552
|
-
});
|
|
553
|
-
}}
|
|
554
|
-
>
|
|
555
|
-
‹ Prev
|
|
556
|
-
</sp-action-button>
|
|
557
|
-
<span class="jx-grid-count">${from}–${to}</span>
|
|
558
|
-
<sp-action-button
|
|
559
|
-
size="s"
|
|
560
|
-
quiet
|
|
561
|
-
title="Next page"
|
|
562
|
-
?disabled=${offset + limit >= state.total || state.loading}
|
|
563
|
-
@click=${() => {
|
|
564
|
-
void controller.setQuery({ ...state.query, limit, offset: offset + limit });
|
|
565
|
-
}}
|
|
566
|
-
>
|
|
567
|
-
Next ›
|
|
568
|
-
</sp-action-button>
|
|
569
|
-
`;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function shellTpl(
|
|
406
|
+
/** Find & replace panel — buffers all replacements as one undo group. */
|
|
407
|
+
function openReplacePopover(
|
|
573
408
|
controller: GridController,
|
|
574
409
|
panel: ActiveGridPanel,
|
|
575
|
-
|
|
576
|
-
) {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
()
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
410
|
+
anchor: HTMLElement,
|
|
411
|
+
): void {
|
|
412
|
+
if (panel.replace?.isOpen()) {
|
|
413
|
+
panel.replace.close();
|
|
414
|
+
panel.replace = null;
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const rect = rectOf(anchor);
|
|
418
|
+
panel.replace = openGridReplaceSurface(
|
|
419
|
+
{ x: Math.max(4, rect.left), y: rect.bottom + 4 },
|
|
420
|
+
{
|
|
421
|
+
dismissed: () => {
|
|
422
|
+
panel.replace = null;
|
|
423
|
+
},
|
|
424
|
+
replaceAll: (find, replace) => {
|
|
425
|
+
const changed = controller.replaceAll(find, replace);
|
|
426
|
+
if (changed === 0) {
|
|
427
|
+
notify.info("No matches.", { key: "grid.replaceAll" });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
notify.success(`Replaced in ${changed} cell${changed === 1 ? "" : "s"} — save to apply.`, {
|
|
431
|
+
action: "file.save",
|
|
432
|
+
key: "grid.replaceAll",
|
|
433
|
+
});
|
|
434
|
+
panel.replace?.close();
|
|
435
|
+
panel.replace = null;
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
anchor,
|
|
439
|
+
);
|
|
599
440
|
}
|
|
600
441
|
|
|
601
442
|
/**
|
|
602
|
-
* Render the grid surface for a tab
|
|
443
|
+
* Render the grid surface for a tab onto a pane's stage. Re-entrant: same-tab calls while the panel
|
|
603
444
|
* is live are no-ops (the panel's own effect keeps it fresh).
|
|
604
445
|
*
|
|
605
|
-
* @param {
|
|
446
|
+
* @param {CanvasSurface} surface
|
|
606
447
|
* @param {Tab} tab
|
|
607
448
|
*/
|
|
608
449
|
export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
@@ -620,17 +461,15 @@ export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
|
620
461
|
void controller.load();
|
|
621
462
|
}
|
|
622
463
|
if (!controller) {
|
|
623
|
-
|
|
464
|
+
_missing.set(paneId, mountGridPanelSurface(canvasWrap, missingView(), missingActions()));
|
|
624
465
|
return;
|
|
625
466
|
}
|
|
626
467
|
|
|
627
468
|
const scope = effectScope();
|
|
628
|
-
|
|
629
|
-
|
|
469
|
+
/* `localStorage` is not reactive and neither is the engine, so the surface needs something that
|
|
470
|
+
is: a saved-view edit, or the host node arriving, bumps this and the panel's one effect
|
|
471
|
+
re-projects like any other change. */
|
|
630
472
|
const local = reactive({ views: 0 });
|
|
631
|
-
/* Named `controller` everywhere else in this file; this alias exists so the closures below
|
|
632
|
-
capture it by a short name. NOT `live` — that is lit's directive, imported above and used
|
|
633
|
-
in shellTpl, and a local of the same name shadows it. */
|
|
634
473
|
const engine = controller;
|
|
635
474
|
let hostEl: HTMLElement | null = null;
|
|
636
475
|
|
|
@@ -645,21 +484,72 @@ export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
|
645
484
|
bump() {
|
|
646
485
|
local.views += 1;
|
|
647
486
|
},
|
|
487
|
+
paneId,
|
|
648
488
|
remount() {
|
|
649
489
|
panel.view?.destroy();
|
|
650
490
|
panel.view = hostEl ? createGridView(hostEl, engine) : null;
|
|
651
491
|
},
|
|
652
|
-
|
|
492
|
+
replace: null,
|
|
653
493
|
scope,
|
|
494
|
+
surface: null,
|
|
654
495
|
tabId: tab.id,
|
|
655
496
|
view: null,
|
|
497
|
+
views: null,
|
|
498
|
+
viewsAt: { x: 0, y: 0 },
|
|
656
499
|
wrap: canvasWrap,
|
|
657
500
|
};
|
|
658
501
|
_active.set(paneId, panel);
|
|
659
502
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
503
|
+
panel.surface = mountGridPanelSurface(canvasWrap, projectPanel(engine), {
|
|
504
|
+
addRow: () => engine.addRow(),
|
|
505
|
+
deleteRows: () => engine.deleteRows(panel.view?.getSelectedRowKeys() ?? []),
|
|
506
|
+
fillDown: () => panel.view?.fillDown(),
|
|
507
|
+
gridHost: (element) => {
|
|
508
|
+
hostEl = element;
|
|
509
|
+
},
|
|
510
|
+
nextPage: () => {
|
|
511
|
+
const limit = engine.state.query.limit ?? 50;
|
|
512
|
+
void engine.setQuery({
|
|
513
|
+
...engine.state.query,
|
|
514
|
+
limit,
|
|
515
|
+
offset: (engine.state.query.offset ?? 0) + limit,
|
|
516
|
+
});
|
|
517
|
+
},
|
|
518
|
+
openReplace: (element) => {
|
|
519
|
+
openReplacePopover(engine, panel, element);
|
|
520
|
+
},
|
|
521
|
+
openViews: (element) => {
|
|
522
|
+
openViewPopover(engine, panel, element);
|
|
523
|
+
},
|
|
524
|
+
prevPage: () => {
|
|
525
|
+
const limit = engine.state.query.limit ?? 50;
|
|
526
|
+
void engine.setQuery({
|
|
527
|
+
...engine.state.query,
|
|
528
|
+
limit,
|
|
529
|
+
offset: Math.max(0, (engine.state.query.offset ?? 0) - limit),
|
|
530
|
+
});
|
|
531
|
+
},
|
|
532
|
+
refresh: () => {
|
|
533
|
+
void engine.refresh();
|
|
534
|
+
},
|
|
535
|
+
save: () => {
|
|
536
|
+
void engine.save();
|
|
537
|
+
},
|
|
538
|
+
setFilter: (term) => {
|
|
539
|
+
// The filter is part of the view, so it persists with the rest of it rather than
|
|
540
|
+
// Evaporating on a tab switch and taking a saved view's meaning with it.
|
|
541
|
+
saveGridLayout(engine.source.id, { filter: term });
|
|
542
|
+
panel.view?.setSearch(term);
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
/* The host node is announced during the mount, one reconcile step before it is in the page. The
|
|
546
|
+
engine is built from the EFFECT rather than from there, so it is never handed a detached node —
|
|
547
|
+
`ready` is the document saying the frame has landed. */
|
|
548
|
+
void panel.surface.ready.then(() => {
|
|
549
|
+
if (activeIn(paneId) === panel) {
|
|
550
|
+
panel.bump();
|
|
551
|
+
}
|
|
552
|
+
});
|
|
663
553
|
|
|
664
554
|
// The stored sort, grouping and filter are applied ONCE, when the engine first exists. Re-running
|
|
665
555
|
// Them on every render would fight the author: every ad-hoc header sort would be undone by the
|
|
@@ -672,30 +562,26 @@ export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
|
672
562
|
return;
|
|
673
563
|
}
|
|
674
564
|
// Track everything the toolbar shows.
|
|
675
|
-
void
|
|
676
|
-
void
|
|
677
|
-
void
|
|
678
|
-
void
|
|
679
|
-
void
|
|
680
|
-
void
|
|
681
|
-
void
|
|
565
|
+
void engine.state.loading;
|
|
566
|
+
void engine.state.saving;
|
|
567
|
+
void engine.state.error;
|
|
568
|
+
void engine.state.total;
|
|
569
|
+
void engine.state.query.offset;
|
|
570
|
+
void engine.state.grouping;
|
|
571
|
+
void engine.buffer.dirtyCount();
|
|
682
572
|
void local.views;
|
|
683
573
|
|
|
684
|
-
|
|
574
|
+
panel.surface?.update(projectPanel(engine));
|
|
575
|
+
panel.views?.update(projectViews(engine, panel.viewsAt));
|
|
685
576
|
|
|
686
577
|
// Create the engine once the columns exist and the host div is in the DOM.
|
|
687
|
-
if (
|
|
688
|
-
|
|
689
|
-
!controller.state.loading &&
|
|
690
|
-
controller.state.columns.length > 0 &&
|
|
691
|
-
hostEl
|
|
692
|
-
) {
|
|
693
|
-
panel.view = createGridView(hostEl, controller);
|
|
578
|
+
if (!panel.view && !engine.state.loading && engine.state.columns.length > 0 && hostEl) {
|
|
579
|
+
panel.view = createGridView(hostEl, engine);
|
|
694
580
|
if (!restored) {
|
|
695
581
|
restored = true;
|
|
696
|
-
const layout = loadGridLayout(
|
|
697
|
-
void
|
|
698
|
-
|
|
582
|
+
const layout = loadGridLayout(engine.source.id);
|
|
583
|
+
void engine.setSort(layout?.sort ?? null);
|
|
584
|
+
engine.setGrouping(layout?.groupBy ?? null);
|
|
699
585
|
panel.view.setSearch(layout?.filter ?? "");
|
|
700
586
|
}
|
|
701
587
|
}
|
|
@@ -703,6 +589,48 @@ export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
|
703
589
|
});
|
|
704
590
|
}
|
|
705
591
|
|
|
592
|
+
/** The frame a tab with no grid source draws: one sentence, and no controls to press. */
|
|
593
|
+
function missingView(): GridPanelView {
|
|
594
|
+
return {
|
|
595
|
+
canDelete: false,
|
|
596
|
+
canInsert: false,
|
|
597
|
+
countLabel: "",
|
|
598
|
+
countState: "total",
|
|
599
|
+
error: "",
|
|
600
|
+
filter: "",
|
|
601
|
+
groupNote: "",
|
|
602
|
+
groupState: "hidden",
|
|
603
|
+
lossyState: "hidden",
|
|
604
|
+
nextDisabled: true,
|
|
605
|
+
pageRange: "",
|
|
606
|
+
pagerState: "hidden",
|
|
607
|
+
prevDisabled: true,
|
|
608
|
+
refreshDisabled: true,
|
|
609
|
+
saveDisabled: true,
|
|
610
|
+
saveLabel: "Save",
|
|
611
|
+
view: "missing",
|
|
612
|
+
viewLabel: "View",
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** No control is drawn in the missing state, so none of these can be reached. */
|
|
617
|
+
function missingActions(): GridPanelActions {
|
|
618
|
+
const nothing = () => {};
|
|
619
|
+
return {
|
|
620
|
+
addRow: nothing,
|
|
621
|
+
deleteRows: nothing,
|
|
622
|
+
fillDown: nothing,
|
|
623
|
+
gridHost: nothing,
|
|
624
|
+
nextPage: nothing,
|
|
625
|
+
openReplace: nothing,
|
|
626
|
+
openViews: nothing,
|
|
627
|
+
prevPage: nothing,
|
|
628
|
+
refresh: nothing,
|
|
629
|
+
save: nothing,
|
|
630
|
+
setFilter: nothing,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
706
634
|
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
707
635
|
|
|
708
636
|
/**
|
|
@@ -721,7 +649,7 @@ function activeGridSurface(): { controller: GridController; panel: ActiveGridPan
|
|
|
721
649
|
return { controller, panel: active };
|
|
722
650
|
}
|
|
723
651
|
|
|
724
|
-
/** The saved-view verbs. Every one of them is also a control in the View
|
|
652
|
+
/** The saved-view verbs. Every one of them is also a control in the View panel. */
|
|
725
653
|
function requireSurface(commandId: string) {
|
|
726
654
|
const surface = activeGridSurface();
|
|
727
655
|
if (!surface) {
|
|
@@ -734,7 +662,7 @@ function requireSurface(commandId: string) {
|
|
|
734
662
|
* Saved views, as commands.
|
|
735
663
|
*
|
|
736
664
|
* A view is named, so it is addressable, so it belongs in the palette, on the `__jxAutomation`
|
|
737
|
-
* surface and in front of the assistant — not only behind a
|
|
665
|
+
* surface and in front of the assistant — not only behind a panel the author has to find. The
|
|
738
666
|
* refusals name what the grid actually holds, in the idiom of `collection.editInGrid`: an unknown
|
|
739
667
|
* view lists the views that exist rather than doing nothing.
|
|
740
668
|
*
|
|
@@ -765,14 +693,10 @@ export function gridViewCommands(): AnyCommand[] {
|
|
|
765
693
|
requires: "a grid on screen",
|
|
766
694
|
when: (ctx) => ctx.project.open,
|
|
767
695
|
enablement: () => activeGridSurface() !== null,
|
|
768
|
-
aiTool:
|
|
769
|
-
description:
|
|
770
|
-
"Save the open grid's columns, sort, grouping and filter as a named view. Prompts for the name.",
|
|
771
|
-
name: "save_grid_view",
|
|
772
|
-
},
|
|
696
|
+
/* No `aiTool`, by §12.4's second deletion rule: it prompts for a name. */
|
|
773
697
|
run: async () => {
|
|
774
698
|
const { controller, panel } = requireSurface("grid.saveView");
|
|
775
|
-
await promptSaveView(controller, panel
|
|
699
|
+
await promptSaveView(controller, panel);
|
|
776
700
|
},
|
|
777
701
|
title: "Save Grid View…",
|
|
778
702
|
},
|
|
@@ -786,10 +710,7 @@ export function gridViewCommands(): AnyCommand[] {
|
|
|
786
710
|
requires: "a grid on screen",
|
|
787
711
|
when: (ctx) => ctx.project.open,
|
|
788
712
|
enablement: () => activeGridSurface() !== null,
|
|
789
|
-
aiTool:
|
|
790
|
-
description: "Apply a saved view to the open grid by name.",
|
|
791
|
-
name: "apply_grid_view",
|
|
792
|
-
},
|
|
713
|
+
/* No `aiTool`, by §12.4's first deletion rule: chrome. */
|
|
793
714
|
run: (_ctx, args) => {
|
|
794
715
|
const { gridId, name } = viewNameArg("grid.applyView", args);
|
|
795
716
|
const { panel } = requireSurface("grid.applyView");
|